@drawcall/design 0.8.1 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -12
- package/dist/browser.d.ts +2 -1
- package/dist/browser.js +1 -0
- package/dist/cli-client.js +6 -2
- package/dist/command/context.d.ts +750 -0
- package/dist/command/context.js +17 -0
- package/dist/command/filesystem.d.ts +2 -0
- package/dist/command/filesystem.js +80 -0
- package/dist/command/frame.d.ts +2 -0
- package/dist/command/frame.js +127 -0
- package/dist/command/image.d.ts +2 -0
- package/dist/command/image.js +69 -0
- package/dist/command/project.d.ts +2 -0
- package/dist/command/project.js +39 -0
- package/dist/command.js +6 -248
- package/dist/frame-image.d.ts +1 -0
- package/dist/frame-image.js +6 -0
- package/dist/image.d.ts +2 -1
- package/dist/image.js +2 -4
- package/dist/mcp/frame.js +17 -14
- package/dist/mcp/skill.js +1 -1
- package/dist/project-state.d.ts +33 -69
- package/dist/skill.generated.d.ts +2 -2
- package/dist/skill.generated.js +2 -2
- package/dist/v1/contract.d.ts +92 -151
- package/dist/v1/contract.js +3 -3
- package/dist/v1/schemas.d.ts +102 -88
- package/dist/v1/schemas.js +88 -97
- package/package.json +1 -1
- package/skills/drawcall-design/SKILL.md +17 -9
package/dist/v1/schemas.js
CHANGED
|
@@ -58,6 +58,10 @@ export const httpUrlSchema = z
|
|
|
58
58
|
.url()
|
|
59
59
|
.refine(isHttpUrl, "URLs must use HTTP or HTTPS");
|
|
60
60
|
export const imageUrlSchema = httpUrlSchema;
|
|
61
|
+
export const imageInputSchema = z.union([
|
|
62
|
+
z.file().mime(["image/png", "image/jpeg", "image/webp"]),
|
|
63
|
+
imageUrlSchema,
|
|
64
|
+
]);
|
|
61
65
|
/** An absolute path in the hosted project filesystem. */
|
|
62
66
|
export const projectPathSchema = z.string().max(1_024).refine(isProjectPath, {
|
|
63
67
|
message: "Project paths must be absolute and cannot contain empty, . or .. segments",
|
|
@@ -122,33 +126,45 @@ export const gltsFrameSchema = frameBaseSchema.extend({
|
|
|
122
126
|
});
|
|
123
127
|
export const imageGenerationOperationSchema = z.enum(["generate", "edit"]);
|
|
124
128
|
export const imageGenerationResultSchema = z.enum(["new", "replace"]);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
const imageGenerationDigestSchema = z
|
|
130
|
+
.string()
|
|
131
|
+
.regex(/^[a-f0-9]{64}$/, "Image generation digests must be SHA-256 hex");
|
|
132
|
+
export const imageInputProvenanceSchema = z.discriminatedUnion("kind", [
|
|
133
|
+
z
|
|
134
|
+
.object({
|
|
135
|
+
kind: z.literal("url"),
|
|
136
|
+
url: imageUrlSchema,
|
|
137
|
+
digest: imageGenerationDigestSchema,
|
|
138
|
+
})
|
|
139
|
+
.strict(),
|
|
140
|
+
z
|
|
141
|
+
.object({
|
|
142
|
+
kind: z.literal("file"),
|
|
143
|
+
name: z
|
|
144
|
+
.string()
|
|
145
|
+
.trim()
|
|
146
|
+
.min(1)
|
|
147
|
+
.max(255)
|
|
148
|
+
.refine(isFileName, "Image filenames cannot contain path separators"),
|
|
149
|
+
digest: imageGenerationDigestSchema,
|
|
150
|
+
})
|
|
151
|
+
.strict(),
|
|
152
|
+
]);
|
|
129
153
|
export const IMAGE_GENERATION_PROVIDER = "fal-ai";
|
|
130
154
|
export const IMAGE_GENERATION_MODEL = "openai/gpt-image-2";
|
|
131
|
-
export const LEGACY_IMAGE_GENERATION_PROVIDER = "cloudflare-workers-ai";
|
|
132
|
-
export const LEGACY_IMAGE_GENERATION_MODEL = "@cf/black-forest-labs/flux-2-klein-4b";
|
|
133
155
|
const imageGenerationProvenanceBaseSchema = z.object({
|
|
134
156
|
kind: z.literal("generated"),
|
|
135
157
|
operation: imageGenerationOperationSchema,
|
|
136
158
|
prompt: z.string().min(1).max(MAX_IMAGE_GENERATION_PROMPT_LENGTH),
|
|
137
159
|
references: z
|
|
138
|
-
.array(
|
|
160
|
+
.array(imageInputProvenanceSchema)
|
|
139
161
|
.max(MAX_IMAGE_GENERATION_REFERENCES),
|
|
140
162
|
result: imageGenerationResultSchema,
|
|
141
163
|
});
|
|
142
|
-
export const imageGenerationProvenanceSchema =
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}),
|
|
147
|
-
imageGenerationProvenanceBaseSchema.extend({
|
|
148
|
-
provider: z.literal(LEGACY_IMAGE_GENERATION_PROVIDER),
|
|
149
|
-
model: z.literal(LEGACY_IMAGE_GENERATION_MODEL),
|
|
150
|
-
}),
|
|
151
|
-
]);
|
|
164
|
+
export const imageGenerationProvenanceSchema = imageGenerationProvenanceBaseSchema.extend({
|
|
165
|
+
provider: z.literal(IMAGE_GENERATION_PROVIDER),
|
|
166
|
+
model: z.literal(IMAGE_GENERATION_MODEL),
|
|
167
|
+
});
|
|
152
168
|
export const imageFrameSchema = frameBaseSchema.extend({
|
|
153
169
|
type: z.literal("image"),
|
|
154
170
|
file: projectFilePathSchema,
|
|
@@ -179,10 +195,7 @@ export const createGltsFrameSchema = createFrameBaseSchema
|
|
|
179
195
|
export const createImageFrameSchema = createFrameBaseSchema
|
|
180
196
|
.extend({
|
|
181
197
|
type: z.literal("image"),
|
|
182
|
-
image:
|
|
183
|
-
z.file().mime(["image/png", "image/jpeg", "image/webp"]),
|
|
184
|
-
imageUrlSchema,
|
|
185
|
-
]),
|
|
198
|
+
image: imageInputSchema,
|
|
186
199
|
})
|
|
187
200
|
.strict();
|
|
188
201
|
export const createMarketFrameSchema = createFrameBaseSchema
|
|
@@ -196,85 +209,56 @@ export const createFrameSchema = z.discriminatedUnion("type", [
|
|
|
196
209
|
createImageFrameSchema,
|
|
197
210
|
createMarketFrameSchema,
|
|
198
211
|
]);
|
|
199
|
-
|
|
200
|
-
.
|
|
212
|
+
const imageGenerationPromptSchema = z
|
|
213
|
+
.string()
|
|
214
|
+
.trim()
|
|
215
|
+
.min(1)
|
|
216
|
+
.max(MAX_IMAGE_GENERATION_PROMPT_LENGTH);
|
|
217
|
+
const imageGenerationBase = {
|
|
201
218
|
project: designIdSchema,
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
.max(MAX_IMAGE_GENERATION_REFERENCES)
|
|
208
|
-
.default([]),
|
|
209
|
-
result: imageGenerationResultSchema,
|
|
210
|
-
name: frameNameSchema.optional(),
|
|
219
|
+
prompt: imageGenerationPromptSchema,
|
|
220
|
+
};
|
|
221
|
+
const newGeneratedFrame = {
|
|
222
|
+
result: z.literal("new"),
|
|
223
|
+
name: frameNameSchema,
|
|
211
224
|
x: framePositionSchema.optional(),
|
|
212
225
|
y: framePositionSchema.optional(),
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
context.addIssue({
|
|
250
|
-
code: "custom",
|
|
251
|
-
path: ["result"],
|
|
252
|
-
message: "Only an image edit can replace a frame",
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
if (input.result === "new" && !input.name) {
|
|
256
|
-
context.addIssue({
|
|
257
|
-
code: "custom",
|
|
258
|
-
path: ["name"],
|
|
259
|
-
message: "A new generated frame requires a name",
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
if (input.result === "replace" && input.name) {
|
|
263
|
-
context.addIssue({
|
|
264
|
-
code: "custom",
|
|
265
|
-
path: ["name"],
|
|
266
|
-
message: "A replacement keeps the target frame name",
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
if (input.result === "replace" &&
|
|
270
|
-
(input.x !== undefined || input.y !== undefined)) {
|
|
271
|
-
context.addIssue({
|
|
272
|
-
code: "custom",
|
|
273
|
-
path: [input.x !== undefined ? "x" : "y"],
|
|
274
|
-
message: "A replacement keeps the target frame position",
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
});
|
|
226
|
+
};
|
|
227
|
+
export const generateImageSchema = imageGenerationSchema(imageInputSchema);
|
|
228
|
+
export const generateImageUrlSchema = imageGenerationSchema(imageUrlSchema);
|
|
229
|
+
function imageGenerationSchema(input) {
|
|
230
|
+
return z.union([
|
|
231
|
+
z
|
|
232
|
+
.object({
|
|
233
|
+
...imageGenerationBase,
|
|
234
|
+
operation: z.literal("generate"),
|
|
235
|
+
references: z.array(input).max(4).default([]),
|
|
236
|
+
...newGeneratedFrame,
|
|
237
|
+
})
|
|
238
|
+
.strict(),
|
|
239
|
+
z
|
|
240
|
+
.object({
|
|
241
|
+
...imageGenerationBase,
|
|
242
|
+
operation: z.literal("edit"),
|
|
243
|
+
target: designIdSchema,
|
|
244
|
+
references: z.array(input).max(3).default([]),
|
|
245
|
+
...newGeneratedFrame,
|
|
246
|
+
})
|
|
247
|
+
.strict(),
|
|
248
|
+
z
|
|
249
|
+
.object({
|
|
250
|
+
...imageGenerationBase,
|
|
251
|
+
operation: z.literal("edit"),
|
|
252
|
+
target: designIdSchema,
|
|
253
|
+
references: z.array(input).max(3).default([]),
|
|
254
|
+
result: z.literal("replace"),
|
|
255
|
+
name: z.never().optional(),
|
|
256
|
+
x: z.never().optional(),
|
|
257
|
+
y: z.never().optional(),
|
|
258
|
+
})
|
|
259
|
+
.strict(),
|
|
260
|
+
]);
|
|
261
|
+
}
|
|
278
262
|
export const fileListSchema = z.object({
|
|
279
263
|
paths: z.array(projectFilePathSchema),
|
|
280
264
|
});
|
|
@@ -387,3 +371,10 @@ function isNormalizedQuaternion(value) {
|
|
|
387
371
|
value.w * value.w;
|
|
388
372
|
return Math.abs(squaredLength - 1) <= 1e-6;
|
|
389
373
|
}
|
|
374
|
+
function isFileName(value) {
|
|
375
|
+
return (value !== "." &&
|
|
376
|
+
value !== ".." &&
|
|
377
|
+
!value.includes("/") &&
|
|
378
|
+
!value.includes("\\") &&
|
|
379
|
+
!value.includes("\0"));
|
|
380
|
+
}
|
package/package.json
CHANGED
|
@@ -7,16 +7,20 @@ description: Create, modify, inspect, compose, and reuse 3D GLTS assets and opti
|
|
|
7
7
|
|
|
8
8
|
## CLI transport
|
|
9
9
|
|
|
10
|
-
The
|
|
10
|
+
The commands below are the Drawcall transport in this environment. Use only this documented command interface. A failed command does not make the transport unavailable.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Image frame creation and image-generation references accept public HTTP(S) URLs or local PNG, JPEG, and WebP files.
|
|
13
|
+
|
|
14
|
+
Select the project with `-p <project-id>`. File commands take project-absolute paths that include the frame ID. Repeat `--reference` to preserve image-generation reference order.
|
|
13
15
|
|
|
14
16
|
```sh
|
|
15
|
-
npx
|
|
16
|
-
npx
|
|
17
|
-
npx
|
|
18
|
-
npx
|
|
19
|
-
npx
|
|
17
|
+
npx drawcall design project list
|
|
18
|
+
npx drawcall design -p r6z2n9k4x8m1qc frame list
|
|
19
|
+
npx drawcall design -p r6z2n9k4x8m1qc ls
|
|
20
|
+
npx drawcall design -p r6z2n9k4x8m1qc read /a4z8m2q7v9kcde/index.glts
|
|
21
|
+
npx drawcall design -p r6z2n9k4x8m1qc edit /a4z8m2q7v9kcde/index.glts 'color: 0xffffff' 'color: 0x000000'
|
|
22
|
+
npx drawcall design -p r6z2n9k4x8m1qc frame generate-image 'Product photograph' --prompt 'A product photograph of this object' --reference https://r6z2n9k4x8m1qc.design.drawcallcontent.com/a4z8m2q7v9kcde.webp
|
|
23
|
+
npx drawcall design -p r6z2n9k4x8m1qc frame edit-image b4z8m2q7v9kcdf --prompt 'Use warmer light' --name 'Warm product photograph' --reference ./lighting.webp
|
|
20
24
|
```
|
|
21
25
|
|
|
22
26
|
Design is a remote, current-state canvas. Inspect the project and its frames before changing them. Use immutable IDs for every project and frame target; names are only labels.
|
|
@@ -25,10 +29,14 @@ Design is a remote, current-state canvas. Inspect the project and its frames bef
|
|
|
25
29
|
|
|
26
30
|
A project is a hosted filesystem at `https://<project-id>.design.drawcallcontent.com/`. Each frame owns one top-level directory, `/<frame-id>`. Files use project-absolute paths that include that directory, for example `/a4z8m2q7v9kcde/index.glts`.
|
|
27
31
|
|
|
28
|
-
Create frames with an explicit type. Choose the type from the requested artifact, not from the word "frame": use GLTS for a 3D object or scene, especially one another frame will reuse. Use image
|
|
32
|
+
Create frames with an explicit type. Choose the type from the requested artifact, not from the word "frame": use GLTS for a 3D object or scene, especially one another frame will reuse. Use image for an existing 2D image and Market only for an exact public asset reference, `name@version`. GLTS frames require a viewport size; image and Market frames derive their canvas size.
|
|
29
33
|
|
|
30
34
|
Read a file before editing it. Use a narrow edit for one known change and write a complete file when replacing it. Only GLTS frame files may be created or deleted. Image and Market frame files are read-only.
|
|
31
35
|
|
|
36
|
+
## Frame images
|
|
37
|
+
|
|
38
|
+
Every frame has a public image at `https://<project-id>.design.drawcallcontent.com/<frame-id>.webp`. This URL represents the current rendered frame whether its type is GLTS, image, or Market. Pass it with `--reference` when one frame's appearance should inform another image. A reference may instead be a local PNG, JPEG, or WebP file.
|
|
39
|
+
|
|
32
40
|
## Failures
|
|
33
41
|
|
|
34
42
|
An error means the requested operation did not happen. Follow its next action without switching transport. Correct invalid arguments from the documented shape. Refresh projects, frames, or files after a not-found or conflict error, then reuse the exact returned IDs and paths. Retry an upstream or internal failure once; if it repeats, report the failed operation and error. Never repeat an unchanged failed operation.
|
|
@@ -63,7 +71,7 @@ For a non-GLTS file from an image or Market frame, preserve the project filesyst
|
|
|
63
71
|
const modelUrl = new URL("/market-frame-id/models/car.glb", import.meta.url);
|
|
64
72
|
```
|
|
65
73
|
|
|
66
|
-
GLTS supports static `.glts`, `three`, Three addons, and bare npm imports. It does not support
|
|
74
|
+
GLTS supports static `.glts`, `three`, Three addons, and bare npm imports. It does not support helper `.ts` modules, dynamic imports, cyclic GLTS graphs, or cross-asset inheritance. Keep the asset self-contained and compose with nested GLTS assets.
|
|
67
75
|
|
|
68
76
|
The viewer uses the first camera found by depth-first traversal. If none exists, it autofits the asset. Put an authored camera in the scene only when its framing is intentional. Double-clicking a frame enters orbit from that resolved view; deselecting restores it.
|
|
69
77
|
|