@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.
@@ -1,17 +1,16 @@
1
1
  # React Component Guide
2
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.
3
+ The maintainable renderer implementation lives in `src/squircle`. The root HTML files mount React examples from `src/pages`; reusable scene work should prefer `SquircleScene` rather than static SVG markup.
4
4
 
5
5
  ## Files
6
6
 
7
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
8
  - `src/squircle/geometry.ts`: generates superellipse prism, hidden edge, inlay, label transform, and gradient bounds from constants.
11
9
  - `src/squircle/palettes.ts`: exports palette constants.
12
10
  - `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`.
11
+ - `src/pages`: repo-local examples that consume `SquircleScene`.
12
+ - Top-plane text is rendered by `SquircleScene` as live SVG text projected with the same isometric matrix as the top face. The demos use `GPU` as example text, but component configs can pass any string.
13
+ - `src/squircle/SquircleEditor.tsx` and `src/squircle/codeExport.ts`: optional constructor UI and code exporter, documented separately in [editor.md](./editor.md).
15
14
 
16
15
  ## Renderer Component
17
16
 
@@ -35,72 +34,112 @@ The maintainable implementation lives in `src/squircle`. Static HTML pages remai
35
34
 
36
35
  ## Editor Component
37
36
 
38
- Use `SquircleEditor` when you want the complete constructor UI:
37
+ The optional constructor UI is documented separately in [Editor Guide](./editor.md). Keep this guide focused on the main renderer and reusable scene data model.
39
38
 
40
- ```tsx
41
- <SquircleEditor
42
- initialLayers={createSquircleLayers(5)}
43
- onChange={(layers) => console.log(layers)}
44
- />
45
- ```
39
+ ## Complete Options Reference
46
40
 
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`.
41
+ This section is the renderer source of truth for public options. Keep it synchronized with `src/squircle/types.ts`.
48
42
 
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.
43
+ ### `SquircleScene`
50
44
 
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.
45
+ | Prop | Type | Default | Meaning |
46
+ | --- | --- | --- | --- |
47
+ | `layers` | `SquircleLayerConfig[]` | required | Draw-order layer array. First item is lowest/backmost; last item is topmost/frontmost. |
48
+ | `geometry` | `SquircleGeometryConfig` | `DEFAULT_GEOMETRY` | Scene geometry, projection, prism height, and dashed inlay scale. |
49
+ | `selectedLayerId` | `string \| null` | `null` | Marks a layer as selected for class/state styling. Does not move or restack layers. |
50
+ | `theme` | `light`, `dark` | `light` | Adds theme classes/data attributes for host styling while keeping the SVG background transparent. |
51
+ | `idPrefix` | `string` | generated React id | Prefix for SVG gradient ids. Use when deterministic ids are required. |
52
+ | `className` | `string` | none | Extra class on the root SVG. |
53
+ | `ariaLabel` | `string` | `Squircle scene` | Accessible label for the SVG. |
54
+ | `fitToLayers` | `boolean` | `true` | Expands the viewBox height to include visible layer offsets. |
55
+ | `transitionMs` | `number` | `220` | Hover crossfade duration in milliseconds. |
56
+ | `onLayerSelect` | `(layerId: string) => void` | none | Called when a rendered layer is clicked. |
52
57
 
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
- ```
58
+ ### `SquircleLayerConfig`
66
59
 
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.
60
+ | Field | Type | Default | Meaning |
61
+ | --- | --- | --- | --- |
62
+ | `id` | `string` | required | Stable layer id. |
63
+ | `visible` | `boolean` | `true` | Hidden layers are skipped without changing other offsets. |
64
+ | `offset` | `{ x?: number; y?: number }` | `{ x: 0, y: 0 }` | SVG translation for this layer. |
65
+ | `base` | `SquircleVariantConfig` | required | Normal layer state. |
66
+ | `hover` | `SquircleVariantConfig` | none | Hover state merged over `base`. If absent or identical, no hover copy is rendered. |
67
+ | `stroke` | `Partial<SquircleStrokeConfig>` | default strokes | Layer-wide stroke overrides. |
68
+ | `opacity` | `Partial<SquircleOpacityConfig>` | default opacity | Layer-wide opacity overrides. |
69
+ | `className` | `string` | none | Extra class on the layer group. |
68
70
 
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
71
+ ### `SquircleVariantConfig`
84
72
 
