@mulmoclaude/shapescript-plugin 1.1.1 → 1.2.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.
- package/README.md +1 -0
- package/dist/core.cjs +1 -1
- package/dist/core.js +3 -2
- package/dist/index.cjs +1 -1
- package/dist/index.js +3 -2
- package/dist/render/index.d.ts +5 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render/page.d.ts +31 -0
- package/dist/render/page.d.ts.map +1 -0
- package/dist/render/renderer.d.ts +45 -0
- package/dist/render/renderer.d.ts.map +1 -0
- package/dist/render.cjs +82 -0
- package/dist/render.js +187 -0
- package/dist/samples-DHEKUNs5.cjs +186 -0
- package/dist/samples-SaXsqbHJ.js +180 -0
- package/dist/{samples-6f1sYMm7.js → toThreeJS-B5LiF110.js} +1865 -2046
- package/dist/toThreeJS-BOBpx5Dc.cjs +10 -0
- package/dist/vue.cjs +31 -31
- package/dist/vue.js +2001 -2000
- package/package.json +18 -4
- package/dist/samples-B3VO3Mnc.cjs +0 -195
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ name, and the `Present3D*` type names, are renamed throughout.
|
|
|
13
13
|
| Entry | Contents |
|
|
14
14
|
| ------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
15
15
|
| `.` | `TOOL_NAME`, `TOOL_DEFINITION`, `executePresentShapeScript`, `pluginCore`, `samples`, `parseShapeScript`, `astToThreeJS`, `executeShapeScriptDispatch` + the `artifacts/shapes` path rules |
|
|
16
|
+
| `./render` | **server-only** — `renderShapeScriptSheet` and the render page. Rasterises a model to a PNG with Puppeteer's headless Chromium (an OPTIONAL peer); a host without one gets `RenderUnavailableError` carrying the install hint. |
|
|
16
17
|
| `./vue` | the `ToolPlugin` (View + Preview + `SYSTEM_PROMPT`), plus everything on `.` |
|
|
17
18
|
| `./style.css` | the compiled component styles (Vite lib mode does not auto-inject them) |
|
|
18
19
|
|
package/dist/core.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./samples-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./samples-DHEKUNs5.cjs"),t=require("./toThreeJS-BOBpx5Dc.cjs");exports.SHAPE_EXTENSIONS=e.s,exports.TOOL_DEFINITION=e.f,exports.TOOL_NAME=e.p,exports.astToThreeJS=t.n,exports.executePresentShapeScript=e.n,exports.executeShapeScriptDispatch=e.a,exports.isPresentableShapePath=e.c,exports.isShapeArtifactPath=e.l,exports.isShapeScriptDispatchArgs=t.Ln,exports.locateShape=e.o,exports.parseShapeScript=t.Pn,exports.pluginCore=e.r,exports.presentShapeScript=e.i,exports.readLoadShapeResult=t.Rn,exports.readSaveShapeResult=t.zn,exports.samples=e.t,exports.shapeArtifactPath=e.u,exports.toArtifactsRelative=e.d;
|
package/dist/core.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, p as c, r as l, s as u, t as d, u as f } from "./samples-SaXsqbHJ.js";
|
|
2
|
+
import { Ln as p, Pn as m, Rn as h, n as g, zn as _ } from "./toThreeJS-B5LiF110.js";
|
|
3
|
+
export { u as SHAPE_EXTENSIONS, r as TOOL_DEFINITION, c as TOOL_NAME, g as astToThreeJS, o as executePresentShapeScript, e as executeShapeScriptDispatch, t as isPresentableShapePath, a as isShapeArtifactPath, p as isShapeScriptDispatchArgs, s as locateShape, m as parseShapeScript, l as pluginCore, i as presentShapeScript, h as readLoadShapeResult, _ as readSaveShapeResult, d as samples, f as shapeArtifactPath, n as toArtifactsRelative };
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./samples-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./samples-DHEKUNs5.cjs"),t=require("./toThreeJS-BOBpx5Dc.cjs");require("./core.cjs"),exports.SHAPE_EXTENSIONS=e.s,exports.TOOL_DEFINITION=e.f,exports.TOOL_NAME=e.p,exports.astToThreeJS=t.n,exports.executePresentShapeScript=e.n,exports.executeShapeScriptDispatch=e.a,exports.isPresentableShapePath=e.c,exports.isShapeArtifactPath=e.l,exports.isShapeScriptDispatchArgs=t.Ln,exports.locateShape=e.o,exports.parseShapeScript=t.Pn,exports.pluginCore=e.r,exports.presentShapeScript=e.i,exports.readLoadShapeResult=t.Rn,exports.readSaveShapeResult=t.zn,exports.samples=e.t,exports.shapeArtifactPath=e.u,exports.toArtifactsRelative=e.d;
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, p as c, r as l, s as u, t as d, u as f } from "./samples-SaXsqbHJ.js";
|
|
2
|
+
import { Ln as p, Pn as m, Rn as h, n as g, zn as _ } from "./toThreeJS-B5LiF110.js";
|
|
2
3
|
import "./core.js";
|
|
3
|
-
export {
|
|
4
|
+
export { u as SHAPE_EXTENSIONS, r as TOOL_DEFINITION, c as TOOL_NAME, g as astToThreeJS, o as executePresentShapeScript, e as executeShapeScriptDispatch, t as isPresentableShapePath, a as isShapeArtifactPath, p as isShapeScriptDispatchArgs, s as locateShape, m as parseShapeScript, l as pluginCore, i as presentShapeScript, h as readLoadShapeResult, _ as readSaveShapeResult, d as samples, f as shapeArtifactPath, n as toArtifactsRelative };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { renderShapeScriptSheet, RenderUnavailableError, CHROMIUM_HINT, RENDER_TIMEOUT_MS, LAUNCH_TIMEOUT_MS, RENDER_BUDGET_MS } from "./renderer";
|
|
2
|
+
export type { RenderShapeScriptOptions } from "./renderer";
|
|
3
|
+
export { buildRenderPage, gridFor } from "./page";
|
|
4
|
+
export type { ViewAngle, RenderPageOptions } from "./page";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,aAAa,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnJ,YAAY,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** One camera angle: where it sits on the sphere around the model, in degrees. */
|
|
2
|
+
export interface ViewAngle {
|
|
3
|
+
/** Rotation around Y, from +Z toward +X. */
|
|
4
|
+
azimuth: number;
|
|
5
|
+
/** Rotation above the XZ plane. 90 looks straight down. */
|
|
6
|
+
elevation: number;
|
|
7
|
+
/** Caption drawn under the tile, so the reader knows which way they are looking. */
|
|
8
|
+
label: string;
|
|
9
|
+
}
|
|
10
|
+
export interface RenderPageOptions {
|
|
11
|
+
/** URL the driver serves `three.module.js` from. */
|
|
12
|
+
threeUrl: string;
|
|
13
|
+
/** `group.toJSON()` of the built model. */
|
|
14
|
+
sceneJson: unknown;
|
|
15
|
+
views: readonly ViewAngle[];
|
|
16
|
+
/** Pixel size of ONE tile. The sheet is tiled to fit every view. */
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
/** Multiplier on the auto-fit camera distance. >1 moves the camera closer. */
|
|
20
|
+
zoom: number;
|
|
21
|
+
projection: "perspective" | "orthographic";
|
|
22
|
+
}
|
|
23
|
+
/** Tile the views into the squarest grid that holds them (1→1x1, 2→2x1, 4→2x2). */
|
|
24
|
+
export declare function gridFor(count: number): {
|
|
25
|
+
columns: number;
|
|
26
|
+
rows: number;
|
|
27
|
+
};
|
|
28
|
+
/** Assemble the page. `JSON.stringify` is the only interpolation into script
|
|
29
|
+
* context — the caller's numbers and labels never reach the page as code. */
|
|
30
|
+
export declare function buildRenderPage(options: RenderPageOptions): string;
|
|
31
|
+
//# sourceMappingURL=page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../src/render/page.ts"],"names":[],"mappings":"AAYA,kFAAkF;AAClF,MAAM,WAAW,SAAS;IACxB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;IAC5B,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,aAAa,GAAG,cAAc,CAAC;CAC5C;AAED,mFAAmF;AACnF,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAGxE;AAyFD;8EAC8E;AAC9E,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAWlE"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type ViewAngle } from "./page";
|
|
2
|
+
/** How long one contact sheet may take in the browser before we give up. The
|
|
3
|
+
* geometry budget already bounds the model; this bounds the rasterisation. */
|
|
4
|
+
export declare const RENDER_TIMEOUT_MS: number;
|
|
5
|
+
/** How long the browser itself may take to start. Set explicitly rather than
|
|
6
|
+
* left to Puppeteer's own 30 s default, so the total below is derived from
|
|
7
|
+
* numbers this file controls instead of one it would silently inherit. */
|
|
8
|
+
export declare const LAUNCH_TIMEOUT_MS: number;
|
|
9
|
+
/** Worst case for one call, launch plus render. The MCP bridge must outlast
|
|
10
|
+
* THIS, not just the render — a slow launch followed by a full render is
|
|
11
|
+
* inside budget, and a bridge sized to the render alone aborts it in transit
|
|
12
|
+
* while the server carries on and saves an image nobody is waiting for
|
|
13
|
+
* (CodeRabbit on #3056). Callers add their own headroom for the work either
|
|
14
|
+
* side of it: serialising the scene, and writing the PNG. */
|
|
15
|
+
export declare const RENDER_BUDGET_MS: number;
|
|
16
|
+
/** The install step a missing browser needs. Repeated verbatim in
|
|
17
|
+
* `error-recovery.md` — the agent reads that file before asking the user. */
|
|
18
|
+
export declare const CHROMIUM_HINT = "renderShapeScript needs Puppeteer's headless Chromium, which this host does not have. Run `npx puppeteer browsers install chrome`, then retry. Everything else, including presentShapeScript, works without it.";
|
|
19
|
+
export interface RenderShapeScriptOptions {
|
|
20
|
+
script: string;
|
|
21
|
+
views: readonly ViewAngle[];
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
zoom: number;
|
|
25
|
+
projection: "perspective" | "orthographic";
|
|
26
|
+
/** Reported for a fault that did not fail the render — currently only a browser
|
|
27
|
+
* that would not close. Optional because this package has no logger of its own:
|
|
28
|
+
* each host passes its own, and one that passes none loses the line rather than
|
|
29
|
+
* the render. */
|
|
30
|
+
onWarning?: (message: string) => void;
|
|
31
|
+
}
|
|
32
|
+
/** Thrown when the host cannot rasterise at all — a missing browser, not a bad
|
|
33
|
+
* model. Separated so the tool reports the install hint instead of implying
|
|
34
|
+
* the script is at fault. */
|
|
35
|
+
export declare class RenderUnavailableError extends Error {
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Render `script` to a PNG contact sheet, returned base64-encoded.
|
|
39
|
+
*
|
|
40
|
+
* Throws `RenderUnavailableError` when no browser is installed, and a plain
|
|
41
|
+
* Error when the script itself fails to parse / build (the same diagnostics
|
|
42
|
+
* `presentShapeScript` returns) or the page never produces an image.
|
|
43
|
+
*/
|
|
44
|
+
export declare function renderShapeScriptSheet(options: RenderShapeScriptOptions): Promise<string>;
|
|
45
|
+
//# sourceMappingURL=renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/render/renderer.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAUzD;+EAC+E;AAC/E,eAAO,MAAM,iBAAiB,QAAqB,CAAC;AAEpD;;2EAE2E;AAC3E,eAAO,MAAM,iBAAiB,QAAqB,CAAC;AAEpD;;;;;8DAK8D;AAC9D,eAAO,MAAM,gBAAgB,QAAwC,CAAC;AAEtE;8EAC8E;AAC9E,eAAO,MAAM,aAAa,oNACyL,CAAC;AAEpN,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,aAAa,GAAG,cAAc,CAAC;IAC3C;;;sBAGkB;IAClB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED;;8BAE8B;AAC9B,qBAAa,sBAAuB,SAAQ,KAAK;CAAG;AA8JpD;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAkD/F"}
|
package/dist/render.cjs
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));const c=require("./toThreeJS-BOBpx5Dc.cjs");let l=require("node:fs/promises"),u=require("node:module"),d=require("node:url"),f=require("node:path");f=s(f,1);function p(e){let t=Math.ceil(Math.sqrt(e));return{columns:t,rows:Math.ceil(e/t)}}function m(e,t,n){return`import { AmbientLight, Box3, DirectionalLight, GridHelper, MathUtils, ObjectLoader, OrthographicCamera, PerspectiveCamera, Scene, Sphere, Vector3, WebGLRenderer } from ${JSON.stringify(e)};
|
|
2
|
+
|
|
3
|
+
const config = ${t};
|
|
4
|
+
const { columns, rows } = ${n};
|
|
5
|
+
const sheet = document.getElementById("sheet");
|
|
6
|
+
const context = sheet.getContext("2d");
|
|
7
|
+
sheet.width = config.width * columns;
|
|
8
|
+
sheet.height = config.height * rows;
|
|
9
|
+
context.fillStyle = "#ffffff";
|
|
10
|
+
context.fillRect(0, 0, sheet.width, sheet.height);
|
|
11
|
+
|
|
12
|
+
const model = new ObjectLoader().parse(config.scene);
|
|
13
|
+
|
|
14
|
+
// Framing is derived from the model's own bounding sphere, so one zoom value
|
|
15
|
+
// means the same thing for a 0.1-unit bead and a 500-unit building.
|
|
16
|
+
const bounds = new Box3().setFromObject(model);
|
|
17
|
+
const sphere = bounds.getBoundingSphere(new Sphere());
|
|
18
|
+
const radius = sphere.radius > 0 && isFinite(sphere.radius) ? sphere.radius : 1;
|
|
19
|
+
|
|
20
|
+
const renderer = new WebGLRenderer({ antialias: true, preserveDrawingBuffer: true });
|
|
21
|
+
renderer.setPixelRatio(1);
|
|
22
|
+
renderer.setSize(config.width, config.height, false);
|
|
23
|
+
renderer.setClearColor(0xffffff, 1);
|
|
24
|
+
|
|
25
|
+
const scene = new Scene();
|
|
26
|
+
scene.add(model);
|
|
27
|
+
// A headlight plus fill and ambient: a single light leaves faces pointing away
|
|
28
|
+
// from it black, which reads as a hole rather than a surface.
|
|
29
|
+
const headlight = new DirectionalLight(0xffffff, 2.2);
|
|
30
|
+
const fill = new DirectionalLight(0xffffff, 0.8);
|
|
31
|
+
fill.position.set(-1, 0.5, -1);
|
|
32
|
+
scene.add(headlight, fill, new AmbientLight(0xffffff, 0.9));
|
|
33
|
+
|
|
34
|
+
// A ground grid gives absolute orientation — without it a symmetric model
|
|
35
|
+
// renders identically from several angles.
|
|
36
|
+
const floor = new GridHelper(radius * 4, 12, 0xc0c0c0, 0xe4e4e4);
|
|
37
|
+
floor.position.y = bounds.min.y;
|
|
38
|
+
scene.add(floor);
|
|
39
|
+
`}function h(){return`
|
|
40
|
+
const aspect = config.width / config.height;
|
|
41
|
+
const distance = (radius * 3.2) / Math.max(config.zoom, 0.01);
|
|
42
|
+
|
|
43
|
+
function cameraFor(angle) {
|
|
44
|
+
const phi = MathUtils.degToRad(90 - angle.elevation);
|
|
45
|
+
const theta = MathUtils.degToRad(angle.azimuth);
|
|
46
|
+
const eye = new Vector3().setFromSphericalCoords(distance, phi, theta).add(sphere.center);
|
|
47
|
+
const far = distance + radius * 10;
|
|
48
|
+
let camera;
|
|
49
|
+
if (config.projection === "orthographic") {
|
|
50
|
+
const halfHeight = (radius * 1.3) / Math.max(config.zoom, 0.01);
|
|
51
|
+
camera = new OrthographicCamera(-halfHeight * aspect, halfHeight * aspect, halfHeight, -halfHeight, 0.01, far);
|
|
52
|
+
} else {
|
|
53
|
+
camera = new PerspectiveCamera(35, aspect, Math.max(distance / 100, 0.01), far);
|
|
54
|
+
}
|
|
55
|
+
camera.position.copy(eye);
|
|
56
|
+
camera.lookAt(sphere.center);
|
|
57
|
+
return camera;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
config.views.forEach((angle, index) => {
|
|
61
|
+
const camera = cameraFor(angle);
|
|
62
|
+
headlight.position.copy(camera.position);
|
|
63
|
+
renderer.render(scene, camera);
|
|
64
|
+
const x = (index % columns) * config.width;
|
|
65
|
+
const y = Math.floor(index / columns) * config.height;
|
|
66
|
+
context.drawImage(renderer.domElement, x, y);
|
|
67
|
+
// Caption last, so it is never painted over by the next tile.
|
|
68
|
+
context.font = "16px system-ui, sans-serif";
|
|
69
|
+
context.fillStyle = "#111111";
|
|
70
|
+
context.fillText(angle.label, x + 12, y + config.height - 12);
|
|
71
|
+
context.strokeStyle = "#d0d0d0";
|
|
72
|
+
context.strokeRect(x + 0.5, y + 0.5, config.width - 1, config.height - 1);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
renderer.dispose();
|
|
76
|
+
window.__shapeSheet = sheet.toDataURL("image/png");`}function g(e){let{threeUrl:t,sceneJson:n,views:r,width:i,height:a,zoom:o,projection:s}=e;return`<!doctype html>
|
|
77
|
+
<meta charset="utf-8">
|
|
78
|
+
<style>html,body{margin:0;background:#ffffff}</style>
|
|
79
|
+
<canvas id="sheet"></canvas>
|
|
80
|
+
<script type="module">
|
|
81
|
+
${`${m(t,JSON.stringify({views:r,width:i,height:a,zoom:o,projection:s,scene:n}),JSON.stringify(p(r.length)))}\n${h()}`}
|
|
82
|
+
<\/script>`}var _=1e3;function v(e){return e instanceof Error?e.message:String(e)}var y=60*_,b=30*_,x=9e4,S="renderShapeScript needs Puppeteer's headless Chromium, which this host does not have. Run `npx puppeteer browsers install chrome`, then retry. Everything else, including presentShapeScript, works without it.",C=class extends Error{};function w(e,t){try{e.onWarning?.(t)}catch{}}function T(){let e={}.url;if(typeof e==`string`&&e.length>0)return e;if(typeof __filename==`string`)return(0,d.pathToFileURL)(__filename).href;throw Error(`shapescript render: cannot locate this module to resolve three from`)}var E=(0,u.createRequire)(T());function D(e){return c.In(e)&&typeof e.launch==`function`}async function O(){try{let e=await import(`puppeteer`);return D(e)?e:c.In(e)&&D(e.default)?e.default:null}catch{return null}}function k(){try{return(0,u.createRequire)(E.resolve(`@mulmoclaude/shapescript-plugin/package.json`))}catch{return E}}function A(){let e=k().resolve(`three`);return e.endsWith(`three.cjs`)?e.replace(/three\.cjs$/,`three.module.js`):e}var j=`https://shapescript.invalid`,M=`${j}/render.html`,N=`./three.module.js`;async function P(e,t,n){let r;try{r=new URL(e)}catch{return null}if(r.origin!==j)return null;let i=f.default.posix.basename(r.pathname);return i===`render.html`?{contentType:`text/html; charset=utf-8`,body:t}:/^three[\w.-]*\.js$/.test(i)?{contentType:`text/javascript; charset=utf-8`,body:await(0,l.readFile)(f.default.join(n,i),`utf-8`)}:null}async function F(e,t){let n=f.default.dirname(A());await e.setRequestInterception(!0),e.on(`request`,e=>{(async()=>{try{let r=await P(e.url(),t,n);await(r?e.respond({status:200,...r}):e.abort())}catch{}})()})}async function I(e){let t=await O();if(!t)throw new C(S);let n=c.n(c.Pn(e.script)).toJSON(),r=g({...e,threeUrl:N,sceneJson:n}),i;try{i=await t.launch({headless:!0,timeout:b,args:[`--enable-unsafe-swiftshader`,`--use-gl=swiftshader`]})}catch(e){throw new C(`${S} (launch failed: ${v(e)})`)}try{let e=await i.newPage();await e.setViewport({width:800,height:600});let t=[];e.on(`pageerror`,e=>t.push(e.message)),await F(e,r),await e.goto(M,{waitUntil:`load`});try{await e.waitForFunction(`typeof window.__shapeSheet === 'string'`,{timeout:y})}catch(e){let n=t.length>0?`the render page failed: ${t.join(`; `)}`:v(e);throw Error(n,{cause:e})}let n=await e.evaluate(`window.__shapeSheet`);if(typeof n!=`string`||!n.startsWith(`data:image/png;base64,`))throw Error(`the render page produced no image`);return n.slice(22)}finally{await i.close().catch(t=>w(e,`chromium close failed: ${v(t)}`))}}exports.CHROMIUM_HINT=S,exports.LAUNCH_TIMEOUT_MS=b,exports.RENDER_BUDGET_MS=x,exports.RENDER_TIMEOUT_MS=y,exports.RenderUnavailableError=C,exports.buildRenderPage=g,exports.gridFor=p,exports.renderShapeScriptSheet=I;
|
package/dist/render.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { In as e, Pn as t, n } from "./toThreeJS-B5LiF110.js";
|
|
2
|
+
import { readFile as r } from "node:fs/promises";
|
|
3
|
+
import { createRequire as i } from "node:module";
|
|
4
|
+
import { pathToFileURL as a } from "node:url";
|
|
5
|
+
import o from "node:path";
|
|
6
|
+
//#region src/render/page.ts
|
|
7
|
+
function s(e) {
|
|
8
|
+
let t = Math.ceil(Math.sqrt(e));
|
|
9
|
+
return {
|
|
10
|
+
columns: t,
|
|
11
|
+
rows: Math.ceil(e / t)
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function c(e, t, n) {
|
|
15
|
+
return `import { AmbientLight, Box3, DirectionalLight, GridHelper, MathUtils, ObjectLoader, OrthographicCamera, PerspectiveCamera, Scene, Sphere, Vector3, WebGLRenderer } from ${JSON.stringify(e)};
|
|
16
|
+
|
|
17
|
+
const config = ${t};
|
|
18
|
+
const { columns, rows } = ${n};
|
|
19
|
+
const sheet = document.getElementById("sheet");
|
|
20
|
+
const context = sheet.getContext("2d");
|
|
21
|
+
sheet.width = config.width * columns;
|
|
22
|
+
sheet.height = config.height * rows;
|
|
23
|
+
context.fillStyle = "#ffffff";
|
|
24
|
+
context.fillRect(0, 0, sheet.width, sheet.height);
|
|
25
|
+
|
|
26
|
+
const model = new ObjectLoader().parse(config.scene);
|
|
27
|
+
|
|
28
|
+
// Framing is derived from the model's own bounding sphere, so one zoom value
|
|
29
|
+
// means the same thing for a 0.1-unit bead and a 500-unit building.
|
|
30
|
+
const bounds = new Box3().setFromObject(model);
|
|
31
|
+
const sphere = bounds.getBoundingSphere(new Sphere());
|
|
32
|
+
const radius = sphere.radius > 0 && isFinite(sphere.radius) ? sphere.radius : 1;
|
|
33
|
+
|
|
34
|
+
const renderer = new WebGLRenderer({ antialias: true, preserveDrawingBuffer: true });
|
|
35
|
+
renderer.setPixelRatio(1);
|
|
36
|
+
renderer.setSize(config.width, config.height, false);
|
|
37
|
+
renderer.setClearColor(0xffffff, 1);
|
|
38
|
+
|
|
39
|
+
const scene = new Scene();
|
|
40
|
+
scene.add(model);
|
|
41
|
+
// A headlight plus fill and ambient: a single light leaves faces pointing away
|
|
42
|
+
// from it black, which reads as a hole rather than a surface.
|
|
43
|
+
const headlight = new DirectionalLight(0xffffff, 2.2);
|
|
44
|
+
const fill = new DirectionalLight(0xffffff, 0.8);
|
|
45
|
+
fill.position.set(-1, 0.5, -1);
|
|
46
|
+
scene.add(headlight, fill, new AmbientLight(0xffffff, 0.9));
|
|
47
|
+
|
|
48
|
+
// A ground grid gives absolute orientation — without it a symmetric model
|
|
49
|
+
// renders identically from several angles.
|
|
50
|
+
const floor = new GridHelper(radius * 4, 12, 0xc0c0c0, 0xe4e4e4);
|
|
51
|
+
floor.position.y = bounds.min.y;
|
|
52
|
+
scene.add(floor);
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
function l() {
|
|
56
|
+
return "\nconst aspect = config.width / config.height;\nconst distance = (radius * 3.2) / Math.max(config.zoom, 0.01);\n\nfunction cameraFor(angle) {\n const phi = MathUtils.degToRad(90 - angle.elevation);\n const theta = MathUtils.degToRad(angle.azimuth);\n const eye = new Vector3().setFromSphericalCoords(distance, phi, theta).add(sphere.center);\n const far = distance + radius * 10;\n let camera;\n if (config.projection === \"orthographic\") {\n const halfHeight = (radius * 1.3) / Math.max(config.zoom, 0.01);\n camera = new OrthographicCamera(-halfHeight * aspect, halfHeight * aspect, halfHeight, -halfHeight, 0.01, far);\n } else {\n camera = new PerspectiveCamera(35, aspect, Math.max(distance / 100, 0.01), far);\n }\n camera.position.copy(eye);\n camera.lookAt(sphere.center);\n return camera;\n}\n\nconfig.views.forEach((angle, index) => {\n const camera = cameraFor(angle);\n headlight.position.copy(camera.position);\n renderer.render(scene, camera);\n const x = (index % columns) * config.width;\n const y = Math.floor(index / columns) * config.height;\n context.drawImage(renderer.domElement, x, y);\n // Caption last, so it is never painted over by the next tile.\n context.font = \"16px system-ui, sans-serif\";\n context.fillStyle = \"#111111\";\n context.fillText(angle.label, x + 12, y + config.height - 12);\n context.strokeStyle = \"#d0d0d0\";\n context.strokeRect(x + 0.5, y + 0.5, config.width - 1, config.height - 1);\n});\n\nrenderer.dispose();\nwindow.__shapeSheet = sheet.toDataURL(\"image/png\");";
|
|
57
|
+
}
|
|
58
|
+
function u(e) {
|
|
59
|
+
let { threeUrl: t, sceneJson: n, views: r, width: i, height: a, zoom: o, projection: u } = e;
|
|
60
|
+
return `<!doctype html>
|
|
61
|
+
<meta charset="utf-8">
|
|
62
|
+
<style>html,body{margin:0;background:#ffffff}</style>
|
|
63
|
+
<canvas id="sheet"></canvas>
|
|
64
|
+
<script type="module">
|
|
65
|
+
${`${c(t, JSON.stringify({
|
|
66
|
+
views: r,
|
|
67
|
+
width: i,
|
|
68
|
+
height: a,
|
|
69
|
+
zoom: o,
|
|
70
|
+
projection: u,
|
|
71
|
+
scene: n
|
|
72
|
+
}), JSON.stringify(s(r.length)))}\n${l()}`}
|
|
73
|
+
<\/script>`;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/render/renderer.ts
|
|
77
|
+
var d = 1e3;
|
|
78
|
+
function f(e) {
|
|
79
|
+
return e instanceof Error ? e.message : String(e);
|
|
80
|
+
}
|
|
81
|
+
var p = 60 * d, m = 30 * d, h = 9e4, g = "renderShapeScript needs Puppeteer's headless Chromium, which this host does not have. Run `npx puppeteer browsers install chrome`, then retry. Everything else, including presentShapeScript, works without it.", _ = class extends Error {};
|
|
82
|
+
function v(e, t) {
|
|
83
|
+
try {
|
|
84
|
+
e.onWarning?.(t);
|
|
85
|
+
} catch {}
|
|
86
|
+
}
|
|
87
|
+
function y() {
|
|
88
|
+
let e = import.meta.url;
|
|
89
|
+
if (typeof e == "string" && e.length > 0) return e;
|
|
90
|
+
if (typeof __filename == "string") return a(__filename).href;
|
|
91
|
+
throw Error("shapescript render: cannot locate this module to resolve three from");
|
|
92
|
+
}
|
|
93
|
+
var b = i(y());
|
|
94
|
+
function x(t) {
|
|
95
|
+
return e(t) && typeof t.launch == "function";
|
|
96
|
+
}
|
|
97
|
+
async function S() {
|
|
98
|
+
try {
|
|
99
|
+
let t = await import("puppeteer");
|
|
100
|
+
return x(t) ? t : e(t) && x(t.default) ? t.default : null;
|
|
101
|
+
} catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function C() {
|
|
106
|
+
try {
|
|
107
|
+
return i(b.resolve("@mulmoclaude/shapescript-plugin/package.json"));
|
|
108
|
+
} catch {
|
|
109
|
+
return b;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function w() {
|
|
113
|
+
let e = C().resolve("three");
|
|
114
|
+
return e.endsWith("three.cjs") ? e.replace(/three\.cjs$/, "three.module.js") : e;
|
|
115
|
+
}
|
|
116
|
+
var T = "https://shapescript.invalid", E = `${T}/render.html`, D = "./three.module.js";
|
|
117
|
+
async function O(e, t, n) {
|
|
118
|
+
let i;
|
|
119
|
+
try {
|
|
120
|
+
i = new URL(e);
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
if (i.origin !== T) return null;
|
|
125
|
+
let a = o.posix.basename(i.pathname);
|
|
126
|
+
return a === "render.html" ? {
|
|
127
|
+
contentType: "text/html; charset=utf-8",
|
|
128
|
+
body: t
|
|
129
|
+
} : /^three[\w.-]*\.js$/.test(a) ? {
|
|
130
|
+
contentType: "text/javascript; charset=utf-8",
|
|
131
|
+
body: await r(o.join(n, a), "utf-8")
|
|
132
|
+
} : null;
|
|
133
|
+
}
|
|
134
|
+
async function k(e, t) {
|
|
135
|
+
let n = o.dirname(w());
|
|
136
|
+
await e.setRequestInterception(!0), e.on("request", (e) => {
|
|
137
|
+
(async () => {
|
|
138
|
+
try {
|
|
139
|
+
let r = await O(e.url(), t, n);
|
|
140
|
+
await (r ? e.respond({
|
|
141
|
+
status: 200,
|
|
142
|
+
...r
|
|
143
|
+
}) : e.abort());
|
|
144
|
+
} catch {}
|
|
145
|
+
})();
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
async function A(e) {
|
|
149
|
+
let r = await S();
|
|
150
|
+
if (!r) throw new _(g);
|
|
151
|
+
let i = n(t(e.script)).toJSON(), a = u({
|
|
152
|
+
...e,
|
|
153
|
+
threeUrl: D,
|
|
154
|
+
sceneJson: i
|
|
155
|
+
}), o;
|
|
156
|
+
try {
|
|
157
|
+
o = await r.launch({
|
|
158
|
+
headless: !0,
|
|
159
|
+
timeout: m,
|
|
160
|
+
args: ["--enable-unsafe-swiftshader", "--use-gl=swiftshader"]
|
|
161
|
+
});
|
|
162
|
+
} catch (e) {
|
|
163
|
+
throw new _(`${g} (launch failed: ${f(e)})`);
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
let e = await o.newPage();
|
|
167
|
+
await e.setViewport({
|
|
168
|
+
width: 800,
|
|
169
|
+
height: 600
|
|
170
|
+
});
|
|
171
|
+
let t = [];
|
|
172
|
+
e.on("pageerror", (e) => t.push(e.message)), await k(e, a), await e.goto(E, { waitUntil: "load" });
|
|
173
|
+
try {
|
|
174
|
+
await e.waitForFunction("typeof window.__shapeSheet === 'string'", { timeout: p });
|
|
175
|
+
} catch (e) {
|
|
176
|
+
let n = t.length > 0 ? `the render page failed: ${t.join("; ")}` : f(e);
|
|
177
|
+
throw Error(n, { cause: e });
|
|
178
|
+
}
|
|
179
|
+
let n = await e.evaluate("window.__shapeSheet");
|
|
180
|
+
if (typeof n != "string" || !n.startsWith("data:image/png;base64,")) throw Error("the render page produced no image");
|
|
181
|
+
return n.slice(22);
|
|
182
|
+
} finally {
|
|
183
|
+
await o.close().catch((t) => v(e, `chromium close failed: ${f(t)}`));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
export { g as CHROMIUM_HINT, m as LAUNCH_TIMEOUT_MS, h as RENDER_BUDGET_MS, p as RENDER_TIMEOUT_MS, _ as RenderUnavailableError, u as buildRenderPage, s as gridFor, A as renderShapeScriptSheet };
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
const e=require("./toThreeJS-BOBpx5Dc.cjs");let t=require("@mulmoclaude/core/artifacts");var n=`presentShapeScript`,r={type:`function`,name:n,description:"Display interactive 3D visualizations using ShapeScript with expressions, variables, control flow, and functions. A new `script` is saved to `artifacts/shapes/` and the returned `filePath` names it; pass `path` instead to present a source that already exists.",parameters:{type:`object`,properties:{title:{type:`string`,description:`Title for the 3D visualization`},script:{type:`string`,description:`ShapeScript code defining the 3D scene. Supported features and syntax are listed below. Syntax, evaluation, geometry and resource-limit errors are returned as diagnostics; correct the script and retry.
|
|
2
|
+
|
|
3
|
+
## SYNTAX OVERVIEW:
|
|
4
|
+
|
|
5
|
+
### Expressions & Operators:
|
|
6
|
+
- Arithmetic: +, -, *, /, % with proper precedence
|
|
7
|
+
- Comparison: =, <>, <, <=, >, >=
|
|
8
|
+
- Boolean: and, or, not
|
|
9
|
+
- Parentheses for grouping: (2 + 3) * 4
|
|
10
|
+
|
|
11
|
+
### Variables:
|
|
12
|
+
define radius 2
|
|
13
|
+
define red (1 0 0)
|
|
14
|
+
sphere { size radius color red }
|
|
15
|
+
|
|
16
|
+
### Control Flow:
|
|
17
|
+
|
|
18
|
+
For loops with variables:
|
|
19
|
+
for i in 1 to 5 {
|
|
20
|
+
cube { position (i * 2) 0 0 size 1 }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
For loops with step:
|
|
24
|
+
for i in 0 to 10 step 2 {
|
|
25
|
+
sphere { position 0 i 0 }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
If/else conditionals:
|
|
29
|
+
define showSphere 1
|
|
30
|
+
if showSphere {
|
|
31
|
+
sphere { size 2 }
|
|
32
|
+
} else {
|
|
33
|
+
cube { size 2 }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
Switch statements:
|
|
37
|
+
define shape 2
|
|
38
|
+
switch shape {
|
|
39
|
+
case 1
|
|
40
|
+
cube
|
|
41
|
+
case 2
|
|
42
|
+
sphere
|
|
43
|
+
else
|
|
44
|
+
cone
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
### Built-in Functions:
|
|
48
|
+
|
|
49
|
+
Math: round, floor, ceil, abs, sign, sqrt, pow, min, max
|
|
50
|
+
Trig: sin, cos, tan, asin, acos, atan, atan2 (uses radians)
|
|
51
|
+
Vector: dot, cross, length, normalize, sum
|
|
52
|
+
|
|
53
|
+
IMPORTANT: Function calls require NO space between name and parenthesis:
|
|
54
|
+
- sin(x) ✓ function call
|
|
55
|
+
- sin (x) ✗ NOT a function call (identifier + parenthesized expression)
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
for i in 1 to 8 {
|
|
59
|
+
define angle (i * 0.785) // 45 degrees in radians
|
|
60
|
+
cube { position (cos(angle) * 3) 0 (sin(angle) * 3) }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
### Primitives & Properties:
|
|
64
|
+
|
|
65
|
+
Shapes: cube, sphere, cylinder, cone, torus, circle, square, polygon (sides 3–256)
|
|
66
|
+
Properties: position X Y Z, rotation X Y Z, size X Y Z
|
|
67
|
+
Materials: color R G B (0-1), opacity (0-1)
|
|
68
|
+
|
|
69
|
+
### CSG Operations:
|
|
70
|
+
union, difference, intersection, xor, stencil
|
|
71
|
+
|
|
72
|
+
Example:
|
|
73
|
+
difference {
|
|
74
|
+
sphere { size 2 color (1 0.5 0) }
|
|
75
|
+
cube { size 1.5 }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
### Builders:
|
|
79
|
+
- extrude: extrude { polygon { sides 3 } } or extrude path { point 0 0 point 1 0 point 0 1 }
|
|
80
|
+
- fill: fill { square } or fill path { ... }
|
|
81
|
+
- lathe: lathe path { point 1 0 curve 0 2 1 -1 } (revolves about Y)
|
|
82
|
+
- loft: loft { square translate 0 0 2 circle } (closed planar sections joined with caps)
|
|
83
|
+
- hull: hull { cube { position -1 0 0 } cube { position 1 0 0 } } (convex envelope)
|
|
84
|
+
- stencil preserves the first shape and paints its surface with later shapes' materials.
|
|
85
|
+
Loft sections must each have one perimeter and enclose an area; extrude/fill primitive profiles must lie in XY.
|
|
86
|
+
|
|
87
|
+
### Additional Expressions:
|
|
88
|
+
- Constants: pi, tau, true, false
|
|
89
|
+
- Scientific notation and unary plus: 1e-3, +2
|
|
90
|
+
- Tuple/vector members: vector.x, vector.y, vector.z; color.red/green/blue/alpha
|
|
91
|
+
- Tuple/string length: value.count; zero-based indexing: values[0]
|
|
92
|
+
- String literals, join(...), trim(...); min/max also accept tuples
|
|
93
|
+
- Custom shapes with options:
|
|
94
|
+
define post { option height 2 cylinder { size 0.2 height } }
|
|
95
|
+
post { height 3 }
|
|
96
|
+
|
|
97
|
+
### Compatibility:
|
|
98
|
+
This plugin implements the documented modeling subset, not all upstream ShapeScript syntax.
|
|
99
|
+
Function calls use name(...); trig functions and rotation/orientation properties use radians.
|
|
100
|
+
Relative rotate/orientation commands and path rotate use turns (1 = 360 degrees).
|
|
101
|
+
Path point/curve coordinates are relative steps. Curves accept optional control-point offsets.
|
|
102
|
+
Imports, textures, text/fonts, arbitrary objects, and general user-defined functions are not supported.
|
|
103
|
+
|
|
104
|
+
### Comments:
|
|
105
|
+
// Single-line comment
|
|
106
|
+
/* Multi-line
|
|
107
|
+
comment */
|
|
108
|
+
|
|
109
|
+
## COMPLETE EXAMPLES:
|
|
110
|
+
|
|
111
|
+
Linear arrangement with expressions:
|
|
112
|
+
define spacing 1.5
|
|
113
|
+
for i in 1 to 4 {
|
|
114
|
+
cylinder { position ((i - 2.5) * spacing) 0 0 size 0.4 1 }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
Circular pattern:
|
|
118
|
+
define count 12
|
|
119
|
+
for i in 1 to count {
|
|
120
|
+
define angle ((i / count) * 6.283) // 2 * PI
|
|
121
|
+
cube {
|
|
122
|
+
position (cos(angle) * 3) 0 (sin(angle) * 3)
|
|
123
|
+
color (i / count) 0.5 (1 - i / count)
|
|
124
|
+
size 0.5
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
Conditional geometry:
|
|
129
|
+
define makeHollow 1
|
|
130
|
+
if makeHollow {
|
|
131
|
+
difference {
|
|
132
|
+
sphere { size 2 color (1 0 0) }
|
|
133
|
+
sphere { size 1.7 }
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
sphere { size 2 color (1 0 0) }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
Mathematical visualization:
|
|
140
|
+
for x in -5 to 5 {
|
|
141
|
+
for z in -5 to 5 {
|
|
142
|
+
define height (sin(x * 0.5) * cos(z * 0.5) * 2)
|
|
143
|
+
cube {
|
|
144
|
+
position (x * 0.3) height (z * 0.3)
|
|
145
|
+
size 0.25 (abs(height) + 0.1) 0.25
|
|
146
|
+
color (0.5 + height * 0.25) 0.3 (0.5 - height * 0.25)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}`},path:{type:`string`,description:"Path to an EXISTING ShapeScript source to present in place, instead of `script` — a `.shape` file this tool saved earlier (`artifacts/shapes/…`) or any other on disk. Provide either `script` or `path`, never both. Edits the user makes in the view write back to that same file."}},required:[`title`]}},i=`shapes`,a=`shape`,o=[`.shape`];function s(){let e=globalThis.crypto;if(e?.getRandomValues){let t=e.getRandomValues(new Uint8Array(4));return Array.from(t,e=>e.toString(16).padStart(2,`0`)).join(``)}return Math.random().toString(16).slice(2,10).padEnd(8,`0`)}function c(e,n=new Date,r=s()){let o=(0,t.buildArtifactRelPath)({dir:i,title:e,ext:`.shape`,fallback:a,now:n,partitioned:!1,suffix:r});return{relPath:o,filePath:(0,t.toWorkspaceArtifactPath)(o)}}function l(e){return!e.startsWith(`${t.ARTIFACTS_ROOT}/${i}/`)||!e.endsWith(`.shape`)?!1:!(0,t.hasUnsafePathSegment)(e)}function u(e){return e.startsWith(`${t.ARTIFACTS_ROOT}/`)?e.slice(t.ARTIFACTS_ROOT.length+1):e}function d(e){return(0,t.classifyFilePath)(e,o)!==null}function f(e,t){if(l(t))return{files:e.files.artifacts,rel:u(t)};let n=e.files.byPath;return n&&d(t)?{files:n,rel:t}:null}async function p(e,t){if(typeof t?.path!=`string`)throw Error(`path must be an existing .shape file`);let n=f(e,t.path);if(!n)throw Error(`path must be an existing .shape file`);switch(t.kind){case`loadShape`:return{script:await n.files.read(n.rel)};case`saveShape`:if(typeof t.script!=`string`)throw Error("saveShape requires `script` as a string");if(!await n.files.exists(n.rel))throw Error(`No ShapeScript exists at ${t.path}`);return await n.files.write(n.rel,t.script),{path:t.path};default:throw Error(`shapescript plugin: unknown dispatch kind ${JSON.stringify(t)}`)}}var m=`Acknowledge that the 3D visualization has been created and is displayed to the user. They can rotate, zoom, and pan the camera.`,h=e=>typeof e==`string`&&e.trim()!==``;function g(t){e.r(e.n(e.Pn(t)))}async function _(e,t){let n=e.files;if(!n)throw Error("This host cannot open a ShapeScript by path — pass the source as `script` instead");let r=f({files:n},t);if(!r)throw Error("`path` must be a .shape file, without `.` / `..` segments");if(!await r.files.exists(r.rel))throw Error(`No ShapeScript exists at ${t}`);return r.files.read(r.rel)}var v=5;async function y(e,t,n){let r=e.files?.artifacts;if(r){for(let e=0;e<v;e++){let{relPath:e,filePath:i}=c(n);if(!await r.exists(e))return await r.write(e,t),i}throw Error(`Could not allocate a free path under artifacts/shapes — try again with a different title`)}}async function b(e,t){if(h(t.path)&&h(t.script))throw Error("Provide either `script` or `path`, not both");if(h(t.path))return{script:await _(e,t.path),filePath:t.path};if(!h(t.script))throw Error(`ShapeScript code is required but was not provided`);return{script:t.script}}var x=async(t,n)=>{let r=`INVALID_ARGUMENT`;try{if(!e.In(n))throw Error("presentShapeScript args must be an object with `script` or `path`");if(!h(n.title))throw Error(`A nonempty visualization title is required`);let i=await b(t??{},n);r=`EVALUATION_ERROR`,g(i.script);let a=i.filePath??await y(t??{},i.script,n.title);return{message:a?`Saved ShapeScript to ${a}`:`Created 3D visualization: ${n.title}`,title:n.title,data:a?{script:i.script,filePath:a}:{script:i.script},instructions:m}}catch(t){let n={code:t instanceof e.Fn?`PARSE_ERROR`:t instanceof e.t?`LIMIT_EXCEEDED`:r,message:t instanceof Error?t.message:String(t),...t instanceof e.Fn&&t.line!==void 0?{line:t.line}:{},...t instanceof e.Fn&&t.column!==void 0?{column:t.column}:{}};return{message:`ShapeScript error: ${n.message}`,error:n,jsonData:{error:n},instructions:`The visualization was not created. Correct the ShapeScript using the returned diagnostic and call presentShapeScript again.`}}},S={toolDefinition:r,execute:x,generatingMessage:`Creating 3D visualization...`,waitingMessage:`Tell the user that the 3D visualization was created and will be presented shortly.`,isEnabled:()=>!0},C=x,w=[{name:`Basic Shapes`,args:{title:`Basic 3D Shapes`,script:`// Basic shapes demonstration
|
|
150
|
+
cube { position -2 0 0 size 1 color (1 0.3 0.3) }
|
|
151
|
+
sphere { position 0 0 0 size 1 color (0.3 1 0.3) }
|
|
152
|
+
cylinder { position 2 0 0 size 0.5 1 color (0.3 0.3 1) }`}},{name:`Circular Pattern`,args:{title:`Circular Pattern`,script:`// 12個の立方体を円形に配置
|
|
153
|
+
|
|
154
|
+
define count 12
|
|
155
|
+
define radius 3
|
|
156
|
+
|
|
157
|
+
for i in 1 to count {
|
|
158
|
+
// 各立方体の角度を計算(2 * PI = 6.283ラジアン)
|
|
159
|
+
define angle ((i / count) * 6.283)
|
|
160
|
+
|
|
161
|
+
// 円形配置のためのX座標とZ座標を計算
|
|
162
|
+
define x (cos(angle) * radius)
|
|
163
|
+
define z (sin(angle) * radius)
|
|
164
|
+
|
|
165
|
+
// グラデーションカラーを作成
|
|
166
|
+
define colorValue (i / count)
|
|
167
|
+
|
|
168
|
+
cube {
|
|
169
|
+
position x 0 z
|
|
170
|
+
size 0.5
|
|
171
|
+
color colorValue 0.5 (1 - colorValue)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 中心に参考用の球体を配置
|
|
176
|
+
sphere {
|
|
177
|
+
position 0 0 0
|
|
178
|
+
size 0.3
|
|
179
|
+
color 1 1 0
|
|
180
|
+
opacity 0.5
|
|
181
|
+
}`}},{name:`CSG Difference`,args:{title:`Hollow Sphere`,script:`// Create a hollow sphere using CSG difference
|
|
182
|
+
difference {
|
|
183
|
+
sphere { size 2 color (1 0.5 0) }
|
|
184
|
+
sphere { size 1.7 color (1 1 1) }
|
|
185
|
+
cube { position 0 0 2 size 2 }
|
|
186
|
+
}`}}];Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return c}});
|