@dstackai/sqircle 0.1.0 → 0.1.1

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.
package/README.md CHANGED
@@ -1,22 +1,45 @@
1
1
  # Squircle
2
2
 
3
- Squircle is a React/TypeScript component for rendering precise isometric squircle-prism SVG compositions. The reusable implementation lives in `src/squircle`; the HTML files are visual fixtures, demos, and constructor prototypes that preserve earlier design decisions.
3
+ [![npm version](https://img.shields.io/npm/v/@dstackai/sqircle.svg)](https://www.npmjs.com/package/@dstackai/sqircle)
4
+ [![npm package](https://img.shields.io/npm/dm/@dstackai/sqircle.svg)](https://www.npmjs.com/package/@dstackai/sqircle)
5
+ [![license](https://img.shields.io/npm/l/@dstackai/sqircle.svg)](LICENSE)
6
+
7
+ Squircle is a React/TypeScript renderer for precise isometric squircle-prism SVG compositions. The reusable implementation lives in `src/squircle`; the root HTML files are Vite/React example shells that exercise the same renderer.
4
8
 
5
9
  Use this repo when you need:
6
10
 
7
11
  - `SquircleScene`: a configurable SVG renderer for 0-N squircle layers.
8
- - `SquircleEditor`: a small constructor UI for editing layers and copying React code.
9
12
  - A documented visual system for squircle geometry, gradients, wireframes, top-plane text, and dashed inlays.
10
- - Static HTML reference pages for regression checks and agent handoff.
13
+ - React-backed local examples for regression checks and agent handoff.
14
+
15
+ ## Package
16
+
17
+ Install from npm:
18
+
19
+ ```sh
20
+ npm install @dstackai/sqircle
21
+ ```
22
+
23
+ Use the renderer and component CSS:
24
+
25
+ ```tsx
26
+ import { SquircleScene, type SquircleLayerConfig } from "@dstackai/sqircle";
27
+ import "@dstackai/sqircle/style.css";
28
+ ```
11
29
 
12
30
  ## Quick Start
13
31
 
32
+ For local development of this repo:
33
+
14
34
  ```sh
15
35
  npm install
16
36
  npm run dev
17
37
  ```
18
38
 
19
- Open `react.html` from the Vite dev server to use the React constructor demo.
39
+ Open the Vite dev server and choose a page:
40
+
41
+ - `/index.html`: hero example plus single-squircle state drawer.
42
+ - `/demo.html`: selectable composition gallery.
20
43
 
21
44
  For production checks:
22
45
 
@@ -29,7 +52,8 @@ git diff --check -- .
29
52
  ## React Usage
30
53
 
31
54
  ```tsx
32
- import { SquircleScene, type SquircleLayerConfig } from "./squircle";
55
+ import { SquircleScene, type SquircleLayerConfig } from "@dstackai/sqircle";
56
+ import "@dstackai/sqircle/style.css";
33
57
 
34
58
  const layers: SquircleLayerConfig[] = [
35
59
  {
@@ -50,15 +74,128 @@ export function ExampleSquircle() {
50
74
  }
51
75
  ```
52
76
 
53
- For the full API, layer model, palette fields, editor props, and generated-code export, read [React Component Guide](docs/react/README.md).
77
+ For the full renderer API, layer model, geometry, strokes, opacity, and palette fields, read [React Component Guide](docs/react/README.md).
78
+
79
+ ## Options Reference
80
+
81
+ This is the package-level renderer API reference. Keep it synchronized with `docs/react/README.md` and `src/squircle/types.ts`.
82
+
83
+ ### `SquircleScene`
84
+
85
+ | Prop | Type | Default | Meaning |
86
+ | --- | --- | --- | --- |
87
+ | `layers` | `SquircleLayerConfig[]` | required | Draw-order layer array. First item is lowest/backmost; last item is topmost/frontmost. |
88
+ | `geometry` | `SquircleGeometryConfig` | `DEFAULT_GEOMETRY` | Scene geometry, projection, prism height, and dashed inlay scale. |
89
+ | `selectedLayerId` | `string \| null` | `null` | Marks a layer as selected for class/state styling. Does not move or restack layers. |
90
+ | `theme` | `light`, `dark` | `light` | Adds theme classes/data attributes for host styling while keeping the SVG background transparent. |
91
+ | `idPrefix` | `string` | generated React id | Prefix for SVG gradient ids. Use when deterministic ids are required. |
92
+ | `className` | `string` | none | Extra class on the root SVG. |
93
+ | `ariaLabel` | `string` | `Squircle scene` | Accessible label for the SVG. |
94
+ | `fitToLayers` | `boolean` | `true` | Expands the viewBox height to include visible layer offsets. |
95
+ | `transitionMs` | `number` | `220` | Hover crossfade duration in milliseconds. |
96
+ | `onLayerSelect` | `(layerId: string) => void` | none | Called when a rendered layer is clicked. |
97
+
98
+ ### `SquircleLayerConfig`
99
+
100
+ | Field | Type | Default | Meaning |
101
+ | --- | --- | --- | --- |
102
+ | `id` | `string` | required | Stable layer id. |
103
+ | `visible` | `boolean` | `true` | Hidden layers are skipped without changing other offsets. |
104
+ | `offset` | `{ x?: number; y?: number }` | `{ x: 0, y: 0 }` | SVG translation for this layer. |
105
+ | `base` | `SquircleVariantConfig` | required | Normal layer state. |
106
+ | `hover` | `SquircleVariantConfig` | none | Hover state merged over `base`. If absent or identical, no hover copy is rendered. |
107
+ | `stroke` | `Partial<SquircleStrokeConfig>` | default strokes | Layer-wide stroke overrides. |
108
+ | `opacity` | `Partial<SquircleOpacityConfig>` | default opacity | Layer-wide opacity overrides. |
109
+ | `className` | `string` | none | Extra class on the layer group. |
110
+
111
+ ### `SquircleVariantConfig`
112
+
113
+ Each layer has a required `base` variant and an optional `hover` variant. Hover is a state swap: the component renders a `.sq-hover` group only when the resolved hover variant differs from base. If hover is absent or identical, the layer has no `.sq-has-hover`, no hover copy, and no crossfade blink.
114
+
115
+ | Field | Values | Default | Meaning |
116
+ | --- | --- | --- | --- |
117
+ | `material` | `solid`, `transparent`, `wireframe` | `wireframe` | Prism rendering mode. |
118
+ | `paletteId` | `13` through `20` | `15` | Palette from `SQUIRCLE_PALETTES`. |
119
+ | `text` | `string`, `boolean` | none | Render top-plane text. Pass a string such as `"GPU"` or `"{}"`; `true` is a compatibility shorthand for `"GPU"`. |
120
+ | `dash` | `boolean` | `false` | Render the dashed inlay. |
121
+ | `textStyle` | `solid`, `wireframe` | `solid` | Filled or outlined text. |
122
+ | `textColor` | `auto`, `white`, `black`, `contrast` | `contrast` | Text paint for solid/transparent material. `auto` and `contrast` currently resolve the same way. |
123
+ | `textSize` | `number` | `62` | Text font size. |
124
+ | `textFontFamily` | `string` | `Arial, Helvetica, sans-serif` | SVG text font family. |
125
+ | `textFontWeight` | `string`, `number` | `400` | SVG text font weight. |
126
+ | `dashColor` | `auto`, `white`, `black`, `contrast` | `contrast` | Dash paint for solid/transparent material. |
127
+ | `stroke` | `Partial<SquircleStrokeConfig>` | default strokes | Per-variant stroke overrides. |
128
+ | `opacity` | `Partial<SquircleOpacityConfig>` | default opacity | Per-variant opacity overrides. |
129
+
130
+ `auto` is the preferred value for palette-managed annotation paint. `contrast` remains a backwards-compatible alias and renders the same way. `gpu`, `gpuStyle`, and `gpuColor` are deprecated aliases accepted only for older snippets; new configs and generated code should use `text`, `textStyle`, and `textColor`.
131
+
132
+ ### `SquircleGeometryConfig`
133
+
134
+ | Field | Type | Default | Meaning |
135
+ | --- | --- | --- | --- |
136
+ | `width` | `number` | `800` | SVG viewBox width and geometry fitting target. |
137
+ | `viewBoxHeight` | `number` | `480` | Base SVG viewBox height before optional layer fitting. |
138
+ | `exponent` | `number` | `12` | Superellipse exponent. Lower is rounder, higher is squarer. |
139
+ | `samples` | `number` | `160` | Number of sampled superellipse points. |
140
+ | `halfSize` | `number` | computed from `width` | Raw superellipse half-size before projection. |
141
+ | `prismHeight` | `number` | `10` | Extrusion height in SVG units. |
142
+ | `angleDegrees` | `number` | `20` | Isometric projection elevation angle. |
143
+ | `inlayScale` | `number` | `0.6` | Dashed inlay scale relative to the outer squircle. |
144
+ | `center` | `{ x: number; y: number }` | computed | Projection center. Override only when manually composing a custom viewBox. |
145
+
146
+ ### `SquircleStrokeConfig`
147
+
148
+ | Field | Default | Meaning |
149
+ | --- | --- | --- |
150
+ | `face` | `0.35` | Hairline stroke for filled top and side faces. |
151
+ | `faceOpacity` | `0.72` | Opacity for filled-face strokes. |
152
+ | `wire` | `1.6` | Visible wireframe outline width. |
153
+ | `wireOpacity` | `0.88` | Visible wireframe outline opacity. |
154
+ | `hidden` | `1.2` | Hidden/back wireframe guide width. |
155
+ | `hiddenOpacity` | `0.28` | Hidden/back wireframe guide opacity. |
156
+ | `dash` | `2.2` | Dashed inlay width for solid/transparent material. |
157
+ | `wireDash` | `1.6` | Dashed inlay width for wireframe material. |
158
+ | `labelWire` | `1.1` | Outlined text stroke width. |
159
+
160
+ ### `SquircleOpacityConfig`
161
+
162
+ | Field | Default | Meaning |
163
+ | --- | --- | --- |
164
+ | `transparentFace` | `0.38` | Face opacity for `material: "transparent"`. |
165
+ | `transparentAnnotation` | `0.62` | Text/dash opacity on transparent material. |
166
+ | `solidAnnotation` | `0.88` | Text/dash opacity on solid or wireframe material. |
167
+
168
+ ### Palettes
169
+
170
+ `paletteId` accepts any id from `SQUIRCLE_PALETTE_IDS`: `13`, `14`, `15`, `16`, `17`, `18`, `19`, `20`. The palette object fields are:
171
+
172
+ | Field | Meaning |
173
+ | --- | --- |
174
+ | `id` | Stable palette id. |
175
+ | `label` | Human-readable palette name. |
176
+ | `top` | Top-face gradient stops. |
177
+ | `side` | Side-wall gradient stops. |
178
+ | `textWire` | Gradient stops used for outlined text. |
179
+ | `labelFill` | Automatic filled text/dash color. |
180
+ | `topEdge` | Top-face hairline stroke color. |
181
+ | `sideEdge` | Side-wall hairline stroke color. |
182
+ | `swatch` | Two-color UI swatch. |
183
+
184
+ ## HTML Pages
185
+
186
+ The root HTML files are intentionally thin shells. They mount React entrypoints from `src/pages`, and those pages render examples built on the package components.
187
+
188
+ Legacy static snapshots from the exploration phase are archived under `legacy-static/`, which is ignored by git. Use them only as local visual references; new app work should use `@dstackai/sqircle` or the source components in `src/squircle`.
54
189
 
55
190
  ## Documentation Map
56
191
 
57
192
  Start at [Documentation Index](docs/README.md). The docs are split by purpose:
58
193
 
59
- - [React Component Guide](docs/react/README.md): how to use and configure `SquircleScene`, `SquircleEditor`, and code export.
194
+ - [React Component Guide](docs/react/README.md): how to use and configure `SquircleScene`.
195
+ - [Editor Guide](docs/react/editor.md): how to use the optional constructor UI and React code exporter.
60
196
  - [Design Documentation](docs/design/README.md): geometry, rendering, color, annotation, wireframe, and single-squircle visual rules.
61
- - [Static Prototype Documentation](docs/static/README.md): `index.html`, `demo.html`, and `constructor.html` contracts.
197
+ - [React Example Pages](docs/examples/README.md): `index.html`, `demo.html`, `constructor.html`, and `react.html` page contracts.
198
+ - [Legacy Static Snapshots](docs/static/README.md): local ignored snapshots kept for reference only.
62
199
  - [Verification Checklist](docs/verification.md): static checks, render checks, and expected behavior.
63
200
 
64
201
  ## Repository Shape
@@ -66,19 +203,20 @@ Start at [Documentation Index](docs/README.md). The docs are split by purpose:
66
203
  | Path | Role |
67
204
  | --- | --- |
68
205
  | `src/squircle` | Main React/TypeScript component implementation |
69
- | `react.html` | Vite entry for the React constructor demo |
70
- | `index.html` | Static CSS-only hero and variant fixture |
71
- | `demo.html` | Static preset gallery fixture |
72
- | `constructor.html` | Legacy dynamic constructor prototype that exports React code |
73
- | `public/static/styles.css` | Shared static-page fixture styles, served at `/static/styles.css` |
74
- | `static/styles.css` | Symlink for opening static HTML files directly with `file://` |
75
- | `docs/` | Agent-facing component, design, static-page, and verification docs |
206
+ | `src/pages` | React example pages mounted by the root HTML files |
207
+ | `index.html` | Vite shell for the React hero and single-state example |
208
+ | `demo.html` | Vite shell for the React composition gallery |
209
+ | `constructor.html` | Vite shell for the React constructor UI |
210
+ | `react.html` | Compatibility Vite shell for the constructor UI |
211
+ | `legacy-static/` | Ignored local archive of the former static HTML/CSS snapshots |
212
+ | `docs/` | Agent-facing component, design, example-page, and verification docs |
76
213
 
77
214
  ## Current Visual Defaults
78
215
 
79
216
  - Superellipse: `n = 12`, `N = 160`, `a = 225.49`
80
217
  - Prism height: `h = 10`
81
218
  - Projection: `20deg`
219
+ - Dashed inlay scale: `60%`
82
220
  - ViewBox: `0 0 800 480`
83
221
  - Default palette: `15 Alpha`
84
222
  - Default three-layer offsets: bottom `y = 176`, middle `y = 88`, top `y = 0`
@@ -86,7 +224,7 @@ Start at [Documentation Index](docs/README.md). The docs are split by purpose:
86
224
  ## Hard Rules
87
225
 
88
226
  - New reusable work should go through `src/squircle`.
89
- - Component styles belong beside their components in `src/squircle`; static fixture styles live under `public/static/`, with `static/styles.css` kept as a direct-file symlink.
227
+ - Component styles belong beside their components in `src/squircle`; example-page styles live under `src/pages`.
90
228
  - Do not hand-edit generated prism, inlay, or hidden-edge coordinates; regenerate from [Geometry Contract](docs/design/geometry.md).
91
229
  - Keep hover swaps opacity-only: no transform, scale, filter, halo, or layer-gap changes.
92
230
  - Render top-plane text as one SVG text element on the projected top plane. Do not duplicate it for filled and wireframe states.
@@ -1,9 +1,12 @@
1
- import type { SquircleLayerConfig, SquircleTheme } from "./types";
1
+ import type { SquircleGeometryConfig, SquircleLayerConfig, SquircleTheme } from "./types";
2
2
  import "./SquircleEditor.css";
3
3
  export interface SquircleEditorProps {
4
4
  value?: SquircleLayerConfig[];
5
5
  initialLayers?: SquircleLayerConfig[];
6
6
  onChange?: (layers: SquircleLayerConfig[]) => void;
7
+ geometry?: SquircleGeometryConfig;
8
+ initialGeometry?: SquircleGeometryConfig;
9
+ onGeometryChange?: (geometry: SquircleGeometryConfig) => void;
7
10
  title?: string;
8
11
  description?: string;
9
12
  /** @deprecated The editor now exports React code instead of schema-tagged JSON. */
@@ -21,4 +24,4 @@ export interface SquircleEditorProps {
21
24
  showThemeSwitch?: boolean;
22
25
  }
23
26
  export declare function createDefaultSquircleEditorLayers(paletteId?: string): SquircleLayerConfig[];
24
- export declare function SquircleEditor({ value, initialLayers, onChange, title, description, className, layerGap, showCode, showConfig, codeComponentName, codeImportPath, theme, defaultTheme, onThemeChange, showThemeSwitch }: SquircleEditorProps): import("react").JSX.Element;
27
+ export declare function SquircleEditor({ value, initialLayers, onChange, geometry, initialGeometry, onGeometryChange, title, description, className, layerGap, showCode, showConfig, codeComponentName, codeImportPath, theme, defaultTheme, onThemeChange, showThemeSwitch }: SquircleEditorProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import{c as e,d as t,f as n,l as r,o as i,p as a,r as o,s,u as c}from"./pages-DQf07DWd.js";var l=a();function u({layers:e,theme:t,geometry:n,componentName:r=`CustomSquircle`,importPath:i=`./squircle`,styleImportPath:a,ariaLabel:o}){let s=te(r),c=JSON.stringify(i),l=d(i,a),u=JSON.stringify(o??`${s} composition`),f=JSON.stringify(e.map(ee),null,2),p=n?JSON.stringify(m(n),null,2):null;return[`import { SquircleScene, ${p?`type SquircleGeometryConfig, type SquircleLayerConfig, type SquircleTheme`:`type SquircleLayerConfig, type SquircleTheme`} } from ${c};`,...l?[`import ${JSON.stringify(l)};`]:[],``,`const theme: SquircleTheme = ${JSON.stringify(t)};`,...p?[``,`const geometry: SquircleGeometryConfig = ${p};`]:[],``,`const layers: SquircleLayerConfig[] = ${f};`,``,`export function ${s}() {`,` return (`,` <SquircleScene`,` theme={theme}`,` layers={layers}`,...p?[` geometry={geometry}`]:[],` ariaLabel={${u}}`,` />`,` );`,`}`,``].join(`
2
+ `)}function d(e,t){return t===!1?null:typeof t==`string`?t:e===`@dstackai/sqircle`?`@dstackai/sqircle/style.css`:null}function ee(e){return m({...e,base:f(e.base),hover:e.hover?f(e.hover):void 0})}function f(e){let{gpu:t,gpuStyle:n,gpuColor:r,...i}=e,a=p(i.text,t),o=i.textStyle??n,s=i.textColor??r;return m({...i,text:a,textStyle:o,textColor:s})}function p(e,t){if(typeof e==`string`)return e;if(e===!0||t)return`GPU`;if(e===!1)return!1}function m(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>e!==void 0))}function te(e){let t=e.trim().split(/[^a-zA-Z0-9]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``)||`CustomSquircle`;return/^[A-Za-z_$]/.test(t)?t:`Squircle${t}`}var h=s(),g=88,_=`GPU`,ne={exponent:c.exponent,prismHeight:c.prismHeight,inlayScale:c.inlayScale},v=0,y=100,b=4,x=24,re=4,ie=36,ae=.35,oe=.82,S=34,C=92,w=62,T=400,se=[{value:`wireframe`,label:`Wire`,title:`Gradient outline with transparent faces`},{value:`solid`,label:`Solid`,title:`Filled prism with top and side gradients`},{value:`transparent`,label:`Glass`,title:`Translucent filled prism`}],ce=[{value:`solid`,label:`Filled`,title:`Filled top-plane text`},{value:`wireframe`,label:`Outline`,title:`Outlined top-plane text`}],le=[{value:`auto`,label:`Auto`,title:`Use the palette contrast color`},{value:`white`,label:`White`,title:`Use fixed white annotation paint`},{value:`black`,label:`Black`,title:`Use fixed black annotation paint`}],ue=[{value:`300`,label:`Light`,title:`Thin top-plane label`},{value:`400`,label:`Regular`,title:`Default top-plane label weight`},{value:`500`,label:`Medium`,title:`Slightly stronger top-plane label`},{value:`600`,label:`Semi`,title:`Bold but still clean on wireframe outlines`},{value:`700`,label:`Bold`,title:`Strongest top-plane label`}];function de(e=`15`){return t([{id:`layer-1`,visible:!0,base:{material:`wireframe`,paletteId:e}},{id:`layer-2`,visible:!0,base:{material:`wireframe`,paletteId:e}},{id:`layer-3`,visible:!0,base:{material:`wireframe`,paletteId:e}}],g)}function fe({value:e,initialLayers:n,onChange:r,geometry:a,initialGeometry:o,onGeometryChange:s,title:d=`Squircle`,description:ee=`React component constructor with 0-N layers and exported code.`,className:f,layerGap:p=g,showCode:m,showConfig:te=!0,codeComponentName:_,codeImportPath:b=`./squircle`,theme:x,defaultTheme:S=`light`,onThemeChange:C,showThemeSwitch:w=!0}){let[T,se]=(0,l.useState)(()=>n??de()),[ce,le]=(0,l.useState)(()=>({...ne,...o})),[ue,fe]=(0,l.useState)(S),O=e??T,k=a??ce,A=x??ue,[j,R]=(0,l.useState)(()=>O.at(-1)?.id??null),[V,H]=(0,l.useState)(`base`),[U,W]=(0,l.useState)(!1),[G,K]=(0,l.useState)(!1),q=O.find(e=>e.id===j)??null,J=q?O.findIndex(e=>e.id===q.id)+1:null,Te=O.filter(e=>e.visible!==!1).length,Ee=m??te,De=Math.max(0,...O.map(e=>e.offset?.y??0)),Oe=(0,l.useMemo)(()=>({layers:O,geometry:{...k,viewBoxHeight:Math.max(k.viewBoxHeight??c.viewBoxHeight,De+320)},selectedLayerId:j}),[k,O,De,j]),Y=(0,l.useMemo)(()=>u({layers:O,theme:A,geometry:we(k),componentName:_??d,importPath:b,ariaLabel:`${d} composition`}),[k,A,_,b,O,d]);(0,l.useEffect)(()=>{j&&O.some(e=>e.id===j)||R(O.at(-1)?.id??null)},[O,j]),(0,l.useEffect)(()=>{K(!1)},[Y]);function X(n,i={}){let a=i.reflow?t(n,p):n;e||se(a),r?.(a)}function Z(e){let t={...k,...e};a||le(t),s?.(t)}function Q(e,t){X(O.map(n=>n.id===e?t(n):n))}function ke(e){q&&Q(q.id,t=>({...t,base:{...t.base,...e}}))}function Ae(e){q&&Q(q.id,t=>({...t,hover:{...t.hover??{},...e}}))}function $(){let e={id:xe(O),visible:!0,base:{material:`wireframe`,paletteId:O.at(-1)?.base.paletteId??`15`}},t=[...O,e];R(e.id),H(`base`),X(t,{reflow:!0})}function je(){if(!q)return;let e=O.filter(e=>e.id!==q.id);R(e.at(-1)?.id??null),H(`base`),X(e,{reflow:!0})}function Me(e){Q(e,e=>({...e,visible:e.visible===!1}))}function Ne(e){x||fe(e),C?.(e)}async function Pe(){try{await navigator.clipboard.writeText(Y)}catch{let e=document.createElement(`textarea`);e.value=Y,e.setAttribute(`readonly`,``),e.style.position=`fixed`,e.style.top=`-9999px`,document.body.append(e),e.select(),document.execCommand(`copy`),e.remove()}K(!0),window.setTimeout(()=>K(!1),1400)}return(0,h.jsxs)(`main`,{className:[`squircle-editor`,`squircle-editor-${A}`,f].filter(Boolean).join(` `),"data-theme":A,"aria-label":`Squircle editor`,children:[(0,h.jsxs)(`header`,{className:`squircle-editor-topbar`,children:[(0,h.jsxs)(`div`,{className:`topbar-title`,children:[(0,h.jsx)(`h1`,{children:d}),(0,h.jsx)(`p`,{children:ee})]}),(0,h.jsx)(`div`,{className:`topbar-actions`,children:w?(0,h.jsxs)(`div`,{className:`theme-switch`,role:`group`,"aria-label":`Theme`,children:[(0,h.jsx)(`button`,{type:`button`,"aria-pressed":A===`light`,onClick:()=>Ne(`light`),children:`Light`}),(0,h.jsx)(`button`,{type:`button`,"aria-pressed":A===`dark`,onClick:()=>Ne(`dark`),children:`Dark`})]}):null})]}),(0,h.jsxs)(`section`,{className:`squircle-editor-workspace`,children:[(0,h.jsxs)(`aside`,{className:`side-panel layers-panel`,children:[(0,h.jsxs)(`div`,{className:`panel-heading`,children:[(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`h2`,{children:`Layers`}),(0,h.jsxs)(`span`,{children:[Te,`/`,O.length,` visible`]})]}),(0,h.jsxs)(`div`,{className:`panel-actions`,children:[(0,h.jsx)(`button`,{type:`button`,className:`icon-button primary-action`,"aria-label":`Add layer`,title:`Add layer`,onClick:$,children:(0,h.jsx)(he,{})}),(0,h.jsx)(`button`,{type:`button`,className:`icon-button`,"aria-label":`Clear layers`,title:`Clear layers`,onClick:()=>X([]),children:(0,h.jsx)(P,{})})]})]}),(0,h.jsx)(`div`,{className:`squircle-editor-layer-list`,children:[...O].reverse().map((e,t)=>{let n=O.length-t,r=F(e.base.paletteId),i=e.base.material??`wireframe`;return(0,h.jsxs)(`article`,{className:e.id===j?`squircle-editor-layer-row is-active`:`squircle-editor-layer-row`,children:[(0,h.jsxs)(`button`,{type:`button`,className:`layer-select`,onClick:()=>R(e.id),title:`Edit ${L(e.id,n,O.length)}`,children:[(0,h.jsxs)(`span`,{className:`layer-card-topline`,children:[(0,h.jsx)(`span`,{className:`layer-number`,children:String(n).padStart(2,`0`)}),(0,h.jsx)(`strong`,{children:L(e.id,n,O.length)}),(0,h.jsx)(`span`,{className:`material-pill material-pill-${i}`,children:I(i)})]}),(0,h.jsxs)(`span`,{className:`layer-card-meta`,children:[(0,h.jsxs)(`span`,{className:`layer-palette-chip`,children:[(0,h.jsx)(`span`,{className:`palette-swatch`,style:{background:`linear-gradient(135deg, ${r.swatch[0]}, ${r.swatch[1]})`}}),r.label]}),(0,h.jsxs)(`span`,{className:`layer-feature-tags`,children:[B(e.base)?(0,h.jsx)(`span`,{children:`Text`}):null,e.base.dash?(0,h.jsx)(`span`,{children:`Dash`}):null,e.hover?(0,h.jsx)(`span`,{children:`Hover`}):null,!B(e.base)&&!e.base.dash&&!e.hover?(0,h.jsx)(`span`,{children:`Clean`}):null]})]})]}),(0,h.jsx)(`button`,{type:`button`,className:`icon-button layer-visibility`,"aria-label":e.visible===!1?`Show layer`:`Hide layer`,"aria-pressed":e.visible!==!1,title:e.visible===!1?`Show layer`:`Hide layer`,onClick:()=>Me(e.id),children:e.visible===!1?(0,h.jsx)(_e,{}):(0,h.jsx)(ge,{})})]},e.id)})}),(0,h.jsxs)(E,{title:`Shape`,children:[(0,h.jsx)(M,{label:`Radius`,value:Se(k.exponent??c.exponent),min:v,max:y,step:1,formatValue:e=>`${Math.round(e)}%`,onChange:e=>Z({exponent:Ce(e)})}),(0,h.jsx)(M,{label:`Height`,value:k.prismHeight??c.prismHeight,min:re,max:ie,step:1,formatValue:e=>`${Math.round(e)}px`,onChange:e=>Z({prismHeight:e})}),(0,h.jsx)(M,{label:`Dash size`,value:(k.inlayScale??c.inlayScale)*100,min:ae*100,max:oe*100,step:1,formatValue:e=>`${Math.round(e)}%`,onChange:e=>Z({inlayScale:z(e/100,2)})})]})]}),(0,h.jsxs)(`section`,{className:`squircle-editor-preview`,children:[(0,h.jsxs)(`div`,{className:`preview-toolbar`,children:[(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`h2`,{children:`Preview`}),(0,h.jsx)(`span`,{children:J?`Editing Layer ${J}`:`${Te} visible layers`})]}),Ee?(0,h.jsxs)(`div`,{className:`preview-actions`,children:[(0,h.jsxs)(`button`,{type:`button`,className:`code-toggle-button`,"aria-pressed":U,onClick:()=>W(e=>!e),children:[(0,h.jsx)(ye,{}),`Code`]}),(0,h.jsxs)(`button`,{type:`button`,className:`copy-code-button`,"aria-label":`Copy React code`,title:`Copy React code`,onClick:Pe,children:[(0,h.jsx)(N,{}),(0,h.jsx)(`span`,{className:`copy-status`,"aria-live":`polite`,children:G?`Copied`:``})]})]}):null]}),(0,h.jsx)(`div`,{className:`preview-stage`,children:(0,h.jsx)(i,{...Oe,theme:A,ariaLabel:`Editable squircle composition`,onLayerSelect:e=>{R(e),H(`base`)}})}),Ee?(0,h.jsxs)(`section`,{className:U?`code-panel is-open`:`code-panel`,"aria-label":`Generated React code`,children:[(0,h.jsxs)(`header`,{className:`code-panel-header`,children:[(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`h2`,{children:`Code`}),(0,h.jsx)(`p`,{children:`Ready-to-use React component.`})]}),(0,h.jsxs)(`button`,{type:`button`,className:`copy-code-button`,"aria-label":`Copy React code`,title:`Copy React code`,onClick:Pe,children:[(0,h.jsx)(N,{}),(0,h.jsx)(`span`,{className:`copy-status`,"aria-live":`polite`,children:G?`Copied`:``})]})]}),U?(0,h.jsx)(`pre`,{className:`code-output`,children:(0,h.jsx)(`code`,{children:Y})}):null]}):null]}),(0,h.jsx)(`aside`,{className:q?`side-panel inspector-panel`:`side-panel inspector-panel is-empty`,children:q?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsxs)(`div`,{className:`inspector-heading`,children:[(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`span`,{className:`inspector-kicker`,children:`Selected`}),(0,h.jsx)(`h2`,{children:J?L(q.id,J,O.length):q.id}),(0,h.jsx)(`p`,{children:be(q.base)})]}),(0,h.jsxs)(`div`,{className:`panel-actions`,children:[(0,h.jsx)(`button`,{type:`button`,className:`icon-button`,"aria-label":`Close inspector`,title:`Close inspector`,onClick:()=>R(null),children:(0,h.jsx)(ve,{})}),(0,h.jsx)(`button`,{type:`button`,className:`icon-button danger`,"aria-label":`Delete layer`,title:`Delete layer`,onClick:je,children:(0,h.jsx)(P,{})})]})]}),(0,h.jsxs)(`div`,{className:`state-switch`,role:`group`,"aria-label":`Layer state`,children:[(0,h.jsx)(`button`,{type:`button`,"aria-pressed":V===`base`,onClick:()=>H(`base`),children:`Base`}),(0,h.jsxs)(`button`,{type:`button`,"aria-pressed":V===`hover`,onClick:()=>H(`hover`),children:[`Hover`,q.hover?(0,h.jsx)(`span`,{className:`state-dot`,"aria-hidden":`true`}):null]})]}),V===`base`?(0,h.jsx)(pe,{title:`Base State`,variant:q.base,onChange:ke}):(0,h.jsxs)(E,{title:`Hover State`,children:[(0,h.jsx)(D,{label:`Hover variant`,checked:!!q.hover,title:`Swap this layer to another variant on hover.`,onChange:e=>{Q(q.id,t=>({...t,hover:e?{...t.base,material:t.base.material===`wireframe`?`solid`:`wireframe`}:void 0}))}}),q.hover?(0,h.jsx)(me,{variant:{...q.base,...q.hover},onChange:Ae}):null]}),(0,h.jsxs)(E,{title:`Stroke Widths`,collapsible:!0,defaultOpen:!1,children:[(0,h.jsx)(M,{label:`Face`,value:q.stroke?.face??.35,min:0,max:1.5,step:.05,onChange:e=>Q(q.id,t=>({...t,stroke:{...t.stroke,face:e}}))}),(0,h.jsx)(M,{label:`Wire`,value:q.stroke?.wire??1.6,min:.4,max:4,step:.1,onChange:e=>Q(q.id,t=>({...t,stroke:{...t.stroke,wire:e}}))}),(0,h.jsx)(M,{label:`Dash`,value:q.stroke?.dash??2.2,min:.6,max:5,step:.1,onChange:e=>Q(q.id,t=>({...t,stroke:{...t.stroke,dash:e}}))}),(0,h.jsx)(M,{label:`Text outline`,value:q.stroke?.labelWire??1.1,min:.4,max:2,step:.05,onChange:e=>Q(q.id,t=>({...t,stroke:{...t.stroke,labelWire:e}}))})]})]}):(0,h.jsxs)(`div`,{className:`empty-note`,children:[(0,h.jsx)(`h2`,{children:`No layer selected`}),(0,h.jsx)(`button`,{type:`button`,className:`primary-action`,onClick:$,children:`Add layer`})]})})]})]})}function E({title:e,children:t,collapsible:n=!1,defaultOpen:r=!0}){return n?(0,h.jsxs)(`details`,{className:`editor-section editor-section-details`,open:r,children:[(0,h.jsx)(`summary`,{children:(0,h.jsx)(`h3`,{children:e})}),(0,h.jsx)(`div`,{className:`editor-section-body`,children:t})]}):(0,h.jsxs)(`section`,{className:`editor-section`,children:[(0,h.jsx)(`h3`,{children:e}),t]})}function pe({title:e,variant:t,onChange:n}){return(0,h.jsx)(E,{title:e,children:(0,h.jsx)(me,{variant:t,onChange:n})})}function me({variant:e,onChange:t}){return(0,h.jsxs)(`div`,{className:`variant-controls`,children:[(0,h.jsx)(k,{label:`Material`,value:e.material??`wireframe`,options:se,onChange:e=>t({material:e})}),(0,h.jsx)(A,{value:e.paletteId??`15`,onChange:e=>t({paletteId:e})}),(0,h.jsxs)(`div`,{className:`feature-grid`,"aria-label":`Top details`,children:[(0,h.jsx)(D,{label:`Text`,checked:B(e),title:`Toggle top-plane text for this state.`,onChange:n=>t(n?{text:V(e),textColor:U(e)}:{text:!1,textColor:void 0})}),(0,h.jsx)(D,{label:`Dash`,checked:e.dash??!1,title:`Toggle the dashed inlay for this state.`,onChange:n=>t(n?{dash:!0,dashColor:W(e)}:{dash:!1,dashColor:void 0})})]}),B(e)?(0,h.jsxs)(`div`,{className:`nested-fields`,children:[(0,h.jsx)(j,{label:`Text`,value:V(e),onChange:e=>t({text:e})}),(0,h.jsx)(k,{label:`Text paint`,value:H(e),options:ce,onChange:e=>t({textStyle:e})}),(0,h.jsx)(O,{label:`Text color`,value:U(e),forcedValue:K(e),onChange:e=>t({textColor:e})}),(0,h.jsx)(M,{label:`Text size`,value:e.textSize??w,min:S,max:C,step:1,formatValue:e=>`${Math.round(e)}px`,onChange:e=>t({textSize:e})}),(0,h.jsx)(k,{label:`Font weight`,value:String(e.textFontWeight??T),options:ue,onChange:e=>t({textFontWeight:Number(e)})})]}):null,e.dash?(0,h.jsx)(`div`,{className:`nested-fields`,children:(0,h.jsx)(O,{label:`Dash color`,value:W(e),forcedValue:q(e),onChange:e=>t({dashColor:e})})}):null]})}function D({label:e,checked:t,title:n,onChange:r}){return(0,h.jsxs)(`button`,{type:`button`,className:`feature-switch`,"aria-pressed":t,title:n,onClick:()=>r(!t),children:[(0,h.jsx)(`span`,{className:`feature-switch-label`,children:e}),(0,h.jsx)(`span`,{className:`feature-switch-state`,children:t?`On`:`Off`})]})}function O({label:e,value:t,forcedValue:n,onChange:r}){return n?(0,h.jsxs)(`div`,{className:`field`,children:[(0,h.jsx)(`span`,{children:e}),(0,h.jsx)(`div`,{className:`forced-token`,title:`This material controls annotation paint from the wireframe palette.`,children:n})]}):(0,h.jsx)(k,{label:e,value:t,options:le,onChange:r})}function k({label:e,value:t,options:n,onChange:r}){return(0,h.jsxs)(`div`,{className:`field`,children:[(0,h.jsx)(`span`,{children:e}),(0,h.jsx)(`div`,{className:`segmented-field`,role:`group`,"aria-label":e,children:n.map(e=>(0,h.jsx)(`button`,{type:`button`,title:e.title,"aria-pressed":e.value===t,onClick:()=>r(e.value),children:e.label},e.value))})]})}function A({value:t,onChange:n}){return(0,h.jsxs)(`div`,{className:`field palette-field`,children:[(0,h.jsx)(`span`,{children:`Palette`}),(0,h.jsx)(`div`,{className:`palette-grid`,role:`group`,"aria-label":`Palette`,children:r.map(r=>(0,h.jsxs)(`button`,{type:`button`,title:e[r].label,"aria-pressed":t===r,onClick:()=>n(r),children:[(0,h.jsx)(`span`,{className:`palette-swatch`,style:{background:`linear-gradient(135deg, ${e[r].swatch[0]}, ${e[r].swatch[1]})`}}),(0,h.jsx)(`span`,{children:r})]},r))})]})}function j({label:e,value:t,onChange:n}){return(0,h.jsxs)(`label`,{className:`field`,children:[(0,h.jsx)(`span`,{children:e}),(0,h.jsx)(`input`,{type:`text`,value:t,onChange:e=>n(e.target.value)})]})}function M({label:e,value:t,min:n,max:r,step:i,formatValue:a,onChange:o}){return(0,h.jsxs)(`label`,{className:`field`,children:[(0,h.jsxs)(`span`,{children:[e,`: `,a?a(t):t.toFixed(2)]}),(0,h.jsx)(`input`,{type:`range`,min:n,max:r,step:i,value:t,onChange:e=>o(Number(e.target.value))})]})}function N(){return(0,h.jsxs)(`svg`,{className:`copy-icon`,viewBox:`0 0 24 24`,"aria-hidden":`true`,focusable:`false`,children:[(0,h.jsx)(`rect`,{x:`9`,y:`9`,width:`11`,height:`11`,rx:`2`}),(0,h.jsx)(`path`,{d:`M5 15V6.8C5 5.8 5.8 5 6.8 5H15`})]})}function he(){return(0,h.jsx)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,focusable:`false`,children:(0,h.jsx)(`path`,{d:`M12 5v14M5 12h14`})})}function P(){return(0,h.jsx)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,focusable:`false`,children:(0,h.jsx)(`path`,{d:`M4 7h16M10 11v6M14 11v6M6 7l1 13h10l1-13M9 7V4h6v3`})})}function ge(){return(0,h.jsxs)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,focusable:`false`,children:[(0,h.jsx)(`path`,{d:`M2.5 12s3.4-6 9.5-6 9.5 6 9.5 6-3.4 6-9.5 6-9.5-6-9.5-6Z`}),(0,h.jsx)(`circle`,{cx:`12`,cy:`12`,r:`2.8`})]})}function _e(){return(0,h.jsx)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,focusable:`false`,children:(0,h.jsx)(`path`,{d:`M3 3l18 18M9.4 5.5A10.9 10.9 0 0 1 12 5c6.1 0 9.5 7 9.5 7a15.2 15.2 0 0 1-3 4M6.7 6.9A15.5 15.5 0 0 0 2.5 12s3.4 7 9.5 7c1.1 0 2.2-.2 3.1-.6`})})}function ve(){return(0,h.jsx)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,focusable:`false`,children:(0,h.jsx)(`path`,{d:`M6 6l12 12M18 6L6 18`})})}function ye(){return(0,h.jsx)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,focusable:`false`,children:(0,h.jsx)(`path`,{d:`M9 18 3 12l6-6M15 6l6 6-6 6`})})}function be(e){let t=[I(e.material??`wireframe`),F(e.paletteId).label];return B(e)&&t.push(`text: ${V(e)}`),e.dash&&t.push(`dash`),t.join(` / `)}function F(t){return e[t]??e[15]}function I(e){return e===`transparent`?`Glass`:e===`solid`?`Solid`:`Wire`}function L(e,t,n){return`Layer ${t}`}function xe(e){let t=new Set(e.map(e=>e.id)),n=e.length+1;for(;t.has(`layer-${n}`);)n+=1;return`layer-${n}`}function Se(e){let t=(x-e)/(x-b);return R(Math.round(t*100),v,y)}function Ce(e){return z(x-R(e,v,y)/100*(x-b),1)}function we(e){return{exponent:e.exponent??c.exponent,prismHeight:e.prismHeight??c.prismHeight,inlayScale:e.inlayScale??c.inlayScale}}function R(e,t,n){return Math.min(n,Math.max(t,e))}function z(e,t){let n=10**t;return Math.round(e*n)/n}function B(e){return typeof e.text==`string`?e.text.trim().length>0:e.text===!0||J(e).gpu===!0}function V(e){return typeof e.text==`string`?e.text:(e.text===!0||J(e).gpu,_)}function H(e){return e.textStyle??J(e).gpuStyle??`solid`}function U(e){return G(e.textColor??J(e).gpuColor??`contrast`)}function W(e){return G(e.dashColor??`contrast`)}function G(e){return e===`contrast`?`auto`:e}function K(e){return(e.material??`wireframe`)===`wireframe`?H(e)===`wireframe`?`Wire gradient`:`Surface gradient`:null}function q(e){return(e.material??`wireframe`)===`wireframe`?`Wire gradient`:null}function J(e){return e}(0,n().createRoot)(document.getElementById(`root`)).render((0,h.jsx)(fe,{title:`Squircle Constructor`,description:`Build 0-N React squircle layers, tune strokes and annotations, then copy the generated component code.`,initialLayers:o(),codeComponentName:`CustomSquircle`,codeImportPath:`@dstackai/sqircle`,showCode:!0}));
@@ -0,0 +1 @@
1
+ :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-faint:#8b98aa;--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-2:#7c58f7;--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 minmax(0,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-faint:#7f8da2;--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-2:#a88cff;--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)}.squircle-editor button svg{fill:none;stroke:currentColor;stroke-width:1.8px;stroke-linecap:round;stroke-linejoin:round;width:17px;height:17px}.icon-button{place-items:center;width:34px;min-width:34px;padding:0;display:inline-grid}.primary-action{border-color:color-mix(in srgb, var(--editor-accent) 62%, var(--editor-line-strong));background:linear-gradient(180deg, color-mix(in srgb, var(--editor-control-bg) 90%, var(--editor-accent)), var(--editor-control-bg));color:var(--editor-active-text);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--editor-accent) 16%, transparent)}.danger{color:var(--editor-danger)}.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(250px,300px) minmax(520px,1fr) minmax(300px,380px);min-height:0;display:grid;overflow:hidden}@media (width>=1061px){.squircle-editor{height:100vh;overflow:hidden}}.side-panel{min-width:0;padding:14px;overflow:auto}.layers-panel{align-content:start;gap:14px;display:grid}.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;display:flex}.panel-heading h2{margin:0;font-size:15px}.panel-heading span{color:var(--editor-muted);margin-top:2px;font-size:12px;font-weight:850;display:block}.panel-actions{justify-content:flex-end;gap:6px;display:flex}.squircle-editor-layer-list{gap:9px;display:grid}.squircle-editor-layer-row{background:0 0;border:1px solid #0000;border-radius:8px;grid-template-columns:minmax(0,1fr) auto;align-items:stretch;gap:7px;padding:3px;transition:border-color .17s,background .17s,box-shadow .17s;display:grid}.layer-select{text-align:left;background:0 0;border-color:#0000;justify-items:start;gap:8px;min-width:0;padding:10px;display:grid}.layer-select strong{font-size:13px}.layer-card-topline,.layer-card-meta{align-items:center;width:100%;min-width:0;display:flex}.layer-card-topline{gap:7px}.layer-card-meta{justify-content:space-between;gap:8px}.layer-number{background:var(--editor-panel-soft);width:25px;min-width:25px;height:22px;color:var(--editor-faint);border-radius:5px;justify-content:center;align-items:center;font-size:11px;font-weight:900;display:inline-flex}.material-pill{border:1px solid var(--editor-line);color:var(--editor-muted);text-transform:uppercase;border-radius:999px;margin-left:auto;padding:3px 6px;font-size:10px;font-weight:900}.material-pill-solid{border-color:color-mix(in srgb, var(--editor-accent) 40%, var(--editor-line));color:var(--editor-active-text)}.material-pill-transparent{border-color:color-mix(in srgb, var(--editor-accent-2) 32%, var(--editor-line));color:color-mix(in srgb, var(--editor-active-text) 72%, var(--editor-accent-2))}.layer-palette-chip,.layer-feature-tags{min-width:0;color:var(--editor-muted);align-items:center;font-size:11px;font-weight:820;display:inline-flex}.layer-palette-chip{gap:6px}.layer-feature-tags{flex-wrap:wrap;justify-content:flex-end;gap:4px}.layer-feature-tags span{border:1px solid var(--editor-line);background:var(--editor-panel-muted);border-radius:999px;padding:2px 5px;font-size:10px}.layer-select span{color:var(--editor-muted)}.squircle-editor-layer-row.is-active{border-color:color-mix(in srgb, var(--editor-accent) 68%, var(--editor-line));background:linear-gradient(90deg, var(--editor-accent-soft), transparent 65%), var(--editor-panel-soft);box-shadow:0 0 0 2px color-mix(in srgb, var(--editor-accent) 13%, transparent), inset 4px 0 0 color-mix(in srgb, var(--editor-accent) 76%, var(--editor-accent-2));color:var(--editor-active-text)}.squircle-editor-layer-row.is-active .layer-number{background:color-mix(in srgb, var(--editor-accent) 18%, var(--editor-control-bg));color:var(--editor-active-text)}.layer-visibility{min-height:100%;color:var(--editor-muted);align-self:stretch}.layer-visibility[aria-pressed=false]{color:var(--editor-faint);opacity:.66}.squircle-editor-preview{grid-template-rows:auto minmax(320px,1fr) auto;gap:12px;min-width:0;min-height:0;padding:18px;display:grid}.preview-toolbar,.preview-actions{justify-content:space-between;align-items:center;gap:10px;display:flex}.preview-toolbar h2{margin:0;font-size:14px}.preview-toolbar span{color:var(--editor-muted);margin-top:2px;font-size:12px;font-weight:820;display:block}.preview-actions{justify-content:flex-end}.code-toggle-button{align-items:center;gap:7px;padding:0 10px;display:inline-flex}.code-toggle-button[aria-pressed=true]{border-color:color-mix(in srgb, var(--editor-accent) 56%, var(--editor-line));background:var(--editor-accent-soft);color:var(--editor-active-text)}.preview-stage{border:1px solid color-mix(in srgb, var(--editor-line) 74%, transparent);background:linear-gradient(180deg, color-mix(in srgb, var(--editor-panel-soft) 72%, transparent), transparent 56%), color-mix(in srgb, var(--editor-panel-muted) 84%, transparent);border-radius:8px;place-items:center;min-height:0;display:grid;overflow:hidden}.preview-stage .squircle-scene{place-self:center;max-width:660px;padding:16px}.code-panel{border:1px solid var(--editor-line);background:var(--editor-code-bg);border-radius:6px;grid-template-rows:auto;min-width:0;display:grid;overflow:hidden}.code-panel.is-open{grid-template-rows:auto minmax(120px,220px)}.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-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}.inspector-heading{border-bottom:1px solid var(--editor-line);justify-content:space-between;align-items:start;gap:12px;padding-bottom:12px;display:flex}.inspector-heading h2{margin:1px 0 0;font-size:18px}.inspector-heading p{color:var(--editor-muted);margin:4px 0 0;font-size:12px;font-weight:760}.inspector-kicker{color:var(--editor-faint);text-transform:uppercase;font-size:11px;font-weight:900}.state-switch{border:1px solid var(--editor-line);background:var(--editor-panel-soft);border-radius:8px;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px;margin:12px 0 2px;padding:4px;display:grid}.state-switch button{min-height:34px;color:var(--editor-muted);background:0 0;border-color:#0000;position:relative}.state-switch button[aria-pressed=true]{border-color:color-mix(in srgb, var(--editor-accent) 58%, var(--editor-line));background:linear-gradient(180deg, color-mix(in srgb, var(--editor-control-bg) 90%, var(--editor-accent)), var(--editor-control-bg));color:var(--editor-active-text);box-shadow:0 1px 5px #0f141a1a}.state-dot{background:var(--editor-accent);border-radius:50%;width:6px;height:6px;position:absolute;top:7px;right:9px}.editor-section-details{gap:0}.editor-section-details summary{cursor:pointer;justify-content:space-between;align-items:center;list-style:none;display:flex}.editor-section-details summary::-webkit-details-marker{display:none}.editor-section-details summary:after{content:"+";color:var(--editor-muted);font-size:16px;font-weight:850}.editor-section-details[open] summary:after{content:"-"}.editor-section-body{gap:10px;padding-top:10px;display:grid}.field{color:var(--editor-muted);gap:5px;font-size:12px;font-weight:800;display:grid}.variant-controls{gap:11px;display:grid}.feature-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:7px;display:grid}.feature-switch{text-align:left;justify-content:space-between;align-items:center;gap:8px;min-width:0;min-height:42px;padding:0 10px;display:flex}.feature-switch[aria-pressed=true]{border-color:color-mix(in srgb, var(--editor-accent) 62%, var(--editor-line));background:linear-gradient(90deg, var(--editor-accent-soft), var(--editor-control-bg) 72%), var(--editor-control-bg);color:var(--editor-active-text);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--editor-accent) 12%, transparent)}.feature-switch-label{font-size:13px;font-weight:900}.feature-switch-state{color:var(--editor-muted);text-transform:uppercase;font-size:11px;font-weight:900}.nested-fields{border:1px solid color-mix(in srgb, var(--editor-line) 82%, transparent);background:var(--editor-panel-muted);border-radius:8px;gap:9px;padding:10px;display:grid}.field input[type=text],.field input[type=range]{width:100%}.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}.segmented-field{border:1px solid var(--editor-line);background:var(--editor-panel-soft);border-radius:8px;grid-template-columns:repeat(auto-fit,minmax(68px,1fr));gap:3px;padding:3px;display:grid}.segmented-field button{min-width:0;min-height:30px;color:var(--editor-muted);background:0 0;border-color:#0000;padding:0 7px;font-size:12px}.segmented-field button[aria-pressed=true]{border-color:color-mix(in srgb, var(--editor-accent) 54%, var(--editor-line));background:linear-gradient(180deg, color-mix(in srgb, var(--editor-control-bg) 90%, var(--editor-accent)), var(--editor-control-bg));color:var(--editor-active-text);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--editor-accent) 14%, transparent), 0 1px 4px #0f141a1a}.palette-grid{grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;display:grid}.palette-grid button{min-width:0;min-height:32px;color:var(--editor-muted);grid-template-columns:auto 1fr;align-items:center;gap:5px;padding:0 6px;font-size:12px;display:grid}.palette-grid button[aria-pressed=true]{border-color:color-mix(in srgb, var(--editor-accent) 68%, var(--editor-line));background:linear-gradient(90deg, var(--editor-accent-soft), var(--editor-control-bg) 62%), var(--editor-control-bg);color:var(--editor-active-text);box-shadow:0 0 0 2px color-mix(in srgb, var(--editor-accent) 12%, transparent)}.palette-swatch{border:1px solid var(--editor-line);border-radius:4px;width:16px;height:16px}.forced-token{border:1px dashed var(--editor-line-strong);background:var(--editor-panel-muted);min-height:32px;color:var(--editor-muted);border-radius:6px;align-items:center;padding:0 9px;font-size:12px;font-weight:850;display:flex}.empty-note{color:var(--editor-muted);align-content:start;gap:12px;margin:0;font-size:13px;font-weight:700;display:grid}.empty-note h2{color:var(--editor-text);margin:0;font-size:16px}.empty-note .primary-action{width:max-content;padding:0 12px}@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{max-width:100%;overflow-x:hidden}.squircle-editor-topbar{flex-direction:column;align-items:stretch}.topbar-title{min-width:0}.squircle-editor-topbar p{overflow-wrap:anywhere}.topbar-actions{justify-content:stretch}.topbar-actions button{flex:1}.squircle-editor-workspace,.side-panel,.squircle-editor-preview,.preview-stage,.code-panel{width:100%;min-width:0;max-width:100%}.squircle-editor-layer-row{grid-template-columns:minmax(0,1fr);padding-right:76px;position:relative}.layer-visibility{z-index:2;height:34px;min-height:34px;position:absolute;top:auto;bottom:9px;right:46px}.layer-select{overflow:hidden}.layer-card-meta{flex-direction:column;align-items:flex-start}.preview-toolbar{flex-direction:column;align-items:stretch}.preview-actions{justify-content:stretch}.code-toggle-button{flex:1;justify-content:center}.squircle-editor-preview{padding:10px}.preview-stage{min-height:320px}.preview-stage .squircle-scene{width:min(92%,360px);max-width:360px;padding:8px}.palette-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.feature-grid{grid-template-columns:1fr}}
@@ -0,0 +1 @@
1
+ import{s as e}from"./pages-DQf07DWd.js";var t=e();function n({title:e,description:n,theme:i,onThemeChange:a,children:o}){return(0,t.jsxs)(`main`,{className:i===`dark`?`sq-page sq-page-dark`:`sq-page`,"data-theme":i,children:[(0,t.jsxs)(`header`,{className:`sq-page-topbar`,children:[(0,t.jsxs)(`div`,{children:[(0,t.jsx)(`a`,{className:`sq-page-mark`,href:`./index.html`,children:`Squircle`}),(0,t.jsx)(`h1`,{children:e}),(0,t.jsx)(`p`,{children:n})]}),(0,t.jsxs)(`nav`,{className:`sq-page-actions`,"aria-label":`Examples`,children:[(0,t.jsx)(`a`,{href:`./index.html`,children:`Index`}),(0,t.jsx)(`a`,{href:`./demo.html`,children:`Demo`}),(0,t.jsx)(`a`,{href:`./constructor.html`,children:`Constructor`}),(0,t.jsx)(r,{theme:i,onThemeChange:a})]})]}),o]})}function r({theme:e,onThemeChange:n}){return(0,t.jsxs)(`div`,{className:`sq-page-theme`,role:`group`,"aria-label":`Theme`,children:[(0,t.jsx)(`button`,{type:`button`,"aria-pressed":e===`light`,onClick:()=>n(`light`),children:`Light`}),(0,t.jsx)(`button`,{type:`button`,"aria-pressed":e===`dark`,onClick:()=>n(`dark`),children:`Dark`})]})}export{n as t};
@@ -0,0 +1 @@
1
+ import{c as e,f as t,n,o as r,p as i,s as a,t as o}from"./pages-DQf07DWd.js";import{t as s}from"./PageShell-CjHSnST7.js";var c=i(),l=t(),u=a();function d(){let[t,i]=(0,c.useState)(`light`),[a,l]=(0,c.useState)(`15`),d=(0,c.useMemo)(()=>n(a,96),[a]),[f,p]=(0,c.useState)(`composition-1`),m=d.find(e=>e.id===f)??d[0];return(0,u.jsxs)(s,{title:`Composition demo`,description:`A React-generated gallery of selectable 3-layer compositions. Hover a visible squircle to test its state and color swap.`,theme:t,onThemeChange:i,children:[(0,u.jsxs)(`section`,{className:`sq-demo-layout`,children:[(0,u.jsxs)(`aside`,{className:`sq-control-panel sq-demo-sidebar`,children:[(0,u.jsx)(`h2`,{children:`Color`}),(0,u.jsx)(`div`,{className:`sq-palette-list`,children:o.map(t=>(0,u.jsxs)(`button`,{type:`button`,className:a===t?`sq-swatch is-active`:`sq-swatch`,"aria-pressed":a===t,onClick:()=>l(t),children:[(0,u.jsx)(`span`,{style:{background:`linear-gradient(135deg, ${e[t].swatch[0]}, ${e[t].swatch[1]})`}}),e[t].label]},t))})]}),(0,u.jsxs)(`section`,{className:`sq-demo-main`,children:[(0,u.jsx)(`div`,{className:`sq-stage sq-demo-stage`,children:(0,u.jsx)(r,{layers:m.layers,theme:t,ariaLabel:m.name,idPrefix:`demo-main-${m.id}`})}),(0,u.jsxs)(`div`,{className:`sq-demo-caption`,children:[(0,u.jsx)(`h2`,{children:m.name}),(0,u.jsx)(`p`,{children:m.note})]})]})]}),(0,u.jsxs)(`details`,{className:`sq-drawer`,open:!0,children:[(0,u.jsx)(`summary`,{children:`Preset compositions`}),(0,u.jsx)(`div`,{className:`sq-card-grid sq-card-grid-dense`,children:d.map(e=>(0,u.jsxs)(`button`,{type:`button`,className:e.id===m.id?`sq-variant-button is-active`:`sq-variant-button`,onClick:()=>p(e.id),children:[(0,u.jsx)(r,{layers:e.layers,theme:t,ariaLabel:e.name,idPrefix:`demo-thumb-${e.id}`,geometry:{width:360,viewBoxHeight:300}}),(0,u.jsx)(`strong`,{children:e.name}),(0,u.jsx)(`span`,{children:e.note})]},e.id))})]})]})}(0,l.createRoot)(document.getElementById(`root`)).render((0,u.jsx)(d,{}));
@@ -0,0 +1 @@
1
+ import{a as e,c as t,f as n,i as r,o as i,p as a,s as o,t as s}from"./pages-DQf07DWd.js";import{t as c}from"./PageShell-CjHSnST7.js";var l=a(),u=n(),d=o();function f(){let[n,a]=(0,l.useState)(`light`),[o,u]=(0,l.useState)(`15`),[f,p]=(0,l.useState)(`single-5`),m=(0,l.useMemo)(()=>e(o),[o]),h=(0,l.useMemo)(()=>r(o),[o]),g=m.find(e=>e.id===f)??m[0];return(0,d.jsxs)(c,{title:`React squircle examples`,description:`Root examples now render through the reusable component instead of static SVG markup.`,theme:n,onThemeChange:a,children:[(0,d.jsxs)(`section`,{className:`sq-hero-grid`,children:[(0,d.jsx)(`div`,{className:`sq-stage sq-hero-stage`,children:(0,d.jsx)(i,{layers:h,theme:n,ariaLabel:`Three-layer squircle hero`,idPrefix:`index-hero`})}),(0,d.jsxs)(`aside`,{className:`sq-control-panel`,children:[(0,d.jsx)(`h2`,{children:`Palette`}),(0,d.jsx)(`div`,{className:`sq-palette-grid`,children:s.map(e=>(0,d.jsxs)(`button`,{type:`button`,className:o===e?`sq-swatch is-active`:`sq-swatch`,"aria-pressed":o===e,onClick:()=>u(e),children:[(0,d.jsx)(`span`,{style:{background:`linear-gradient(135deg, ${t[e].swatch[0]}, ${t[e].swatch[1]})`}}),t[e].label]},e))})]})]}),(0,d.jsxs)(`details`,{className:`sq-drawer`,open:!0,children:[(0,d.jsx)(`summary`,{children:`Single squircle states`}),(0,d.jsxs)(`div`,{className:`sq-selected-single`,children:[(0,d.jsx)(`div`,{className:`sq-stage`,children:(0,d.jsx)(i,{layers:g.layers,theme:n,ariaLabel:g.name,idPrefix:`index-${g.id}`})}),(0,d.jsxs)(`div`,{children:[(0,d.jsx)(`h2`,{children:g.name}),(0,d.jsx)(`p`,{children:g.note})]})]}),(0,d.jsx)(`div`,{className:`sq-card-grid`,children:m.map(e=>(0,d.jsxs)(`button`,{type:`button`,className:e.id===g.id?`sq-variant-button is-active`:`sq-variant-button`,onClick:()=>p(e.id),children:[(0,d.jsx)(i,{layers:e.layers,theme:n,ariaLabel:e.name,idPrefix:`single-thumb-${e.id}`,geometry:{width:360,viewBoxHeight:240}}),(0,d.jsx)(`strong`,{children:e.name}),(0,d.jsx)(`span`,{children:e.note})]},e.id))})]})]})}(0,u.createRoot)(document.getElementById(`root`)).render((0,d.jsx)(f,{}));