@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,116 @@
1
+ # Rendering Contract
2
+
3
+ This file documents gradients, edge sharpness, top-plane annotations, and the text-label technique. The current demo text spells `GPU`, but reusable React config calls the feature `text`.
4
+
5
+ For exact variant stops and CSS color variables, see [colors.md](./colors.md).
6
+
7
+ ## Gradient Bboxes
8
+
9
+ All face gradients use `gradientUnits="userSpaceOnUse"` and coordinates derived from generated points:
10
+
11
+ - Top gradient bbox: `x1 = 0`, `y1 = 0`, `x2 = 800`, `y2 = 291.18`
12
+ - Text surface gradient: `x1 = -425.63`, `y1 = -0.1`, `x2 = 425.6`, `y2 = 0.07`, with the same stops as the matching top gradient
13
+ - Side gradient bbox: `x1 = 0`, `y1 = 0`, `x2 = 800`, `y2 = 301.18`, computed from top face plus side wall
14
+
15
+ Top gradients span only the top face. Side gradients span the full top-plus-wall silhouette so the shadow ramp continues in the same projected coordinate space instead of restarting on the wall.
16
+
17
+ React uses `text-surface-*` gradients to remap the top face stops into the label's local projected plane. Static fixtures still use `gpu-surface-*` ids because those pages render the GPU example glyph. These gradients are used for `textStyle: "solid"` on wireframe material, where filled text should read as top-surface paint rather than wire stroke color.
18
+
19
+ Wireframe mode uses the top gradient as the stroke gradient so upper and lower curves match. This intentionally avoids fake lighting on wireframes.
20
+
21
+ ## Sharpness Edge
22
+
23
+ Every filled face has a tiny in-family edge stroke:
24
+
25
+ - `--top-edge` on the top face
26
+ - `--side-edge` on the side wall
27
+ - `--face-edge-width: 0.35`
28
+ - `--face-edge-opacity: 0.72`
29
+
30
+ These edge colors are set per variant and must stay in the same color family. Never use black for prism edges; it flattens the gradients.
31
+
32
+ ## Draw Order
33
+
34
+ Each prism definition follows this order:
35
+
36
+ 1. Side wall polygon
37
+ 2. Top face polygon
38
+
39
+ The top face is drawn after the side wall, so it hides the back half of the extrusion without masks or z-buffering.
40
+
41
+ ## Inlay
42
+
43
+ The middle-layer dashed squircle is generated from the same superellipse at `0.6 * a` and projected at `z = h`, so it lies on the top plane. The static copies are:
44
+
45
+ - `#top-inlay` for solid/transparent rendering
46
+ - `#top-inlay-wire` for wireframe rendering
47
+
48
+ By default, solid and transparent inlays use `stroke: var(--label-fill)`, the same contrast token as the filled text label. Wireframe inlays use `stroke: var(--top-fill)`, the same gradient token as the wireframe text outline.
49
+
50
+ `constructor.html` can override dash color per layer. `dashColor: "contrast"` keeps the default token for the material's dash symbol, while `dashColor: "white"` and `dashColor: "black"` intentionally use fixed stroke colors. Dash symbol choice follows material: solid and transparent states use `#top-inlay`; wireframe states use `#top-inlay-wire`.
51
+
52
+ ## Label Source
53
+
54
+ React renders top-plane text as one live SVG `<text>` element, so component configs can pass arbitrary strings such as `text: "GPU"`, `text: "CUDA"`, or `text: "AI"`. The static fixtures still store the current `GPU` demo glyph as `#label-gpu` in `<defs>` because those pages are fixed examples.
55
+
56
+ Current label parameters:
57
+
58
+ - Label outline source: `/System/Library/Fonts/Supplemental/Arial.ttf`
59
+ - Label outline size: `62px`
60
+ - Source bbox before centering: `x = 3.2998..129.3584`, `y = -45.1377..0.7568`
61
+ - Centering translation before projection: `x = -66.3291`, `y = 22.1904`
62
+ - Centered bbox: `x = -63.0293..63.0293`, `y = -22.9473..22.9473`
63
+ - Label wire stroke: `1.1`
64
+
65
+ For static fixture glyphs, generate the path with `opentype.js@2.0.0`:
66
+
67
+ ```js
68
+ const font = opentype.parse(fs.readFileSync("/System/Library/Fonts/Supplemental/Arial.ttf").buffer);
69
+ const path = font.getPath("GPU", 0, 0, 62, { kerning: true });
70
+ const box = path.getBoundingBox();
71
+ const tx = -((box.x1 + box.x2) / 2);
72
+ const ty = -((box.y1 + box.y2) / 2);
73
+ ```
74
+
75
+ Apply `tx` and `ty` to every path command coordinate before writing the static `d` attribute. Keep `fill-rule="evenodd"` and `clip-rule="evenodd"` on the path so counters, such as the hole inside `P`, remain true holes.
76
+
77
+ ## Label Transform And Paint
78
+
79
+ Every visible static label is a single `<use>` of `#label-gpu`, and every React label is a single `<text>` element. Both are transformed onto the top plane with:
80
+
81
+ ```text
82
+ matrix(cosA, sinA, -cosA, sinA, cx, cy - h)
83
+ ```
84
+
85
+ For the current geometry:
86
+
87
+ ```text
88
+ matrix(0.94, 0.342, -0.94, 0.342, 400, 145.6)
89
+ ```
90
+
91
+ The transform never changes between solid and wireframe modes; only paint changes. React and constructor defaults intentionally mirror the original generated GPU path: `textSize: 62`, `textFontFamily: "Arial, Helvetica, sans-serif"`, and `textFontWeight: 400`.
92
+
93
+ Solid label style:
94
+
95
+ - `.plane-label` uses `fill: var(--label-fill)` and `stroke: none` when `textColor` is `contrast`.
96
+ - `#top-inlay` uses `stroke: var(--label-fill)` so dashed inlays match the filled label color.
97
+ - `15 Alpha` uses `#f7fbff` because its top gradient is dark.
98
+ - Lighter variants use dark in-family label fills.
99
+
100
+ Wireframe label style:
101
+
102
+ - `textStyle: "wireframe"` uses the same live text element as filled mode.
103
+ - It sets `fill:none !important` and uses `stroke: var(--gpu-wire-stroke)`.
104
+ - On solid/transparent material, `--gpu-wire-stroke` comes from `textColor`; `contrast` resolves to `--label-fill`, matching filled text and solid dash.
105
+ - On wireframe material, `--gpu-wire-stroke` resolves to the label-local `--gpu-wire-fill` gradient.
106
+ - `#top-inlay-wire` also uses `stroke: var(--top-fill)` so dashed inlays match the wireframe label and prism wires.
107
+ - It must remain an outline around the font figures, not a single-stroke lettering replacement.
108
+ - Keep the outline stroke thin relative to text size. The default `labelWire` is `1.1` at `textSize: 62`, safely below the ratio where counters start merging.
109
+
110
+ Do not sample the full-face `--top-fill` ramp directly for the text outline on wireframe material; it is too large for the label geometry and makes the color read wrong. Use one text element for all text styles. Use `textColor` for solid/transparent outline paint and the label-local wire gradient for wireframe-material outline paint.
111
+
112
+ Constructor `gpuColor: "white"` and `gpuColor: "black"` remain in the static GPU example schema and map to React `textColor` in generated code. They override label paint only on solid/transparent material. On wireframe material, React `textStyle: "solid"` renders a fully opaque filled label using the text surface gradient, and `textStyle: "wireframe"` renders an outlined label using the text wire gradient.
113
+
114
+ Do not introduce `textStyle: "transparent"` as a third paint control. Legacy static configs with `gpuStyle: "transparent"` should normalize to `solid`.
115
+
116
+ Do not build filled letters from `<rect>`, `<line>`, or separate stem/bowl paths. Do not add a second label copy and do not replace the text with monoline lettering for outline mode.
@@ -0,0 +1,121 @@
1
+ # Single-Squircle State Constructors
2
+
3
+ This file documents the `Single Squircle States` drawer as reusable constructors. Use these recipes when another agent wants to compose a single squircle outside the full three-layer composition.
4
+
5
+ ## Shared Setup
6
+
7
+ Each single-state card uses:
8
+
9
+ ```html
10
+ <svg class="single-squircle" viewBox="-6 -6 812 314" aria-label="...">
11
+ ...
12
+ </svg>
13
+ ```
14
+
15
+ The viewBox frames exactly one prism plus a small margin for hairline strokes. Single-state cards inherit the currently selected variant colors from `.single-drawer`, which receives the same CSS variables as `.hero-card`:
16
+
17
+ - `--top-fill`
18
+ - `--side-fill`
19
+ - `--label-fill`
20
+ - `--top-edge`
21
+ - `--side-edge`
22
+ - `--label-wire-width`
23
+ - `--face-edge-width`
24
+ - `--face-edge-opacity`
25
+
26
+ The global transparent/wireframe switch does not affect this drawer. The drawer always shows all constructor states at once.
27
+
28
+ ## Building Blocks
29
+
30
+ | Part | SVG use | CSS class | Purpose |
31
+ | --- | --- | --- | --- |
32
+ | Solid prism | `<use href="#prism-active" />` | `.single-active` | Filled, selected-looking prism with top and side gradients |
33
+ | Transparent prism | `<use href="#prism-ghost" />` | `.single-ghost` inside `.single-transparent` | Translucent filled prism |
34
+ | Wireframe prism | `<use href="#prism-ghost" />` | `.single-ghost` inside `.single-wire` | Gradient outline with transparent faces |
35
+ | Solid/transparent dash | `<use href="#top-inlay" />` | `.single-inlay` | Dashed top-plane squircle using `--label-fill` |
36
+ | Wireframe dash | `<use href="#top-inlay-wire" />` | `.single-wire-inlay.wire-inlay` | Gradient dashed top-plane squircle |
37
+ | Text label | `<use href="#label-gpu" />` | `.single-label.plane-label` | Compound path label on the top plane; the current static glyph spells `GPU` |
38
+
39
+ The label always uses this transform:
40
+
41
+ ```html
42
+ transform="matrix(0.94,0.342,-0.94,0.342,400,145.6)"
43
+ ```
44
+
45
+ ## Constructor Matrix
46
+
47
+ | State | Card class | SVG children in order | Paint behavior |
48
+ | --- | --- | --- | --- |
49
+ | `Solid` | `.single-card` | `.single-active` | Filled prism using selected variant top/side gradients |
50
+ | `Solid Text` | `.single-card` | `.single-active`, `.single-label.plane-label` | Solid prism plus filled contrast label |
51
+ | `Solid Dash` | `.single-card` | `.single-active`, `.single-inlay` | Solid prism plus contrast dashed inlay |
52
+ | `Solid Text + Dash` | `.single-card` | `.single-active`, `.single-inlay`, `.single-label.plane-label` | Solid prism plus dashed inlay and filled label |
53
+ | `Transparent` | `.single-card.single-transparent` | `.single-ghost` | Translucent prism at `0.38` opacity |
54
+ | `Transparent Text` | `.single-card.single-transparent` | `.single-ghost`, `.single-label.plane-label` | Translucent prism plus filled label at `0.62` opacity |
55
+ | `Transparent Dash` | `.single-card.single-transparent` | `.single-ghost`, `.single-inlay` | Translucent prism plus contrast dashed inlay |
56
+ | `Transparent Text + Dash` | `.single-card.single-transparent` | `.single-ghost`, `.single-inlay`, `.single-label.plane-label` | Translucent prism plus dashed inlay and filled label |
57
+ | `Wireframe` | `.single-card.single-wire` | `.single-ghost` | Transparent faces, gradient wire strokes |
58
+ | `Wireframe Text` | `.single-card.single-wire` | `.single-ghost`, `.single-label.plane-label` | Wireframe prism plus outlined gradient label |
59
+ | `Wireframe Dash` | `.single-card.single-wire` | `.single-ghost`, `.single-wire-inlay.wire-inlay` | Wireframe prism plus gradient dashed inlay |
60
+ | `Wireframe Text + Dash` | `.single-card.single-wire` | `.single-ghost`, `.single-wire-inlay.wire-inlay`, `.single-label.plane-label` | Wireframe prism plus gradient dashed inlay and gradient label |
61
+
62
+ ## Copy-Paste Recipes
63
+
64
+ Solid with text:
65
+
66
+ ```html
67
+ <svg class="single-squircle" viewBox="-6 -6 812 314" aria-label="Solid single squircle with text">
68
+ <use class="single-active" href="#prism-active" />
69
+ <use class="single-label plane-label" href="#label-gpu" transform="matrix(0.94,0.342,-0.94,0.342,400,145.6)" />
70
+ </svg>
71
+ ```
72
+
73
+ Transparent with text and dash:
74
+
75
+ ```html
76
+ <svg class="single-squircle" viewBox="-6 -6 812 314" aria-label="Transparent single squircle with text and dashed inlay">
77
+ <use class="single-ghost" href="#prism-ghost" />
78
+ <use class="single-inlay" href="#top-inlay" />
79
+ <use class="single-label plane-label" href="#label-gpu" transform="matrix(0.94,0.342,-0.94,0.342,400,145.6)" />
80
+ </svg>
81
+ ```
82
+
83
+ Wireframe with text and dash:
84
+
85
+ ```html
86
+ <svg class="single-squircle" viewBox="-6 -6 812 314" aria-label="Wireframe single squircle with text and dashed inlay">
87
+ <use class="single-ghost" href="#prism-ghost" />
88
+ <use class="single-wire-inlay wire-inlay" href="#top-inlay-wire" />
89
+ <use class="single-label plane-label" href="#label-gpu" transform="matrix(0.94,0.342,-0.94,0.342,400,145.6)" />
90
+ </svg>
91
+ ```
92
+
93
+ The text + dash states are reusable variants that combine the top-plane text path with a dashed inlay.
94
+
95
+ In the static single-state drawer, solid and transparent text + dash states use `--label-fill` for both the filled text path and dashed inlay. Wireframe text + dash states use `--top-fill` for both the outlined text path and dashed inlay. `constructor.html` can override GPU color, GPU style, and dash color per layer because it is the GPU example; React generated code maps those controls to `textColor`, `textStyle`, and `dashColor`.
96
+
97
+ ## Ordering Rules
98
+
99
+ - Draw the prism first.
100
+ - Draw the inlay after the prism.
101
+ - Draw the label last.
102
+ - Use `#top-inlay` for solid/transparent states.
103
+ - Use `#top-inlay-wire` for wireframe states.
104
+ - Do not use two label copies. The same `.plane-label` object changes paint through CSS.
105
+
106
+ ## Color Inheritance
107
+
108
+ The drawer must be a sibling after the hidden variant radios. The selected variant selectors target both `.hero-card` and `.single-drawer`, e.g.:
109
+
110
+ ```css
111
+ #variant-15:checked ~ .hero-card,
112
+ #variant-15:checked ~ .single-drawer {
113
+ --top-fill: url("#top-15");
114
+ --side-fill: url("#side-15");
115
+ --label-fill: #f7fbff;
116
+ --top-edge: #7c5fd0;
117
+ --side-edge: #2d1466;
118
+ }
119
+ ```
120
+
121
+ See [colors.md](./colors.md) for the full palette contract.
@@ -0,0 +1,168 @@
1
+ # React Component Guide
2
+
3
+ The maintainable implementation lives in `src/squircle`. Static HTML pages remain useful visual fixtures, but new constructor work should prefer the React component.
4
+
5
+ ## Files
6
+
7
+ - `src/squircle/SquircleScene.tsx`: renders the SVG scene.
8
+ - `src/squircle/SquircleEditor.tsx`: reusable editor UI that owns layer editing, preview, React code output, and optional controlled state.
9
+ - `src/squircle/codeExport.ts`: serializes the current editor state into a copyable `SquircleScene` React component snippet.
10
+ - `src/squircle/geometry.ts`: generates superellipse prism, hidden edge, inlay, label transform, and gradient bounds from constants.
11
+ - `src/squircle/palettes.ts`: exports palette constants.
12
+ - `src/squircle/types.ts`: public config and prop types.
13
+ - Top-plane text is rendered by `SquircleScene` as live SVG text projected with the same isometric matrix as the top face. The constructor demo uses `GPU` as example text, but component configs can pass any string.
14
+ - `src/App.tsx`: tiny app entry that renders `SquircleEditor`.
15
+
16
+ ## Renderer Component
17
+
18
+ ```tsx
19
+ <SquircleScene
20
+ theme="dark"
21
+ layers={[
22
+ {
23
+ id: "bottom",
24
+ offset: { y: 176 },
25
+ base: { material: "wireframe", paletteId: "15" },
26
+ hover: { material: "solid", paletteId: "20" },
27
+ stroke: { wire: 1.6, face: 0.35, labelWire: 1.1 }
28
+ }
29
+ ]}
30
+ onLayerSelect={(id) => setSelectedId(id)}
31
+ />
32
+ ```
33
+
34
+ `layers` can contain 0-N items. The array order is draw order: first item is lowest/backmost, last item is topmost/frontmost. A layer with `visible: false` is skipped without changing the other layer offsets.
35
+
36
+ ## Editor Component
37
+
38
+ Use `SquircleEditor` when you want the complete constructor UI:
39
+
40
+ ```tsx
41
+ <SquircleEditor
42
+ initialLayers={createSquircleLayers(5)}
43
+ onChange={(layers) => console.log(layers)}
44
+ />
45
+ ```
46
+
47
+ The editor uses `SquircleScene` for the center preview. It can be uncontrolled with `initialLayers`, or controlled with `value` plus `onChange`. It supports 0-N layers, add/delete/clear, layer visibility, preview click selection, base/hover variant editing, stroke controls, and a copyable Code panel through `showCode`.
48
+
49
+ `showConfig` remains as a deprecated compatibility alias for `showCode`; new callers should use `showCode`. The Code panel shows actual TSX: it imports `SquircleScene`, defines a typed `SquircleLayerConfig[]`, includes the active `theme`, and renders a ready-to-use component. The icon button copies that React code to the clipboard.
50
+
51
+ Both public components accept `theme="light" | "dark"`. `SquircleScene` keeps the SVG transparent and palette-driven, but adds `.sq-theme-light` / `.sq-theme-dark` classes plus `data-theme` for host styling. `SquircleEditor` applies the theme to the whole constructor shell and passes it into the preview scene.
52
+
53
+ The editor can own theme state or be controlled:
54
+
55
+ ```tsx
56
+ <SquircleEditor
57
+ defaultTheme="dark"
58
+ showThemeSwitch
59
+ />
60
+
61
+ <SquircleEditor
62
+ theme={theme}
63
+ onThemeChange={setTheme}
64
+ />
65
+ ```
66
+
67
+ The constructor page shows a Light/Dark segmented control in the top bar by default. Hide it with `showThemeSwitch={false}` when the host app owns theme elsewhere.
68
+
69
+ The generated React code includes the active `theme` next to `layers` so a handoff can recreate the same constructor appearance.
70
+
71
+ Use the exporter directly when you need the same code string outside the full editor:
72
+
73
+ ```ts
74
+ import { createSquircleReactCode } from "./squircle";
75
+
76
+ const code = createSquircleReactCode({
77
+ theme: "light",
78
+ layers,
79
+ componentName: "CustomSquircle"
80
+ });
81
+ ```
82
+
83
+ ## Layer Variant Fields
84
+
85
+ Each layer has a required `base` variant and an optional `hover` variant. Hover is still 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.
86
+
87
+ Variant fields:
88
+
89
+ | Field | Values | Meaning |
90
+ | --- | --- | --- |
91
+ | `material` | `solid`, `transparent`, `wireframe` | Prism rendering mode |
92
+ | `paletteId` | `13` through `20` | Palette from `SQUIRCLE_PALETTES` |
93
+ | `text` | string, boolean | Render top-plane text. Pass a string such as `"GPU"` or `"CUDA"`; `true` is a compatibility shorthand for `"GPU"` |
94
+ | `dash` | boolean | Render the dashed inlay |
95
+ | `textStyle` | `solid`, `wireframe` | Filled or outlined text |
96
+ | `textColor` | `contrast`, `auto`, `white`, `black` | Text paint for solid/transparent material |
97
+ | `textSize` | number | Text font size, default `62` |
98
+ | `textFontFamily` | string | SVG text font family, default `Arial, Helvetica, sans-serif` |
99
+ | `textFontWeight` | string, number | SVG text font weight, default `400` |
100
+ | `dashColor` | `contrast`, `auto`, `white`, `black` | Dash paint for solid/transparent material |
101
+ | `stroke` | partial `SquircleStrokeConfig` | Per-variant stroke overrides |
102
+
103
+ `auto` is accepted as a friendly alias for `contrast`. `gpu`, `gpuStyle`, and `gpuColor` are deprecated aliases accepted only for older snippets; new configs and generated code should use `text`, `textStyle`, and `textColor`.
104
+
105
+ ## Stroke Parameters
106
+
107
+ Stroke widths and opacities are explicit data, not hard-coded CSS side effects:
108
+
109
+ ```ts
110
+ {
111
+ face: 0.35,
112
+ faceOpacity: 0.72,
113
+ wire: 1.6,
114
+ wireOpacity: 0.88,
115
+ hidden: 1.2,
116
+ hiddenOpacity: 0.28,
117
+ dash: 2.2,
118
+ wireDash: 1.6,
119
+ labelWire: 1.1
120
+ }
121
+ ```
122
+
123
+ Set `layer.stroke` for layer-wide defaults. Set `base.stroke` or `hover.stroke` only when a particular state needs to override that layer. Geometry does not move when strokes change.
124
+
125
+ ## Palettes
126
+
127
+ `SQUIRCLE_PALETTES` is the source of truth for React:
128
+
129
+ - `top`: top-face gradient stops
130
+ - `side`: side-wall gradient stops
131
+ - `textWire`: text-local wireframe gradient stops
132
+ - `labelFill`: automatic annotation color
133
+ - `topEdge` and `sideEdge`: hairline edge strokes
134
+ - `swatch`: UI preview colors
135
+
136
+ Do not invent colors inside a layer config. Add new palettes to `palettes.ts` and update [colors.md](../design/colors.md).
137
+
138
+ ## Geometry
139
+
140
+ `createSquircleGeometry()` implements the math from [geometry.md](../design/geometry.md):
141
+
142
+ - superellipse exponent `n = 12`
143
+ - samples `N = 160`
144
+ - projection angle `20deg`
145
+ - prism height `10`
146
+ - inlay scale `0.6`
147
+
148
+ The component generates polygons at render time. Do not paste generated point lists into React source.
149
+
150
+ ## 0-N Layer Helpers
151
+
152
+ Use `createSquircleLayers(count)` for quick default layers and `reflowLayerOffsets(layers, gap)` when adding/removing layers in an editor. These helpers preserve the convention that the top layer has `offset.y = 0` and lower layers move downward by `gap`.
153
+
154
+ ## Verification
155
+
156
+ Run:
157
+
158
+ ```sh
159
+ npm run typecheck
160
+ npm run build
161
+ ```
162
+
163
+ Then open `/react.html` from the Vite dev server and verify:
164
+
165
+ - empty layer arrays render an empty SVG without crashing.
166
+ - adding layers keeps array order as draw order.
167
+ - no-op hover layers do not blink.
168
+ - wireframe text outline uses the text-local gradient, not a single-stroke replacement.
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@dstackai/sqircle",
3
+ "version": "0.1.0",
4
+ "description": "React components for precise isometric squircle-prism SVG compositions.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/dstackai/sqircle.git"
10
+ },
11
+ "homepage": "https://github.com/dstackai/sqircle#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/dstackai/sqircle/issues"
14
+ },
15
+ "sideEffects": [
16
+ "**/*.css"
17
+ ],
18
+ "files": [
19
+ "dist",
20
+ "README.md",
21
+ "docs/react",
22
+ "docs/design"
23
+ ],
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/sqircle.js"
28
+ },
29
+ "./style.css": "./dist/style.css"
30
+ },
31
+ "types": "./dist/index.d.ts",
32
+ "module": "./dist/sqircle.js",
33
+ "scripts": {
34
+ "dev": "vite --host 127.0.0.1",
35
+ "build": "npm run typecheck && npm run build:lib",
36
+ "build:demo": "tsc --noEmit && vite build",
37
+ "build:lib": "vite build --config vite.lib.config.ts && npm run build:types",
38
+ "build:types": "tsc -p tsconfig.build.json",
39
+ "typecheck": "tsc --noEmit"
40
+ },
41
+ "peerDependencies": {
42
+ "react": ">=18.0.0",
43
+ "react-dom": ">=18.0.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/react": "^19.2.17",
47
+ "@types/react-dom": "^19.2.3",
48
+ "@vitejs/plugin-react": "^6.0.3",
49
+ "react": "^19.0.0",
50
+ "react-dom": "^19.0.0",
51
+ "typescript": "^6.0.3",
52
+ "vite": "^8.1.0"
53
+ }
54
+ }