@dstackai/sqircle 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqircle.js","names":[],"sources":["../src/squircle/geometry.ts","../src/squircle/palettes.ts","../src/squircle/SquircleScene.tsx","../src/squircle/codeExport.ts","../src/squircle/SquircleEditor.tsx"],"sourcesContent":["import type { SquircleGeometryConfig, SquircleLayerConfig, SquirclePoint } from \"./types\";\n\nexport const DEFAULT_GEOMETRY = {\n width: 800,\n viewBoxHeight: 480,\n exponent: 12,\n samples: 160,\n prismHeight: 10,\n angleDegrees: 20,\n inlayScale: 0.6\n} as const;\n\ninterface SampledPoint extends SquirclePoint {\n nx: number;\n ny: number;\n}\n\ninterface Bounds {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n}\n\nexport interface SquircleGeometry {\n config: Required<Omit<SquircleGeometryConfig, \"halfSize\" | \"center\">> & {\n halfSize: number;\n center: SquirclePoint;\n };\n cosA: number;\n sinA: number;\n topPoints: SquirclePoint[];\n bottomPoints: SquirclePoint[];\n wallPoints: SquirclePoint[];\n hiddenPoints: SquirclePoint[];\n inlayPoints: SquirclePoint[];\n labelTransform: string;\n topBounds: Bounds;\n sideBounds: Bounds;\n viewBox: string;\n}\n\nexport function createSquircleGeometry(input: SquircleGeometryConfig = {}): SquircleGeometry {\n const exponent = input.exponent ?? DEFAULT_GEOMETRY.exponent;\n const samples = input.samples ?? DEFAULT_GEOMETRY.samples;\n const width = input.width ?? DEFAULT_GEOMETRY.width;\n const viewBoxHeight = input.viewBoxHeight ?? DEFAULT_GEOMETRY.viewBoxHeight;\n const prismHeight = input.prismHeight ?? DEFAULT_GEOMETRY.prismHeight;\n const angleDegrees = input.angleDegrees ?? DEFAULT_GEOMETRY.angleDegrees;\n const inlayScale = input.inlayScale ?? DEFAULT_GEOMETRY.inlayScale;\n const angle = (angleDegrees * Math.PI) / 180;\n const cosA = Math.cos(angle);\n const sinA = Math.sin(angle);\n\n const unit = sampleSuperellipse(1, exponent, samples);\n const unitScreenX = unit.map((point) => (point.x - point.y) * cosA);\n const unitWidth = Math.max(...unitScreenX) - Math.min(...unitScreenX);\n const halfSize = input.halfSize ?? width / unitWidth;\n const rawPoints = sampleSuperellipse(halfSize, exponent, samples);\n const rawScreenX = rawPoints.map((point) => (point.x - point.y) * cosA);\n const rawScreenY = rawPoints.map((point) => (point.x + point.y) * sinA);\n const center = input.center ?? {\n x: -Math.min(...rawScreenX),\n y: prismHeight - Math.min(...rawScreenY)\n };\n\n const topPoints = rawPoints.map((point) => project(point, prismHeight, center, cosA, sinA));\n const bottomPoints = rawPoints.map((point) => project(point, 0, center, cosA, sinA));\n const frontRun = longestCircularRun(rawPoints.map((point) => point.nx + point.ny >= 0));\n const backRun = complementRun(frontRun, samples);\n const wallTop = collectCircular(topPoints, frontRun);\n const wallBottom = collectCircular(bottomPoints, frontRun).reverse();\n const wallPoints = [...wallTop, ...wallBottom];\n const hiddenPoints = collectCircular(bottomPoints, backRun);\n const inlayPoints = sampleSuperellipse(halfSize * inlayScale, exponent, samples).map((point) =>\n project(point, prismHeight, center, cosA, sinA)\n );\n const topBounds = boundsFor(topPoints);\n const sideBounds = boundsFor([...topPoints, ...wallPoints]);\n\n return {\n config: {\n width,\n viewBoxHeight,\n exponent,\n samples,\n prismHeight,\n angleDegrees,\n inlayScale,\n halfSize,\n center\n },\n cosA,\n sinA,\n topPoints,\n bottomPoints,\n wallPoints,\n hiddenPoints,\n inlayPoints,\n labelTransform: `matrix(${round(cosA)},${round(sinA)},${round(-cosA)},${round(sinA)},${round(center.x)},${round(center.y - prismHeight)})`,\n topBounds,\n sideBounds,\n viewBox: `0 0 ${round(width)} ${round(viewBoxHeight)}`\n };\n}\n\nexport function pointsToString(points: SquirclePoint[]): string {\n return points.map((point) => `${round(point.x)},${round(point.y)}`).join(\" \");\n}\n\nexport function createSquircleLayers(\n count: number,\n options: {\n gap?: number;\n paletteId?: string;\n material?: \"solid\" | \"transparent\" | \"wireframe\";\n } = {}\n): SquircleLayerConfig[] {\n const gap = options.gap ?? 88;\n const material = options.material ?? \"wireframe\";\n const paletteId = options.paletteId ?? \"15\";\n\n return Array.from({ length: Math.max(0, count) }, (_, index) => ({\n id: `layer-${index + 1}`,\n visible: true,\n offset: { x: 0, y: (count - index - 1) * gap },\n base: { material, paletteId, text: false, dash: false },\n hover: undefined\n }));\n}\n\nexport function reflowLayerOffsets(layers: SquircleLayerConfig[], gap = 88): SquircleLayerConfig[] {\n const count = layers.length;\n return layers.map((layer, index) => ({\n ...layer,\n offset: { x: layer.offset?.x ?? 0, y: (count - index - 1) * gap }\n }));\n}\n\nfunction sampleSuperellipse(halfSize: number, exponent: number, samples: number): SampledPoint[] {\n return Array.from({ length: samples }, (_, index) => {\n const t = (2 * Math.PI * index) / samples;\n const c = Math.cos(t);\n const s = Math.sin(t);\n return {\n x: halfSize * signedPower(c, 2 / exponent),\n y: halfSize * signedPower(s, 2 / exponent),\n nx: signedPower(c, (2 * (exponent - 1)) / exponent),\n ny: signedPower(s, (2 * (exponent - 1)) / exponent)\n };\n });\n}\n\nfunction signedPower(value: number, power: number): number {\n return Math.sign(value) * Math.pow(Math.abs(value), power);\n}\n\nfunction project(point: SquirclePoint, z: number, center: SquirclePoint, cosA: number, sinA: number): SquirclePoint {\n return {\n x: center.x + (point.x - point.y) * cosA,\n y: center.y + (point.x + point.y) * sinA - z\n };\n}\n\nfunction longestCircularRun(flags: boolean[]): { start: number; length: number } {\n const doubled = [...flags, ...flags];\n let best = { start: 0, length: 0 };\n let currentStart = 0;\n let currentLength = 0;\n\n doubled.forEach((flag, index) => {\n if (flag) {\n if (currentLength === 0) currentStart = index;\n currentLength += 1;\n if (currentLength > best.length && currentLength <= flags.length) {\n best = { start: currentStart % flags.length, length: currentLength };\n }\n } else {\n currentLength = 0;\n }\n });\n\n return best.length > 0 ? best : { start: 0, length: flags.length };\n}\n\nfunction complementRun(run: { start: number; length: number }, total: number): { start: number; length: number } {\n return {\n start: (run.start + run.length) % total,\n length: Math.max(0, total - run.length)\n };\n}\n\nfunction collectCircular<T>(items: T[], run: { start: number; length: number }): T[] {\n return Array.from({ length: run.length }, (_, index) => items[(run.start + index) % items.length]);\n}\n\nfunction boundsFor(points: SquirclePoint[]): Bounds {\n const xs = points.map((point) => point.x);\n const ys = points.map((point) => point.y);\n return {\n minX: Math.min(...xs),\n minY: Math.min(...ys),\n maxX: Math.max(...xs),\n maxY: Math.max(...ys)\n };\n}\n\nfunction round(value: number): string {\n return Number(value.toFixed(2)).toString();\n}\n","export interface SquircleGradientStop {\n offset: number;\n color: string;\n}\n\nexport interface SquirclePalette {\n id: string;\n label: string;\n top: SquircleGradientStop[];\n side: SquircleGradientStop[];\n textWire: SquircleGradientStop[];\n labelFill: string;\n topEdge: string;\n sideEdge: string;\n swatch: [string, string];\n}\n\nexport const SQUIRCLE_PALETTES = {\n \"13\": {\n id: \"13\",\n label: \"13 Alpha\",\n top: [\n { offset: 0, color: \"#f5f0ff\" },\n { offset: 0.48, color: \"#b388ff\" },\n { offset: 1, color: \"#0099ff\" }\n ],\n side: [\n { offset: 0, color: \"#8250e6\" },\n { offset: 0.5, color: \"#596ce8\" },\n { offset: 1, color: \"#0084e8\" }\n ],\n textWire: [\n { offset: 0, color: \"#b388ff\" },\n { offset: 0.52, color: \"#5c7fff\" },\n { offset: 1, color: \"#0099ff\" }\n ],\n labelFill: \"#2a1060\",\n topEdge: \"#7c5fd0\",\n sideEdge: \"#2d1466\",\n swatch: [\"#b388ff\", \"#0099ff\"]\n },\n \"14\": {\n id: \"14\",\n label: \"14 Alpha\",\n top: [\n { offset: 0, color: \"#e9fffb\" },\n { offset: 0.44, color: \"#44b9d6\" },\n { offset: 1, color: \"#006ce0\" }\n ],\n side: [\n { offset: 0, color: \"#38acbc\" },\n { offset: 0.52, color: \"#268ccf\" },\n { offset: 1, color: \"#005fc4\" }\n ],\n textWire: [\n { offset: 0, color: \"#44b9d6\" },\n { offset: 0.52, color: \"#2f8fe2\" },\n { offset: 1, color: \"#006ce0\" }\n ],\n labelFill: \"#063f6a\",\n topEdge: \"#237aa8\",\n sideEdge: \"#063f6a\",\n swatch: [\"#44b9d6\", \"#006ce0\"]\n },\n \"15\": {\n id: \"15\",\n label: \"15 Alpha\",\n top: [\n { offset: 0, color: \"#f0fbff\" },\n { offset: 0.34, color: \"#0099ff\" },\n { offset: 0.66, color: \"#5c7fff\" },\n { offset: 1, color: \"#962eff\" }\n ],\n side: [\n { offset: 0, color: \"#008fec\" },\n { offset: 0.34, color: \"#139cff\" },\n { offset: 0.56, color: \"#587fff\" },\n { offset: 0.78, color: \"#7c58f7\" },\n { offset: 1, color: \"#962eff\" }\n ],\n textWire: [\n { offset: 0, color: \"#0099ff\" },\n { offset: 0.52, color: \"#5c7fff\" },\n { offset: 1, color: \"#962eff\" }\n ],\n labelFill: \"#f7fbff\",\n topEdge: \"#7c5fd0\",\n sideEdge: \"#2d1466\",\n swatch: [\"#0099ff\", \"#962eff\"]\n },\n \"16\": {\n id: \"16\",\n label: \"16 Alpha\",\n top: [\n { offset: 0, color: \"#e9fbff\" },\n { offset: 0.46, color: \"#75cfff\" },\n { offset: 1, color: \"#2d49d8\" }\n ],\n side: [\n { offset: 0, color: \"#59b7ec\" },\n { offset: 0.54, color: \"#5874e0\" },\n { offset: 1, color: \"#263fb8\" }\n ],\n textWire: [\n { offset: 0, color: \"#75cfff\" },\n { offset: 0.52, color: \"#5c7fff\" },\n { offset: 1, color: \"#2d49d8\" }\n ],\n labelFill: \"#10246a\",\n topEdge: \"#3e65c8\",\n sideEdge: \"#10246a\",\n swatch: [\"#75cfff\", \"#2d49d8\"]\n },\n \"17\": {\n id: \"17\",\n label: \"17 Alpha\",\n top: [\n { offset: 0, color: \"#edf1ff\" },\n { offset: 0.46, color: \"#7f91ff\" },\n { offset: 1, color: \"#0099ff\" }\n ],\n side: [\n { offset: 0, color: \"#7081ef\" },\n { offset: 0.5, color: \"#617fee\" },\n { offset: 1, color: \"#0084e1\" }\n ],\n textWire: [\n { offset: 0, color: \"#7f91ff\" },\n { offset: 0.52, color: \"#5c9fff\" },\n { offset: 1, color: \"#0099ff\" }\n ],\n labelFill: \"#17245e\",\n topEdge: \"#5f6ed0\",\n sideEdge: \"#17245e\",\n swatch: [\"#7f91ff\", \"#0099ff\"]\n },\n \"18\": {\n id: \"18\",\n label: \"18 Alpha\",\n top: [\n { offset: 0, color: \"#f8fdff\" },\n { offset: 0.42, color: \"#b8e7ff\" },\n { offset: 1, color: \"#006ce0\" }\n ],\n side: [\n { offset: 0, color: \"#86d4f3\" },\n { offset: 0.5, color: \"#5da6dd\" },\n { offset: 1, color: \"#005dbc\" }\n ],\n textWire: [\n { offset: 0, color: \"#b8e7ff\" },\n { offset: 0.52, color: \"#42b4ff\" },\n { offset: 1, color: \"#006ce0\" }\n ],\n labelFill: \"#064272\",\n topEdge: \"#4f94c0\",\n sideEdge: \"#064272\",\n swatch: [\"#b8e7ff\", \"#006ce0\"]\n },\n \"19\": {\n id: \"19\",\n label: \"19 Alpha\",\n top: [\n { offset: 0, color: \"#e6fff9\" },\n { offset: 0.42, color: \"#42b4ff\" },\n { offset: 1, color: \"#8575ff\" }\n ],\n side: [\n { offset: 0, color: \"#1ea9eb\" },\n { offset: 0.5, color: \"#4f8cee\" },\n { offset: 1, color: \"#7866e8\" }\n ],\n textWire: [\n { offset: 0, color: \"#42b4ff\" },\n { offset: 0.52, color: \"#5c7fff\" },\n { offset: 1, color: \"#8575ff\" }\n ],\n labelFill: \"#12306f\",\n topEdge: \"#6470d8\",\n sideEdge: \"#12306f\",\n swatch: [\"#42b4ff\", \"#8575ff\"]\n },\n \"20\": {\n id: \"20\",\n label: \"20 Alpha\",\n top: [\n { offset: 0, color: \"#eef3ff\" },\n { offset: 0.38, color: \"#5c7fff\" },\n { offset: 0.7, color: \"#0099ff\" },\n { offset: 1, color: \"#b8e7ff\" }\n ],\n side: [\n { offset: 0, color: \"#536fe8\" },\n { offset: 0.52, color: \"#168fe8\" },\n { offset: 1, color: \"#8fd2ee\" }\n ],\n textWire: [\n { offset: 0, color: \"#5c7fff\" },\n { offset: 0.52, color: \"#0099ff\" },\n { offset: 1, color: \"#b8e7ff\" }\n ],\n labelFill: \"#17245e\",\n topEdge: \"#5a7bd0\",\n sideEdge: \"#17245e\",\n swatch: [\"#5c7fff\", \"#b8e7ff\"]\n }\n} satisfies Record<string, SquirclePalette>;\n\nexport type SquirclePaletteId = keyof typeof SQUIRCLE_PALETTES;\n\nexport const SQUIRCLE_PALETTE_IDS = Object.keys(SQUIRCLE_PALETTES) as SquirclePaletteId[];\nexport const DEFAULT_PALETTE_ID: SquirclePaletteId = \"15\";\n\nexport function getSquirclePalette(paletteId: string | undefined): SquirclePalette {\n return SQUIRCLE_PALETTES[paletteId as SquirclePaletteId] ?? SQUIRCLE_PALETTES[DEFAULT_PALETTE_ID];\n}\n","import { useId, useMemo } from \"react\";\nimport type { CSSProperties, SVGProps } from \"react\";\nimport { createSquircleGeometry, pointsToString } from \"./geometry\";\nimport { getSquirclePalette, SQUIRCLE_PALETTES } from \"./palettes\";\nimport type {\n SquircleAnnotationColor,\n SquircleGeometryConfig,\n SquircleLayerConfig,\n SquircleMaterial,\n SquircleOpacityConfig,\n SquircleSceneProps,\n SquircleStrokeConfig,\n SquircleTextStyle,\n SquircleVariantConfig\n} from \"./types\";\nimport \"./SquircleScene.css\";\n\nconst DEFAULT_STROKES: SquircleStrokeConfig = {\n face: 0.35,\n faceOpacity: 0.72,\n wire: 1.6,\n wireOpacity: 0.88,\n hidden: 1.2,\n hiddenOpacity: 0.28,\n dash: 2.2,\n wireDash: 1.6,\n labelWire: 1.1\n};\n\nconst DEFAULT_OPACITY: SquircleOpacityConfig = {\n transparentFace: 0.38,\n transparentAnnotation: 0.62,\n solidAnnotation: 0.88\n};\n\nconst DEFAULT_TEXT = \"GPU\";\nconst DEFAULT_TEXT_SIZE = 62;\nconst DEFAULT_TEXT_FONT_FAMILY = \"Arial, Helvetica, sans-serif\";\nconst DEFAULT_TEXT_FONT_WEIGHT = 400;\n\ntype ResolvedVariant = {\n material: SquircleMaterial;\n paletteId: string;\n text: string | null;\n dash: boolean;\n textStyle: SquircleTextStyle;\n textColor: Exclude<SquircleAnnotationColor, \"auto\">;\n textSize: number;\n textFontFamily: string;\n textFontWeight: string | number;\n dashColor: Exclude<SquircleAnnotationColor, \"auto\">;\n stroke: SquircleStrokeConfig;\n opacity: SquircleOpacityConfig;\n};\n\ninterface LegacyTextVariantConfig {\n gpu?: boolean;\n gpuStyle?: SquircleTextStyle | string;\n gpuColor?: SquircleAnnotationColor;\n}\n\nexport function SquircleScene({\n layers,\n geometry,\n selectedLayerId,\n theme = \"light\",\n idPrefix,\n className,\n ariaLabel = \"Squircle scene\",\n fitToLayers = true,\n transitionMs = 220,\n onLayerSelect\n}: SquircleSceneProps) {\n const reactId = useId().replace(/[^a-zA-Z0-9_-]/g, \"\");\n const prefix = idPrefix ?? `sq-${reactId}`;\n const geometryKey = JSON.stringify(geometry ?? {});\n const baseGeometry = useMemo(() => createSquircleGeometry(geometry), [geometryKey]);\n const visibleLayers = layers.filter((layer) => layer.visible !== false);\n const maxLayerY = Math.max(0, ...visibleLayers.map((layer) => layer.offset?.y ?? 0));\n const viewBoxHeight = geometry?.viewBoxHeight ?? (fitToLayers ? Math.max(baseGeometry.config.viewBoxHeight, maxLayerY + baseGeometry.sideBounds.maxY + 18) : baseGeometry.config.viewBoxHeight);\n const sceneGeometry = geometry?.viewBoxHeight === viewBoxHeight\n ? baseGeometry\n : createSquircleGeometry({ ...geometry, viewBoxHeight });\n const svgStyle = { \"--sq-transition-ms\": `${transitionMs}ms` } as CSSProperties;\n\n return (\n <svg\n className={[\"squircle-scene\", `sq-theme-${theme}`, className].filter(Boolean).join(\" \")}\n data-theme={theme}\n viewBox={sceneGeometry.viewBox}\n role=\"img\"\n aria-label={ariaLabel}\n style={svgStyle}\n >\n <SquircleDefinitions prefix={prefix} geometry={sceneGeometry} />\n {visibleLayers.map((layer) => (\n <SquircleLayer\n key={layer.id}\n layer={layer}\n geometry={sceneGeometry}\n prefix={prefix}\n selected={selectedLayerId === layer.id}\n onSelect={onLayerSelect}\n />\n ))}\n </svg>\n );\n}\n\nfunction SquircleDefinitions({ prefix, geometry }: { prefix: string; geometry: ReturnType<typeof createSquircleGeometry> }) {\n return (\n <defs>\n {Object.values(SQUIRCLE_PALETTES).map((palette) => (\n <g key={palette.id}>\n <LinearGradient\n id={`${prefix}-top-${palette.id}`}\n x1={geometry.topBounds.minX}\n y1={geometry.topBounds.minY}\n x2={geometry.topBounds.maxX}\n y2={geometry.topBounds.maxY}\n stops={palette.top}\n />\n <LinearGradient\n id={`${prefix}-side-${palette.id}`}\n x1={geometry.sideBounds.minX}\n y1={geometry.sideBounds.minY}\n x2={geometry.sideBounds.maxX}\n y2={geometry.sideBounds.maxY}\n stops={palette.side}\n />\n <LinearGradient\n id={`${prefix}-text-surface-${palette.id}`}\n x1={-425.63}\n y1={-0.1}\n x2={425.6}\n y2={0.07}\n stops={palette.top}\n />\n <LinearGradient\n id={`${prefix}-text-wire-${palette.id}`}\n x1={-64}\n y1={-24}\n x2={64}\n y2={24}\n stops={palette.textWire}\n />\n </g>\n ))}\n </defs>\n );\n}\n\nfunction LinearGradient({\n id,\n x1,\n y1,\n x2,\n y2,\n stops\n}: {\n id: string;\n x1: number;\n y1: number;\n x2: number;\n y2: number;\n stops: { offset: number; color: string }[];\n}) {\n return (\n <linearGradient id={id} x1={x1} y1={y1} x2={x2} y2={y2} gradientUnits=\"userSpaceOnUse\">\n {stops.map((stop) => (\n <stop key={`${stop.offset}-${stop.color}`} offset={stop.offset} stopColor={stop.color} />\n ))}\n </linearGradient>\n );\n}\n\nfunction SquircleLayer({\n layer,\n geometry,\n prefix,\n selected,\n onSelect\n}: {\n layer: SquircleLayerConfig;\n geometry: ReturnType<typeof createSquircleGeometry>;\n prefix: string;\n selected: boolean;\n onSelect?: (layerId: string) => void;\n}) {\n const base = resolveVariant(layer.base, layer.stroke, layer.opacity);\n const hover = layer.hover ? resolveVariant({ ...layer.base, ...layer.hover }, layer.stroke, layer.opacity) : null;\n const hasHover = Boolean(hover && variantSignature(hover) !== variantSignature(base));\n const x = layer.offset?.x ?? 0;\n const y = layer.offset?.y ?? 0;\n\n return (\n <g\n className={[\n \"sq-layer\",\n hasHover ? \"sq-has-hover\" : \"\",\n selected ? \"is-selected\" : \"\",\n layer.className\n ].filter(Boolean).join(\" \")}\n data-layer-id={layer.id}\n data-hover-enabled={String(hasHover)}\n transform={`translate(${x} ${y})`}\n onClick={onSelect ? () => onSelect(layer.id) : undefined}\n >\n <SquircleVariant className=\"sq-base\" variant={base} geometry={geometry} prefix={prefix} />\n {hasHover && hover ? <SquircleVariant className=\"sq-hover\" variant={hover} geometry={geometry} prefix={prefix} /> : null}\n </g>\n );\n}\n\nfunction SquircleVariant({\n className,\n variant,\n geometry,\n prefix\n}: {\n className: string;\n variant: ResolvedVariant;\n geometry: ReturnType<typeof createSquircleGeometry>;\n prefix: string;\n}) {\n const palette = getSquirclePalette(variant.paletteId);\n const topFill = `url(#${prefix}-top-${palette.id})`;\n const sideFill = `url(#${prefix}-side-${palette.id})`;\n const textSurfaceFill = `url(#${prefix}-text-surface-${palette.id})`;\n const textWireFill = `url(#${prefix}-text-wire-${palette.id})`;\n const wallPoints = pointsToString(geometry.wallPoints);\n const topPoints = pointsToString(geometry.topPoints);\n const hiddenPoints = pointsToString(geometry.hiddenPoints);\n const inlayPoints = pointsToString(geometry.inlayPoints);\n\n return (\n <g className={[\"sq-variant\", className, `sq-material-${variant.material}`].join(\" \")}>\n {variant.material === \"wireframe\" ? (\n <>\n <polyline\n className=\"sq-hidden\"\n points={hiddenPoints}\n stroke={topFill}\n strokeWidth={variant.stroke.hidden}\n opacity={variant.stroke.hiddenOpacity}\n />\n <polygon\n className=\"sq-face sq-wire-side\"\n points={wallPoints}\n fill=\"none\"\n stroke={topFill}\n strokeWidth={variant.stroke.wire}\n strokeOpacity={variant.stroke.wireOpacity}\n />\n <polygon\n className=\"sq-face sq-wire-top\"\n points={topPoints}\n fill=\"none\"\n stroke={topFill}\n strokeWidth={variant.stroke.wire}\n strokeOpacity={variant.stroke.wireOpacity}\n />\n </>\n ) : (\n <>\n <polygon\n className=\"sq-face sq-solid-side\"\n points={wallPoints}\n fill={sideFill}\n fillOpacity={variant.material === \"transparent\" ? variant.opacity.transparentFace : 1}\n stroke={palette.sideEdge}\n strokeWidth={variant.stroke.face}\n strokeOpacity={variant.stroke.faceOpacity}\n />\n <polygon\n className=\"sq-face sq-solid-top\"\n points={topPoints}\n fill={topFill}\n fillOpacity={variant.material === \"transparent\" ? variant.opacity.transparentFace : 1}\n stroke={palette.topEdge}\n strokeWidth={variant.stroke.face}\n strokeOpacity={variant.stroke.faceOpacity}\n />\n </>\n )}\n {variant.dash ? (\n <polygon\n className=\"sq-inlay\"\n points={inlayPoints}\n stroke={dashPaint(variant, palette, topFill)}\n strokeWidth={variant.material === \"wireframe\" ? variant.stroke.wireDash : variant.stroke.dash}\n opacity={annotationOpacity(variant)}\n />\n ) : null}\n {variant.text ? (\n <text\n className=\"sq-label\"\n transform={geometry.labelTransform}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n fontFamily={variant.textFontFamily}\n fontSize={variant.textSize}\n fontWeight={variant.textFontWeight}\n {...textPaintProps(variant, palette, textSurfaceFill, textWireFill)}\n >\n {variant.text}\n </text>\n ) : null}\n </g>\n );\n}\n\nfunction resolveVariant(\n variant: SquircleVariantConfig,\n layerStroke: Partial<SquircleStrokeConfig> = {},\n layerOpacity: Partial<SquircleOpacityConfig> = {}\n): ResolvedVariant {\n return {\n material: variant.material ?? \"wireframe\",\n paletteId: variant.paletteId ?? \"15\",\n text: normalizeTextValue(variant.text, legacyVariant(variant).gpu),\n dash: variant.dash ?? false,\n textStyle: normalizeTextStyle(variant.textStyle ?? legacyVariant(variant).gpuStyle ?? \"solid\"),\n textColor: normalizeAnnotationColor(variant.textColor ?? legacyVariant(variant).gpuColor ?? \"contrast\"),\n textSize: variant.textSize ?? DEFAULT_TEXT_SIZE,\n textFontFamily: variant.textFontFamily ?? DEFAULT_TEXT_FONT_FAMILY,\n textFontWeight: variant.textFontWeight ?? DEFAULT_TEXT_FONT_WEIGHT,\n dashColor: normalizeAnnotationColor(variant.dashColor ?? \"contrast\"),\n stroke: { ...DEFAULT_STROKES, ...layerStroke, ...variant.stroke },\n opacity: { ...DEFAULT_OPACITY, ...layerOpacity, ...variant.opacity }\n };\n}\n\nfunction legacyVariant(variant: SquircleVariantConfig): LegacyTextVariantConfig {\n return variant as SquircleVariantConfig & LegacyTextVariantConfig;\n}\n\nfunction normalizeTextValue(value: SquircleVariantConfig[\"text\"], legacyGpu: boolean | undefined): string | null {\n if (typeof value === \"string\") return value.trim() ? value : null;\n if (value === true || legacyGpu) return DEFAULT_TEXT;\n return null;\n}\n\nfunction normalizeTextStyle(style: SquircleTextStyle | string): SquircleTextStyle {\n return style === \"wireframe\" ? \"wireframe\" : \"solid\";\n}\n\nfunction normalizeAnnotationColor(color: SquircleAnnotationColor): Exclude<SquircleAnnotationColor, \"auto\"> {\n return color === \"auto\" ? \"contrast\" : color;\n}\n\nfunction annotationPaint(color: SquircleAnnotationColor, labelFill: string): string {\n const normalized = normalizeAnnotationColor(color);\n if (normalized === \"white\") return \"#ffffff\";\n if (normalized === \"black\") return \"#05070a\";\n return labelFill;\n}\n\nfunction dashPaint(variant: ResolvedVariant, palette: ReturnType<typeof getSquirclePalette>, topFill: string): string {\n if (variant.material === \"wireframe\") return topFill;\n return annotationPaint(variant.dashColor, palette.labelFill);\n}\n\nfunction annotationOpacity(variant: ResolvedVariant): number {\n if (variant.material === \"transparent\") return variant.opacity.transparentAnnotation;\n return variant.opacity.solidAnnotation;\n}\n\nfunction textPaintProps(\n variant: ResolvedVariant,\n palette: ReturnType<typeof getSquirclePalette>,\n textSurfaceFill: string,\n textWireFill: string\n): SVGProps<SVGTextElement> {\n const opacity = annotationOpacity(variant);\n\n if (variant.material === \"wireframe\") {\n if (variant.textStyle === \"wireframe\") {\n return {\n fill: \"none\",\n stroke: textWireFill,\n strokeWidth: variant.stroke.labelWire,\n opacity\n };\n }\n return {\n fill: textSurfaceFill,\n stroke: \"none\",\n strokeWidth: 0,\n opacity: 1\n };\n }\n\n if (variant.textStyle === \"wireframe\") {\n return {\n fill: \"none\",\n stroke: annotationPaint(variant.textColor, palette.labelFill),\n strokeWidth: variant.stroke.labelWire,\n opacity\n };\n }\n\n return {\n fill: annotationPaint(variant.textColor, palette.labelFill),\n stroke: \"none\",\n strokeWidth: 0,\n opacity\n };\n}\n\nfunction variantSignature(variant: ResolvedVariant): string {\n return JSON.stringify(variant);\n}\n","import type { SquircleLayerConfig, SquircleTheme, SquircleVariantConfig } from \"./types\";\n\nexport interface SquircleReactCodeOptions {\n layers: SquircleLayerConfig[];\n theme: SquircleTheme;\n componentName?: string;\n importPath?: string;\n ariaLabel?: string;\n}\n\ninterface LegacyTextVariantConfig {\n gpu?: boolean;\n gpuStyle?: \"solid\" | \"wireframe\";\n gpuColor?: SquircleVariantConfig[\"textColor\"];\n}\n\nexport function createSquircleReactCode({\n layers,\n theme,\n componentName = \"CustomSquircle\",\n importPath = \"./squircle\",\n ariaLabel\n}: SquircleReactCodeOptions): string {\n const normalizedName = toComponentName(componentName);\n const normalizedImportPath = JSON.stringify(importPath);\n const normalizedAriaLabel = JSON.stringify(ariaLabel ?? `${normalizedName} composition`);\n const layersCode = JSON.stringify(layers.map(layerForReactCode), null, 2);\n\n return [\n `import { SquircleScene, type SquircleLayerConfig, type SquircleTheme } from ${normalizedImportPath};`,\n \"\",\n `const theme: SquircleTheme = ${JSON.stringify(theme)};`,\n \"\",\n `const layers: SquircleLayerConfig[] = ${layersCode};`,\n \"\",\n `export function ${normalizedName}() {`,\n \" return (\",\n \" <SquircleScene\",\n \" theme={theme}\",\n \" layers={layers}\",\n ` ariaLabel={${normalizedAriaLabel}}`,\n \" />\",\n \" );\",\n \"}\",\n \"\"\n ].join(\"\\n\");\n}\n\nfunction layerForReactCode(layer: SquircleLayerConfig): SquircleLayerConfig {\n return pruneUndefined({\n ...layer,\n base: variantForReactCode(layer.base),\n hover: layer.hover ? variantForReactCode(layer.hover) : undefined\n });\n}\n\nfunction variantForReactCode(variant: SquircleVariantConfig): SquircleVariantConfig {\n const { gpu, gpuStyle, gpuColor, ...rest } = variant as SquircleVariantConfig & LegacyTextVariantConfig;\n const text = textForExport(rest.text, gpu);\n const textStyle = rest.textStyle ?? gpuStyle;\n const textColor = rest.textColor ?? gpuColor;\n\n return pruneUndefined({\n ...rest,\n text,\n textStyle,\n textColor\n });\n}\n\nfunction textForExport(text: SquircleVariantConfig[\"text\"], legacyGpu: boolean | undefined): SquircleVariantConfig[\"text\"] | undefined {\n if (typeof text === \"string\") return text;\n if (text === true || legacyGpu) return \"GPU\";\n if (text === false) return false;\n return undefined;\n}\n\nfunction pruneUndefined<T extends Record<string, unknown>>(input: T): T {\n return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined)) as T;\n}\n\nexport function toComponentName(input: string): string {\n const words = input\n .trim()\n .split(/[^a-zA-Z0-9]+/)\n .filter(Boolean);\n const name = words\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\"\");\n const candidate = name || \"CustomSquircle\";\n\n return /^[A-Za-z_$]/.test(candidate) ? candidate : `Squircle${candidate}`;\n}\n","import { useEffect, useMemo, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createSquircleReactCode } from \"./codeExport\";\nimport { reflowLayerOffsets } from \"./geometry\";\nimport { SQUIRCLE_PALETTE_IDS, SQUIRCLE_PALETTES } from \"./palettes\";\nimport { SquircleScene } from \"./SquircleScene\";\nimport type { SquircleLayerConfig, SquircleMaterial, SquircleTheme, SquircleVariantConfig } from \"./types\";\nimport \"./SquircleEditor.css\";\n\nconst DEFAULT_LAYER_GAP = 88;\nconst DEFAULT_TEXT = \"GPU\";\nconst MATERIALS: SquircleMaterial[] = [\"wireframe\", \"solid\", \"transparent\"];\n\ninterface LegacyTextVariantConfig {\n gpu?: boolean;\n gpuStyle?: \"solid\" | \"wireframe\";\n}\n\nexport interface SquircleEditorProps {\n value?: SquircleLayerConfig[];\n initialLayers?: SquircleLayerConfig[];\n onChange?: (layers: SquircleLayerConfig[]) => void;\n title?: string;\n description?: string;\n /** @deprecated The editor now exports React code instead of schema-tagged JSON. */\n schema?: string;\n className?: string;\n layerGap?: number;\n showCode?: boolean;\n /** @deprecated Use showCode. */\n showConfig?: boolean;\n codeComponentName?: string;\n codeImportPath?: string;\n theme?: SquircleTheme;\n defaultTheme?: SquircleTheme;\n onThemeChange?: (theme: SquircleTheme) => void;\n showThemeSwitch?: boolean;\n}\n\nexport function createDefaultSquircleEditorLayers(paletteId = \"15\"): SquircleLayerConfig[] {\n return reflowLayerOffsets([\n {\n id: \"bottom\",\n visible: true,\n base: { material: \"wireframe\", paletteId }\n },\n {\n id: \"middle\",\n visible: true,\n base: { material: \"wireframe\", paletteId }\n },\n {\n id: \"top\",\n visible: true,\n base: { material: \"wireframe\", paletteId }\n }\n ], DEFAULT_LAYER_GAP);\n}\n\nexport function SquircleEditor({\n value,\n initialLayers,\n onChange,\n title = \"Squircle\",\n description = \"React component constructor with 0-N layers and exported code.\",\n className,\n layerGap = DEFAULT_LAYER_GAP,\n showCode,\n showConfig = true,\n codeComponentName,\n codeImportPath = \"./squircle\",\n theme,\n defaultTheme = \"light\",\n onThemeChange,\n showThemeSwitch = true\n}: SquircleEditorProps) {\n const [internalLayers, setInternalLayers] = useState<SquircleLayerConfig[]>(\n () => initialLayers ?? createDefaultSquircleEditorLayers()\n );\n const [internalTheme, setInternalTheme] = useState<SquircleTheme>(defaultTheme);\n const layers = value ?? internalLayers;\n const activeTheme = theme ?? internalTheme;\n const [selectedId, setSelectedId] = useState<string | null>(() => layers.at(-1)?.id ?? null);\n const [copiedCode, setCopiedCode] = useState(false);\n const selectedLayer = layers.find((layer) => layer.id === selectedId) ?? null;\n const shouldShowCode = showCode ?? showConfig;\n const maxOffsetY = Math.max(0, ...layers.map((layer) => layer.offset?.y ?? 0));\n const sceneConfig = useMemo(\n () => ({\n layers,\n geometry: { viewBoxHeight: Math.max(480, maxOffsetY + 320) },\n selectedLayerId: selectedId\n }),\n [layers, maxOffsetY, selectedId]\n );\n const reactCode = useMemo(\n () => createSquircleReactCode({\n layers,\n theme: activeTheme,\n componentName: codeComponentName ?? title,\n importPath: codeImportPath,\n ariaLabel: `${title} composition`\n }),\n [activeTheme, codeComponentName, codeImportPath, layers, title]\n );\n\n useEffect(() => {\n if (selectedId && layers.some((layer) => layer.id === selectedId)) return;\n setSelectedId(layers.at(-1)?.id ?? null);\n }, [layers, selectedId]);\n\n useEffect(() => {\n setCopiedCode(false);\n }, [reactCode]);\n\n function commitLayers(nextLayers: SquircleLayerConfig[], options: { reflow?: boolean } = {}) {\n const finalLayers = options.reflow ? reflowLayerOffsets(nextLayers, layerGap) : nextLayers;\n if (!value) setInternalLayers(finalLayers);\n onChange?.(finalLayers);\n }\n\n function updateLayer(id: string, patcher: (layer: SquircleLayerConfig) => SquircleLayerConfig) {\n commitLayers(layers.map((layer) => (layer.id === id ? patcher(layer) : layer)));\n }\n\n function updateBase(patch: SquircleVariantConfig) {\n if (!selectedLayer) return;\n updateLayer(selectedLayer.id, (layer) => ({\n ...layer,\n base: { ...layer.base, ...patch }\n }));\n }\n\n function updateHover(patch: SquircleVariantConfig) {\n if (!selectedLayer) return;\n updateLayer(selectedLayer.id, (layer) => ({\n ...layer,\n hover: { ...(layer.hover ?? layer.base), ...patch }\n }));\n }\n\n function addLayer() {\n const newLayer: SquircleLayerConfig = {\n id: `layer-${layers.length + 1}`,\n visible: true,\n base: { material: \"wireframe\", paletteId: layers.at(-1)?.base.paletteId ?? \"15\" }\n };\n const next = [...layers, newLayer];\n setSelectedId(newLayer.id);\n commitLayers(next, { reflow: true });\n }\n\n function removeSelectedLayer() {\n if (!selectedLayer) return;\n const next = layers.filter((layer) => layer.id !== selectedLayer.id);\n setSelectedId(next.at(-1)?.id ?? null);\n commitLayers(next, { reflow: true });\n }\n\n function toggleVisibility(id: string) {\n updateLayer(id, (layer) => ({ ...layer, visible: layer.visible === false }));\n }\n\n function commitTheme(nextTheme: SquircleTheme) {\n if (!theme) setInternalTheme(nextTheme);\n onThemeChange?.(nextTheme);\n }\n\n async function copyReactCode() {\n try {\n await navigator.clipboard.writeText(reactCode);\n } catch {\n const copySource = document.createElement(\"textarea\");\n copySource.value = reactCode;\n copySource.setAttribute(\"readonly\", \"\");\n copySource.style.position = \"fixed\";\n copySource.style.top = \"-9999px\";\n document.body.append(copySource);\n copySource.select();\n document.execCommand(\"copy\");\n copySource.remove();\n }\n setCopiedCode(true);\n window.setTimeout(() => setCopiedCode(false), 1400);\n }\n\n return (\n <main className={[\"squircle-editor\", `squircle-editor-${activeTheme}`, className].filter(Boolean).join(\" \")} data-theme={activeTheme} aria-label=\"Squircle editor\">\n <header className=\"squircle-editor-topbar\">\n <div>\n <h1>{title}</h1>\n <p>{description}</p>\n </div>\n <div className=\"topbar-actions\">\n {showThemeSwitch ? (\n <div className=\"theme-switch\" role=\"group\" aria-label=\"Theme\">\n <button\n type=\"button\"\n aria-pressed={activeTheme === \"light\"}\n onClick={() => commitTheme(\"light\")}\n >\n Light\n </button>\n <button\n type=\"button\"\n aria-pressed={activeTheme === \"dark\"}\n onClick={() => commitTheme(\"dark\")}\n >\n Dark\n </button>\n </div>\n ) : null}\n <button type=\"button\" onClick={addLayer}>Add layer</button>\n <button type=\"button\" onClick={() => commitLayers([])}>Clear</button>\n </div>\n </header>\n\n <section className=\"squircle-editor-workspace\">\n <aside className=\"side-panel\">\n <div className=\"panel-heading\">\n <h2>Layers</h2>\n <span>{layers.length}</span>\n </div>\n <div className=\"squircle-editor-layer-list\">\n {[...layers].reverse().map((layer, reverseIndex) => {\n const index = layers.length - reverseIndex;\n return (\n <div className=\"squircle-editor-layer-row\" key={layer.id}>\n <button\n type=\"button\"\n className={layer.id === selectedId ? \"layer-select is-active\" : \"layer-select\"}\n onClick={() => setSelectedId(layer.id)}\n >\n <strong>{index === layers.length ? \"Top\" : index === 1 ? \"Bottom\" : `Layer ${index}`}</strong>\n <span>{summary(layer.base)}</span>\n </button>\n <button\n type=\"button\"\n className=\"icon-text\"\n aria-pressed={layer.visible !== false}\n onClick={() => toggleVisibility(layer.id)}\n >\n {layer.visible === false ? \"Show\" : \"Hide\"}\n </button>\n </div>\n );\n })}\n </div>\n </aside>\n\n <section className=\"squircle-editor-preview\">\n <SquircleScene\n {...sceneConfig}\n theme={activeTheme}\n ariaLabel=\"Editable squircle composition\"\n onLayerSelect={setSelectedId}\n />\n {shouldShowCode ? (\n <section className=\"code-panel\" aria-label=\"Generated React code\">\n <header className=\"code-panel-header\">\n <div>\n <h2>Code</h2>\n <p>Copy a ready-to-use React component.</p>\n </div>\n <button\n type=\"button\"\n className=\"copy-code-button\"\n aria-label=\"Copy React code\"\n title=\"Copy React code\"\n onClick={copyReactCode}\n >\n <CopyIcon />\n <span className=\"copy-status\" aria-live=\"polite\">{copiedCode ? \"Copied\" : \"\"}</span>\n </button>\n </header>\n <pre className=\"code-output\"><code>{reactCode}</code></pre>\n </section>\n ) : null}\n </section>\n\n <aside className={selectedLayer ? \"side-panel inspector-panel\" : \"side-panel inspector-panel is-empty\"}>\n {selectedLayer ? (\n <>\n <div className=\"panel-heading\">\n <h2>{selectedLayer.id}</h2>\n <button type=\"button\" className=\"icon-text danger\" onClick={removeSelectedLayer}>Delete</button>\n </div>\n\n <EditorSection title=\"Base\">\n <SelectField\n label=\"Material\"\n value={selectedLayer.base.material ?? \"wireframe\"}\n options={MATERIALS}\n onChange={(nextValue) => updateBase({ material: nextValue as SquircleMaterial })}\n />\n <PaletteField\n value={selectedLayer.base.paletteId ?? \"15\"}\n onChange={(paletteId) => updateBase({ paletteId })}\n />\n <ToggleField label=\"Text\" checked={variantHasText(selectedLayer.base)} onChange={(enabled) => updateBase({ text: enabled ? variantTextValue(selectedLayer.base) : false })} />\n {variantHasText(selectedLayer.base) ? (\n <TextField\n label=\"Text\"\n value={variantTextValue(selectedLayer.base)}\n onChange={(text) => updateBase({ text })}\n />\n ) : null}\n <ToggleField label=\"Dash\" checked={selectedLayer.base.dash ?? false} onChange={(dash) => updateBase({ dash })} />\n <SelectField\n label=\"Text paint\"\n value={variantTextStyle(selectedLayer.base)}\n options={[\"solid\", \"wireframe\"]}\n onChange={(textStyle) => updateBase({ textStyle: textStyle as \"solid\" | \"wireframe\" })}\n />\n </EditorSection>\n\n <EditorSection title=\"Hover\">\n <ToggleField\n label=\"Enabled\"\n checked={Boolean(selectedLayer.hover)}\n onChange={(enabled) => {\n updateLayer(selectedLayer.id, (layer) => ({\n ...layer,\n hover: enabled ? { ...layer.base, material: layer.base.material === \"wireframe\" ? \"solid\" : \"wireframe\" } : undefined\n }));\n }}\n />\n {selectedLayer.hover ? (\n <>\n <SelectField\n label=\"Material\"\n value={selectedLayer.hover.material ?? selectedLayer.base.material ?? \"wireframe\"}\n options={MATERIALS}\n onChange={(nextValue) => updateHover({ material: nextValue as SquircleMaterial })}\n />\n <PaletteField\n value={selectedLayer.hover.paletteId ?? selectedLayer.base.paletteId ?? \"15\"}\n onChange={(paletteId) => updateHover({ paletteId })}\n />\n </>\n ) : null}\n </EditorSection>\n\n <EditorSection title=\"Stroke Widths\">\n <RangeField\n label=\"Face\"\n value={selectedLayer.stroke?.face ?? 0.35}\n min={0}\n max={1.5}\n step={0.05}\n onChange={(face) => updateLayer(selectedLayer.id, (layer) => ({ ...layer, stroke: { ...layer.stroke, face } }))}\n />\n <RangeField\n label=\"Wire\"\n value={selectedLayer.stroke?.wire ?? 1.6}\n min={0.4}\n max={4}\n step={0.1}\n onChange={(wire) => updateLayer(selectedLayer.id, (layer) => ({ ...layer, stroke: { ...layer.stroke, wire } }))}\n />\n <RangeField\n label=\"Dash\"\n value={selectedLayer.stroke?.dash ?? 2.2}\n min={0.6}\n max={5}\n step={0.1}\n onChange={(dash) => updateLayer(selectedLayer.id, (layer) => ({ ...layer, stroke: { ...layer.stroke, dash } }))}\n />\n <RangeField\n label=\"Text outline\"\n value={selectedLayer.stroke?.labelWire ?? 1.1}\n min={0.4}\n max={2}\n step={0.05}\n onChange={(labelWire) => updateLayer(selectedLayer.id, (layer) => ({ ...layer, stroke: { ...layer.stroke, labelWire } }))}\n />\n </EditorSection>\n </>\n ) : (\n <p className=\"empty-note\">Select a layer in the list or preview.</p>\n )}\n </aside>\n </section>\n </main>\n );\n}\n\nfunction EditorSection({ title, children }: { title: string; children: ReactNode }) {\n return (\n <section className=\"editor-section\">\n <h3>{title}</h3>\n {children}\n </section>\n );\n}\n\nfunction SelectField({\n label,\n value,\n options,\n onChange\n}: {\n label: string;\n value: string;\n options: string[];\n onChange: (value: string) => void;\n}) {\n return (\n <label className=\"field\">\n <span>{label}</span>\n <select value={value} onChange={(event) => onChange(event.target.value)}>\n {options.map((option) => (\n <option key={option} value={option}>{option}</option>\n ))}\n </select>\n </label>\n );\n}\n\nfunction PaletteField({ value, onChange }: { value: string; onChange: (value: string) => void }) {\n return (\n <label className=\"field\">\n <span>Palette</span>\n <select value={value} onChange={(event) => onChange(event.target.value)}>\n {SQUIRCLE_PALETTE_IDS.map((id) => (\n <option key={id} value={id}>{SQUIRCLE_PALETTES[id].label}</option>\n ))}\n </select>\n </label>\n );\n}\n\nfunction ToggleField({ label, checked, onChange }: { label: string; checked: boolean; onChange: (value: boolean) => void }) {\n return (\n <label className=\"toggle-field\">\n <input type=\"checkbox\" checked={checked} onChange={(event) => onChange(event.target.checked)} />\n <span>{label}</span>\n </label>\n );\n}\n\nfunction TextField({\n label,\n value,\n onChange\n}: {\n label: string;\n value: string;\n onChange: (value: string) => void;\n}) {\n return (\n <label className=\"field\">\n <span>{label}</span>\n <input type=\"text\" value={value} onChange={(event) => onChange(event.target.value)} />\n </label>\n );\n}\n\nfunction RangeField({\n label,\n value,\n min,\n max,\n step,\n onChange\n}: {\n label: string;\n value: number;\n min: number;\n max: number;\n step: number;\n onChange: (value: number) => void;\n}) {\n return (\n <label className=\"field\">\n <span>{label}: {value.toFixed(2)}</span>\n <input type=\"range\" min={min} max={max} step={step} value={value} onChange={(event) => onChange(Number(event.target.value))} />\n </label>\n );\n}\n\nfunction CopyIcon() {\n return (\n <svg className=\"copy-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\" />\n <path d=\"M5 15V6.8C5 5.8 5.8 5 6.8 5H15\" />\n </svg>\n );\n}\n\nfunction summary(variant: SquircleVariantConfig): string {\n const parts = [variant.material ?? \"wireframe\", variant.paletteId ?? \"15\"];\n if (variantHasText(variant)) parts.push(`text: ${variantTextValue(variant)}`);\n if (variant.dash) parts.push(\"dash\");\n return parts.join(\" / \");\n}\n\nfunction variantHasText(variant: SquircleVariantConfig): boolean {\n if (typeof variant.text === \"string\") return variant.text.trim().length > 0;\n return variant.text === true || legacyVariant(variant).gpu === true;\n}\n\nfunction variantTextValue(variant: SquircleVariantConfig): string {\n if (typeof variant.text === \"string\") return variant.text;\n if (variant.text === true || legacyVariant(variant).gpu) return DEFAULT_TEXT;\n return DEFAULT_TEXT;\n}\n\nfunction variantTextStyle(variant: SquircleVariantConfig): \"solid\" | \"wireframe\" {\n return variant.textStyle ?? legacyVariant(variant).gpuStyle ?? \"solid\";\n}\n\nfunction legacyVariant(variant: SquircleVariantConfig): LegacyTextVariantConfig {\n return variant as SquircleVariantConfig & LegacyTextVariantConfig;\n}\n"],"mappings":"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"}
package/dist/style.css ADDED
@@ -0,0 +1,2 @@
1
+ .squircle-scene{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;background:0 0;width:100%;height:auto;display:block;overflow:visible}.squircle-scene.sq-theme-dark{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}.sq-layer{cursor:pointer}.sq-layer.is-selected{outline:none}.sq-variant{transition:opacity var(--sq-transition-ms,.22s) ease}.sq-hover{opacity:0;pointer-events:none}.sq-layer.sq-has-hover:hover>.sq-base{opacity:0}.sq-layer.sq-has-hover:hover>.sq-hover{opacity:1}.sq-face{pointer-events:all;shape-rendering:geometricprecision;stroke-linejoin:round}.sq-hidden{fill:none;pointer-events:none;stroke-dasharray:8 8;stroke-linecap:round;stroke-linejoin:round}.sq-inlay{fill:none;pointer-events:none;shape-rendering:geometricprecision;stroke-dasharray:9 8;stroke-linejoin:round}.sq-label{clip-rule:evenodd;fill-rule:evenodd;paint-order:stroke;pointer-events:none;shape-rendering:geometricprecision;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;transition:fill .25s,stroke .25s,stroke-width .25s,opacity .25s}:root{color:#111821;background:0 0;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}*{box-sizing:border-box}body{background:0 0;margin:0}button,input,select{font:inherit}.squircle-editor{--editor-text:#111821;--editor-muted:#697484;--editor-soft:#f6f9fc;--editor-panel:#ffffffdb;--editor-panel-soft:#ffffffb8;--editor-panel-muted:#ffffff75;--editor-line:#dfe7f1;--editor-line-strong:#dbe5ef;--editor-control-bg:#fff;--editor-control-text:#17202b;--editor-active-text:#073c68;--editor-accent:#09f;--editor-accent-soft:#0099ff24;--editor-danger:#9c2340;--editor-code-bg:#ffffffb8;--editor-code-text:#263140;--editor-stage-sheen:#b8e7ff2e;color:var(--editor-text);--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;grid-template-rows:auto 1fr;width:min(100%,1320px);min-height:100vh;margin:0 auto;padding:clamp(14px,3vw,32px);display:grid}.squircle-editor-dark{--editor-text:#f4f8ff;--editor-muted:#aab7c8;--editor-soft:#101925;--editor-panel:#0d141ff0;--editor-panel-soft:#151f2ddb;--editor-panel-muted:#0c131db8;--editor-line:#25364a;--editor-line-strong:#33485f;--editor-control-bg:#121d2b;--editor-control-text:#eef6ff;--editor-active-text:#e5f6ff;--editor-accent:#42b4ff;--editor-accent-soft:#42b4ff2b;--editor-danger:#ff8ca3;--editor-code-bg:#090e16d6;--editor-code-text:#d8e8ff;--editor-stage-sheen:#42b4ff17;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}.squircle-editor-topbar{justify-content:space-between;align-items:end;gap:18px;padding-bottom:14px;display:flex}.squircle-editor-topbar h1{letter-spacing:0;margin:0;font-size:clamp(28px,5vw,48px)}.squircle-editor-topbar p{color:var(--editor-muted);margin:4px 0 0;font-size:14px;font-weight:650}.topbar-actions{flex-wrap:wrap;justify-content:flex-end;gap:8px;display:flex}.squircle-editor button{border:1px solid var(--editor-line-strong);background:var(--editor-control-bg);min-height:34px;color:var(--editor-control-text);cursor:pointer;border-radius:6px;font-weight:800}.squircle-editor button:hover{border-color:color-mix(in srgb, var(--editor-accent) 52%, transparent);color:var(--editor-active-text)}.theme-switch{border:1px solid var(--editor-line);background:var(--editor-panel-soft);border-radius:8px;gap:2px;padding:3px;display:inline-flex}.theme-switch button{min-width:68px;min-height:28px;color:var(--editor-muted);background:0 0;border-color:#0000}.theme-switch button[aria-pressed=true]{border-color:color-mix(in srgb, var(--editor-accent) 48%, var(--editor-line));color:var(--editor-active-text);background:linear-gradient(180deg, color-mix(in srgb, var(--editor-control-bg) 92%, var(--editor-accent)), var(--editor-panel-soft));box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--editor-accent) 16%, transparent), 0 1px 5px #0f141a1f}.squircle-editor-workspace{border:1px solid var(--editor-line);background:linear-gradient(145deg, var(--editor-stage-sheen), transparent 48%), var(--editor-panel);border-radius:8px;grid-template-columns:minmax(220px,270px) minmax(420px,1fr) minmax(280px,350px);min-height:0;display:grid;overflow:hidden}.side-panel{min-width:0;padding:14px;overflow:auto}.side-panel:first-child{border-right:1px solid var(--editor-line)}.inspector-panel{border-left:1px solid var(--editor-line)}.panel-heading{justify-content:space-between;align-items:center;gap:10px;margin-bottom:12px;display:flex}.panel-heading h2{margin:0;font-size:15px}.panel-heading span{color:var(--editor-muted);font-size:12px;font-weight:850}.squircle-editor-layer-list{gap:8px;display:grid}.squircle-editor-layer-row{grid-template-columns:1fr auto;align-items:stretch;gap:6px;display:grid}.layer-select{text-align:left;justify-items:start;min-width:0;padding:10px;display:grid}.layer-select strong{font-size:13px}.layer-select span{color:var(--editor-muted);font-size:11px;font-weight:760}.layer-select.is-active{border-color:color-mix(in srgb, var(--editor-accent) 68%, var(--editor-line));background:linear-gradient(90deg, var(--editor-accent-soft), var(--editor-panel-soft) 48%), var(--editor-control-bg);box-shadow:0 0 0 2px color-mix(in srgb, var(--editor-accent) 12%, transparent);color:var(--editor-active-text);border-left-width:4px}.icon-text{color:var(--editor-muted);padding:0 10px;font-size:12px}.icon-text.danger{color:var(--editor-danger)}.squircle-editor-preview{grid-template-rows:minmax(260px,1fr) minmax(120px,220px);gap:12px;min-width:0;min-height:0;padding:18px;display:grid}.squircle-editor-preview .squircle-scene{place-self:center;max-width:620px}.code-panel{border:1px solid var(--editor-line);background:var(--editor-code-bg);border-radius:6px;grid-template-rows:auto 1fr;min-width:0;display:grid;overflow:hidden}.code-panel-header{border-bottom:1px solid var(--editor-line);justify-content:space-between;align-items:center;gap:12px;padding:9px 10px;display:flex}.code-panel-header h2{color:var(--editor-control-text);letter-spacing:0;margin:0;font-size:13px}.code-panel-header p{color:var(--editor-muted);margin:1px 0 0;font-size:11px;font-weight:760}.copy-code-button{place-items:center;width:34px;min-width:34px;padding:0;display:inline-grid;position:relative}.copy-code-button .copy-icon{fill:none;stroke:currentColor;stroke-width:1.8px;stroke-linecap:round;stroke-linejoin:round;width:17px;height:17px}.copy-status{border:1px solid var(--editor-line);background:var(--editor-control-bg);color:var(--editor-active-text);opacity:0;pointer-events:none;border-radius:5px;padding:3px 6px;font-size:11px;font-weight:850;transition:opacity .16s,transform .16s;position:absolute;bottom:calc(100% + 5px);right:0;transform:translateY(2px)}.copy-status:not(:empty){opacity:1;transform:translateY(0)}.code-output{min-width:0;color:var(--editor-code-text);margin:0;padding:12px;font-size:11px;line-height:1.45;overflow:auto}.code-output code{font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace}.editor-section{border-top:1px solid var(--editor-line);gap:10px;padding:12px 0;display:grid}.editor-section:first-of-type{border-top:0;padding-top:0}.editor-section h3{color:var(--editor-muted);letter-spacing:0;text-transform:uppercase;margin:0;font-size:12px}.field{color:var(--editor-muted);gap:5px;font-size:12px;font-weight:800;display:grid}.field select,.field input[type=text],.field input[type=range]{width:100%}.field select,.field input[type=text]{border:1px solid var(--editor-line-strong);background:var(--editor-control-bg);min-height:34px;color:var(--editor-control-text);border-radius:6px;padding:0 8px}.toggle-field{color:var(--editor-control-text);align-items:center;gap:8px;font-size:13px;font-weight:800;display:flex}.toggle-field input{width:16px;height:16px}.empty-note{color:var(--editor-muted);margin:0;font-size:13px;font-weight:700}@media (width<=1060px){.squircle-editor-workspace{grid-template-columns:1fr}.side-panel:first-child,.inspector-panel{border:0;border-bottom:1px solid var(--editor-line)}}@media (width<=700px){.squircle-editor-topbar{flex-direction:column;align-items:stretch}.topbar-actions{justify-content:stretch}.topbar-actions button{flex:1}.squircle-editor-preview{padding:10px}}
2
+ /*$vite$:1*/
@@ -0,0 +1,71 @@
1
+ export type SquircleMaterial = "solid" | "transparent" | "wireframe";
2
+ export type SquircleAnnotationColor = "contrast" | "auto" | "white" | "black";
3
+ export type SquircleTextStyle = "solid" | "wireframe";
4
+ export type SquircleTheme = "light" | "dark";
5
+ export interface SquirclePoint {
6
+ x: number;
7
+ y: number;
8
+ }
9
+ export interface SquircleGeometryConfig {
10
+ width?: number;
11
+ viewBoxHeight?: number;
12
+ exponent?: number;
13
+ samples?: number;
14
+ halfSize?: number;
15
+ prismHeight?: number;
16
+ angleDegrees?: number;
17
+ inlayScale?: number;
18
+ center?: SquirclePoint;
19
+ }
20
+ export interface SquircleStrokeConfig {
21
+ face: number;
22
+ faceOpacity: number;
23
+ wire: number;
24
+ wireOpacity: number;
25
+ hidden: number;
26
+ hiddenOpacity: number;
27
+ dash: number;
28
+ wireDash: number;
29
+ labelWire: number;
30
+ }
31
+ export interface SquircleOpacityConfig {
32
+ transparentFace: number;
33
+ transparentAnnotation: number;
34
+ solidAnnotation: number;
35
+ }
36
+ export interface SquircleVariantConfig {
37
+ material?: SquircleMaterial;
38
+ paletteId?: string;
39
+ text?: string | boolean;
40
+ dash?: boolean;
41
+ textStyle?: SquircleTextStyle;
42
+ textColor?: SquircleAnnotationColor;
43
+ textSize?: number;
44
+ textFontFamily?: string;
45
+ textFontWeight?: string | number;
46
+ dashColor?: SquircleAnnotationColor;
47
+ stroke?: Partial<SquircleStrokeConfig>;
48
+ opacity?: Partial<SquircleOpacityConfig>;
49
+ }
50
+ export interface SquircleLayerConfig {
51
+ id: string;
52
+ visible?: boolean;
53
+ offset?: Partial<SquirclePoint>;
54
+ base: SquircleVariantConfig;
55
+ hover?: SquircleVariantConfig;
56
+ stroke?: Partial<SquircleStrokeConfig>;
57
+ opacity?: Partial<SquircleOpacityConfig>;
58
+ className?: string;
59
+ }
60
+ export interface SquircleSceneProps {
61
+ layers: SquircleLayerConfig[];
62
+ geometry?: SquircleGeometryConfig;
63
+ selectedLayerId?: string | null;
64
+ theme?: SquircleTheme;
65
+ idPrefix?: string;
66
+ className?: string;
67
+ ariaLabel?: string;
68
+ fitToLayers?: boolean;
69
+ transitionMs?: number;
70
+ onLayerSelect?: (layerId: string) => void;
71
+ }
package/docs/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # Documentation Index
2
+
3
+ This repo is primarily about the reusable Squircle React component in `src/squircle`. Static HTML pages remain important as design fixtures and regression references, but new product work should start with the component docs.
4
+
5
+ ## Start Here
6
+
7
+ | Need | Read |
8
+ | --- | --- |
9
+ | Use the React renderer or editor | [React Component Guide](./react/README.md) |
10
+ | Change geometry, gradients, labels, wireframes, or colors | [Design Documentation](./design/README.md) |
11
+ | Maintain the static HTML fixtures | [Static Prototype Documentation](./static/README.md) |
12
+ | Check work before handoff | [Verification Checklist](./verification.md) |
13
+
14
+ ## Agent Routing
15
+
16
+ - Building an app or exporting a reusable artifact: use `src/squircle` and [react/README.md](./react/README.md).
17
+ - Adjusting the squircle's look: read [design/geometry.md](./design/geometry.md), [design/rendering.md](./design/rendering.md), and [design/colors.md](./design/colors.md).
18
+ - Updating `index.html`, `demo.html`, or `constructor.html`: read the matching file in [static/](./static/README.md), then verify against the design docs.
19
+ - Adding palettes or annotations: update both the React constants and the design docs so future agents do not invent one-off colors.
20
+
21
+ ## Important Boundaries
22
+
23
+ - React component docs describe public TypeScript APIs and reusable configuration.
24
+ - Design docs describe visual truth: math, gradients, label paths, wireframe paint, and state constructors.
25
+ - Static docs describe the legacy/prototype HTML pages and their CSS/DOM contracts.
26
+ - Verification docs describe what must still be true after edits.
@@ -0,0 +1,20 @@
1
+ # Design Documentation
2
+
3
+ Read these files when changing how a squircle looks. They are visual contracts shared by the React component and the static fixtures.
4
+
5
+ | File | Purpose |
6
+ | --- | --- |
7
+ | [geometry.md](./geometry.md) | Superellipse sampling, projection, side-wall visibility, layer offsets, regeneration checklist |
8
+ | [rendering.md](./rendering.md) | Gradients, edge sharpness, draw order, dashed inlay, text path and paint rules |
9
+ | [colors.md](./colors.md) | Palette stops, text contrast colors, edge colors, hover palette rules |
10
+ | [single-squircle-states.md](./single-squircle-states.md) | Reusable one-squircle state recipes for solid, transparent, wireframe, text, dash, and text + dash |
11
+
12
+ ## Design Source Of Truth
13
+
14
+ - Geometry is generated from math, not hand-edited point lists.
15
+ - Filled faces use user-space gradients plus tiny in-family edge strokes.
16
+ - Wireframe faces use gradient strokes with matching top and bottom curves.
17
+ - React top-plane text is one live SVG text element in every mode. Static fixtures keep a single compound `GPU` path for their fixed example.
18
+ - Solid/transparent annotations use contrast colors; wireframe annotations use wire gradients.
19
+
20
+ If a visual change affects reusable rendering, update `src/squircle` and these design docs together.
@@ -0,0 +1,150 @@
1
+ # Color System
2
+
3
+ This file is the palette contract for variants, gradients, text labels, and edge strokes.
4
+
5
+ ## Sources Of Truth
6
+
7
+ Colors are defined in shared CSS and in each self-contained HTML page that embeds SVG definitions. Keep them synchronized:
8
+
9
+ 1. SVG gradients in `index.html`, `demo.html`, and `constructor.html`:
10
+ - `top-13..top-20`
11
+ - `gpu-surface-13..gpu-surface-20` in static fixtures, mirrored as `text-surface-*` in React output
12
+ - `side-13..side-20`
13
+ 2. CSS variables in `public/static/styles.css`:
14
+ - Selected-variant selectors for `.hero-card` and `.single-drawer`
15
+ - `.v13..v20` classes for variant cards
16
+
17
+ Do not add or rename a variant in only one place.
18
+
19
+ ## Gradient Geometry
20
+
21
+ All gradients use `gradientUnits="userSpaceOnUse"`.
22
+
23
+ | Gradient family | Coordinates | Meaning |
24
+ | --- | --- | --- |
25
+ | `top-*` | `x1="0" y1="0" x2="800" y2="291.18"` | Top face bbox |
26
+ | `gpu-surface-*` / `text-surface-*` | `x1="-425.63" y1="-0.1" x2="425.6" y2="0.07"` | Matching top-face stops remapped into the text label's local projected plane |
27
+ | `side-*` | `x1="0" y1="0" x2="800" y2="301.18"` | Full top-plus-wall silhouette bbox |
28
+
29
+ The side gradient intentionally starts at `y=0`, not at the wall's visible `y=145.59`, so the side shadow continues from the same projected coordinate space.
30
+
31
+ ## Variant Palette Table
32
+
33
+ | Variant | Top gradient stops | Side gradient stops | Label fill | Top edge | Side edge |
34
+ | --- | --- | --- | --- | --- | --- |
35
+ | `13 Alpha` | `0 #f5f0ff`, `0.48 #b388ff`, `1 #0099ff` | `0 #8250e6`, `0.5 #596ce8`, `1 #0084e8` | `#2a1060` | `#7c5fd0` | `#2d1466` |
36
+ | `14 Alpha` | `0 #e9fffb`, `0.44 #44b9d6`, `1 #006ce0` | `0 #38acbc`, `0.52 #268ccf`, `1 #005fc4` | `#063f6a` | `#237aa8` | `#063f6a` |
37
+ | `15 Alpha` | `0 #f0fbff`, `0.34 #0099ff`, `0.66 #5c7fff`, `1 #962eff` | `0 #008fec`, `0.34 #139cff`, `0.56 #587fff`, `0.78 #7c58f7`, `1 #962eff` | `#f7fbff` | `#7c5fd0` | `#2d1466` |
38
+ | `16 Alpha` | `0 #e9fbff`, `0.46 #75cfff`, `1 #2d49d8` | `0 #59b7ec`, `0.54 #5874e0`, `1 #263fb8` | `#10246a` | `#3e65c8` | `#10246a` |
39
+ | `17 Alpha` | `0 #edf1ff`, `0.46 #7f91ff`, `1 #0099ff` | `0 #7081ef`, `0.5 #617fee`, `1 #0084e1` | `#17245e` | `#5f6ed0` | `#17245e` |
40
+ | `18 Alpha` | `0 #f8fdff`, `0.42 #b8e7ff`, `1 #006ce0` | `0 #86d4f3`, `0.5 #5da6dd`, `1 #005dbc` | `#064272` | `#4f94c0` | `#064272` |
41
+ | `19 Alpha` | `0 #e6fff9`, `0.42 #42b4ff`, `1 #8575ff` | `0 #1ea9eb`, `0.5 #4f8cee`, `1 #7866e8` | `#12306f` | `#6470d8` | `#12306f` |
42
+ | `20 Alpha` | `0 #eef3ff`, `0.38 #5c7fff`, `0.7 #0099ff`, `1 #b8e7ff` | `0 #536fe8`, `0.52 #168fe8`, `1 #8fd2ee` | `#17245e` | `#5a7bd0` | `#17245e` |
43
+
44
+ The text surface gradient repeats the matching `top-*` stops with label-local coordinates. It is used when a wireframe squircle has `textStyle: "solid"`, so filled text reads as top surface material instead of pale wire line-art.
45
+
46
+ ## CSS Variable Contract
47
+
48
+ Every selected-variant selector must target both `.hero-card` and `.single-drawer`:
49
+
50
+ ```css
51
+ #variant-15:checked ~ .hero-card,
52
+ #variant-15:checked ~ .single-drawer {
53
+ --top-fill: url("#top-15");
54
+ --side-fill: url("#side-15");
55
+ --gpu-surface-fill: url("#gpu-surface-15");
56
+ --label-fill: #f7fbff;
57
+ --top-edge: #7c5fd0;
58
+ --side-edge: #2d1466;
59
+ }
60
+ ```
61
+
62
+ Every `.v13..v20` class must define the same core variables for variant cards:
63
+
64
+ ```css
65
+ .v15 {
66
+ --top-fill: url("#top-15");
67
+ --side-fill: url("#side-15");
68
+ --label-fill: #f7fbff;
69
+ --gpu-surface-fill: url("#gpu-surface-15");
70
+ --top-edge: #7c5fd0;
71
+ --side-edge: #2d1466;
72
+ }
73
+ ```
74
+
75
+ Variant classes may also define fallback ghost colors, but those must remain visually compatible with the top and side gradients.
76
+
77
+ ## Hover Palettes
78
+
79
+ `constructor.html` and `demo.html` can use any palette id as a layer's `hoverPalette`. The base variant keeps `.v${palette}` or inherits the selected page variant, and the hover variant receives `.v${hoverPalette}`. This means hover color changes use the same documented gradients, label contrast, and wireframe stroke colors as normal rendering.
80
+
81
+ Do not implement hover color with filters, opacity hacks, or untracked ad hoc colors. Hover color is a normal palette swap. `hoverPalette` may be the same id as the base `palette`; that is useful when hover should change only material while preserving color. If both hover state and hover palette match the base state and palette, the result is a deliberate no-op and should still be exported exactly, but it should not render or crossfade a hover variant.
82
+
83
+ ## Annotation Auto And Overrides
84
+
85
+ Static pages and constructor layers with `gpuColor: "contrast"` and `dashColor: "contrast"` use the palette's automatic annotation colors. React configs use `textColor: "contrast"` for the same concept. Solid and transparent top-plane annotations use `--label-fill`. This applies to both the filled text path and the solid dashed `#top-inlay`, so default text + dash states keep one automatic contrast color.
86
+
87
+ - Darker top gradients may use a near-white label. Current example: `15 Alpha` uses `#f7fbff`.
88
+ - Lighter top gradients should use dark in-family annotation colors.
89
+ - Do not add a second outline copy for automatic contrast.
90
+
91
+ Wireframe text labels ignore `--label-fill` and use gradient wire paint:
92
+
93
+ ```css
94
+ fill: none !important;
95
+ stroke: var(--top-fill);
96
+ stroke-width: var(--label-wire-width);
97
+ ```
98
+
99
+ `#top-inlay-wire` also uses `stroke: var(--top-fill)`.
100
+
101
+ In React, `textStyle: "wireframe"` outlines the same live SVG text element used by filled mode. On solid/transparent material, outline paint comes from `textColor`; `contrast` resolves to `--label-fill`, matching filled text and solid dash. On wireframe material, outline paint comes from label-local `textWire` gradients; do not sample the full-face `--top-fill` ramp there, and do not replace the text with single-stroke lettering. The static constructor keeps `gpuStyle`, `gpuColor`, and `#gpu-wire-*` names only for its GPU example schema.
102
+
103
+ `constructor.html` adds explicit annotation overrides:
104
+
105
+ | Field | Values | Paint |
106
+ | --- | --- | --- |
107
+ | `gpuColor` | `contrast`, `white`, `black` | Filled or outlined GPU paint for solid/transparent material; `contrast` appears as `Auto` in the UI |
108
+ | `gpuStyle` | `solid`, `wireframe` | Filled or outlined GPU label |
109
+ | `dashColor` | `contrast`, `white`, `black` | Dash stroke for solid/transparent material; `contrast` appears as `Auto` in the UI |
110
+
111
+ Use `Auto` when the annotation should follow the palette. The exported value is `contrast`. Use `white` or `black` only when the user deliberately wants fixed annotation paint. React generated code emits `textColor`; the static constructor stores `gpuColor`. On wireframe material, dash always uses `--top-fill`; text uses the text surface gradient at full opacity when `textStyle` is `solid` and the text wire gradient when `textStyle` is `wireframe`. Dash symbol selection is not configurable in the constructor; it follows the squircle material.
112
+
113
+ Legacy configs with `gpuStyle: "transparent"` normalize to `solid`; there is no third text paint control.
114
+
115
+ ## Edge Colors
116
+
117
+ Filled faces use:
118
+
119
+ - `--top-edge` for `.active-top` and `.ghost-top`
120
+ - `--side-edge` for `.active-side` and `.ghost-side`
121
+ - `--face-edge-width: 0.35`
122
+ - `--face-edge-opacity: 0.72`
123
+
124
+ Edge colors must be darker members of the same family as the face gradient. Never use black.
125
+
126
+ ## Wireframe Colors
127
+
128
+ Wireframe mode and `.single-wire` use the top gradient for all visible line art:
129
+
130
+ - Prism strokes: `var(--top-fill)`
131
+ - Hidden back edge: `var(--top-fill)`
132
+ - Wire inlay: `var(--top-fill)`
133
+ - Wire text: `var(--top-fill)`
134
+
135
+ This is intentional: light does not interact with a wireframe surface, so top and lower curves should match instead of using separate side lighting.
136
+
137
+ ## Adding A Variant
138
+
139
+ When adding a new variant:
140
+
141
+ 1. Add `top-*`, `gpu-surface-*`, and `side-*` gradients in `index.html`.
142
+ 2. Mirror those gradients in `demo.html` and `constructor.html` if those pages need the new palette.
143
+ 3. Add a hidden variant radio.
144
+ 4. Add a selected-variant CSS selector targeting `.hero-card` and `.single-drawer`.
145
+ 5. Add a `.v*` class with matching variables.
146
+ 6. Add the palette to the constructor `PALETTES` array.
147
+ 7. Add a variant card in the drawer.
148
+ 8. Add the title span in the hero copy.
149
+ 9. Update this palette table.
150
+ 10. Render hero, variant drawer, single-state drawer, demo presets, and constructor controls to confirm synchronized colors.
@@ -0,0 +1,118 @@
1
+ # Geometry Contract
2
+
3
+ This file documents how to reproduce the squircle-prism coordinates in `index.html`. The SVG point lists are generated data. Do not hand-edit prism, inlay, or hidden-edge coordinates.
4
+
5
+ ## Current Parameters
6
+
7
+ - Superellipse exponent: `n = 12`
8
+ - Samples: `N = 160`
9
+ - Half-size: `a = 225.49`
10
+ - Prism height: `h = 10`
11
+ - Camera elevation: `ANG = 20deg`
12
+ - Horizontal projection: `cosA = 0.9396926207859084`
13
+ - Vertical projection: `sinA = 0.3420201433256687`
14
+ - Computed center: `cx = 400`, `cy = 155.59`
15
+ - Top-plane origin: `(400, 145.6)`, which is `(cx, cy - h)`
16
+ - SVG viewBox: `0 0 800 480`
17
+ - Generated top-face bbox: `x = 0..800`, `y = 0..291.18`
18
+ - Generated side/wire bbox: `x = 0..800`, `y = 145.59..301.18`
19
+ - Layer offsets: bottom `translate(0 176)`, middle `translate(0 88)`, top `translate(0 0)`
20
+ - Inlay scale: `0.6 * a`
21
+
22
+ The high exponent makes the squircle much squarer than a standard `n = 4` squircle. The `20deg` elevation is the low-angle isometric projection currently used by both the prism and label/inlay transforms.
23
+
24
+ ## Superellipse Outline
25
+
26
+ Generate the outline by sampling a full turn:
27
+
28
+ ```js
29
+ const t = (2 * Math.PI * i) / N;
30
+ const c = Math.cos(t);
31
+ const s = Math.sin(t);
32
+
33
+ const x = a * Math.sign(c) * Math.pow(Math.abs(c), 2 / n);
34
+ const y = a * Math.sign(s) * Math.pow(Math.abs(s), 2 / n);
35
+ ```
36
+
37
+ For the current design, `i` runs from `0` to `159`.
38
+
39
+ ## Projection
40
+
41
+ Project every outline point twice, once at the prism top and once at the bottom:
42
+
43
+ ```js
44
+ const sx = cx + (x - y) * cosA;
45
+ const sy = cy + (x + y) * sinA - z;
46
+ ```
47
+
48
+ Solve `a` so the projected width is exactly `800`:
49
+
50
+ ```js
51
+ const unitScreenX = unitPoints.map((p) => (p.x - p.y) * cosA);
52
+ const unitWidth = Math.max(...unitScreenX) - Math.min(...unitScreenX);
53
+ const a = 800 / unitWidth;
54
+ ```
55
+
56
+ Then solve the center values:
57
+
58
+ ```js
59
+ const screenX = points.map((p) => (p.x - p.y) * cosA);
60
+ const screenY = points.map((p) => (p.x + p.y) * sinA);
61
+
62
+ const cx = -Math.min(...screenX);
63
+ const cy = h - Math.min(...screenY);
64
+ ```
65
+
66
+ ## Visible Side Wall
67
+
68
+ Only the front-facing half of the prism wall is drawn. For every sampled point, compute the superellipse outward normal direction:
69
+
70
+ ```js
71
+ const nx = Math.sign(c) * Math.pow(Math.abs(c), (2 * (n - 1)) / n);
72
+ const ny = Math.sign(s) * Math.pow(Math.abs(s), (2 * (n - 1)) / n);
73
+ ```
74
+
75
+ A segment belongs to the visible front wall when:
76
+
77
+ ```js
78
+ nx + ny >= 0
79
+ ```
80
+
81
+ The side wall polygon is built from:
82
+
83
+ ```text
84
+ top visible points in order
85
+ bottom visible points in reverse order
86
+ ```
87
+
88
+ Rotate the sampled array if needed so the selected front-facing indices are emitted as one uninterrupted run. Do not emit separate left/right wall pieces; that makes the vertical sides drift away from the real silhouette tips.
89
+
90
+ The complementary back-bottom edge is emitted as `ghost-hidden` and is only shown in wireframe mode.
91
+
92
+ ## Generated SVG Blocks
93
+
94
+ The generator emits:
95
+
96
+ - `#prism-ghost` with `ghost-hidden`, `ghost-side`, and `ghost-top`
97
+ - `#prism-active` with `active-side` and `active-top`
98
+ - `#top-inlay` and `#top-inlay-wire`
99
+ - `#label-gpu`, documented separately in [rendering.md](./rendering.md)
100
+
101
+ ## Regeneration Checklist
102
+
103
+ When changing geometry, regenerate the polygon coordinates from the formulas above and keep these in sync:
104
+
105
+ - The generated comment in `index.html`
106
+ - All SVG `viewBox` values
107
+ - `.hero-squircle`, `.mini-squircle`, and `.single-squircle` aspect ratios in `public/static/styles.css`
108
+ - Both generated inlay copies (`#top-inlay` and `#top-inlay-wire`)
109
+ - Every `.plane-label` transform if `cosA`, `sinA`, `cx`, or `cy - h` changes
110
+ - Gradient bboxes documented in [rendering.md](./rendering.md)
111
+
112
+ For small visual changes:
113
+
114
+ - Increase `n` for squarer corners and a smaller visible radius.
115
+ - Decrease `n` for softer, rounder corners.
116
+ - Decrease `ANG` to make the view less top-down.
117
+ - Increase `ANG` to show more of the top face.
118
+ - Increase `h` to make the prism side taller.