85
73
  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
74
 
87
- Variant fields:
88
-
89
- | Field | Values | Meaning |
75
+ | Field | Values | Default | Meaning |
76
+ | --- | --- | --- | --- |
77
+ | `material` | `solid`, `transparent`, `wireframe` | `wireframe` | Prism rendering mode. |
78
+ | `paletteId` | `13` through `20` | `15` | Palette from `SQUIRCLE_PALETTES`. |
79
+ | `text` | `string`, `boolean` | none | Render top-plane text. Pass a string such as `"GPU"` or `"{}"`; `true` is a compatibility shorthand for `"GPU"`. |
80
+ | `dash` | `boolean` | `false` | Render the dashed inlay. |
81
+ | `textStyle` | `solid`, `wireframe` | `solid` | Filled or outlined text. |
82
+ | `textColor` | `auto`, `white`, `black`, `contrast` | `contrast` | Text paint for solid/transparent material. `auto` and `contrast` currently resolve the same way. |
83
+ | `textSize` | `number` | `62` | Text font size. |
84
+ | `textFontFamily` | `string` | `Arial, Helvetica, sans-serif` | SVG text font family. |
85
+ | `textFontWeight` | `string`, `number` | `400` | SVG text font weight. |
86
+ | `dashColor` | `auto`, `white`, `black`, `contrast` | `contrast` | Dash paint for solid/transparent material. |
87
+ | `stroke` | `Partial<SquircleStrokeConfig>` | default strokes | Per-variant stroke overrides. |
88
+ | `opacity` | `Partial<SquircleOpacityConfig>` | default opacity | Per-variant opacity overrides. |
89
+
90
+ `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`.
91
+
92
+ ### `SquircleGeometryConfig`
93
+
94
+ | Field | Type | Default | Meaning |
95
+ | --- | --- | --- | --- |
96
+ | `width` | `number` | `800` | SVG viewBox width and geometry fitting target. |
97
+ | `viewBoxHeight` | `number` | `480` | Base SVG viewBox height before optional layer fitting. |
98
+ | `exponent` | `number` | `12` | Superellipse exponent. Lower is rounder, higher is squarer. |
99
+ | `samples` | `number` | `160` | Number of sampled superellipse points. |
100
+ | `halfSize` | `number` | computed from `width` | Raw superellipse half-size before projection. |
101
+ | `prismHeight` | `number` | `10` | Extrusion height in SVG units. |
102
+ | `angleDegrees` | `number` | `20` | Isometric projection elevation angle. |
103
+ | `inlayScale` | `number` | `0.6` | Dashed inlay scale relative to the outer squircle. |
104
+ | `center` | `{ x: number; y: number }` | computed | Projection center. Override only when manually composing a custom viewBox. |
105
+
106
+ ### `SquircleStrokeConfig`
107
+
108
+ | Field | Default | Meaning |
109
+ | --- | --- | --- |
110
+ | `face` | `0.35` | Hairline stroke for filled top and side faces. |
111
+ | `faceOpacity` | `0.72` | Opacity for filled-face strokes. |
112
+ | `wire` | `1.6` | Visible wireframe outline width. |
113
+ | `wireOpacity` | `0.88` | Visible wireframe outline opacity. |
114
+ | `hidden` | `1.2` | Hidden/back wireframe guide width. |
115
+ | `hiddenOpacity` | `0.28` | Hidden/back wireframe guide opacity. |
116
+ | `dash` | `2.2` | Dashed inlay width for solid/transparent material. |
117
+ | `wireDash` | `1.6` | Dashed inlay width for wireframe material. |
118
+ | `labelWire` | `1.1` | Outlined text stroke width. |
119
+
120
+ ### `SquircleOpacityConfig`
121
+
122
+ | Field | Default | Meaning |
90
123
  | --- | --- | --- |
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`.
124
+ | `transparentFace` | `0.38` | Face opacity for `material: "transparent"`. |
125
+ | `transparentAnnotation` | `0.62` | Text/dash opacity on transparent material. |
126
+ | `solidAnnotation` | `0.88` | Text/dash opacity on solid or wireframe material. |
127
+
128
+ ### Palettes
129
+
130
+ `paletteId` accepts any id from `SQUIRCLE_PALETTE_IDS`: `13`, `14`, `15`, `16`, `17`, `18`, `19`, `20`. The palette object fields are:
131
+
132
+ | Field | Meaning |
133
+ | --- | --- |
134
+ | `id` | Stable palette id. |
135
+ | `label` | Human-readable palette name. |
136
+ | `top` | Top-face gradient stops. |
137
+ | `side` | Side-wall gradient stops. |
138
+ | `textWire` | Gradient stops used for outlined text. |
139
+ | `labelFill` | Automatic filled text/dash color. |
140
+ | `topEdge` | Top-face hairline stroke color. |
141
+ | `sideEdge` | Side-wall hairline stroke color. |
142
+ | `swatch` | Two-color UI swatch. |
104
143
 
105
144
  ## Stroke Parameters
106
145
 
@@ -149,7 +188,7 @@ The component generates polygons at render time. Do not paste generated point li
149
188
 
150
189
  ## 0-N Layer Helpers
151
190
 
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`.
191
+ Use `createSquircleLayers(count)` for quick default layers and `reflowLayerOffsets(layers, gap)` when adding/removing layers in a host UI. These helpers preserve the convention that the top layer has `offset.y = 0` and lower layers move downward by `gap`.
153
192
 
