@mulmoclaude/shapescript-plugin 2.5.1 → 2.7.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
@@ -58,6 +58,30 @@ The file lands at `artifacts/shapes/<slug>-<epoch-ms>-<token>.usdz`. The View's
58
58
  button builds the same archive in the browser with `shapeScriptToUsdz` and saves it locally.
59
59
  USDZ units are metres, so `size 1` is one metre in AR.
60
60
 
61
+ ## Publishing to the gallery
62
+
63
+ `publishShapeScript` posts a model to the public gallery on mulmoserver (server.mulmocast.com/shapes)
64
+ and returns its URL. The tool's contract — schema, description, the document a post is
65
+ (`SHAPE_POST_KEYS`, which mulmoserver's rules pin with `hasOnly`), the keyword normalisation — is the
66
+ package's; Firebase is not. A host supplies a `ShapeGalleryWriter` over its own signed-in session
67
+ (the remote-host session, which is the user's account on mulmoserver's Firebase) and a
68
+ `renderThumbnail` — `renderShapeThumbnail` from `./render`, which answers `null` where no headless
69
+ browser is installed, so the post still lands, without a picture:
70
+
71
+ ```ts
72
+ import { executePublishShapeScript, PUBLISH_TOOL_NAME, PUBLISH_DESCRIPTION, PUBLISH_SCHEMA, PUBLISH_PROMPT } from "@mulmoclaude/shapescript-plugin";
73
+ import { renderShapeThumbnail } from "@mulmoclaude/shapescript-plugin/render";
74
+
75
+ // gallery: { uid, authorName, createPost(id, doc), uploadThumbnail(id, png), deleteObject(id, objectId) }
76
+ // — every member required; null when not signed in
77
+ const { message, url } = await executePublishShapeScript({ files: shapeFiles, gallery, renderThumbnail: renderShapeThumbnail }, args);
78
+ ```
79
+
80
+ `createPost` must add `createdAt` / `updatedAt` as `serverTimestamp()`; the rules refuse a client
81
+ clock. `deleteObject` is what takes an uploaded thumbnail back out when `createPost` is refused, so
82
+ no object is left that nothing references. With `gallery: null` the tool throws
83
+ `NOT_CONNECTED_MESSAGE`, which tells the user to connect Remote Host.
84
+
61
85
  ## ShapeScript language
62
86
 
63
87
  - **Primitives**: `cube`, `sphere`, `icosphere`, `cylinder`, `cone`, `torus`, `circle`, `square`,
@@ -8,8 +8,10 @@ export { isShapeScriptDispatchArgs, readLoadShapeResult, readSaveShapeResult } f
8
8
  export type { LoadShapeArgs, SaveShapeArgs, ShapeScriptDispatchArgs, ShapeScriptDispatchResult } from "./contract";
9
9
  export { isPresentableShapePath, isShapeArtifactPath, shapeArtifactPath, usdzArtifactPath, toArtifactsRelative, SHAPE_EXTENSIONS } from "./paths";
10
10
  export { sceneToUsdz, shapeScriptToUsdz, USDZ_MIME_TYPE, USDZ_EXTENSION } from "../export/usdz";
11
- export { executeExportShapeScriptUsdz, EXPORT_USDZ_TOOL_NAME, EXPORT_USDZ_DESCRIPTION, EXPORT_USDZ_PROMPT, EXPORT_USDZ_SCHEMA, EXPORT_USDZ_TOOL_TIMEOUT_MS, } from "../export/tool";
11
+ export { resolveShapeSource, executeExportShapeScriptUsdz, EXPORT_USDZ_TOOL_NAME, EXPORT_USDZ_DESCRIPTION, EXPORT_USDZ_PROMPT, EXPORT_USDZ_SCHEMA, EXPORT_USDZ_TOOL_TIMEOUT_MS, } from "../export/tool";
12
12
  export type { ExportUsdzResult } from "../export/tool";
13
+ export { executePublishShapeScript, shapePostFrom, shapePostUrl, normalizeKeywords as normalizeShapeKeywords, PUBLISH_TOOL_NAME, PUBLISH_DESCRIPTION, PUBLISH_PROMPT, PUBLISH_SCHEMA, SHAPE_GALLERY_URL, SHAPE_POST_KEYS, SHAPE_POST_LIMITS, NOT_CONNECTED_MESSAGE, } from "./publish";
14
+ export type { PublishShapeScriptContext, PublishShapeResult, ShapeGalleryWriter, ShapePostDoc } from "./publish";
13
15
  export { samples } from "./samples";
14
16
  export { parseShapeScript } from "../shapescript/parser";
15
17
  export { astToThreeJS, sceneInfoOf } from "../shapescript/toThreeJS";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,gCAAgC,EAChC,6BAA6B,EAC7B,iCAAiC,EACjC,qBAAqB,GACtB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AACrF,YAAY,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,EAAE,0BAA0B,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACrE,YAAY,EAAE,0BAA0B,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjG,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AACnH,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAIlJ,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EACL,4BAA4B,EAC5B,qBAAqB,EACrB,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,2BAA2B,GAC5B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACrE,YAAY,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrE,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,gCAAgC,EAChC,6BAA6B,EAC7B,iCAAiC,EACjC,qBAAqB,GACtB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AACrF,YAAY,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,EAAE,0BAA0B,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACrE,YAAY,EAAE,0BAA0B,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjG,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AACnH,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAIlJ,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EACL,kBAAkB,EAClB,4BAA4B,EAC5B,qBAAqB,EACrB,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,2BAA2B,GAC5B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EACL,yBAAyB,EACzB,aAAa,EACb,YAAY,EACZ,iBAAiB,IAAI,sBAAsB,EAC3C,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACjH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACrE,YAAY,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrE,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,145 @@
1
+ import type { ShapeScriptDispatchContext } from "./dispatch";
2
+ export declare const PUBLISH_TOOL_NAME = "publishShapeScript";
3
+ /** Where the gallery lives. A host may override it (a staging deploy). */
4
+ export declare const SHAPE_GALLERY_URL = "https://server.mulmocast.com";
5
+ /** The caps mulmoserver's rules enforce, mirrored so a post is refused here
6
+ * with a reason rather than there as a bare permission error. */
7
+ export declare const SHAPE_POST_LIMITS: {
8
+ readonly titleMax: 120;
9
+ readonly descriptionMax: 2000;
10
+ readonly scriptMax: 100000;
11
+ readonly promptMax: 4000;
12
+ readonly authorNameMax: 80;
13
+ readonly keywordsMax: 10;
14
+ readonly keywordMax: 30;
15
+ };
16
+ export declare const PUBLISH_DESCRIPTION = "Publish a ShapeScript model to the public gallery at server.mulmocast.com/shapes, where anyone can view it in 3D, read the source, download the USDZ and fork it. Takes the same source as presentShapeScript: inline `script`, or `path` to a saved .shape file. Posts under the user's own Google account \u2014 the app must be connected to Remote Host (signed in) first \u2014 and returns the model's URL. A thumbnail is rendered and attached when the host can rasterise; the post still lands without one.";
17
+ export declare const PUBLISH_PROMPT = "Use publishShapeScript ONLY when the user asks to publish, post or share a model to the gallery \u2014 never on your own initiative, since it makes the model public under their name. Before calling it, make sure the model previews correctly (presentShapeScript / renderShapeScript) and give it a short title, a sentence of description and a few lowercase keywords someone would search for. Pass the user's original request as `prompt` so the post records how the model was made. If the tool answers that Remote Host is not connected, tell the user to connect it (the Remote Host control in the app, Google sign-in) and offer to try again.";
18
+ /** The tool's JSON schema, in the shape both a gui-chat-protocol
19
+ * `ToolDefinition` (`parameters`) and an MCP tool (`inputSchema`) take. */
20
+ export declare const PUBLISH_SCHEMA: {
21
+ type: "object";
22
+ properties: {
23
+ title: {
24
+ type: string;
25
+ description: string;
26
+ };
27
+ script: {
28
+ type: string;
29
+ description: string;
30
+ };
31
+ path: {
32
+ type: string;
33
+ description: string;
34
+ };
35
+ description: {
36
+ type: string;
37
+ description: string;
38
+ };
39
+ keywords: {
40
+ type: string;
41
+ items: {
42
+ type: string;
43
+ };
44
+ description: string;
45
+ };
46
+ prompt: {
47
+ type: string;
48
+ description: string;
49
+ };
50
+ published: {
51
+ type: string;
52
+ description: string;
53
+ };
54
+ };
55
+ required: string[];
56
+ };
57
+ /** The document a post is, minus the two server-stamped times the host adds
58
+ * (`createdAt` / `updatedAt` must be `serverTimestamp()` — the rules refuse
59
+ * anything else). Every key present with a value: the rules pin the set and
60
+ * Firestore rejects `undefined`. */
61
+ export interface ShapePostDoc {
62
+ uid: string;
63
+ authorName: string;
64
+ title: string;
65
+ description: string;
66
+ script: string;
67
+ source: "prompt";
68
+ prompt: string;
69
+ photoIds: string[];
70
+ thumbnailId: string;
71
+ forkedFrom: null;
72
+ keywords: string[];
73
+ published: boolean;
74
+ }
75
+ /** The key set mulmoserver's rules accept, in the rules' own order. */
76
+ export declare const SHAPE_POST_KEYS: readonly ["uid", "authorName", "title", "description", "script", "source", "prompt", "photoIds", "thumbnailId", "forkedFrom", "keywords", "published"];
77
+ /** What a host supplies: who is posting, and the writes, over its own
78
+ * signed-in session — the Firestore document and the Storage object the
79
+ * gallery card shows. */
80
+ export interface ShapeGalleryWriter {
81
+ /** The signed-in user's Firebase uid — the post's owner. */
82
+ uid: string;
83
+ /** The Google display name, as the gallery shows it. Empty is allowed. */
84
+ authorName: string;
85
+ /** Overrides `SHAPE_GALLERY_URL` for the returned link. */
86
+ siteUrl?: string;
87
+ /** Create `shapes/{id}` from `doc` plus the server timestamps. */
88
+ createPost: (id: string, doc: ShapePostDoc) => Promise<void>;
89
+ /** Store a PNG under the post and return the object id the document carries. */
90
+ uploadThumbnail: (id: string, png: Uint8Array) => Promise<string>;
91
+ /** Remove an object under the post — the thumbnail of a post that was never written. */
92
+ deleteObject: (id: string, objectId: string) => Promise<void>;
93
+ }
94
+ export interface PublishShapeScriptContext extends ShapeScriptDispatchContext {
95
+ /** null when the host has no signed-in session — the tool then says how to get one. */
96
+ gallery: ShapeGalleryWriter | null;
97
+ /** Rasterise one view of `script` to a PNG, or null where this host cannot
98
+ * (no headless browser — a Docker image, an install that skipped the
99
+ * Chromium download). Supplied from `@mulmoclaude/shapescript-plugin/render`.
100
+ * The picture is best effort because the gallery tolerates its absence: a
101
+ * card without one shows the model icon, and the post's owner can add one
102
+ * from the web editor. */
103
+ renderThumbnail: (script: string) => Promise<Uint8Array | null>;
104
+ /** A fault that did not stop the post — a thumbnail that could not be made. */
105
+ onWarning?: (message: string) => void;
106
+ }
107
+ export interface PublishShapeResult {
108
+ message: string;
109
+ /** The post's id under `shapes/`. */
110
+ id: string;
111
+ /** The model's page. */
112
+ url: string;
113
+ /** Whether a thumbnail was attached. */
114
+ thumbnail: boolean;
115
+ }
116
+ export declare const NOT_CONNECTED_MESSAGE = "Not connected to the gallery: publishing posts under the user's Google account, which needs the app's Remote Host connected (sign in with Google in the Remote Host control), then try again.";
117
+ /** Keywords as the gallery stores them: trimmed, lowercased, deduplicated,
118
+ * each cut to `keywordMax`, at most `keywordsMax`. The same rule as
119
+ * mulmoserver's `normalizeKeywords`, so a tag typed there and one sent from
120
+ * here can never be two spellings of one word. A comma-separated string is
121
+ * accepted too, since a model sometimes sends one. */
122
+ export declare function normalizeKeywords(raw: unknown): string[];
123
+ /** The document for one post, built field by field so nothing the caller
124
+ * passed can reach Firestore uninvited. Throws on a limit the rules would
125
+ * refuse, naming the field. */
126
+ export declare function shapePostFrom(writer: Pick<ShapeGalleryWriter, "uid" | "authorName">, fields: {
127
+ title: string;
128
+ script: string;
129
+ description?: string | undefined;
130
+ prompt?: string | undefined;
131
+ keywords?: unknown;
132
+ published?: boolean | undefined;
133
+ thumbnailId?: string | undefined;
134
+ }): ShapePostDoc;
135
+ /** The gallery's address for one post. */
136
+ export declare function shapePostUrl(id: string, siteUrl?: string): string;
137
+ /**
138
+ * Run one `publishShapeScript` call. Throws on a missing session, a missing or
139
+ * invalid source, a limit the gallery would refuse, and on ShapeScript errors
140
+ * — the host's error path reports those to the model as it does for
141
+ * `renderShapeScript`. Everything that can be refused is checked BEFORE the
142
+ * thumbnail is uploaded, so a refusal writes nothing.
143
+ */
144
+ export declare function executePublishShapeScript(context: PublishShapeScriptContext, args: Record<string, unknown>): Promise<PublishShapeResult>;
145
+ //# sourceMappingURL=publish.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/core/publish.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAE7D,eAAO,MAAM,iBAAiB,uBAAuB,CAAC;AAEtD,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,iCAAiC,CAAC;AAEhE;kEACkE;AAClE,eAAO,MAAM,iBAAiB;;;;;;;;CAQpB,CAAC;AAEX,eAAO,MAAM,mBAAmB,0fAC+c,CAAC;AAEhf,eAAO,MAAM,cAAc,moBACkmB,CAAC;AAE9nB;4EAC4E;AAC5E,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmC1B,CAAC;AAEF;;;qCAGqC;AACrC,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,IAAI,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,uEAAuE;AACvE,eAAO,MAAM,eAAe,wJAalB,CAAC;AAEX;;0BAE0B;AAC1B,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,gFAAgF;IAChF,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClE,wFAAwF;IACxF,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/D;AAED,MAAM,WAAW,yBAA0B,SAAQ,0BAA0B;IAC3E,uFAAuF;IACvF,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnC;;;;;+BAK2B;IAC3B,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IAChE,+EAA+E;IAC/E,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,wCAAwC;IACxC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,qBAAqB,kMAC+J,CAAC;AAMlM;;;;uDAIuD;AACvD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,EAAE,CAIxD;AAQD;;gCAEgC;AAChC,wBAAgB,aAAa,CAC3B,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,KAAK,GAAG,YAAY,CAAC,EACtD,MAAM,EAAE;IACN,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,GACA,YAAY,CAed;AAED,0CAA0C;AAC1C,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,SAAoB,GAAG,MAAM,CAG5E;AAsCD;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,yBAAyB,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAuB9I"}
@@ -0,0 +1,186 @@
1
+ import { _ as e, a as t, u as n } from "./samples-BZSbBJmx.js";
2
+ import { a as r, r as i, s as a, t as o } from "./toThreeJS-Bo3j0vOj.js";
3
+ //#region src/export/tool.ts
4
+ var s = "exportShapeScriptUsdz", c = o + 3e4, l = "Export a ShapeScript model to a USDZ file (Apple's AR / 3D format, openable with AR Quick Look on iPhone, iPad and Mac) and save it under artifacts/shapes/. Returns the saved path. Takes the same source as presentShapeScript: inline `script`, or `path` to a saved .shape file. USDZ units are metres, so a `size 1` cube becomes a one-metre object in AR — scale the model in the script if that is not what the user wants.", u = "Use exportShapeScriptUsdz when the user wants to take a 3D model out of the chat — to view it in AR, open it in a 3D app, or share the file. It saves a .usdz next to the model's .shape file and returns the path; tell the user where it is. The user can also press \"Download USDZ\" in the model's view themselves.", d = {
5
+ type: "object",
6
+ properties: {
7
+ script: {
8
+ type: "string",
9
+ description: "ShapeScript source to export. Provide either this or `path`, not both."
10
+ },
11
+ path: {
12
+ type: "string",
13
+ description: "Path to an existing .shape file to export (e.g. one presentShapeScript saved under artifacts/shapes/)."
14
+ },
15
+ title: {
16
+ type: "string",
17
+ description: "Name for the exported file; it is slugified into the filename. Defaults to the .shape file's own name when `path` is given."
18
+ }
19
+ },
20
+ required: []
21
+ };
22
+ function f(e) {
23
+ return typeof e == "string" && e.trim() !== "" ? e : void 0;
24
+ }
25
+ function p(e) {
26
+ return (e.split(/[\\/]/).pop() ?? e).replace(/\.shape$/i, "");
27
+ }
28
+ async function m(e, t) {
29
+ let r = f(t.script), i = f(t.path), a = f(t.title);
30
+ if (r && i) throw Error("Provide either `script` or `path`, not both");
31
+ if (r) return {
32
+ script: r,
33
+ title: a
34
+ };
35
+ if (!i) throw Error("Provide either `script` (inline source) or `path` (an existing .shape file)");
36
+ let o = n(e, i);
37
+ if (!o) throw Error("`path` must name a .shape file, without `.` / `..` segments");
38
+ if (!await o.files.exists(o.rel)) throw Error(`No ShapeScript exists at ${i}`);
39
+ return {
40
+ script: await o.files.read(o.rel),
41
+ title: a ?? p(i)
42
+ };
43
+ }
44
+ async function h(n, r) {
45
+ let { script: i, title: a } = await m(n, r), o = await t(i), { relPath: s, filePath: c } = e(a);
46
+ return await n.files.artifacts.write(s, o), {
47
+ message: `Saved USDZ to ${c} (${o.byteLength} bytes). Open it with AR Quick Look or any USD viewer.`,
48
+ filePath: c,
49
+ bytes: o.byteLength
50
+ };
51
+ }
52
+ //#endregion
53
+ //#region src/core/publish.ts
54
+ var g = "publishShapeScript", _ = "https://server.mulmocast.com", v = {
55
+ titleMax: 120,
56
+ descriptionMax: 2e3,
57
+ scriptMax: 1e5,
58
+ promptMax: 4e3,
59
+ authorNameMax: 80,
60
+ keywordsMax: 10,
61
+ keywordMax: 30
62
+ }, y = "Publish a ShapeScript model to the public gallery at server.mulmocast.com/shapes, where anyone can view it in 3D, read the source, download the USDZ and fork it. Takes the same source as presentShapeScript: inline `script`, or `path` to a saved .shape file. Posts under the user's own Google account — the app must be connected to Remote Host (signed in) first — and returns the model's URL. A thumbnail is rendered and attached when the host can rasterise; the post still lands without one.", b = "Use publishShapeScript ONLY when the user asks to publish, post or share a model to the gallery — never on your own initiative, since it makes the model public under their name. Before calling it, make sure the model previews correctly (presentShapeScript / renderShapeScript) and give it a short title, a sentence of description and a few lowercase keywords someone would search for. Pass the user's original request as `prompt` so the post records how the model was made. If the tool answers that Remote Host is not connected, tell the user to connect it (the Remote Host control in the app, Google sign-in) and offer to try again.", x = {
63
+ type: "object",
64
+ properties: {
65
+ title: {
66
+ type: "string",
67
+ description: `The model's title (1–${v.titleMax} characters).`
68
+ },
69
+ script: {
70
+ type: "string",
71
+ description: "ShapeScript source to publish. Provide either this or `path`, not both."
72
+ },
73
+ path: {
74
+ type: "string",
75
+ description: "Path to an existing .shape file — an `artifacts/shapes/...` path presentShapeScript saved, or any .shape the host can read. Provide either this or `script`, not both."
76
+ },
77
+ description: {
78
+ type: "string",
79
+ description: `What the model is, for the gallery page (up to ${v.descriptionMax} characters). Optional.`
80
+ },
81
+ keywords: {
82
+ type: "array",
83
+ items: { type: "string" },
84
+ description: `Up to ${v.keywordsMax} short lowercase tags someone would search for. Optional.`
85
+ },
86
+ prompt: {
87
+ type: "string",
88
+ description: `The request the model was made from, recorded as its provenance (up to ${v.promptMax} characters). Optional.`
89
+ },
90
+ published: {
91
+ type: "boolean",
92
+ description: "false saves a draft only the user can see in the gallery's My models. Default true."
93
+ }
94
+ },
95
+ required: ["title"]
96
+ }, S = [
97
+ "uid",
98
+ "authorName",
99
+ "title",
100
+ "description",
101
+ "script",
102
+ "source",
103
+ "prompt",
104
+ "photoIds",
105
+ "thumbnailId",
106
+ "forkedFrom",
107
+ "keywords",
108
+ "published"
109
+ ], C = "Not connected to the gallery: publishing posts under the user's Google account, which needs the app's Remote Host connected (sign in with Google in the Remote Host control), then try again.", w = (e) => typeof e == "string" && e.trim() !== "" ? e : void 0, T = (e) => typeof e == "string" ? e.trim().toLowerCase().slice(0, v.keywordMax) : "";
110
+ function E(e) {
111
+ let t = (Array.isArray(e) ? e : typeof e == "string" ? e.split(",") : []).map(T).filter((e) => e !== "");
112
+ return [...new Set(t)].slice(0, v.keywordsMax);
113
+ }
114
+ function D(e, t, n, r = 0) {
115
+ if (t.length < r) throw Error(`\`${e}\` is required`);
116
+ if (t.length > n) throw Error(`\`${e}\` is too long (${t.length} characters; the gallery allows ${n})`);
117
+ return t;
118
+ }
119
+ function O(e, t) {
120
+ return {
121
+ uid: e.uid,
122
+ authorName: e.authorName.slice(0, v.authorNameMax),
123
+ title: D("title", t.title.trim(), v.titleMax, 1),
124
+ description: D("description", t.description ?? "", v.descriptionMax),
125
+ script: D("script", t.script, v.scriptMax, 1),
126
+ source: "prompt",
127
+ prompt: D("prompt", t.prompt ?? "", v.promptMax),
128
+ photoIds: [],
129
+ thumbnailId: t.thumbnailId ?? "",
130
+ forkedFrom: null,
131
+ keywords: E(t.keywords),
132
+ published: t.published !== !1
133
+ };
134
+ }
135
+ function k(e, t = _) {
136
+ return `${t.endsWith("/") ? t.slice(0, -1) : t}/shapes/${e}`;
137
+ }
138
+ function A(e) {
139
+ r(i(a(e)));
140
+ }
141
+ var j = (e) => e instanceof Error ? e.message : String(e);
142
+ async function M(e, t, n, r) {
143
+ try {
144
+ let i = await e.renderThumbnail(r);
145
+ return i ? await t.uploadThumbnail(n, i) : "";
146
+ } catch (t) {
147
+ return e.onWarning?.(`thumbnail skipped: ${j(t)}`), "";
148
+ }
149
+ }
150
+ async function N(e, t, n, r) {
151
+ try {
152
+ await t.createPost(n, r);
153
+ } catch (i) {
154
+ throw r.thumbnailId && await t.deleteObject(n, r.thumbnailId).catch((t) => e.onWarning?.(`orphaned thumbnail ${r.thumbnailId}: ${j(t)}`)), i;
155
+ }
156
+ }
157
+ var P = () => globalThis.crypto.randomUUID();
158
+ async function F(e, t) {
159
+ let n = e.gallery;
160
+ if (!n) throw Error(C);
161
+ let r = w(t.title);
162
+ if (!r) throw Error("`title` is required");
163
+ let { script: i } = await m(e, t);
164
+ A(i);
165
+ let a = O(n, {
166
+ title: r,
167
+ script: i,
168
+ description: w(t.description),
169
+ prompt: w(t.prompt),
170
+ keywords: t.keywords,
171
+ published: t.published !== !1
172
+ }), o = P(), s = await M(e, n, o, i), c = {
173
+ ...a,
174
+ thumbnailId: s
175
+ };
176
+ await N(e, n, o, c);
177
+ let l = k(o, n.siteUrl), u = c.published ? "Published" : "Saved as a draft (only the user can see it, under My models)", d = s ? "" : " No thumbnail could be attached; the gallery shows a placeholder until the user edits the post.";
178
+ return {
179
+ message: `${u}: "${c.title}" is at ${l}.${d}`,
180
+ id: o,
181
+ url: l,
182
+ thumbnail: s !== ""
183
+ };
184
+ }
185
+ //#endregion
186
+ export { c as _, g as a, v as c, O as d, k as f, s as g, d as h, x as i, F as l, u as m, y as n, _ as o, l as p, b as r, S as s, C as t, E as u, h as v, m as y };
@@ -0,0 +1 @@
1
+ const e=require("./samples-C8zn1JOi.cjs"),t=require("./toThreeJS-BBwttw4K.cjs");var n=`exportShapeScriptUsdz`,r=t.t+3e4,i="Export a ShapeScript model to a USDZ file (Apple's AR / 3D format, openable with AR Quick Look on iPhone, iPad and Mac) and save it under artifacts/shapes/. Returns the saved path. Takes the same source as presentShapeScript: inline `script`, or `path` to a saved .shape file. USDZ units are metres, so a `size 1` cube becomes a one-metre object in AR — scale the model in the script if that is not what the user wants.",a=`Use exportShapeScriptUsdz when the user wants to take a 3D model out of the chat — to view it in AR, open it in a 3D app, or share the file. It saves a .usdz next to the model's .shape file and returns the path; tell the user where it is. The user can also press "Download USDZ" in the model's view themselves.`,o={type:`object`,properties:{script:{type:`string`,description:"ShapeScript source to export. Provide either this or `path`, not both."},path:{type:`string`,description:`Path to an existing .shape file to export (e.g. one presentShapeScript saved under artifacts/shapes/).`},title:{type:`string`,description:"Name for the exported file; it is slugified into the filename. Defaults to the .shape file's own name when `path` is given."}},required:[]};function s(e){return typeof e==`string`&&e.trim()!==``?e:void 0}function c(e){return(e.split(/[\\/]/).pop()??e).replace(/\.shape$/i,``)}async function l(t,n){let r=s(n.script),i=s(n.path),a=s(n.title);if(r&&i)throw Error("Provide either `script` or `path`, not both");if(r)return{script:r,title:a};if(!i)throw Error("Provide either `script` (inline source) or `path` (an existing .shape file)");let o=e.u(t,i);if(!o)throw Error("`path` must name a .shape file, without `.` / `..` segments");if(!await o.files.exists(o.rel))throw Error(`No ShapeScript exists at ${i}`);return{script:await o.files.read(o.rel),title:a??c(i)}}async function u(t,n){let{script:r,title:i}=await l(t,n),a=await e.a(r),{relPath:o,filePath:s}=e._(i);return await t.files.artifacts.write(o,a),{message:`Saved USDZ to ${s} (${a.byteLength} bytes). Open it with AR Quick Look or any USD viewer.`,filePath:s,bytes:a.byteLength}}var d=`publishShapeScript`,f=`https://server.mulmocast.com`,p={titleMax:120,descriptionMax:2e3,scriptMax:1e5,promptMax:4e3,authorNameMax:80,keywordsMax:10,keywordMax:30},m="Publish a ShapeScript model to the public gallery at server.mulmocast.com/shapes, where anyone can view it in 3D, read the source, download the USDZ and fork it. Takes the same source as presentShapeScript: inline `script`, or `path` to a saved .shape file. Posts under the user's own Google account — the app must be connected to Remote Host (signed in) first — and returns the model's URL. A thumbnail is rendered and attached when the host can rasterise; the post still lands without one.",h="Use publishShapeScript ONLY when the user asks to publish, post or share a model to the gallery — never on your own initiative, since it makes the model public under their name. Before calling it, make sure the model previews correctly (presentShapeScript / renderShapeScript) and give it a short title, a sentence of description and a few lowercase keywords someone would search for. Pass the user's original request as `prompt` so the post records how the model was made. If the tool answers that Remote Host is not connected, tell the user to connect it (the Remote Host control in the app, Google sign-in) and offer to try again.",g={type:`object`,properties:{title:{type:`string`,description:`The model's title (1–${p.titleMax} characters).`},script:{type:`string`,description:"ShapeScript source to publish. Provide either this or `path`, not both."},path:{type:`string`,description:"Path to an existing .shape file — an `artifacts/shapes/...` path presentShapeScript saved, or any .shape the host can read. Provide either this or `script`, not both."},description:{type:`string`,description:`What the model is, for the gallery page (up to ${p.descriptionMax} characters). Optional.`},keywords:{type:`array`,items:{type:`string`},description:`Up to ${p.keywordsMax} short lowercase tags someone would search for. Optional.`},prompt:{type:`string`,description:`The request the model was made from, recorded as its provenance (up to ${p.promptMax} characters). Optional.`},published:{type:`boolean`,description:`false saves a draft only the user can see in the gallery's My models. Default true.`}},required:[`title`]},_=[`uid`,`authorName`,`title`,`description`,`script`,`source`,`prompt`,`photoIds`,`thumbnailId`,`forkedFrom`,`keywords`,`published`],v=`Not connected to the gallery: publishing posts under the user's Google account, which needs the app's Remote Host connected (sign in with Google in the Remote Host control), then try again.`,y=e=>typeof e==`string`&&e.trim()!==``?e:void 0,b=e=>typeof e==`string`?e.trim().toLowerCase().slice(0,p.keywordMax):``;function x(e){let t=(Array.isArray(e)?e:typeof e==`string`?e.split(`,`):[]).map(b).filter(e=>e!==``);return[...new Set(t)].slice(0,p.keywordsMax)}function S(e,t,n,r=0){if(t.length<r)throw Error(`\`${e}\` is required`);if(t.length>n)throw Error(`\`${e}\` is too long (${t.length} characters; the gallery allows ${n})`);return t}function C(e,t){return{uid:e.uid,authorName:e.authorName.slice(0,p.authorNameMax),title:S(`title`,t.title.trim(),p.titleMax,1),description:S(`description`,t.description??``,p.descriptionMax),script:S(`script`,t.script,p.scriptMax,1),source:`prompt`,prompt:S(`prompt`,t.prompt??``,p.promptMax),photoIds:[],thumbnailId:t.thumbnailId??``,forkedFrom:null,keywords:x(t.keywords),published:t.published!==!1}}function w(e,t=f){return`${t.endsWith(`/`)?t.slice(0,-1):t}/shapes/${e}`}function T(e){t.a(t.r(t.s(e)))}var E=e=>e instanceof Error?e.message:String(e);async function D(e,t,n,r){try{let i=await e.renderThumbnail(r);return i?await t.uploadThumbnail(n,i):``}catch(t){return e.onWarning?.(`thumbnail skipped: ${E(t)}`),``}}async function O(e,t,n,r){try{await t.createPost(n,r)}catch(i){throw r.thumbnailId&&await t.deleteObject(n,r.thumbnailId).catch(t=>e.onWarning?.(`orphaned thumbnail ${r.thumbnailId}: ${E(t)}`)),i}}var k=()=>globalThis.crypto.randomUUID();async function A(e,t){let n=e.gallery;if(!n)throw Error(v);let r=y(t.title);if(!r)throw Error("`title` is required");let{script:i}=await l(e,t);T(i);let a=C(n,{title:r,script:i,description:y(t.description),prompt:y(t.prompt),keywords:t.keywords,published:t.published!==!1}),o=k(),s=await D(e,n,o,i),c={...a,thumbnailId:s};await O(e,n,o,c);let u=w(o,n.siteUrl),d=c.published?`Published`:`Saved as a draft (only the user can see it, under My models)`,f=s?``:` No thumbnail could be attached; the gallery shows a placeholder until the user edits the post.`;return{message:`${d}: "${c.title}" is at ${u}.${f}`,id:o,url:u,thumbnail:s!==``}}Object.defineProperty(exports,"_",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,"g",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"h",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,"v",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"y",{enumerable:!0,get:function(){return l}});
package/dist/core.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./samples-b6J0NoFm.cjs"),t=require("./toThreeJS-BETrskvw.cjs"),n=require("./core-1E7QiANi.cjs");exports.EXPORT_USDZ_DESCRIPTION=n.t,exports.EXPORT_USDZ_PROMPT=n.n,exports.EXPORT_USDZ_SCHEMA=n.r,exports.EXPORT_USDZ_TOOL_NAME=n.i,exports.EXPORT_USDZ_TOOL_TIMEOUT_MS=n.a,exports.SHAPE_EXTENSIONS=e.d,exports.TOOL_DEFINITION=e.v,exports.TOOL_NAME=e.y,exports.USDZ_EXTENSION=e.n,exports.USDZ_MIME_TYPE=e.r,exports.astToThreeJS=t.r,exports.executeExportShapeScriptUsdz=n.o,exports.executePresentShapeScript=e.o,exports.executeShapeScriptDispatch=e.l,exports.isPresentableShapePath=e.f,exports.isShapeArtifactPath=e.p,exports.isShapeScriptDispatchArgs=t.Vn,exports.locateShape=e.u,exports.parseShapeScript=t.s,exports.pluginCore=e.s,exports.presentShapeScript=e.c,exports.readLoadShapeResult=t.Hn,exports.readSaveShapeResult=t.Un,exports.samples=e.t,exports.sceneInfoOf=t.i,exports.sceneToUsdz=e.i,exports.shapeArtifactPath=e.m,exports.shapeScriptToUsdz=e.a,exports.toArtifactsRelative=e.g,exports.usdzArtifactPath=e._;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./samples-C8zn1JOi.cjs"),t=require("./toThreeJS-BBwttw4K.cjs"),n=require("./core-DfEbc4Me.cjs");exports.EXPORT_USDZ_DESCRIPTION=n.p,exports.EXPORT_USDZ_PROMPT=n.m,exports.EXPORT_USDZ_SCHEMA=n.h,exports.EXPORT_USDZ_TOOL_NAME=n.g,exports.EXPORT_USDZ_TOOL_TIMEOUT_MS=n._,exports.NOT_CONNECTED_MESSAGE=n.t,exports.PUBLISH_DESCRIPTION=n.n,exports.PUBLISH_PROMPT=n.r,exports.PUBLISH_SCHEMA=n.i,exports.PUBLISH_TOOL_NAME=n.a,exports.SHAPE_EXTENSIONS=e.d,exports.SHAPE_GALLERY_URL=n.o,exports.SHAPE_POST_KEYS=n.s,exports.SHAPE_POST_LIMITS=n.c,exports.TOOL_DEFINITION=e.v,exports.TOOL_NAME=e.y,exports.USDZ_EXTENSION=e.n,exports.USDZ_MIME_TYPE=e.r,exports.astToThreeJS=t.r,exports.executeExportShapeScriptUsdz=n.v,exports.executePresentShapeScript=e.o,exports.executePublishShapeScript=n.l,exports.executeShapeScriptDispatch=e.l,exports.isPresentableShapePath=e.f,exports.isShapeArtifactPath=e.p,exports.isShapeScriptDispatchArgs=t.Vn,exports.locateShape=e.u,exports.normalizeShapeKeywords=n.u,exports.parseShapeScript=t.s,exports.pluginCore=e.s,exports.presentShapeScript=e.c,exports.readLoadShapeResult=t.Hn,exports.readSaveShapeResult=t.Un,exports.resolveShapeSource=n.y,exports.samples=e.t,exports.sceneInfoOf=t.i,exports.sceneToUsdz=e.i,exports.shapeArtifactPath=e.m,exports.shapePostFrom=n.d,exports.shapePostUrl=n.f,exports.shapeScriptToUsdz=e.a,exports.toArtifactsRelative=e.g,exports.usdzArtifactPath=e._;
package/dist/core.js CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as e, a as t, c as n, d as r, f as i, g as a, i as o, l as s, m as c, n as l, o as u, p as d, r as f, s as p, t as m, u as h, v as g, y as _ } from "./samples-BwyNyHha.js";
2
- import { Hn as v, Un as y, Vn as b, i as x, r as S, s as C } from "./toThreeJS-DyLC4g1t.js";
3
- import { a as w, i as T, n as E, o as D, r as O, t as k } from "./core-CiJHiO3H.js";
4
- export { k as EXPORT_USDZ_DESCRIPTION, E as EXPORT_USDZ_PROMPT, O as EXPORT_USDZ_SCHEMA, T as EXPORT_USDZ_TOOL_NAME, w as EXPORT_USDZ_TOOL_TIMEOUT_MS, r as SHAPE_EXTENSIONS, g as TOOL_DEFINITION, _ as TOOL_NAME, l as USDZ_EXTENSION, f as USDZ_MIME_TYPE, S as astToThreeJS, D as executeExportShapeScriptUsdz, u as executePresentShapeScript, s as executeShapeScriptDispatch, i as isPresentableShapePath, d as isShapeArtifactPath, b as isShapeScriptDispatchArgs, h as locateShape, C as parseShapeScript, p as pluginCore, n as presentShapeScript, v as readLoadShapeResult, y as readSaveShapeResult, m as samples, x as sceneInfoOf, o as sceneToUsdz, c as shapeArtifactPath, t as shapeScriptToUsdz, a as toArtifactsRelative, e as usdzArtifactPath };
1
+ import { _ as e, a as t, c as n, d as r, f as i, g as a, i as o, l as s, m as c, n as l, o as u, p as d, r as f, s as p, t as m, u as h, v as g, y as _ } from "./samples-BZSbBJmx.js";
2
+ import { Hn as v, Un as y, Vn as b, i as x, r as S, s as C } from "./toThreeJS-Bo3j0vOj.js";
3
+ import { _ as w, a as T, c as E, d as D, f as O, g as k, h as A, i as j, l as M, m as N, n as P, o as F, p as I, r as L, s as R, t as z, u as B, v as V, y as H } from "./core-BW88rCqf.js";
4
+ export { I as EXPORT_USDZ_DESCRIPTION, N as EXPORT_USDZ_PROMPT, A as EXPORT_USDZ_SCHEMA, k as EXPORT_USDZ_TOOL_NAME, w as EXPORT_USDZ_TOOL_TIMEOUT_MS, z as NOT_CONNECTED_MESSAGE, P as PUBLISH_DESCRIPTION, L as PUBLISH_PROMPT, j as PUBLISH_SCHEMA, T as PUBLISH_TOOL_NAME, r as SHAPE_EXTENSIONS, F as SHAPE_GALLERY_URL, R as SHAPE_POST_KEYS, E as SHAPE_POST_LIMITS, g as TOOL_DEFINITION, _ as TOOL_NAME, l as USDZ_EXTENSION, f as USDZ_MIME_TYPE, S as astToThreeJS, V as executeExportShapeScriptUsdz, u as executePresentShapeScript, M as executePublishShapeScript, s as executeShapeScriptDispatch, i as isPresentableShapePath, d as isShapeArtifactPath, b as isShapeScriptDispatchArgs, h as locateShape, B as normalizeShapeKeywords, C as parseShapeScript, p as pluginCore, n as presentShapeScript, v as readLoadShapeResult, y as readSaveShapeResult, H as resolveShapeSource, m as samples, x as sceneInfoOf, o as sceneToUsdz, c as shapeArtifactPath, D as shapePostFrom, O as shapePostUrl, t as shapeScriptToUsdz, a as toArtifactsRelative, e as usdzArtifactPath };
@@ -36,6 +36,13 @@ export interface ExportUsdzResult {
36
36
  filePath: string;
37
37
  bytes: number;
38
38
  }
