@drawcall/design 0.12.3 → 0.12.5

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
@@ -10,7 +10,7 @@ const projects = await design.project.list();
10
10
  ```
11
11
 
12
12
  Projects and frames have immutable 14-character lowercase IDs. A project is a hosted filesystem at `https://<project-id>.design.drawcallcontent.com/`; every frame owns `/<frame-id>/` within it.
13
- The current rendered image for any GLTS, Markdown, image, or Market frame is available at
13
+ Every GLTS, Markdown, image, or Market frame includes a canonical `previewUrl` at
14
14
  `https://<project-id>.design.drawcallcontent.com/<frame-id>.webp`.
15
15
 
16
16
  ```sh
@@ -19,13 +19,17 @@ npx drawcall design project create showroom
19
19
  # `project list` prints: <project-id> <name>
20
20
 
21
21
  npx drawcall design -p r6z2n9k4x8m1qc frame create racecar \
22
- --type glts --size 800x800
23
- # `frame create` prints its frame ID and /<frame-id>/ path.
24
-
25
- npx drawcall design -p r6z2n9k4x8m1qc write /a4z8m2q7v9kcde/index.glts \
26
- 'export default class Racecar extends THREE.Group {}'
22
+ --type glts --size 800x800 \
23
+ --import ./parts/wheel.glts=-0.5,0,-0.5:0.5,1,0.5 \
24
+ --text 'import * as THREE from "three"; import Wheel from "./parts/wheel.glts"; export default class Racecar extends THREE.Group { constructor() { super(); this.add(new Wheel()); } }'
25
+ # `frame create` prints its frame ID and /<frame-id>/ path. `--text -` reads stdin.
26
+ # Each `--import` box becomes a placeholder file until the real part is written.
27
+
28
+ npx drawcall design -p r6z2n9k4x8m1qc write /a4z8m2q7v9kcde/parts/wheel.glts \
29
+ 'import * as THREE from "three"; export default class Wheel extends THREE.Group {}'
27
30
  npx drawcall design -p r6z2n9k4x8m1qc ls /
28
31
  npx drawcall design -p r6z2n9k4x8m1qc read /a4z8m2q7v9kcde/index.glts
32
+ npx drawcall design -p r6z2n9k4x8m1qc frame preview a4z8m2q7v9kcde
29
33
  npx drawcall design -p r6z2n9k4x8m1qc frame screenshot a4z8m2q7v9kcde
30
34
 
31
35
  npx drawcall design -p r6z2n9k4x8m1qc frame create release-notes \
package/dist/browser.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * as v1 from "./v1/index.js";
2
2
  export { frameImageUrl } from "./frame-image.js";
3
+ export { framePreviewUrl } from "./frame-preview.js";
3
4
  export { registerDesignTools, type DesignMcpOptions, type DesignMcpServer, type DesignMcpToolResult, } from "./mcp/index.js";
4
5
  export { cliDesignSkill, mcpDesignSkill } from "./skill.generated.js";
5
6
  export { frameCreationCapabilities, type FrameCreationCapability, type FrameCreationCapabilityId, type FrameCreationInput, } from "./v1/capabilities.js";
@@ -7,4 +8,4 @@ export { cameraPoseSchema, commentBodySchema, commentPositionInputSchema, commen
7
8
  export { parseFrameSize } from "./target.js";
8
9
  export { projectFrameLayoutSchema, projectCommentsChangedServerMessageSchema, projectFrameRecordSchema, projectGltsFrameRecordSchema, projectImageFrameRecordSchema, projectMarkdownFrameRecordSchema, projectMarketFileSchema, projectMarketFrameRecordSchema, projectPresenceClientMessageSchema, projectPresenceServerMessageSchema, projectStateSchema, type ProjectFrameLayout, type ProjectCommentsChangedServerMessage, type ProjectFrameRecord, type ProjectGltsFrameRecord, type ProjectImageFrameRecord, type ProjectMarkdownFrameRecord, type ProjectMarketFile, type ProjectMarketFrameRecord, type ProjectPresenceClientMessage, type ProjectPresenceServerMessage, type ProjectState, } from "./project-state.js";
9
10
  export { cameraCaptureCommandSchema, cameraCaptureResultSchema, cameraMessageTypes, cameraSetCommandSchema, commentActionMessageSchema, commentActionResultSchema, commentPickCommandSchema, commentPickResultSchema, commentRuntimeMessageTypes, commentSelectionMessageSchema, commentStateCommandSchema, frameNavigationMessageSchema, frameNavigationMessageTypes, frameNavigationV2MessageSchema, frameNavigationV2MessageTypes, frameInteractionMessageSchema, frameInteractionMessageTypes, framePointerMessageSchema, framePointerMessageTypes, frameZoomMessageSchema, frameZoomMessageTypes, frameRenderResultSchema, gltsExportCancelCommandSchema, gltsExportCancelledResultSchema, gltsExportCommandSchema, gltsExportFrameMessageSchema, gltsExportMessageTypes, gltsExportParentMessageSchema, gltsExportResultSchema, gltsRuntimeExportFormatSchema, immersiveCommandSchema, immersiveFrameMessageSchema, immersiveMessageTypes, immersiveModeSchema, inspectionCommandSchema, inspectionFrameMessageSchema, inspectionMessageTypes, posterMessageTypes, posterPagePath, posterPageUrl, posterRenderCommandSchema, posterRenderResultSchema, previewConfigureCommandSchema, previewLoadCommandSchema, previewLoadResultSchema, previewMessageTypes, previewPagePath, previewPageUrl, previewPixelRatioMax, previewSessionEventSchema, projectSyncUrl, sourceFileChangeSchema, sourceFileMessageTypes, sourceFileReloadResultSchema, sourceFilesChangedMessageSchema, type GltsExportCancelCommand, type GltsExportCancelledResult, type GltsExportCommand, type GltsExportFrameMessage, type GltsExportParentMessage, type GltsExportResult, type GltsRuntimeExportFormat, type CameraCaptureCommand, type CameraSetCommand, type CameraCaptureFailure, type CameraCaptureResult, type CommentActionMessage, type CommentActionResult, type CommentPickCommand, type CommentPickResult, type CommentSelectionMessage, type CommentStateCommand, type FrameRenderResult, type FramePointerMessage, type FrameZoomMessage, type InspectionAppliedMessage, type InspectionCommand, type InspectionFrameMessage, type PosterRenderCommand, type PosterRenderResult, type PreviewLoadCommand, type PreviewLoadResult, type ImmersiveCommand, type ImmersiveFrameMessage, type ImmersiveMode, type SourceFileChange, type SourceFileReloadResult, type SourceFilesChangedMessage, } from "./protocol.js";
10
- export type { BinaryFile, CameraPose, Comment, CommentAuthor, CommentPosition, CommentPositionInput, CommentReply, CommentResolution, CreateComment, CreateFrame, CreateProjectFromBrief, CreateGltsFrame, CreateImageFrame, CreateMarkdownFrame, CreateMarketFrame, GenerateImage, GenerateImageUrl, DesignFile, FileList, FileMutation, FrameExport, Frame, GltsFrame, GltsFrameExport, ImageFrame, ImageFrameExport, ImageGenerationOperation, ImageGenerationProvenance, ImageGenerationResult, ImageInput, ImageInputProvenance, MarketFrame, MarketFrameExport, MarkdownFrame, MarkdownFrameExport, Project, ProjectDerivationStatus, Screenshot, TextFile, User, } from "./v1/schemas.js";
11
+ export type { BinaryFile, BoundingBox, CameraPose, Imports, Comment, CommentAuthor, CommentPosition, CommentPositionInput, CommentReply, CommentResolution, CreateComment, CreateFrame, CreateProjectFromBrief, CreateGltsFrame, CreateImageFrame, CreateMarkdownFrame, CreateMarketFrame, GenerateImage, GenerateImageUrl, DesignFile, FileList, FileMutation, FrameExport, Frame, GltsFrame, GltsFrameExport, ImageFrame, ImageFrameExport, ImageGenerationOperation, ImageGenerationProvenance, ImageGenerationResult, ImageInput, ImageInputProvenance, MarketFrame, MarketFrameExport, MarkdownFrame, MarkdownFrameExport, Project, ProjectDerivationStatus, Screenshot, TextFile, User, } from "./v1/schemas.js";
package/dist/browser.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * as v1 from "./v1/index.js";
2
2
  export { frameImageUrl } from "./frame-image.js";
3
+ export { framePreviewUrl } from "./frame-preview.js";
3
4
  export { registerDesignTools, } from "./mcp/index.js";
4
5
  export { cliDesignSkill, mcpDesignSkill } from "./skill.generated.js";
5
6
  export { frameCreationCapabilities, } from "./v1/capabilities.js";
@@ -112,6 +112,7 @@ export declare function clientFor(command: Command): Promise<{
112
112
  width: import("zod").ZodNumber;
113
113
  height: import("zod").ZodNumber;
114
114
  viewUrl: import("zod").ZodString;
115
+ previewUrl: import("zod").ZodString;
115
116
  createdAt: import("zod").ZodString;
116
117
  updatedAt: import("zod").ZodString;
117
118
  type: import("zod").ZodLiteral<"glts">;
@@ -138,6 +139,7 @@ export declare function clientFor(command: Command): Promise<{
138
139
  width: import("zod").ZodNumber;
139
140
  height: import("zod").ZodNumber;
140
141
  viewUrl: import("zod").ZodString;
142
+ previewUrl: import("zod").ZodString;
141
143
  createdAt: import("zod").ZodString;
142
144
  updatedAt: import("zod").ZodString;
143
145
  type: import("zod").ZodLiteral<"markdown">;
@@ -151,6 +153,7 @@ export declare function clientFor(command: Command): Promise<{
151
153
  width: import("zod").ZodNumber;
152
154
  height: import("zod").ZodNumber;
153
155
  viewUrl: import("zod").ZodString;
156
+ previewUrl: import("zod").ZodString;
154
157
  contentRevision: import("zod").ZodOptional<import("zod").ZodNumber>;
155
158
  createdAt: import("zod").ZodString;
156
159
  updatedAt: import("zod").ZodString;
@@ -188,6 +191,7 @@ export declare function clientFor(command: Command): Promise<{
188
191
  width: import("zod").ZodNumber;
189
192
  height: import("zod").ZodNumber;
190
193
  viewUrl: import("zod").ZodString;
194
+ previewUrl: import("zod").ZodString;
191
195
  contentRevision: import("zod").ZodOptional<import("zod").ZodNumber>;
192
196
  createdAt: import("zod").ZodString;
193
197
  updatedAt: import("zod").ZodString;
@@ -202,6 +206,11 @@ export declare function clientFor(command: Command): Promise<{
202
206
  type: import("zod").ZodLiteral<"glts">;
203
207
  width: import("zod").ZodNumber;
204
208
  height: import("zod").ZodNumber;
209
+ text: import("zod").ZodOptional<import("zod").ZodString>;
210
+ imports: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{
211
+ min: import("zod").ZodTuple<[import("zod").ZodNumber, import("zod").ZodNumber, import("zod").ZodNumber], null>;
212
+ max: import("zod").ZodTuple<[import("zod").ZodNumber, import("zod").ZodNumber, import("zod").ZodNumber], null>;
213
+ }, import("zod/v4/core").$strict>>>;
205
214
  }, import("zod/v4/core").$strict>, import("zod").ZodObject<{
206
215
  project: import("zod").ZodString;
207
216
  name: import("zod").ZodString;
@@ -210,6 +219,7 @@ export declare function clientFor(command: Command): Promise<{
210
219
  type: import("zod").ZodLiteral<"markdown">;
211
220
  width: import("zod").ZodNumber;
212
221
  height: import("zod").ZodNumber;
222
+ text: import("zod").ZodOptional<import("zod").ZodString>;
213
223
  }, import("zod/v4/core").$strict>, import("zod").ZodObject<{
214
224
  project: import("zod").ZodString;
215
225
  name: import("zod").ZodString;
@@ -233,6 +243,7 @@ export declare function clientFor(command: Command): Promise<{
233
243
  width: import("zod").ZodNumber;
234
244
  height: import("zod").ZodNumber;
235
245
  viewUrl: import("zod").ZodString;
246
+ previewUrl: import("zod").ZodString;
236
247
  createdAt: import("zod").ZodString;
237
248
  updatedAt: import("zod").ZodString;
238
249
  type: import("zod").ZodLiteral<"glts">;
@@ -259,6 +270,7 @@ export declare function clientFor(command: Command): Promise<{
259
270
  width: import("zod").ZodNumber;
260
271
  height: import("zod").ZodNumber;
261
272
  viewUrl: import("zod").ZodString;
273
+ previewUrl: import("zod").ZodString;
262
274
  createdAt: import("zod").ZodString;
263
275
  updatedAt: import("zod").ZodString;
264
276
  type: import("zod").ZodLiteral<"markdown">;
@@ -272,6 +284,7 @@ export declare function clientFor(command: Command): Promise<{
272
284
  width: import("zod").ZodNumber;
273
285
  height: import("zod").ZodNumber;
274
286
  viewUrl: import("zod").ZodString;
287
+ previewUrl: import("zod").ZodString;
275
288
  contentRevision: import("zod").ZodOptional<import("zod").ZodNumber>;
276
289
  createdAt: import("zod").ZodString;
277
290
  updatedAt: import("zod").ZodString;
@@ -309,6 +322,7 @@ export declare function clientFor(command: Command): Promise<{
309
322
  width: import("zod").ZodNumber;
310
323
  height: import("zod").ZodNumber;
311
324
  viewUrl: import("zod").ZodString;
325
+ previewUrl: import("zod").ZodString;
312
326
  contentRevision: import("zod").ZodOptional<import("zod").ZodNumber>;
313
327
  createdAt: import("zod").ZodString;
314
328
  updatedAt: import("zod").ZodString;
@@ -353,6 +367,7 @@ export declare function clientFor(command: Command): Promise<{
353
367
  width: import("zod").ZodNumber;
354
368
  height: import("zod").ZodNumber;
355
369
  viewUrl: import("zod").ZodString;
370
+ previewUrl: import("zod").ZodString;
356
371
  createdAt: import("zod").ZodString;
357
372
  updatedAt: import("zod").ZodString;
358
373
  type: import("zod").ZodLiteral<"glts">;
@@ -379,6 +394,7 @@ export declare function clientFor(command: Command): Promise<{
379
394
  width: import("zod").ZodNumber;
380
395
  height: import("zod").ZodNumber;
381
396
  viewUrl: import("zod").ZodString;
397
+ previewUrl: import("zod").ZodString;
382
398
  createdAt: import("zod").ZodString;
383
399
  updatedAt: import("zod").ZodString;
384
400
  type: import("zod").ZodLiteral<"markdown">;
@@ -392,6 +408,7 @@ export declare function clientFor(command: Command): Promise<{
392
408
  width: import("zod").ZodNumber;
393
409
  height: import("zod").ZodNumber;
394
410
  viewUrl: import("zod").ZodString;
411
+ previewUrl: import("zod").ZodString;
395
412
  contentRevision: import("zod").ZodOptional<import("zod").ZodNumber>;
396
413
  createdAt: import("zod").ZodString;
397
414
  updatedAt: import("zod").ZodString;
@@ -429,6 +446,7 @@ export declare function clientFor(command: Command): Promise<{
429
446
  width: import("zod").ZodNumber;
430
447
  height: import("zod").ZodNumber;
431
448
  viewUrl: import("zod").ZodString;
449
+ previewUrl: import("zod").ZodString;
432
450
  contentRevision: import("zod").ZodOptional<import("zod").ZodNumber>;
433
451
  createdAt: import("zod").ZodString;
434
452
  updatedAt: import("zod").ZodString;
@@ -448,6 +466,7 @@ export declare function clientFor(command: Command): Promise<{
448
466
  width: import("zod").ZodNumber;
449
467
  height: import("zod").ZodNumber;
450
468
  viewUrl: import("zod").ZodString;
469
+ previewUrl: import("zod").ZodString;
451
470
  createdAt: import("zod").ZodString;
452
471
  updatedAt: import("zod").ZodString;
453
472
  type: import("zod").ZodLiteral<"glts">;
@@ -474,6 +493,7 @@ export declare function clientFor(command: Command): Promise<{
474
493
  width: import("zod").ZodNumber;
475
494
  height: import("zod").ZodNumber;
476
495
  viewUrl: import("zod").ZodString;
496
+ previewUrl: import("zod").ZodString;
477
497
  createdAt: import("zod").ZodString;
478
498
  updatedAt: import("zod").ZodString;
479
499
  type: import("zod").ZodLiteral<"markdown">;
@@ -487,6 +507,7 @@ export declare function clientFor(command: Command): Promise<{
487
507
  width: import("zod").ZodNumber;
488
508
  height: import("zod").ZodNumber;
489
509
  viewUrl: import("zod").ZodString;
510
+ previewUrl: import("zod").ZodString;
490
511
  contentRevision: import("zod").ZodOptional<import("zod").ZodNumber>;
491
512
  createdAt: import("zod").ZodString;
492
513
  updatedAt: import("zod").ZodString;
@@ -524,6 +545,7 @@ export declare function clientFor(command: Command): Promise<{
524
545
  width: import("zod").ZodNumber;
525
546
  height: import("zod").ZodNumber;
526
547
  viewUrl: import("zod").ZodString;
548
+ previewUrl: import("zod").ZodString;
527
549
  contentRevision: import("zod").ZodOptional<import("zod").ZodNumber>;
528
550
  createdAt: import("zod").ZodString;
529
551
  updatedAt: import("zod").ZodString;
@@ -557,6 +579,7 @@ export declare function clientFor(command: Command): Promise<{
557
579
  width: import("zod").ZodNumber;
558
580
  height: import("zod").ZodNumber;
559
581
  viewUrl: import("zod").ZodString;
582
+ previewUrl: import("zod").ZodString;
560
583
  createdAt: import("zod").ZodString;
561
584
  updatedAt: import("zod").ZodString;
562
585
  type: import("zod").ZodLiteral<"glts">;
@@ -583,6 +606,7 @@ export declare function clientFor(command: Command): Promise<{
583
606
  width: import("zod").ZodNumber;
584
607
  height: import("zod").ZodNumber;
585
608
  viewUrl: import("zod").ZodString;
609
+ previewUrl: import("zod").ZodString;
586
610
  createdAt: import("zod").ZodString;
587
611
  updatedAt: import("zod").ZodString;
588
612
  type: import("zod").ZodLiteral<"markdown">;
@@ -596,6 +620,7 @@ export declare function clientFor(command: Command): Promise<{
596
620
  width: import("zod").ZodNumber;
597
621
  height: import("zod").ZodNumber;
598
622
  viewUrl: import("zod").ZodString;
623
+ previewUrl: import("zod").ZodString;
599
624
  contentRevision: import("zod").ZodOptional<import("zod").ZodNumber>;
600
625
  createdAt: import("zod").ZodString;
601
626
  updatedAt: import("zod").ZodString;
@@ -633,6 +658,7 @@ export declare function clientFor(command: Command): Promise<{
633
658
  width: import("zod").ZodNumber;
634
659
  height: import("zod").ZodNumber;
635
660
  viewUrl: import("zod").ZodString;
661
+ previewUrl: import("zod").ZodString;
636
662
  contentRevision: import("zod").ZodOptional<import("zod").ZodNumber>;
637
663
  createdAt: import("zod").ZodString;
638
664
  updatedAt: import("zod").ZodString;
@@ -655,6 +681,7 @@ export declare function clientFor(command: Command): Promise<{
655
681
  width: import("zod").ZodNumber;
656
682
  height: import("zod").ZodNumber;
657
683
  viewUrl: import("zod").ZodString;
684
+ previewUrl: import("zod").ZodString;
658
685
  createdAt: import("zod").ZodString;
659
686
  updatedAt: import("zod").ZodString;
660
687
  type: import("zod").ZodLiteral<"glts">;
@@ -681,6 +708,7 @@ export declare function clientFor(command: Command): Promise<{
681
708
  width: import("zod").ZodNumber;
682
709
  height: import("zod").ZodNumber;
683
710
  viewUrl: import("zod").ZodString;
711
+ previewUrl: import("zod").ZodString;
684
712
  createdAt: import("zod").ZodString;
685
713
  updatedAt: import("zod").ZodString;
686
714
  type: import("zod").ZodLiteral<"markdown">;
@@ -694,6 +722,7 @@ export declare function clientFor(command: Command): Promise<{
694
722
  width: import("zod").ZodNumber;
695
723
  height: import("zod").ZodNumber;
696
724
  viewUrl: import("zod").ZodString;
725
+ previewUrl: import("zod").ZodString;
697
726
  contentRevision: import("zod").ZodOptional<import("zod").ZodNumber>;
698
727
  createdAt: import("zod").ZodString;
699
728
  updatedAt: import("zod").ZodString;
@@ -731,6 +760,7 @@ export declare function clientFor(command: Command): Promise<{
731
760
  width: import("zod").ZodNumber;
732
761
  height: import("zod").ZodNumber;
733
762
  viewUrl: import("zod").ZodString;
763
+ previewUrl: import("zod").ZodString;
734
764
  contentRevision: import("zod").ZodOptional<import("zod").ZodNumber>;
735
765
  createdAt: import("zod").ZodString;
736
766
  updatedAt: import("zod").ZodString;
@@ -1109,6 +1139,10 @@ export declare function clientFor(command: Command): Promise<{
1109
1139
  project: import("zod").ZodString;
1110
1140
  path: import("zod").ZodString;
1111
1141
  text: import("zod").ZodString;
1142
+ imports: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{
1143
+ min: import("zod").ZodTuple<[import("zod").ZodNumber, import("zod").ZodNumber, import("zod").ZodNumber], null>;
1144
+ max: import("zod").ZodTuple<[import("zod").ZodNumber, import("zod").ZodNumber, import("zod").ZodNumber], null>;
1145
+ }, import("zod/v4/core").$strict>>>;
1112
1146
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
1113
1147
  paths: import("zod").ZodArray<import("zod").ZodString>;
1114
1148
  }, import("zod/v4/core").$strip>, Record<never, never>>;
@@ -1117,6 +1151,10 @@ export declare function clientFor(command: Command): Promise<{
1117
1151
  path: import("zod").ZodString;
1118
1152
  oldText: import("zod").ZodString;
1119
1153
  newText: import("zod").ZodString;
1154
+ imports: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{
1155
+ min: import("zod").ZodTuple<[import("zod").ZodNumber, import("zod").ZodNumber, import("zod").ZodNumber], null>;
1156
+ max: import("zod").ZodTuple<[import("zod").ZodNumber, import("zod").ZodNumber, import("zod").ZodNumber], null>;
1157
+ }, import("zod/v4/core").$strict>>>;
1120
1158
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
1121
1159
  paths: import("zod").ZodArray<import("zod").ZodString>;
1122
1160
  }, import("zod/v4/core").$strip>, Record<never, never>>;
@@ -1,4 +1,5 @@
1
1
  import { clientFor, projectId, requireConfirmation } from "./context.js";
2
+ import { importOption, parseImports } from "./imports.js";
2
3
  import { textArgument } from "./input.js";
3
4
  export function registerFilesystemCommands(program) {
4
5
  program
@@ -35,12 +36,15 @@ export function registerFilesystemCommands(program) {
35
36
  .description("Create or overwrite a text project file")
36
37
  .argument("<path>", "Absolute project path")
37
38
  .argument("[text]", "Inline text; omit or use - to read stdin")
38
- .action(async (path, text, _options, command) => {
39
+ .addOption(importOption)
40
+ .action(async (path, text, options, command) => {
39
41
  const client = await clientFor(command);
42
+ const imports = parseImports(options.import);
40
43
  await client.filesystem.write({
41
44
  project: projectId(command),
42
45
  path,
43
46
  text: await textArgument(text),
47
+ ...(imports === undefined ? {} : { imports }),
44
48
  });
45
49
  console.log(`Wrote ${path}.`);
46
50
  });
@@ -50,13 +54,16 @@ export function registerFilesystemCommands(program) {
50
54
  .argument("<path>", "Absolute project path")
51
55
  .argument("<old-text>", "Text that must occur exactly once")
52
56
  .argument("<new-text>", "Replacement text")
53
- .action(async (path, oldText, newText, _options, command) => {
57
+ .addOption(importOption)
58
+ .action(async (path, oldText, newText, options, command) => {
54
59
  const client = await clientFor(command);
60
+ const imports = parseImports(options.import);
55
61
  await client.filesystem.edit({
56
62
  project: projectId(command),
57
63
  path,
58
64
  oldText,
59
65
  newText,
66
+ ...(imports === undefined ? {} : { imports }),
60
67
  });
61
68
  console.log(`Edited ${path}.`);
62
69
  });
@@ -1,5 +1,7 @@
1
1
  import { readImageInput } from "../image.js";
2
2
  import { parseFrameSize } from "../target.js";
3
+ import { importOption, parseImports } from "./imports.js";
4
+ import { textArgument } from "./input.js";
3
5
  import { designIdSchema, marketAssetSchema, } from "../v1/schemas.js";
4
6
  import { clientFor, projectId, requireConfirmation } from "./context.js";
5
7
  import { registerImageCommands } from "./image.js";
@@ -27,6 +29,8 @@ export function registerFrameCommands(program) {
27
29
  .option("-s, --size <width>x<height>", "GLTS or Markdown viewport size")
28
30
  .option("--image <url-or-path>", "Image URL or local PNG, JPEG, or WebP")
29
31
  .option("--asset <name@version>", "Exact public Market asset version")
32
+ .option("--text <text>", "Initial index.glts or index.md content; use - to read stdin")
33
+ .addOption(importOption)
30
34
  .action(async (name, options, command) => {
31
35
  const project = projectId(command);
32
36
  const input = await createFrameInput(project, name, options);
@@ -65,6 +69,22 @@ export function registerFrameCommands(program) {
65
69
  console.log(`Deleted ${id}.`);
66
70
  }
67
71
  });
72
+ frame
73
+ .command("preview")
74
+ .description("Print frames' current WebP preview URLs")
75
+ .argument("<frame-id...>", "Frame IDs")
76
+ .action(async (frameIds, _options, command) => {
77
+ const project = projectId(command);
78
+ const client = await clientFor(command);
79
+ const frames = await client.frame.list({ project });
80
+ for (const frameId of frameIds) {
81
+ const id = designIdSchema.parse(frameId);
82
+ const target = frames.find((item) => item.id === id);
83
+ if (!target)
84
+ throw new Error(`Frame ${id} does not exist.`);
85
+ console.log(`${id}\t${target.previewUrl}`);
86
+ }
87
+ });
68
88
  frame
69
89
  .command("screenshot")
70
90
  .description("Render frames and print their screenshot URLs")
@@ -81,6 +101,9 @@ export function registerFrameCommands(program) {
81
101
  registerImageCommands(frame);
82
102
  }
83
103
  async function createFrameInput(project, name, options) {
104
+ if (options.import !== undefined && options.type !== "glts") {
105
+ throw new Error("Only GLTS frames accept --import");
106
+ }
84
107
  if (options.type === "glts") {
85
108
  if (options.image !== undefined || options.asset !== undefined) {
86
109
  throw new Error("GLTS frames do not accept --image or --asset");
@@ -88,17 +111,26 @@ async function createFrameInput(project, name, options) {
88
111
  if (options.size === undefined) {
89
112
  throw new Error("GLTS frames require --size <width>x<height>");
90
113
  }
114
+ const imports = parseImports(options.import);
115
+ const text = options.text === undefined
116
+ ? {}
117
+ : { text: await textArgument(options.text) };
91
118
  return {
92
119
  project,
93
120
  name,
94
121
  type: "glts",
95
122
  ...parseFrameSize(options.size),
123
+ ...text,
124
+ ...(imports === undefined ? {} : { imports }),
96
125
  };
97
126
  }
98
127
  if (options.type === "image") {
99
128
  if (options.size !== undefined || options.asset !== undefined) {
100
129
  throw new Error("Image frames do not accept --size or --asset");
101
130
  }
131
+ if (options.text !== undefined) {
132
+ throw new Error("Image frames do not accept --text");
133
+ }
102
134
  if (options.image === undefined) {
103
135
  throw new Error("Image frames require --image");
104
136
  }
@@ -116,17 +148,24 @@ async function createFrameInput(project, name, options) {
116
148
  if (options.size === undefined) {
117
149
  throw new Error("Markdown frames require --size <width>x<height>");
118
150
  }
151
+ const text = options.text === undefined
152
+ ? {}
153
+ : { text: await textArgument(options.text) };
119
154
  return {
120
155
  project,
121
156
  name,
122
157
  type: "markdown",
123
158
  ...parseFrameSize(options.size),
159
+ ...text,
124
160
  };
125
161
  }
126
162
  if (options.type === "market") {
127
163
  if (options.size !== undefined || options.image !== undefined) {
128
164
  throw new Error("Market frames do not accept --size or --image");
129
165
  }
166
+ if (options.text !== undefined) {
167
+ throw new Error("Market frames do not accept --text");
168
+ }
130
169
  if (options.asset === undefined) {
131
170
  throw new Error("Market frames require --asset");
132
171
  }
@@ -66,4 +66,5 @@ function collect(value, previous) {
66
66
  function printGeneratedFrame(frame) {
67
67
  console.log(`Generated ${frame.id}\t${frame.name}\timage`);
68
68
  console.log(`Path /${frame.id}/`);
69
+ console.log(`Preview ${frame.previewUrl}`);
69
70
  }
@@ -0,0 +1,5 @@
1
+ import { Option } from "commander";
2
+ import { type Imports } from "../v1/schemas.js";
3
+ /** `--import <specifier>=<minx,miny,minz>:<maxx,maxy,maxz>`, repeatable. */
4
+ export declare const importOption: Option;
5
+ export declare function parseImports(values: string[] | undefined): Imports | undefined;
@@ -0,0 +1,34 @@
1
+ import { Option } from "commander";
2
+ import { boundingBoxSchema } from "../v1/schemas.js";
3
+ /** `--import <specifier>=<minx,miny,minz>:<maxx,maxy,maxz>`, repeatable. */
4
+ export const importOption = new Option("--import <specifier=min:max>", "Bounding box for an imported .glts file that does not exist yet, as x,y,z:x,y,z").argParser((value, previous = []) => [...previous, value]);
5
+ export function parseImports(values) {
6
+ if (values === undefined)
7
+ return undefined;
8
+ const imports = {};
9
+ for (const value of values) {
10
+ const separator = value.indexOf("=");
11
+ if (separator === -1)
12
+ throw new Error(`--import needs <specifier>=<min>:<max>, got ${value}`);
13
+ const specifier = value.slice(0, separator);
14
+ const [min, max, ...rest] = value.slice(separator + 1).split(":");
15
+ if (min === undefined || max === undefined || rest.length > 0) {
16
+ throw new Error(`--import ${specifier} needs <minx,miny,minz>:<maxx,maxy,maxz>`);
17
+ }
18
+ imports[specifier] = boundingBoxSchema.parse({
19
+ min: corner(min),
20
+ max: corner(max),
21
+ });
22
+ }
23
+ return imports;
24
+ }
25
+ function corner(value) {
26
+ const coordinates = value.split(",");
27
+ const numbers = coordinates.map(Number);
28
+ if (coordinates.length !== 3 ||
29
+ coordinates.some((coordinate) => coordinate.trim() === "") ||
30
+ numbers.some((number) => !Number.isFinite(number))) {
31
+ throw new Error(`Bounding box corners are three comma-separated numbers, got ${value}`);
32
+ }
33
+ return numbers;
34
+ }
@@ -1 +1,2 @@
1
- export declare function frameImageUrl(viewUrl: string, frameId: string): string;
1
+ /** @deprecated Use framePreviewUrl. */
2
+ export { framePreviewUrl as frameImageUrl } from "./frame-preview.js";
@@ -1,6 +1,2 @@
1
- import { designIdSchema, httpUrlSchema } from "./v1/schemas.js";
2
- export function frameImageUrl(viewUrl, frameId) {
3
- const view = new URL(httpUrlSchema.parse(viewUrl));
4
- const id = designIdSchema.parse(frameId);
5
- return new URL(`/${id}.webp`, view.origin).href;
6
- }
1
+ /** @deprecated Use framePreviewUrl. */
2
+ export { framePreviewUrl as frameImageUrl } from "./frame-preview.js";
@@ -0,0 +1 @@
1
+ export declare function framePreviewUrl(viewUrl: string, frameId: string): string;
@@ -0,0 +1,6 @@
1
+ import { designIdSchema, httpUrlSchema } from "./v1/schemas.js";
2
+ export function framePreviewUrl(viewUrl, frameId) {
3
+ const view = new URL(httpUrlSchema.parse(viewUrl));
4
+ const id = designIdSchema.parse(frameId);
5
+ return new URL(`/${id}.webp`, view.origin).href;
6
+ }
package/dist/mcp/file.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { fileTextSchema } from "../v1/schemas.js";
2
2
  import { fileResult, result } from "./result.js";
3
- import { directoryPathSchema, fileInput, projectInput } from "./schema.js";
3
+ import { directoryPathSchema, fileInput, importsInput, projectInput, } from "./schema.js";
4
4
  export function registerFileTools(server, client) {
5
5
  server.registerTool("list_design_files", {
6
6
  title: "List Design files",
@@ -34,27 +34,42 @@ export function registerFileTools(server, client) {
34
34
  });
35
35
  server.registerTool("write_design_file", {
36
36
  title: "Write Design file",
37
- description: "Create or replace one .glts file at a canonical project-absolute path. The path includes the frame ID; this tool does not accept a frame argument.",
38
- inputSchema: fileInput.extend({ text: fileTextSchema }),
37
+ description: "Create or replace one .glts or .md file at a canonical project-absolute path. Every .glts file it imports must exist or be given a bounding box in imports, which creates a placeholder file to replace later. The path includes the frame ID; this tool does not accept a frame argument.",
38
+ inputSchema: fileInput.extend({
39
+ text: fileTextSchema,
40
+ imports: importsInput.optional(),
41
+ }),
39
42
  annotations: {
40
43
  readOnlyHint: false,
41
44
  destructiveHint: true,
42
45
  openWorldHint: true,
43
46
  },
44
- }, async ({ project, path, text }) => result(await client.filesystem.write({ project, path, text })));
47
+ }, async ({ project, path, text, imports }) => result(await client.filesystem.write({
48
+ project,
49
+ path,
50
+ text,
51
+ ...(imports === undefined ? {} : { imports }),
52
+ })));
45
53
  server.registerTool("edit_design_file", {
46
54
  title: "Edit Design file",
47
- description: "Replace oldText when it occurs exactly once at a canonical project-absolute .glts path. The path includes the frame ID; this tool does not accept a frame argument.",
55
+ description: "Replace oldText when it occurs exactly once at a canonical project-absolute .glts or .md path. A newly imported .glts file that does not exist needs a bounding box in imports. The path includes the frame ID; this tool does not accept a frame argument.",
48
56
  inputSchema: fileInput.extend({
49
57
  oldText: fileTextSchema.min(1),
50
58
  newText: fileTextSchema,
59
+ imports: importsInput.optional(),
51
60
  }),
52
61
  annotations: {
53
62
  readOnlyHint: false,
54
63
  destructiveHint: true,
55
64
  openWorldHint: true,
56
65
  },
57
- }, async ({ project, path, oldText, newText }) => result(await client.filesystem.edit({ project, path, oldText, newText })));
66
+ }, async ({ project, path, oldText, newText, imports }) => result(await client.filesystem.edit({
67
+ project,
68
+ path,
69
+ oldText,
70
+ newText,
71
+ ...(imports === undefined ? {} : { imports }),
72
+ })));
58
73
  server.registerTool("delete_design_file", {
59
74
  title: "Delete Design file",
60
75
  description: "Permanently delete one .glts file at a canonical project-absolute path. The path includes the frame ID; this tool does not accept a frame argument.",