154
193
  ## Verification
155
194
 
@@ -160,7 +199,7 @@ npm run typecheck
160
199
  npm run build
161
200
  ```
162
201
 
163
- Then open `/react.html` from the Vite dev server and verify:
202
+ Then open `/index.html` and `/demo.html` from the Vite dev server and verify:
164
203
 
165
204
  - empty layer arrays render an empty SVG without crashing.
166
205
  - adding layers keeps array order as draw order.
@@ -0,0 +1,159 @@
1
+ # Editor Guide
2
+
3
+ `SquircleEditor` is the optional constructor UI for building `SquircleScene` layer configs and copying ready-to-use React code. Keep renderer details in [README.md](./README.md); this file owns editor behavior, editor props, and code export.
4
+
5
+ ## Files
6
+
7
+ - `src/squircle/SquircleEditor.tsx`: reusable constructor UI.
8
+ - `src/squircle/SquircleEditor.css`: editor shell, layer list, inspector, preview, and responsive styles.
9
+ - `src/squircle/codeExport.ts`: serializes editor state into a copyable `SquircleScene` component snippet.
10
+ - `src/pages/ConstructorPage.tsx`: local example page that mounts `SquircleEditor`.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { SquircleEditor } from "@dstackai/sqircle";
16
+ import "@dstackai/sqircle/style.css";
17
+
18
+ export function Constructor() {
19
+ return (
20
+ <SquircleEditor
21
+ title="Squircle Constructor"
22
+ codeImportPath="@dstackai/sqircle"
23
+ showCode
24
+ />
25
+ );
26
+ }
27
+ ```
28
+
29
+ Use controlled state when the host app owns layer, geometry, or theme data:
30
+
31
+ ```tsx
32
+ <SquircleEditor
33
+ value={layers}
34
+ onChange={setLayers}
35
+ geometry={geometry}
36
+ onGeometryChange={setGeometry}
37
+ theme={theme}
38
+ onThemeChange={setTheme}
39
+ />
40
+ ```
41
+
42
+ ## UX Model
43
+
44
+ The editor is organized as three zones:
45
+
46
+ - `Layers`: cards with material, palette, text/dash/hover tags, and an eye icon for visibility. Add and clear actions live beside the layer count.
47
+ - `Preview`: the live `SquircleScene` stage. Clicking a squircle selects that layer. When `showCode` is enabled, the Code drawer can be opened without permanently shrinking the stage; the copy icon is available from the preview toolbar and the drawer header.
48
+ - `Inspector`: selected-layer editing. `Base` and `Hover` are explicit state tabs, and both states use the same variant controls.
49
+
50
+ On desktop, the editor workspace has a fixed app-like height and the side panels scroll internally. This keeps the preview stage stable when toggling controls such as Text or Dash; annotations must never appear to move layer positions.
51
+
52
+ The controls are intentionally visual: material and text-paint choices use segmented buttons, text/dash/hover use action chips, palette choices use swatches, and editable annotation colors use `Auto / White / Black` segmented controls. Avoid generic dropdowns or checkboxes unless the option set becomes too large to scan.
53
+
54
+ Hover editing is a pure variant swap. Enabling hover creates a hover variant by copying the base state and flipping the material (`wireframe` <-> `solid`) as a starting point. No hover control should add transforms, filters, shadows, spacing changes, or layer-gap changes.
55
+
56
+ ## Editor Options
57
+
58
+ | Prop | Type | Default | Meaning |
59
+ | --- | --- | --- | --- |
60
+ | `value` | `SquircleLayerConfig[]` | uncontrolled | Controlled layer state. Pair with `onChange`. |
61
+ | `initialLayers` | `SquircleLayerConfig[]` | three wireframe layers | Initial uncontrolled layer state. |
62
+ | `onChange` | `(layers) => void` | none | Receives layer updates. |
63
+ | `geometry` | `SquircleGeometryConfig` | uncontrolled | Controlled geometry state. Pair with `onGeometryChange`. |
64
+ | `initialGeometry` | `SquircleGeometryConfig` | editor defaults | Initial uncontrolled geometry state. |
65
+ | `onGeometryChange` | `(geometry) => void` | none | Receives geometry updates. |
66
+ | `title` | `string` | `Squircle` | Editor heading and generated component-name fallback. |
67
+ | `description` | `string` | constructor description | Editor subtitle. |
68
+ | `schema` | `string` | none | Deprecated compatibility prop; unused by the React-code exporter. |
69
+ | `className` | `string` | none | Extra class on the editor root. |
70
+ | `layerGap` | `number` | `88` | Gap used when adding/removing layers with `reflowLayerOffsets()`. |
71
+ | `showCode` | `boolean` | `showConfig` | Shows the React Code drawer. |
72
+ | `showConfig` | `boolean` | `true` | Deprecated alias for `showCode`. |
73
+ | `codeComponentName` | `string` | `title` | Generated React component name. |
74
+ | `codeImportPath` | `string` | `./squircle` | Import path used by the generated code. |
75
+ | `theme` | `light`, `dark` | uncontrolled | Controlled editor and preview theme. Pair with `onThemeChange`. |
76
+ | `defaultTheme` | `light`, `dark` | `light` | Initial uncontrolled editor theme. |
77
+ | `onThemeChange` | `(theme) => void` | none | Receives theme updates. |
78
+ | `showThemeSwitch` | `boolean` | `true` | Shows the Light/Dark switcher. |
79
+
80
+ ## Editor Coverage
81
+
82
+ The editor exposes the common constructor surface:
83
+
84
+ - layer add/delete/visibility and preview selection
85
+ - theme
86
+ - scene radius, height, and dash size
87
+ - base material, palette, text, text paint, text color, text size, font weight, dash, and dash color
88
+ - hover material, palette, text, text paint, text color, text size, font weight, dash, and dash color
89
+ - collapsed stroke width controls for face, wire, dash, and text outline
90
+ - generated React code
91
+
92
+ Layer labels are generic (`Layer 1`, `Layer 2`, ...), because the editor supports 0-N layers. The array order remains draw order: lower-numbered layers are lower/backmost, and higher-numbered layers sit above them after `reflowLayerOffsets()`.
93
+
94
+ Supported renderer API fields that remain intentionally code-only in the editor:
95
+
96
+ - advanced text typography: `textFontFamily`
97
+ - advanced opacity: `transparentFace`, `transparentAnnotation`, `solidAnnotation`
98
+ - deeper stroke tuning: `faceOpacity`, `wireOpacity`, `hidden`, `hiddenOpacity`, `wireDash`
99
+ - advanced geometry beyond Radius/Height/Dash size: `angleDegrees`, `center`, `width`, `viewBoxHeight`, `samples`, `halfSize`
100
+ - manual layer offsets and scene fitting
101
+
102
+ ## Annotation Rules
103
+
104
+ Annotation color controls are contextual. Text color appears after enabling text; dash color appears after enabling dash. Wireframe material owns annotation paint through gradients, so the editor shows the forced gradient token instead of pretending `Auto / White / Black` would apply.
105
+
106
+ `Auto` is the default annotation color. Enabling text or dash writes `auto` explicitly unless the layer already has a color choice. Text size and font weight appear with the text controls because they are per-variant typography settings.
107
+
108
+ Radius, Height, and Dash size are scene-level controls in the left panel. Radius maps to `geometry.exponent`, Height maps to `geometry.prismHeight`, and Dash size maps to `geometry.inlayScale`.
109
+
110
+ ## Code Export
111
+
112
+ `showConfig` remains as a deprecated compatibility alias for `showCode`; new callers should use `showCode`. The Code drawer 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.
113
+
114
+ When `codeImportPath="@dstackai/sqircle"`, generated code also imports `@dstackai/sqircle/style.css`. Set `styleImportPath={false}` through `createSquircleReactCode()` if a host app already bundles the component CSS another way.
115
+
116
+ Use the exporter directly when you need the same code string outside the full editor:
117
+
118
+ ```ts
119
+ import { createSquircleReactCode } from "@dstackai/sqircle";
120
+
121
+ const code = createSquircleReactCode({
122
+ theme: "light",
123
+ layers,
124
+ geometry,
125
+ componentName: "CustomSquircle",
126
+ importPath: "@dstackai/sqircle"
127
+ });
128
+ ```
129
+
130
+ ### `createSquircleReactCode()`
131
+
132
+ | Option | Type | Default | Meaning |
133
+ | --- | --- | --- | --- |
134
+ | `layers` | `SquircleLayerConfig[]` | required | Layers to serialize into TSX. |
135
+ | `theme` | `SquircleTheme` | required | Theme to serialize into TSX. |
136
+ | `geometry` | `SquircleGeometryConfig` | none | Optional geometry object to serialize. |
137
+ | `componentName` | `string` | `CustomSquircle` | Generated component name. Sanitized with `toComponentName()`. |
138
+ | `importPath` | `string` | `./squircle` | Import path for `SquircleScene` and types. |
139
+ | `styleImportPath` | `string \| false` | auto for `@dstackai/sqircle` | CSS import path. Set `false` to omit CSS import. |
140
+ | `ariaLabel` | `string` | `${componentName} composition` | Serialized `ariaLabel` for `SquircleScene`. |
141
+
142
+ ## Verification
143
+
144
+ Run:
145
+
146
+ ```sh
147
+ npm run typecheck
148
+ npm run build
149
+ npm run build:demo
150
+ ```
151
+
152
+ Then open `/constructor.html` from the Vite dev server and verify:
153
+
154
+ - three default wireframe layers render.
155
+ - click selection works in the layer list and preview.
156
+ - visibility, add, delete, theme switching, and shape controls update the preview.
157
+ - base and hover state tabs expose the same variant controls.
158
+ - no-op hover layers do not blink.
159
+ - copied React code imports the expected package path and includes active layers, theme, and geometry.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dstackai/sqircle",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "React components for precise isometric squircle-prism SVG compositions.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -19,7 +19,8 @@
19
19
  "dist",
20
20
  "README.md",
21
21
  "docs/react",
22
- "docs/design"
22
+ "docs/design",
23
+ "docs/examples"
23
24
  ],
24
25
  "exports": {
25
26
  ".": {