39
+ /** The source one tool call names — inline `script`, or `path` to a saved `.shape`
40
+ * — plus the title the call gave it or the file's stem. Shared by the export
41
+ * and publish tools so both accept exactly the same paths. */
42
+ export declare function resolveShapeSource(context: ShapeScriptDispatchContext, args: Record<string, unknown>): Promise<{
43
+ script: string;
44
+ title: string | undefined;
45
+ }>;
39
46
  /**
40
47
  * Run one `exportShapeScriptUsdz` call. Throws on a missing / invalid source
41
48
  * and on ShapeScript errors — the host's own error path reports those to the
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/export/tool.ts"],"names":[],"mappings":"AASA,OAAO,EAAe,KAAK,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAKhF,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAE7D;;;;4EAI4E;AAC5E,eAAO,MAAM,2BAA2B,QAAmC,CAAC;AAE5E,eAAO,MAAM,uBAAuB,6aACmY,CAAC;AAExa,eAAO,MAAM,kBAAkB,kUAC6R,CAAC;AAE7T;2DAC2D;AAC3D,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;cAgBb,MAAM,EAAE;CACzB,CAAC;AAEF;oCACoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AA0BD;;;;GAIG;AACH,wBAAsB,4BAA4B,CAAC,OAAO,EAAE,0BAA0B,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAUhJ"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/export/tool.ts"],"names":[],"mappings":"AASA,OAAO,EAAe,KAAK,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAKhF,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAE7D;;;;4EAI4E;AAC5E,eAAO,MAAM,2BAA2B,QAAmC,CAAC;AAE5E,eAAO,MAAM,uBAAuB,6aACmY,CAAC;AAExa,eAAO,MAAM,kBAAkB,kUAC6R,CAAC;AAE7T;2DAC2D;AAC3D,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;cAgBb,MAAM,EAAE;CACzB,CAAC;AAEF;oCACoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAaD;;+DAE+D;AAC/D,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,0BAA0B,EACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC,CAWxD;AAED;;;;GAIG;AACH,wBAAsB,4BAA4B,CAAC,OAAO,EAAE,0BAA0B,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAUhJ"}
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./samples-b6J0NoFm.cjs"),t=require("./toThreeJS-BETrskvw.cjs"),n=require("./core-1E7QiANi.cjs");exports.EXPORT_USDZ_DESCRIPTION=n.t,exports.EXPORT_USDZ_PROMPT=n.n,exports.EXPORT_USDZ_SCHEMA=n.r,exports.EXPORT_USDZ_TOOL_NAME=n.i,exports.EXPORT_USDZ_TOOL_TIMEOUT_MS=n.a,exports.SHAPE_EXTENSIONS=e.d,exports.TOOL_DEFINITION=e.v,exports.TOOL_NAME=e.y,exports.USDZ_EXTENSION=e.n,exports.USDZ_MIME_TYPE=e.r,exports.astToThreeJS=t.r,exports.executeExportShapeScriptUsdz=n.o,exports.executePresentShapeScript=e.o,exports.executeShapeScriptDispatch=e.l,exports.isPresentableShapePath=e.f,exports.isShapeArtifactPath=e.p,exports.isShapeScriptDispatchArgs=t.Vn,exports.locateShape=e.u,exports.parseShapeScript=t.s,exports.pluginCore=e.s,exports.presentShapeScript=e.c,exports.readLoadShapeResult=t.Hn,exports.readSaveShapeResult=t.Un,exports.samples=e.t,exports.sceneInfoOf=t.i,exports.sceneToUsdz=e.i,exports.shapeArtifactPath=e.m,exports.shapeScriptToUsdz=e.a,exports.toArtifactsRelative=e.g,exports.usdzArtifactPath=e._;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./samples-C8zn1JOi.cjs"),t=require("./toThreeJS-BBwttw4K.cjs"),n=require("./core-DfEbc4Me.cjs");exports.EXPORT_USDZ_DESCRIPTION=n.p,exports.EXPORT_USDZ_PROMPT=n.m,exports.EXPORT_USDZ_SCHEMA=n.h,exports.EXPORT_USDZ_TOOL_NAME=n.g,exports.EXPORT_USDZ_TOOL_TIMEOUT_MS=n._,exports.NOT_CONNECTED_MESSAGE=n.t,exports.PUBLISH_DESCRIPTION=n.n,exports.PUBLISH_PROMPT=n.r,exports.PUBLISH_SCHEMA=n.i,exports.PUBLISH_TOOL_NAME=n.a,exports.SHAPE_EXTENSIONS=e.d,exports.SHAPE_GALLERY_URL=n.o,exports.SHAPE_POST_KEYS=n.s,exports.SHAPE_POST_LIMITS=n.c,exports.TOOL_DEFINITION=e.v,exports.TOOL_NAME=e.y,exports.USDZ_EXTENSION=e.n,exports.USDZ_MIME_TYPE=e.r,exports.astToThreeJS=t.r,exports.executeExportShapeScriptUsdz=n.v,exports.executePresentShapeScript=e.o,exports.executePublishShapeScript=n.l,exports.executeShapeScriptDispatch=e.l,exports.isPresentableShapePath=e.f,exports.isShapeArtifactPath=e.p,exports.isShapeScriptDispatchArgs=t.Vn,exports.locateShape=e.u,exports.normalizeShapeKeywords=n.u,exports.parseShapeScript=t.s,exports.pluginCore=e.s,exports.presentShapeScript=e.c,exports.readLoadShapeResult=t.Hn,exports.readSaveShapeResult=t.Un,exports.resolveShapeSource=n.y,exports.samples=e.t,exports.sceneInfoOf=t.i,exports.sceneToUsdz=e.i,exports.shapeArtifactPath=e.m,exports.shapePostFrom=n.d,exports.shapePostUrl=n.f,exports.shapeScriptToUsdz=e.a,exports.toArtifactsRelative=e.g,exports.usdzArtifactPath=e._;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as e, a as t, c as n, d as r, f as i, g as a, i as o, l as s, m as c, n as l, o as u, p as d, r as f, s as p, t as m, u as h, v as g, y as _ } from "./samples-BwyNyHha.js";
2
- import { Hn as v, Un as y, Vn as b, i as x, r as S, s as C } from "./toThreeJS-DyLC4g1t.js";
3
- import { a as w, i as T, n as E, o as D, r as O, t as k } from "./core-CiJHiO3H.js";
4
- export { k as EXPORT_USDZ_DESCRIPTION, E as EXPORT_USDZ_PROMPT, O as EXPORT_USDZ_SCHEMA, T as EXPORT_USDZ_TOOL_NAME, w as EXPORT_USDZ_TOOL_TIMEOUT_MS, r as SHAPE_EXTENSIONS, g as TOOL_DEFINITION, _ as TOOL_NAME, l as USDZ_EXTENSION, f as USDZ_MIME_TYPE, S as astToThreeJS, D as executeExportShapeScriptUsdz, u as executePresentShapeScript, s as executeShapeScriptDispatch, i as isPresentableShapePath, d as isShapeArtifactPath, b as isShapeScriptDispatchArgs, h as locateShape, C as parseShapeScript, p as pluginCore, n as presentShapeScript, v as readLoadShapeResult, y as readSaveShapeResult, m as samples, x as sceneInfoOf, o as sceneToUsdz, c as shapeArtifactPath, t as shapeScriptToUsdz, a as toArtifactsRelative, e as usdzArtifactPath };
1
+ import { _ as e, a as t, c as n, d as r, f as i, g as a, i as o, l as s, m as c, n as l, o as u, p as d, r as f, s as p, t as m, u as h, v as g, y as _ } from "./samples-BZSbBJmx.js";
2
+ import { Hn as v, Un as y, Vn as b, i as x, r as S, s as C } from "./toThreeJS-Bo3j0vOj.js";
3
+ import { _ as w, a as T, c as E, d as D, f as O, g as k, h as A, i as j, l as M, m as N, n as P, o as F, p as I, r as L, s as R, t as z, u as B, v as V, y as H } from "./core-BW88rCqf.js";
4
+ export { I as EXPORT_USDZ_DESCRIPTION, N as EXPORT_USDZ_PROMPT, A as EXPORT_USDZ_SCHEMA, k as EXPORT_USDZ_TOOL_NAME, w as EXPORT_USDZ_TOOL_TIMEOUT_MS, z as NOT_CONNECTED_MESSAGE, P as PUBLISH_DESCRIPTION, L as PUBLISH_PROMPT, j as PUBLISH_SCHEMA, T as PUBLISH_TOOL_NAME, r as SHAPE_EXTENSIONS, F as SHAPE_GALLERY_URL, R as SHAPE_POST_KEYS, E as SHAPE_POST_LIMITS, g as TOOL_DEFINITION, _ as TOOL_NAME, l as USDZ_EXTENSION, f as USDZ_MIME_TYPE, S as astToThreeJS, V as executeExportShapeScriptUsdz, u as executePresentShapeScript, M as executePublishShapeScript, s as executeShapeScriptDispatch, i as isPresentableShapePath, d as isShapeArtifactPath, b as isShapeScriptDispatchArgs, h as locateShape, B as normalizeShapeKeywords, C as parseShapeScript, p as pluginCore, n as presentShapeScript, v as readLoadShapeResult, y as readSaveShapeResult, H as resolveShapeSource, m as samples, x as sceneInfoOf, o as sceneToUsdz, c as shapeArtifactPath, D as shapePostFrom, O as shapePostUrl, t as shapeScriptToUsdz, a as toArtifactsRelative, e as usdzArtifactPath };
@@ -4,4 +4,5 @@ export { buildRenderPage, gridFor } from "./page";
4
4
  export type { ViewAngle, RenderPageOptions } from "./page";
5
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
6
  export type { RenderToolDeps, RenderToolResult } from "./tool";
7
+ export { renderShapeThumbnail } from "./thumbnail";
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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"}
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;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** Render `script` to a PNG, or null where this host has no browser. A script
2
+ * that will not build throws, as the renderer does — the publish tool has
3
+ * already built it once by then, so that is a renderer fault, which the tool
4
+ * reports as a warning and posts without a picture. */
5
+ export declare function renderShapeThumbnail(script: string, onWarning?: (message: string) => void): Promise<Uint8Array | null>;
6
+ //# sourceMappingURL=thumbnail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thumbnail.d.ts","sourceRoot":"","sources":["../../src/render/thumbnail.ts"],"names":[],"mappings":"AAYA;;;wDAGwD;AACxD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAgB5H"}
package/dist/render.cjs CHANGED
@@ -1,4 +1,4 @@
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-BETrskvw.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, Color, DirectionalLight, GridHelper, MathUtils, ObjectLoader, OrthographicCamera, PerspectiveCamera, Scene, Sphere, Vector3, WebGLRenderer } from ${JSON.stringify(e)};
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-BBwttw4K.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 ee(e,t,n){return`import { AmbientLight, Box3, Color, DirectionalLight, GridHelper, MathUtils, ObjectLoader, OrthographicCamera, PerspectiveCamera, Scene, Sphere, Vector3, WebGLRenderer } from ${JSON.stringify(e)};
2
2
 
3
3
  const config = ${t};
4
4
  const { columns, rows } = ${n};
@@ -40,7 +40,7 @@ scene.add(headlight, fill, new AmbientLight(0xffffff, 0.9));
40
40
  const floor = new GridHelper(radius * 4, 12, 0xc0c0c0, 0xe4e4e4);
41
41
  floor.position.y = bounds.min.y;
42
42
  scene.add(floor);
43
- `}function h(){return`
43
+ `}function m(){return`
44
44
  const aspect = config.width / config.height;
45
45
  const distance = (radius * 3.2) / Math.max(config.zoom, 0.01);
46
46
 
@@ -77,10 +77,10 @@ config.views.forEach((angle, index) => {
77
77
  });
78
78
 
79
79
  renderer.dispose();
80
- 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>
80
+ window.__shapeSheet = sheet.toDataURL("image/png");`}function h(e){let{threeUrl:t,sceneJson:n,views:r,width:i,height:a,zoom:o,projection:s}=e;return`<!doctype html>
81
81
  <meta charset="utf-8">
82
82
  <style>html,body{margin:0;background:#ffffff}</style>
83
83
  <canvas id="sheet"></canvas>
84
84
  <script type="module">
85
- ${`${m(t,JSON.stringify({views:r,width:i,height:a,zoom:o,projection:s,scene:n}),JSON.stringify(p(r.length)))}\n${h()}`}
86
- <\/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.Bn(e)&&typeof e.launch==`function`}async function ee(){try{let e=await import(`puppeteer`);return D(e)?e:c.Bn(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.r(c.s(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;
85
+ ${`${ee(t,JSON.stringify({views:r,width:i,height:a,zoom:o,projection:s,scene:n}),JSON.stringify(p(r.length)))}\n${m()}`}
86
+ <\/script>`}var g=1e3;function _(e){return e instanceof Error?e.message:String(e)}var v=60*g,y=30*g,b=9e4,x="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.",S=class extends Error{};function C(e,t){try{e?.(t)}catch{}}function w(){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 T=(0,u.createRequire)(w());function E(e){return c.Bn(e)&&typeof e.launch==`function`}async function D(){try{let e=await import(`puppeteer`);return E(e)?e:c.Bn(e)&&E(e.default)?e.default:null}catch{return null}}function O(){try{return(0,u.createRequire)(T.resolve(`@mulmoclaude/shapescript-plugin/package.json`))}catch{return T}}function te(){let e=O().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(te());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 D();if(!t)throw new S(x);let n=c.r(c.s(e.script)).toJSON(),r=h({...e,threeUrl:j,sceneJson:n}),i;try{i=await t.launch({headless:!0,timeout:y,args:[`--enable-unsafe-swiftshader`,`--use-gl=swiftshader`]})}catch(e){throw new S(`${x} (launch failed: ${_(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:v})}catch(e){let n=t.length>0?`the render page failed: ${t.join(`; `)}`:_(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=>C(e.onWarning,`chromium close failed: ${_(t)}`))}}var F=30,I=25,ne=1,L=480,R=900,z=160,B=85,V=b+3e4,H=`renderShapeScript`,U="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.",W=`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.`,G={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 ${L}, clamped to ${z}–${R}.`},height:{type:`number`,description:`Pixel height of ONE view. Default ${L}, clamped to ${z}–${R}.`}},required:[]};function K(e,t){return typeof e!=`number`||!Number.isFinite(e)?t:Math.min(R,Math.max(z,Math.round(e)))}function q(e,t){return typeof e==`number`&&Number.isFinite(e)?e:t}var J=e=>`${Math.round(e)}°`;function Y(e,t,n){let r=(e,t,n)=>({azimuth:e,elevation:t,label:`${n} — az ${J(e)}, el ${J(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,B,`top`)]}async function X(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 Z(e,t,n,r){return{script:t,views:n,width:K(e.width,L),height:K(e.height,L),zoom:Math.max(q(e.zoom,ne),.01),projection:e.projection===`orthographic`?`orthographic`:`perspective`,...r?{onWarning:r}:{}}}function Q(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 re(e,t){let n=await X(e,t),r=Y(q(t.azimuth,F),q(t.elevation,I),t.views===`single`);try{let i=await P(Z(t,n,r,e.onWarning));return{message:Q(await e.saveImage(i),r),rendered:!0}}catch(t){if(t instanceof S)return C(e.onWarning,`renderShapeScript: unavailable — ${t.message}`),{message:t.message,rendered:!1};throw t}}var $=640,ie=480,ae=30,oe=25;async function se(e,t){try{let n=await P({script:e,views:[{azimuth:ae,elevation:oe,label:``}],width:$,height:ie,zoom:1,projection:`perspective`,...t?{onWarning:t}:{}});return new Uint8Array(Buffer.from(n,`base64`))}catch(e){if(e instanceof S)return null;throw e}}exports.CHROMIUM_HINT=x,exports.LAUNCH_TIMEOUT_MS=y,exports.RENDER_BUDGET_MS=b,exports.RENDER_SHAPE_SCRIPT_DESCRIPTION=U,exports.RENDER_SHAPE_SCRIPT_PROMPT=W,exports.RENDER_SHAPE_SCRIPT_SCHEMA=G,exports.RENDER_SHAPE_SCRIPT_TOOL_NAME=H,exports.RENDER_TIMEOUT_MS=v,exports.RENDER_TOOL_TIMEOUT_MS=V,exports.RenderUnavailableError=S,exports.buildRenderPage=h,exports.executeRenderShapeScript=re,exports.gridFor=p,exports.renderOptionsFrom=Z,exports.renderShapeScriptSheet=P,exports.renderShapeThumbnail=se,exports.savedMessage=Q,exports.viewAngles=Y;
package/dist/render.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Bn as e, r as t, s as n } from "./toThreeJS-DyLC4g1t.js";
1
+ import { Bn as e, r as t, s as n } from "./toThreeJS-Bo3j0vOj.js";
2
2
  import { readFile as r } from "node:fs/promises";
3
3
  import { createRequire as i } from "node:module";
4
4
  import { pathToFileURL as a } from "node:url";
@@ -292,4 +292,28 @@ async function X(e, t) {
292
292
  }
293
293
  }
294
294
  //#endregion
295
- export { g as CHROMIUM_HINT, m as LAUNCH_TIMEOUT_MS, h as RENDER_BUDGET_MS, B as RENDER_SHAPE_SCRIPT_DESCRIPTION, V as RENDER_SHAPE_SCRIPT_PROMPT, H as RENDER_SHAPE_SCRIPT_SCHEMA, z as RENDER_SHAPE_SCRIPT_TOOL_NAME, p as RENDER_TIMEOUT_MS, R as RENDER_TOOL_TIMEOUT_MS, _ as RenderUnavailableError, u as buildRenderPage, X as executeRenderShapeScript, s as gridFor, J as renderOptionsFrom, A as renderShapeScriptSheet, Y as savedMessage, K as viewAngles };
295
+ //#region src/render/thumbnail.ts
296
+ var Z = 640, Q = 480, $ = 30, ee = 25;
297
+ async function te(e, t) {
298
+ try {
299
+ let n = await A({
300
+ script: e,
301
+ views: [{
302
+ azimuth: $,
303
+ elevation: ee,
304
+ label: ""
305
+ }],
306
+ width: Z,
307
+ height: Q,
308
+ zoom: 1,
309
+ projection: "perspective",
310
+ ...t ? { onWarning: t } : {}
311
+ });
312
+ return new Uint8Array(Buffer.from(n, "base64"));
313
+ } catch (e) {
314
+ if (e instanceof _) return null;
315
+ throw e;
316
+ }
317
+ }
318
+ //#endregion
319
+ export { g as CHROMIUM_HINT, m as LAUNCH_TIMEOUT_MS, h as RENDER_BUDGET_MS, B as RENDER_SHAPE_SCRIPT_DESCRIPTION, V as RENDER_SHAPE_SCRIPT_PROMPT, H as RENDER_SHAPE_SCRIPT_SCHEMA, z as RENDER_SHAPE_SCRIPT_TOOL_NAME, p as RENDER_TIMEOUT_MS, R as RENDER_TOOL_TIMEOUT_MS, _ as RenderUnavailableError, u as buildRenderPage, X as executeRenderShapeScript, s as gridFor, J as renderOptionsFrom, A as renderShapeScriptSheet, te as renderShapeThumbnail, Y as savedMessage, K as viewAngles };
@@ -1,4 +1,4 @@
1
- import { Bn as e, I as t, Y as n, a as r, g as i, i as a, j as o, n as s, p as c, r as l, s as u, st as d, zn as f } from "./toThreeJS-DyLC4g1t.js";
1
+ import { Bn as e, I as t, Y as n, a as r, g as i, i as a, j as o, n as s, p as c, r as l, s as u, st as d, zn as f } from "./toThreeJS-Bo3j0vOj.js";
2
2
  import { ARTIFACTS_ROOT as p, buildArtifactRelPath as m, classifyFilePath as h, hasUnsafePathSegment as g, slugifyArtifact as _, toWorkspaceArtifactPath as v } from "@mulmoclaude/core/artifacts";
3
3
  //#region src/core/definition.ts
4
4
  var y = "presentShapeScript", b = {