@react-scad/core 0.1.7 → 0.1.9

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
@@ -2,23 +2,51 @@
2
2
 
3
3
  Render JSX to **OpenSCAD** models using the [React reconciler](https://github.com/facebook/react/tree/main/packages/react-reconciler).
4
4
 
5
- - Write declarative 3D with React components; no imperative OpenSCAD scripting
6
- - Compose shapes with familiar JSX; get `.scad` source for OpenSCAD or 3D printing
7
- - Use [@react-scad/cli](../cli) via npx to build and run
5
+ - Describe models as a tree of components instead of imperative SCAD; avoids nested modules and parameter threading.
6
+ - Same React/JSX mental model (components, props, composition), output is 3D.
7
+ - Writes plain `.scad` files for [OpenSCAD](https://openscad.org/) or any slicer.
8
8
 
9
- ![Example](https://github.com/react-scad/react-scad/raw/main/assets/example.png)
9
+ ## Preview
10
10
 
11
- Install the library and React:
11
+ ![Example](./assets/example.gif)
12
+
13
+ *Rocket example with animated rotation.*
14
+
15
+ ## Why react-scad?
16
+
17
+ SCAD is good for parametric 3D but scripts are imperative and nesting gets heavy; composing modules and passing parameters is tedious.
18
+
19
+ A lot of people already think in components and JSX from building UIs. **react-scad** aims to facilitate that same way of thinking for parametric 3D.
20
+
21
+ ## Getting Started
22
+
23
+ ### Prerequisites
24
+
25
+ - **Node.js** 18+
26
+ - **React** 18 or later (peer dependency)
27
+
28
+ ### Install
12
29
 
13
30
  ```bash
14
31
  npm install react @react-scad/core
15
32
  ```
16
33
 
34
+ With pnpm or yarn:
35
+
36
+ ```bash
37
+ pnpm add react @react-scad/core
38
+ # or
39
+ yarn add react @react-scad/core
40
+ ```
41
+
17
42
  ### Minimal example
18
43
 
44
+ Create a file `main.tsx` (or `main.jsx`):
45
+
19
46
  ```jsx
20
47
  import { createRoot, Cube, Sphere, Union } from "@react-scad/core";
21
48
 
49
+ // Output path: the .scad file that will be created
22
50
  const root = createRoot("model.scad");
23
51
 
24
52
  root.render(
@@ -29,18 +57,58 @@ root.render(
29
57
  );
30
58
  ```
31
59
 
32
- Save as `main.tsx`, then run it:
60
+ - `createRoot("model.scad")`: creates a root that writes to `model.scad`.
61
+ - `Union`: CSG union of all children (like `union()` in SCAD).
62
+ - `Cube` / `Sphere`: props match SCAD: `size`, `center`, `r`, `$fn`, etc.
63
+
64
+ ### Run and write the `.scad` file
65
+
66
+ Run your entry file with [tsx](https://github.com/privatenumber/tsx) so Node can execute the `.tsx`. The `.scad` file is written to the **current working directory** when you call `root.render()`.
33
67
 
34
68
  ```bash
35
- npx @react-scad/cli run main.tsx
36
- npx @react-scad/cli run main.tsx --watch
69
+ npx tsx main.tsx
37
70
  ```
38
71
 
39
- ## Primitives (OpenSCAD coverage)
72
+ Watch mode (re-run on save):
73
+
74
+ ```bash
75
+ npx tsx watch main.tsx
76
+ ```
40
77
 
41
- All listed OpenSCAD primitives and operations are implemented. Prop names follow OpenSCAD where it makes sense (`r`, `h`, `size`, `center`, `$fn`, etc.).
78
+ Run from the directory that contains `main.tsx` (or use the path to it).
42
79
 
43
- | OpenSCAD | react-scad | Implemented |
80
+ ### View the result
81
+
82
+ - Open the generated `.scad` file in [OpenSCAD](https://openscad.org/) to preview, export STL, or tweak.
83
+ - Or import the `.scad` (or an exported STL) into your slicer for 3D printing.
84
+
85
+ ## Advanced
86
+
87
+ To write to a custom path or get the SCAD string in memory instead of using `createRoot(path)`, use a container and `toScad`:
88
+
89
+ ```jsx
90
+ import { createContainer, render, toScad, Cube, Sphere, Union } from "@react-scad/core";
91
+ import { writeFileSync } from "fs";
92
+
93
+ const container = createContainer();
94
+ render(
95
+ <Union>
96
+ <Cube size={[10, 10, 10]} center />
97
+ <Sphere r={6} />
98
+ </Union>,
99
+ container
100
+ );
101
+
102
+ const scadCode = toScad(container);
103
+ writeFileSync("out/model.scad", scadCode);
104
+ // or use scadCode however you like
105
+ ```
106
+
107
+ ## Primitives (SCAD coverage)
108
+
109
+ All listed SCAD primitives and operations are implemented. Prop names follow SCAD where it makes sense (`r`, `h`, `size`, `center`, `$fn`, etc.).
110
+
111
+ | SCAD | react-scad | Implemented |
44
112
  | -------- | ---------- | :---------: |
45
113
  | **3D primitives** | | |
46
114
  | `cube()` | `Cube` | ✓ |
@@ -70,22 +138,41 @@ All listed OpenSCAD primitives and operations are implemented. Prop names follow
70
138
  | `surface()` | `Surface` | ✓ |
71
139
  | `import()` | `Import` | ✓ |
72
140
 
73
- ## Why react-scad?
141
+ ## Contributing
142
+
143
+ 1. **Fork and clone** the repo, then install dependencies:
144
+ ```bash
145
+ git clone https://github.com/YOUR_USER/react-scad.git
146
+ cd react-scad
147
+ pnpm install
148
+ ```
149
+
150
+ 2. **Create a branch** for your change:
151
+ ```bash
152
+ git checkout -b fix/your-change
153
+ ```
74
154
 
75
- OpenSCAD is great for parametric 3D, but code is imperative and nesting gets messy. Composing modules and passing parameters can be tedious.
155
+ 3. **Build and test** before committing:
156
+ ```bash
157
+ pnpm run build
158
+ pnpm run dev # optional: smoke-test the example
159
+ ```
76
160
 
77
- With react-scad you build a tree of SCAD primitives using React components. The reconciler runs with a custom host config that builds an internal SCAD tree (no DOM). Serialization turns that tree into OpenSCAD source.
161
+ 4. **Format and lint** (all code in the repo):
162
+ ```bash
163
+ pnpm run format
164
+ pnpm run lint
165
+ ```
78
166
 
79
- **JSX React tree host config SCAD tree OpenSCAD source.**
167
+ 5. **Open a PR** against `main` with a short description of the change. For bugs, reference the issue if one exists.
80
168
 
81
- You get declarative composition, reuse via components, and the same `.scad` output you’d use in OpenSCAD or slicers.
169
+ 6. **Publishing** is done via GitHub Actions on push to `main`; no need to publish from a PR.
82
170
 
83
171
  ## Acknowledgements
84
172
 
85
173
  - [React](https://react.dev/) and the [React reconciler](https://github.com/facebook/react/tree/main/packages/react-reconciler) for the rendering model that makes this approach possible.
86
- - [OpenSCAD](https://openscad.org/) for the script-based CAD language and documentation.
174
+ - [OpenSCAD](https://openscad.org/) for the SCAD language and documentation.
87
175
 
88
176
  ## License
89
177
 
90
- React SCAD is MIT-licensed open-source software by Leon Meka and
91
- contributors.
178
+ MIT
package/dist/log.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export declare const c: {
2
+ dim: (s: string) => string;
3
+ cyan: (s: string) => string;
4
+ green: (s: string) => string;
5
+ red: (s: string) => string;
6
+ yellow: (s: string) => string;
7
+ bold: (s: string) => string;
8
+ };
9
+ export declare const log: {
10
+ buildOk(ms: number): void;
11
+ wrote(path: string, ms: number): void;
12
+ watchCycle(ms: number, code: number | null): void;
13
+ };
14
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,CAAC;aACJ,MAAM;cACL,MAAM;eACL,MAAM;aACR,MAAM;gBACH,MAAM;cACR,MAAM;CAChB,CAAC;AAMF,eAAO,MAAM,GAAG;gBACH,MAAM,GAAG,IAAI;gBAGb,MAAM,MAAM,MAAM,GAAG,IAAI;mBAGtB,MAAM,QAAQ,MAAM,GAAG,IAAI,GAAG,IAAI;CAKjD,CAAC"}
package/dist/log.js ADDED
@@ -0,0 +1,25 @@
1
+ const noColor = process.env.NO_COLOR != null || !process.stdout.isTTY;
2
+ export const c = {
3
+ dim: (s) => (noColor ? s : `\x1b[2m${s}\x1b[0m`),
4
+ cyan: (s) => (noColor ? s : `\x1b[36m${s}\x1b[0m`),
5
+ green: (s) => (noColor ? s : `\x1b[32m${s}\x1b[0m`),
6
+ red: (s) => (noColor ? s : `\x1b[31m${s}\x1b[0m`),
7
+ yellow: (s) => (noColor ? s : `\x1b[33m${s}\x1b[0m`),
8
+ bold: (s) => (noColor ? s : `\x1b[1m${s}\x1b[0m`),
9
+ };
10
+ function time() {
11
+ return new Date().toLocaleTimeString("en-US", { hour12: false });
12
+ }
13
+ export const log = {
14
+ buildOk(ms) {
15
+ console.log(c.dim(`[${time()}] `) + c.green("✓ Built") + c.dim(` in ${ms}ms`));
16
+ },
17
+ wrote(path, ms) {
18
+ console.log(c.dim(`[${time()}] `) + c.green("✓ Wrote") + c.dim(` ${path} in ${ms}ms`));
19
+ },
20
+ watchCycle(ms, code) {
21
+ const ok = code === 0;
22
+ const status = ok ? c.green("done") : c.red(`exit ${code}`);
23
+ console.log(` ${ok ? c.green("✓") : c.red("✗")}${c.dim(` ${ms}ms `)}${status}`);
24
+ },
25
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../src/runtime/root.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAKjD,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B,MAAM,WAAW,QAAQ;IACxB,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC;IAC1C,MAAM,IAAI,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC;AAE1B,wBAAgB,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,QAAQ,CAchD;AAED,wBAAgB,eAAe,IAAI,aAAa,CAE/C;AAED,wBAAgB,MAAM,CACrB,OAAO,EAAE,KAAK,CAAC,YAAY,EAC3B,SAAS,EAAE,aAAa,EACxB,QAAQ,CAAC,EAAE,MAAM,IAAI,GACnB,IAAI,CAGN;AAED,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../src/runtime/root.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAKjD,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B,MAAM,WAAW,QAAQ;IACxB,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC;IAC1C,MAAM,IAAI,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC;AAE1B,wBAAgB,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,QAAQ,CAgBhD;AAED,wBAAgB,eAAe,IAAI,aAAa,CAE/C;AAED,wBAAgB,MAAM,CACrB,OAAO,EAAE,KAAK,CAAC,YAAY,EAC3B,SAAS,EAAE,aAAa,EACxB,QAAQ,CAAC,EAAE,MAAM,IAAI,GACnB,IAAI,CAGN;AAED,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { log } from "../log.js";
1
2
  import { toScad } from "../serialize/index.js";
2
3
  import { createScadContainer } from "./node-ops.js";
3
4
  import { createFiberRoot, updateContainer } from "./reconciler.js";
@@ -9,9 +10,11 @@ export function createRoot(path) {
9
10
  registerWriteOnCommit(container, path);
10
11
  return {
11
12
  render(element) {
13
+ const start = Date.now();
12
14
  updateContainer(element, fiberRoot, null, null);
13
15
  if (path)
14
16
  writeAfterCommit(container);
17
+ log.watchCycle(Date.now() - start, 0);
15
18
  },
16
19
  toScad() {
17
20
  return toScad(container);
@@ -1 +1 @@
1
- {"version":3,"file":"write-on-commit.d.ts","sourceRoot":"","sources":["../../src/runtime/write-on-commit.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAElF;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,aAAa,GAAG,IAAI,CAG/D"}
1
+ {"version":3,"file":"write-on-commit.d.ts","sourceRoot":"","sources":["../../src/runtime/write-on-commit.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,wBAAgB,qBAAqB,CACpC,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,MAAM,GACV,IAAI,CAEN;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,aAAa,GAAG,IAAI,CAU/D"}
@@ -1,4 +1,6 @@
1
- import { writeFileSync } from "node:fs";
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { log } from "../log.js";
2
4
  import { toScad } from "../serialize/index.js";
3
5
  const writeOnCommitPaths = new WeakMap();
4
6
  export function registerWriteOnCommit(container, path) {
@@ -6,6 +8,13 @@ export function registerWriteOnCommit(container, path) {
6
8
  }
7
9
  export function writeAfterCommit(container) {
8
10
  const path = writeOnCommitPaths.get(container);
9
- if (path)
11
+ if (path) {
12
+ const start = Date.now();
13
+ const dir = dirname(path);
14
+ if (dir !== ".")
15
+ mkdirSync(dir, { recursive: true });
10
16
  writeFileSync(path, toScad(container), "utf8");
17
+ const ms = Date.now() - start;
18
+ log.wrote(path, ms);
19
+ }
11
20
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-scad/core",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Render JSX to OpenSCAD models using the React reconciler",
5
5
  "keywords": [
6
6
  "react",