@mulmoclaude/shapescript-plugin 1.1.1 → 1.3.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 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-B3VO3Mnc.cjs");exports.SHAPE_EXTENSIONS=e.Bn,exports.TOOL_DEFINITION=e.Jn,exports.TOOL_NAME=e.Yn,exports.astToThreeJS=e.a,exports.executePresentShapeScript=e.n,exports.executeShapeScriptDispatch=e.Rn,exports.isPresentableShapePath=e.Vn,exports.isShapeArtifactPath=e.Hn,exports.isShapeScriptDispatchArgs=e.Gn,exports.locateShape=e.zn,exports.parseShapeScript=e.Ln,exports.pluginCore=e.r,exports.presentShapeScript=e.i,exports.readLoadShapeResult=e.Kn,exports.readSaveShapeResult=e.qn,exports.samples=e.t,exports.shapeArtifactPath=e.Un,exports.toArtifactsRelative=e.Wn;
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 { Bn as e, Gn as t, Hn as n, Jn as r, Kn as i, Ln as a, Rn as o, Un as s, Vn as c, Wn as l, Yn as u, a as d, i as f, n as p, qn as m, r as h, t as g, zn as _ } from "./samples-6f1sYMm7.js";
2
- export { e as SHAPE_EXTENSIONS, r as TOOL_DEFINITION, u as TOOL_NAME, d as astToThreeJS, p as executePresentShapeScript, o as executeShapeScriptDispatch, c as isPresentableShapePath, n as isShapeArtifactPath, t as isShapeScriptDispatchArgs, _ as locateShape, a as parseShapeScript, h as pluginCore, f as presentShapeScript, i as readLoadShapeResult, m as readSaveShapeResult, g as samples, s as shapeArtifactPath, l as toArtifactsRelative };
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-B3VO3Mnc.cjs");require("./core.cjs"),exports.SHAPE_EXTENSIONS=e.Bn,exports.TOOL_DEFINITION=e.Jn,exports.TOOL_NAME=e.Yn,exports.astToThreeJS=e.a,exports.executePresentShapeScript=e.n,exports.executeShapeScriptDispatch=e.Rn,exports.isPresentableShapePath=e.Vn,exports.isShapeArtifactPath=e.Hn,exports.isShapeScriptDispatchArgs=e.Gn,exports.locateShape=e.zn,exports.parseShapeScript=e.Ln,exports.pluginCore=e.r,exports.presentShapeScript=e.i,exports.readLoadShapeResult=e.Kn,exports.readSaveShapeResult=e.qn,exports.samples=e.t,exports.shapeArtifactPath=e.Un,exports.toArtifactsRelative=e.Wn;
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 { Bn as e, Gn as t, Hn as n, Jn as r, Kn as i, Ln as a, Rn as o, Un as s, Vn as c, Wn as l, Yn as u, a as d, i as f, n as p, qn as m, r as h, t as g, zn as _ } from "./samples-6f1sYMm7.js";
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 { e as SHAPE_EXTENSIONS, r as TOOL_DEFINITION, u as TOOL_NAME, d as astToThreeJS, p as executePresentShapeScript, o as executeShapeScriptDispatch, c as isPresentableShapePath, n as isShapeArtifactPath, t as isShapeScriptDispatchArgs, _ as locateShape, a as parseShapeScript, h as pluginCore, f as presentShapeScript, i as readLoadShapeResult, m as readSaveShapeResult, g as samples, s as shapeArtifactPath, l as toArtifactsRelative };
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,7 @@
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
+ export { executeRenderShapeScript, viewAngles, RENDER_SHAPE_SCRIPT_TOOL_NAME, RENDER_SHAPE_SCRIPT_DESCRIPTION, RENDER_SHAPE_SCRIPT_PROMPT, RENDER_SHAPE_SCRIPT_SCHEMA, RENDER_TOOL_TIMEOUT_MS, renderOptionsFrom, savedMessage, } from "./tool";
6
+ export type { RenderToolDeps, RenderToolResult } from "./tool";
7
+ //# 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;AAC3D,OAAO,EACL,wBAAwB,EACxB,UAAU,EACV,6BAA6B,EAC7B,+BAA+B,EAC/B,0BAA0B,EAC1B,0BAA0B,EAC1B,sBAAsB,EACtB,iBAAiB,EACjB,YAAY,GACb,MAAM,QAAQ,CAAC;AAChB,YAAY,EAAE,cAAc,EAAE,gBAAgB,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,56 @@
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
+ /** Report something the host should record, without letting the report become
38
+ * the outcome. The callback is the HOST's code, and it is called from paths
39
+ * whose whole purpose is to answer gracefully — the `finally` that releases the
40
+ * browser after a finished render (CodeRabbit on #3059), and the
41
+ * browser-unavailable branch that returns install guidance (codex on #3060). A
42
+ * synchronous throw from a logger must not turn either into a rejection.
43
+ *
44
+ * Exported because both callers need the same guarantee and one of them lives
45
+ * in `./tool`; two copies of "wrap the host's logger" is exactly the kind of
46
+ * thing that ends up guarded in one place and not the other. */
47
+ export declare function safeWarn(onWarning: ((message: string) => void) | undefined, message: string): void;
48
+ /**
49
+ * Render `script` to a PNG contact sheet, returned base64-encoded.
50
+ *
51
+ * Throws `RenderUnavailableError` when no browser is installed, and a plain
52
+ * Error when the script itself fails to parse / build (the same diagnostics
53
+ * `presentShapeScript` returns) or the page never produces an image.
54
+ */
55
+ export declare function renderShapeScriptSheet(options: RenderShapeScriptOptions): Promise<string>;
56
+ //# 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;AAEpD;;;;;;;;;iEASiE;AACjE,wBAAgB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAMlG;AAkJD;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAkD/F"}
@@ -0,0 +1,118 @@
1
+ import type { ViewAngle } from "./page";
2
+ /** Milliseconds a host must allow this tool before its own transport gives up.
3
+ * Covers launch plus render, plus room for the work either side — serialising
4
+ * the scene and writing the PNG. A transport sized to the render alone aborts a
5
+ * call that was about to succeed. */
6
+ export declare const RENDER_TOOL_TIMEOUT_MS: number;
7
+ export declare const RENDER_SHAPE_SCRIPT_TOOL_NAME = "renderShapeScript";
8
+ export declare const RENDER_SHAPE_SCRIPT_DESCRIPTION = "Render a ShapeScript model to a PNG image and save it, so you can LOOK at the model you wrote and check it before showing it to the user. Returns the image path \u2014 read that file to see the result. By default it renders four camera angles onto one sheet, because a single view cannot settle what is in front of what. Takes the same source as presentShapeScript: inline `script`, or `path` to a saved .shape file.";
9
+ export declare const RENDER_SHAPE_SCRIPT_PROMPT = "Use renderShapeScript to CHECK a 3D model you wrote before presenting it \u2014 it saves a PNG and returns the path, which you then read to see what the model actually looks like. Fix what you see and re-render. Rendering needs a local headless browser; if it reports one is missing, say so and continue with presentShapeScript rather than retrying.";
10
+ /** The tool's JSON schema, in the shape both a gui-chat-protocol `ToolDefinition`
11
+ * (`parameters`) and an MCP tool (`inputSchema`) take. */
12
+ export declare const RENDER_SHAPE_SCRIPT_SCHEMA: {
13
+ type: "object";
14
+ properties: {
15
+ script: {
16
+ type: string;
17
+ description: string;
18
+ };
19
+ path: {
20
+ type: string;
21
+ description: string;
22
+ };
23
+ azimuth: {
24
+ type: string;
25
+ description: string;
26
+ };
27
+ elevation: {
28
+ type: string;
29
+ description: string;
30
+ };
31
+ zoom: {
32
+ type: string;
33
+ description: string;
34
+ };
35
+ views: {
36
+ type: string;
37
+ enum: string[];
38
+ description: string;
39
+ };
40
+ projection: {
41
+ type: string;
42
+ enum: string[];
43
+ description: string;
44
+ };
45
+ width: {
46
+ type: string;
47
+ description: string;
48
+ };
49
+ height: {
50
+ type: string;
51
+ description: string;
52
+ };
53
+ };
54
+ required: string[];
55
+ };
56
+ /**
57
+ * The angles one call renders.
58
+ *
59
+ * The default is FOUR views, not one, and that is the point of the tool: a
60
+ * single projection is ambiguous about depth and occlusion, and a model asked
61
+ * to judge one confidently gets "which of these is in front" wrong. Three
62
+ * quarters around the model plus a top-down resolves it.
63
+ */
64
+ export declare function viewAngles(azimuth: number, elevation: number, single: boolean): ViewAngle[];
65
+ /** What a host has to supply: the two things that are actually its own. */
66
+ export interface RenderToolDeps {
67
+ /** Read the `.shape` a `path` argument names, through this host's file layer.
68
+ * Throws — with a message the agent can act on — when the path is not one it
69
+ * will open. */
70
+ readShape: (path: string) => Promise<string>;
71
+ /** Persist the PNG (base64) and answer with the path the AGENT should read.
72
+ * Absolute or relative is the host's call: it depends on where its sessions
73
+ * run, and only the host knows that. */
74
+ saveImage: (base64: string) => Promise<string>;
75
+ /** Optional host logger for anything the host should record but the model does
76
+ * not need as an error — a browser that would not close, and the unavailable
77
+ * browser below. */
78
+ onWarning?: (message: string) => void;
79
+ }
80
+ /** What one call did. `rendered: false` means the tool answered without an image
81
+ * — today only a host with no usable browser.
82
+ *
83
+ * The flag exists because the two outcomes look identical from the outside: both
84
+ * return a sentence for the model. Before the extraction the host logged
85
+ * `unavailable` on this path and `ok` on the other; collapsing them to a string
86
+ * made a missing Chromium invisible to host monitoring while the host went on
87
+ * logging success (codex on #3060). A host that ignores the flag still behaves
88
+ * correctly — it just cannot tell the two apart, which is the caller's choice
89
+ * to make rather than this function's. */
90
+ export interface RenderToolResult {
91
+ /** The sentence the agent reads — the saved path, or why there is none. */
92
+ message: string;
93
+ rendered: boolean;
94
+ }
95
+ /**
96
+ * Run one `renderShapeScript` call.
97
+ *
98
+ * A missing browser comes back as a message rather than a throw: the model can
99
+ * do nothing about it, and the useful response is the install hint plus "carry
100
+ * on without me". Everything else throws, so a host's own error path reports it.
101
+ */
102
+ /** The renderer's options for one call: every model-supplied number defaulted and
103
+ * clamped, so nothing downstream has to wonder whether it was checked. */
104
+ export declare function renderOptionsFrom(args: Record<string, unknown>, script: string, views: readonly ViewAngle[], onWarning?: (message: string) => void): {
105
+ onWarning?: (message: string) => void;
106
+ script: string;
107
+ views: readonly ViewAngle[];
108
+ width: number;
109
+ height: number;
110
+ zoom: number;
111
+ projection: "perspective" | "orthographic";
112
+ };
113
+ /** What the model is told about a sheet that WAS produced. The captions are
114
+ * repeated here rather than left to the image alone: a model that has not opened
115
+ * the file yet still knows which angles it is about to see. */
116
+ export declare function savedMessage(imagePath: string, views: readonly ViewAngle[]): string;
117
+ export declare function executeRenderShapeScript(deps: RenderToolDeps, args: Record<string, unknown>): Promise<RenderToolResult>;
118
+ //# sourceMappingURL=tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/render/tool.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAcxC;;;sCAGsC;AACtC,eAAO,MAAM,sBAAsB,QAA4B,CAAC;AAEhE,eAAO,MAAM,6BAA6B,sBAAsB,CAAC;AAEjE,eAAO,MAAM,+BAA+B,qaACmX,CAAC;AAEha,eAAO,MAAM,0BAA0B,kWACqT,CAAC;AAE7V;2DAC2D;AAC3D,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA2CrB,MAAM,EAAE;CACzB,CAAC;AAcF;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,CAa3F;AAED,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B;;qBAEiB;IACjB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C;;6CAEyC;IACzC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C;;yBAEqB;IACrB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAWD;;;;;;;;;2CAS2C;AAC3C,MAAM,WAAW,gBAAgB;IAC/B,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH;2EAC2E;AAC3E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,EAAE,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;0BAAf,MAAM,KAAK,IAAI;;;;;;;EAUlJ;AAED;;gEAEgE;AAChE,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAInF;AAED,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiB7H"}
@@ -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?.(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 ee(){try{let e=await import(`puppeteer`);return D(e)?e:c.In(e)&&D(e.default)?e.default:null}catch{return null}}function te(){try{return(0,u.createRequire)(E.resolve(`@mulmoclaude/shapescript-plugin/package.json`))}catch{return E}}function O(){let e=te().resolve(`three`);return e.endsWith(`three.cjs`)?e.replace(/three\.cjs$/,`three.module.js`):e}var k=`https://shapescript.invalid`,A=`${k}/render.html`,j=`./three.module.js`;async function M(e,t,n){let r;try{r=new URL(e)}catch{return null}if(r.origin!==k)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 N(e,t){let n=f.default.dirname(O());await e.setRequestInterception(!0),e.on(`request`,e=>{(async()=>{try{let r=await M(e.url(),t,n);await(r?e.respond({status:200,...r}):e.abort())}catch{}})()})}async function P(e){let t=await ee();if(!t)throw new C(S);let n=c.n(c.Pn(e.script)).toJSON(),r=g({...e,threeUrl:j,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 N(e,r),await e.goto(A,{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.onWarning,`chromium close failed: ${v(t)}`))}}var F=30,I=25,L=1,R=480,z=900,B=160,V=85,H=x+3e4,U=`renderShapeScript`,W="Render a ShapeScript model to a PNG image and save it, so you can LOOK at the model you wrote and check it before showing it to the user. Returns the image path — read that file to see the result. By default it renders four camera angles onto one sheet, because a single view cannot settle what is in front of what. Takes the same source as presentShapeScript: inline `script`, or `path` to a saved .shape file.",G=`Use renderShapeScript to CHECK a 3D model you wrote before presenting it — it saves a PNG and returns the path, which you then read to see what the model actually looks like. Fix what you see and re-render. Rendering needs a local headless browser; if it reports one is missing, say so and continue with presentShapeScript rather than retrying.`,K={type:`object`,properties:{script:{type:`string`,description:"ShapeScript source to render. Provide either this or `path`, not both."},path:{type:`string`,description:`Path to an existing .shape file to render (e.g. one presentShapeScript saved under artifacts/shapes/).`},azimuth:{type:`number`,description:`Camera rotation around the model in degrees, 0 = looking from +Z. Default ${F}. In the default four-view sheet this is the FIRST view's angle; the others are offset from it.`},elevation:{type:`number`,description:`Camera height in degrees above the horizon, 90 = straight down. Default ${I}. Avoid 0 and 90 for the main view — an axis-aligned shot flattens depth.`},zoom:{type:`number`,description:`Multiplier on the automatic framing, which fits the whole model. 1 = fit (default), 2 = twice as close, 0.5 = further out. Prefer this over guessing a camera distance: it means the same thing at any model scale.`},views:{type:`string`,enum:[`quad`,`single`],description:"`quad` (default) renders four angles on one sheet — use it to judge shape and layout. `single` renders only `azimuth`/`elevation`, for a close look at one detail."},projection:{type:`string`,enum:[`perspective`,`orthographic`],description:"`perspective` (default) looks natural; `orthographic` keeps parallel edges parallel, which is better for comparing proportions."},width:{type:`number`,description:`Pixel width of ONE view. Default ${R}, clamped to ${B}–${z}.`},height:{type:`number`,description:`Pixel height of ONE view. Default ${R}, clamped to ${B}–${z}.`}},required:[]};function q(e,t){return typeof e!=`number`||!Number.isFinite(e)?t:Math.min(z,Math.max(B,Math.round(e)))}function J(e,t){return typeof e==`number`&&Number.isFinite(e)?e:t}var Y=e=>`${Math.round(e)}°`;function X(e,t,n){let r=(e,t,n)=>({azimuth:e,elevation:t,label:`${n} — az ${Y(e)}, el ${Y(t)}`});return n?[r(e,t,`view`)]:[r(e,t,`front-right`),r(e+90,t,`back-right`),r(e+180,t,`back-left`),r(e,V,`top`)]}async function Z(e,t){let n=typeof t.script==`string`&&t.script.trim()!==``?t.script:void 0,r=typeof t.path==`string`&&t.path.trim()!==``?t.path:void 0;if(n&&r)throw Error("Provide either `script` or `path`, not both");if(n)return n;if(r)return e.readShape(r);throw Error("Provide either `script` (inline source) or `path` (an existing .shape file)")}function Q(e,t,n,r){return{script:t,views:n,width:q(e.width,R),height:q(e.height,R),zoom:Math.max(J(e.zoom,L),.01),projection:e.projection===`orthographic`?`orthographic`:`perspective`,...r?{onWarning:r}:{}}}function $(e,t){let n=t.map(e=>e.label).join(`; `);return`Saved render to ${e} (${t.length===1?`1 view`:`${t.length} views`}: ${n}). Read that file to see the model.`}async function ne(e,t){let n=await Z(e,t),r=X(J(t.azimuth,F),J(t.elevation,I),t.views===`single`);try{let i=await P(Q(t,n,r,e.onWarning));return{message:$(await e.saveImage(i),r),rendered:!0}}catch(t){if(t instanceof C)return w(e.onWarning,`renderShapeScript: unavailable — ${t.message}`),{message:t.message,rendered:!1};throw t}}exports.CHROMIUM_HINT=S,exports.LAUNCH_TIMEOUT_MS=b,exports.RENDER_BUDGET_MS=x,exports.RENDER_SHAPE_SCRIPT_DESCRIPTION=W,exports.RENDER_SHAPE_SCRIPT_PROMPT=G,exports.RENDER_SHAPE_SCRIPT_SCHEMA=K,exports.RENDER_SHAPE_SCRIPT_TOOL_NAME=U,exports.RENDER_TIMEOUT_MS=y,exports.RENDER_TOOL_TIMEOUT_MS=H,exports.RenderUnavailableError=C,exports.buildRenderPage=g,exports.executeRenderShapeScript=ne,exports.gridFor=p,exports.renderOptionsFrom=Q,exports.renderShapeScriptSheet=P,exports.savedMessage=$,exports.viewAngles=X;