@drawcall/design 0.8.1 → 0.9.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 +15 -2
- package/dist/browser.d.ts +2 -1
- package/dist/browser.js +1 -0
- package/dist/cli-client.js +5 -1
- 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 +12 -4
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getCliClient } from "../cli-client.js";
|
|
2
|
+
import { apiOption } from "../options.js";
|
|
3
|
+
import { designIdSchema } from "../v1/schemas.js";
|
|
4
|
+
export function clientFor(command) {
|
|
5
|
+
return getCliClient(apiOption(command));
|
|
6
|
+
}
|
|
7
|
+
export function projectId(command) {
|
|
8
|
+
const value = command.optsWithGlobals().project;
|
|
9
|
+
if (typeof value !== "string") {
|
|
10
|
+
throw new Error("Select a project with -p, --project <project-id>.");
|
|
11
|
+
}
|
|
12
|
+
return designIdSchema.parse(value);
|
|
13
|
+
}
|
|
14
|
+
export function requireConfirmation(confirmed) {
|
|
15
|
+
if (!confirmed)
|
|
16
|
+
throw new Error("Destructive commands require --yes.");
|
|
17
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { clientFor, projectId, requireConfirmation } from "./context.js";
|
|
2
|
+
export function registerFilesystemCommands(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("ls")
|
|
5
|
+
.description("List paths in the selected project filesystem")
|
|
6
|
+
.argument("[path]", "Absolute directory path", "/")
|
|
7
|
+
.action(async (path, _options, command) => {
|
|
8
|
+
const client = await clientFor(command);
|
|
9
|
+
const result = await client.filesystem.list({
|
|
10
|
+
project: projectId(command),
|
|
11
|
+
path,
|
|
12
|
+
});
|
|
13
|
+
for (const item of result.paths)
|
|
14
|
+
console.log(item);
|
|
15
|
+
});
|
|
16
|
+
program
|
|
17
|
+
.command("read")
|
|
18
|
+
.description("Read a project file")
|
|
19
|
+
.argument("<path>", "Absolute project path")
|
|
20
|
+
.action(async (path, _options, command) => {
|
|
21
|
+
const client = await clientFor(command);
|
|
22
|
+
const file = await client.filesystem.read({
|
|
23
|
+
project: projectId(command),
|
|
24
|
+
path,
|
|
25
|
+
});
|
|
26
|
+
if (file.type === "text") {
|
|
27
|
+
process.stdout.write(file.text);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.log(file.url);
|
|
31
|
+
});
|
|
32
|
+
program
|
|
33
|
+
.command("write")
|
|
34
|
+
.description("Create or overwrite a text project file")
|
|
35
|
+
.argument("<path>", "Absolute project path")
|
|
36
|
+
.argument("[text]", "Inline text; omit or use - to read stdin")
|
|
37
|
+
.action(async (path, text, _options, command) => {
|
|
38
|
+
const client = await clientFor(command);
|
|
39
|
+
await client.filesystem.write({
|
|
40
|
+
project: projectId(command),
|
|
41
|
+
path,
|
|
42
|
+
text: text === undefined || text === "-" ? await readStdin() : text,
|
|
43
|
+
});
|
|
44
|
+
console.log(`Wrote ${path}.`);
|
|
45
|
+
});
|
|
46
|
+
program
|
|
47
|
+
.command("edit")
|
|
48
|
+
.description("Replace text that occurs exactly once in a project file")
|
|
49
|
+
.argument("<path>", "Absolute project path")
|
|
50
|
+
.argument("<old-text>", "Text that must occur exactly once")
|
|
51
|
+
.argument("<new-text>", "Replacement text")
|
|
52
|
+
.action(async (path, oldText, newText, _options, command) => {
|
|
53
|
+
const client = await clientFor(command);
|
|
54
|
+
await client.filesystem.edit({
|
|
55
|
+
project: projectId(command),
|
|
56
|
+
path,
|
|
57
|
+
oldText,
|
|
58
|
+
newText,
|
|
59
|
+
});
|
|
60
|
+
console.log(`Edited ${path}.`);
|
|
61
|
+
});
|
|
62
|
+
program
|
|
63
|
+
.command("delete")
|
|
64
|
+
.description("Delete a project file")
|
|
65
|
+
.argument("<path>", "Absolute project path")
|
|
66
|
+
.option("-y, --yes", "Confirm deletion", false)
|
|
67
|
+
.action(async (path, options, command) => {
|
|
68
|
+
requireConfirmation(options.yes);
|
|
69
|
+
const client = await clientFor(command);
|
|
70
|
+
await client.filesystem.delete({ project: projectId(command), path });
|
|
71
|
+
console.log(`Deleted ${path}.`);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async function readStdin() {
|
|
75
|
+
const chunks = [];
|
|
76
|
+
for await (const chunk of process.stdin) {
|
|
77
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
78
|
+
}
|
|
79
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
80
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { readImageInput } from "../image.js";
|
|
2
|
+
import { parseFrameSize } from "../target.js";
|
|
3
|
+
import { designIdSchema, marketAssetSchema, } from "../v1/schemas.js";
|
|
4
|
+
import { clientFor, projectId, requireConfirmation } from "./context.js";
|
|
5
|
+
import { registerImageCommands } from "./image.js";
|
|
6
|
+
export function registerFrameCommands(program) {
|
|
7
|
+
const frame = program.command("frame").description("Manage frames");
|
|
8
|
+
frame
|
|
9
|
+
.command("list")
|
|
10
|
+
.description("List frames in the selected project")
|
|
11
|
+
.action(async (_options, command) => {
|
|
12
|
+
const client = await clientFor(command);
|
|
13
|
+
const frames = await client.frame.list({ project: projectId(command) });
|
|
14
|
+
if (frames.length === 0) {
|
|
15
|
+
console.log("No frames.");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
for (const item of frames) {
|
|
19
|
+
console.log(`${item.id}\t${item.name}\t${item.type}\t${item.width}x${item.height}`);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
frame
|
|
23
|
+
.command("create")
|
|
24
|
+
.description("Create a GLTS, image, or pinned Market frame")
|
|
25
|
+
.argument("<name>", "Frame name")
|
|
26
|
+
.requiredOption("-t, --type <type>", "glts, image, or market")
|
|
27
|
+
.option("-s, --size <width>x<height>", "GLTS viewport size")
|
|
28
|
+
.option("--image <url-or-path>", "Image URL or local PNG, JPEG, or WebP")
|
|
29
|
+
.option("--asset <name@version>", "Exact public Market asset version")
|
|
30
|
+
.action(async (name, options, command) => {
|
|
31
|
+
const project = projectId(command);
|
|
32
|
+
const input = await createFrameInput(project, name, options);
|
|
33
|
+
const client = await clientFor(command);
|
|
34
|
+
const created = await client.frame.create(input);
|
|
35
|
+
console.log(`Created ${created.id}\t${created.name}\t${created.type}`);
|
|
36
|
+
console.log(`Path /${created.id}/`);
|
|
37
|
+
});
|
|
38
|
+
frame
|
|
39
|
+
.command("rename")
|
|
40
|
+
.description("Rename a frame")
|
|
41
|
+
.argument("<frame-id>", "Frame ID")
|
|
42
|
+
.argument("<name>", "New frame name")
|
|
43
|
+
.action(async (frameId, name, _options, command) => {
|
|
44
|
+
const id = designIdSchema.parse(frameId);
|
|
45
|
+
const client = await clientFor(command);
|
|
46
|
+
const renamed = await client.frame.rename({
|
|
47
|
+
project: projectId(command),
|
|
48
|
+
frame: id,
|
|
49
|
+
name,
|
|
50
|
+
});
|
|
51
|
+
console.log(`Renamed ${renamed.id}\t${renamed.name}.`);
|
|
52
|
+
});
|
|
53
|
+
frame
|
|
54
|
+
.command("delete")
|
|
55
|
+
.description("Delete frames")
|
|
56
|
+
.argument("<frame-id...>", "Frame IDs")
|
|
57
|
+
.option("-y, --yes", "Confirm deletion", false)
|
|
58
|
+
.action(async (frameIds, options, command) => {
|
|
59
|
+
requireConfirmation(options.yes);
|
|
60
|
+
const project = projectId(command);
|
|
61
|
+
const client = await clientFor(command);
|
|
62
|
+
for (const frameId of frameIds) {
|
|
63
|
+
const id = designIdSchema.parse(frameId);
|
|
64
|
+
await client.frame.delete({ project, frame: id });
|
|
65
|
+
console.log(`Deleted ${id}.`);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
frame
|
|
69
|
+
.command("screenshot")
|
|
70
|
+
.description("Render frames and print their screenshot URLs")
|
|
71
|
+
.argument("<frame-id...>", "Frame IDs")
|
|
72
|
+
.action(async (frameIds, _options, command) => {
|
|
73
|
+
const project = projectId(command);
|
|
74
|
+
const client = await clientFor(command);
|
|
75
|
+
for (const frameId of frameIds) {
|
|
76
|
+
const id = designIdSchema.parse(frameId);
|
|
77
|
+
const result = await client.frame.screenshot({ project, frame: id });
|
|
78
|
+
console.log(`${id}\t${result.url}`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
registerImageCommands(frame);
|
|
82
|
+
}
|
|
83
|
+
async function createFrameInput(project, name, options) {
|
|
84
|
+
if (options.type === "glts") {
|
|
85
|
+
if (options.image !== undefined || options.asset !== undefined) {
|
|
86
|
+
throw new Error("GLTS frames do not accept --image or --asset");
|
|
87
|
+
}
|
|
88
|
+
if (options.size === undefined) {
|
|
89
|
+
throw new Error("GLTS frames require --size <width>x<height>");
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
project,
|
|
93
|
+
name,
|
|
94
|
+
type: "glts",
|
|
95
|
+
...parseFrameSize(options.size),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (options.type === "image") {
|
|
99
|
+
if (options.size !== undefined || options.asset !== undefined) {
|
|
100
|
+
throw new Error("Image frames do not accept --size or --asset");
|
|
101
|
+
}
|
|
102
|
+
if (options.image === undefined) {
|
|
103
|
+
throw new Error("Image frames require --image");
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
project,
|
|
107
|
+
name,
|
|
108
|
+
type: "image",
|
|
109
|
+
image: await readImageInput(options.image),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (options.type === "market") {
|
|
113
|
+
if (options.size !== undefined || options.image !== undefined) {
|
|
114
|
+
throw new Error("Market frames do not accept --size or --image");
|
|
115
|
+
}
|
|
116
|
+
if (options.asset === undefined) {
|
|
117
|
+
throw new Error("Market frames require --asset");
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
project,
|
|
121
|
+
name,
|
|
122
|
+
type: "market",
|
|
123
|
+
asset: marketAssetSchema.parse(options.asset),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
throw new Error("--type must be glts, image, or market");
|
|
127
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Command, Option } from "commander";
|
|
2
|
+
import { readImageInput } from "../image.js";
|
|
3
|
+
import { designIdSchema, generateImageSchema, } from "../v1/schemas.js";
|
|
4
|
+
import { clientFor, projectId } from "./context.js";
|
|
5
|
+
export function registerImageCommands(frame) {
|
|
6
|
+
frame
|
|
7
|
+
.command("generate-image")
|
|
8
|
+
.description("Generate a new image frame")
|
|
9
|
+
.argument("<name>", "New frame name")
|
|
10
|
+
.requiredOption("--prompt <text>", "Image generation prompt")
|
|
11
|
+
.option("--reference <url-or-path>", "Reference image URL or local PNG, JPEG, or WebP; repeat to preserve order", collect, [])
|
|
12
|
+
.action(async (name, options, command) => {
|
|
13
|
+
const input = await resolveImageReferences({
|
|
14
|
+
project: projectId(command),
|
|
15
|
+
operation: "generate",
|
|
16
|
+
prompt: options.prompt,
|
|
17
|
+
references: options.reference,
|
|
18
|
+
result: "new",
|
|
19
|
+
name,
|
|
20
|
+
});
|
|
21
|
+
const client = await clientFor(command);
|
|
22
|
+
printGeneratedFrame(await client.frame.generateImage(input));
|
|
23
|
+
});
|
|
24
|
+
frame
|
|
25
|
+
.command("edit-image")
|
|
26
|
+
.description("Edit an image frame")
|
|
27
|
+
.argument("<target>", "Image frame ID")
|
|
28
|
+
.requiredOption("--prompt <text>", "Image edit prompt")
|
|
29
|
+
.addOption(new Option("--replace", "Replace the target image frame").conflicts("name"))
|
|
30
|
+
.addOption(new Option("--name <new>", "Create the edit as a new image frame").conflicts("replace"))
|
|
31
|
+
.option("--reference <url-or-path>", "Additional image URL or local PNG, JPEG, or WebP; repeat to preserve order", collect, [])
|
|
32
|
+
.action(async (target, options, command) => {
|
|
33
|
+
const input = await resolveImageReferences({
|
|
34
|
+
project: projectId(command),
|
|
35
|
+
operation: "edit",
|
|
36
|
+
prompt: options.prompt,
|
|
37
|
+
target: designIdSchema.parse(target),
|
|
38
|
+
references: options.reference,
|
|
39
|
+
...editOutput(options),
|
|
40
|
+
});
|
|
41
|
+
const client = await clientFor(command);
|
|
42
|
+
printGeneratedFrame(await client.frame.generateImage(input));
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function resolveImageReferences(input) {
|
|
46
|
+
const maximum = input.operation === "edit" ? 3 : 4;
|
|
47
|
+
if (input.references.length > maximum) {
|
|
48
|
+
throw new Error(`${input.operation === "edit" ? "Image editing" : "Image generation"} supports at most ${maximum} --reference values.`);
|
|
49
|
+
}
|
|
50
|
+
return generateImageSchema.parse({
|
|
51
|
+
...input,
|
|
52
|
+
references: await Promise.all(input.references.map(readImageInput)),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function editOutput(options) {
|
|
56
|
+
if (options.replace)
|
|
57
|
+
return { result: "replace" };
|
|
58
|
+
if (options.name !== undefined) {
|
|
59
|
+
return { result: "new", name: options.name };
|
|
60
|
+
}
|
|
61
|
+
throw new Error("Choose exactly one of --replace or --name <new>.");
|
|
62
|
+
}
|
|
63
|
+
function collect(value, previous) {
|
|
64
|
+
return [...previous, value];
|
|
65
|
+
}
|
|
66
|
+
function printGeneratedFrame(frame) {
|
|
67
|
+
console.log(`Generated ${frame.id}\t${frame.name}\timage`);
|
|
68
|
+
console.log(`Path /${frame.id}/`);
|
|
69
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { designIdSchema } from "../v1/schemas.js";
|
|
2
|
+
import { clientFor, requireConfirmation } from "./context.js";
|
|
3
|
+
export function registerProjectCommands(program) {
|
|
4
|
+
const project = program.command("project").description("Manage projects");
|
|
5
|
+
project
|
|
6
|
+
.command("list")
|
|
7
|
+
.description("List projects")
|
|
8
|
+
.action(async (_options, command) => {
|
|
9
|
+
const client = await clientFor(command);
|
|
10
|
+
const projects = await client.project.list();
|
|
11
|
+
if (projects.length === 0) {
|
|
12
|
+
console.log("No projects.");
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
for (const item of projects)
|
|
16
|
+
console.log(`${item.id}\t${item.name}`);
|
|
17
|
+
});
|
|
18
|
+
project
|
|
19
|
+
.command("create")
|
|
20
|
+
.description("Create a project")
|
|
21
|
+
.argument("<name>", "Project name")
|
|
22
|
+
.action(async (name, _options, command) => {
|
|
23
|
+
const client = await clientFor(command);
|
|
24
|
+
const created = await client.project.create({ name });
|
|
25
|
+
console.log(`Created ${created.id}\t${created.name}`);
|
|
26
|
+
});
|
|
27
|
+
project
|
|
28
|
+
.command("delete")
|
|
29
|
+
.description("Delete a project")
|
|
30
|
+
.argument("<project-id>", "Project ID")
|
|
31
|
+
.option("-y, --yes", "Confirm deletion", false)
|
|
32
|
+
.action(async (projectId, options, command) => {
|
|
33
|
+
requireConfirmation(options.yes);
|
|
34
|
+
const id = designIdSchema.parse(projectId);
|
|
35
|
+
const client = await clientFor(command);
|
|
36
|
+
await client.project.delete({ project: id });
|
|
37
|
+
console.log(`Deleted ${id}.`);
|
|
38
|
+
});
|
|
39
|
+
}
|
package/dist/command.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { Command, Option } from "commander";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { registerFilesystemCommands } from "./command/filesystem.js";
|
|
4
|
+
import { registerFrameCommands } from "./command/frame.js";
|
|
5
|
+
import { registerProjectCommands } from "./command/project.js";
|
|
6
6
|
import { cliDesignSkill } from "./skill.generated.js";
|
|
7
|
-
import { parseFrameSize } from "./target.js";
|
|
8
|
-
import { designIdSchema, marketAssetSchema } from "./v1/schemas.js";
|
|
9
7
|
export function createDesignCommand(options = {}) {
|
|
10
8
|
const program = new Command()
|
|
11
9
|
.name(options.name ?? "design")
|
|
@@ -19,251 +17,11 @@ export function createDesignCommand(options = {}) {
|
|
|
19
17
|
.action(() => {
|
|
20
18
|
process.stdout.write(cliDesignSkill);
|
|
21
19
|
});
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
.description("List projects")
|
|
26
|
-
.action(async (_options, command) => {
|
|
27
|
-
const client = await clientFor(command);
|
|
28
|
-
const projects = await client.project.list();
|
|
29
|
-
if (projects.length === 0) {
|
|
30
|
-
console.log("No projects.");
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
for (const item of projects)
|
|
34
|
-
console.log(`${item.id}\t${item.name}`);
|
|
35
|
-
});
|
|
36
|
-
project
|
|
37
|
-
.command("create")
|
|
38
|
-
.description("Create a project")
|
|
39
|
-
.argument("<name>", "Project name")
|
|
40
|
-
.action(async (name, _options, command) => {
|
|
41
|
-
const client = await clientFor(command);
|
|
42
|
-
const created = await client.project.create({ name });
|
|
43
|
-
console.log(`Created ${created.id}\t${created.name}`);
|
|
44
|
-
});
|
|
45
|
-
project
|
|
46
|
-
.command("delete")
|
|
47
|
-
.description("Delete a project")
|
|
48
|
-
.argument("<project-id>", "Project ID")
|
|
49
|
-
.option("-y, --yes", "Confirm deletion", false)
|
|
50
|
-
.action(async (projectId, options, command) => {
|
|
51
|
-
requireConfirmation(options.yes);
|
|
52
|
-
const id = designIdSchema.parse(projectId);
|
|
53
|
-
const client = await clientFor(command);
|
|
54
|
-
await client.project.delete({ project: id });
|
|
55
|
-
console.log(`Deleted ${id}.`);
|
|
56
|
-
});
|
|
57
|
-
const frame = program.command("frame").description("Manage frames");
|
|
58
|
-
frame
|
|
59
|
-
.command("list")
|
|
60
|
-
.description("List frames in the selected project")
|
|
61
|
-
.action(async (_options, command) => {
|
|
62
|
-
const client = await clientFor(command);
|
|
63
|
-
const frames = await client.frame.list({ project: projectId(command) });
|
|
64
|
-
if (frames.length === 0) {
|
|
65
|
-
console.log("No frames.");
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
for (const item of frames) {
|
|
69
|
-
console.log(`${item.id}\t${item.name}\t${item.type}\t${item.width}x${item.height}`);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
frame
|
|
73
|
-
.command("create")
|
|
74
|
-
.description("Create a GLTS, image, or pinned Market frame")
|
|
75
|
-
.argument("<name>", "Frame name")
|
|
76
|
-
.requiredOption("-t, --type <type>", "glts, image, or market")
|
|
77
|
-
.option("-s, --size <width>x<height>", "GLTS viewport size")
|
|
78
|
-
.option("--image <url-or-path>", "Image URL or local PNG, JPEG, or WebP")
|
|
79
|
-
.option("--asset <name@version>", "Exact public Market asset version")
|
|
80
|
-
.action(async (name, options, command) => {
|
|
81
|
-
const project = projectId(command);
|
|
82
|
-
const input = await createFrameInput(project, name, options);
|
|
83
|
-
const client = await clientFor(command);
|
|
84
|
-
const created = await client.frame.create(input);
|
|
85
|
-
console.log(`Created ${created.id}\t${created.name}\t${created.type}`);
|
|
86
|
-
console.log(`Path /${created.id}/`);
|
|
87
|
-
});
|
|
88
|
-
frame
|
|
89
|
-
.command("rename")
|
|
90
|
-
.description("Rename a frame")
|
|
91
|
-
.argument("<frame-id>", "Frame ID")
|
|
92
|
-
.argument("<name>", "New frame name")
|
|
93
|
-
.action(async (frameId, name, _options, command) => {
|
|
94
|
-
const id = designIdSchema.parse(frameId);
|
|
95
|
-
const client = await clientFor(command);
|
|
96
|
-
const renamed = await client.frame.rename({
|
|
97
|
-
project: projectId(command),
|
|
98
|
-
frame: id,
|
|
99
|
-
name,
|
|
100
|
-
});
|
|
101
|
-
console.log(`Renamed ${renamed.id}\t${renamed.name}.`);
|
|
102
|
-
});
|
|
103
|
-
frame
|
|
104
|
-
.command("delete")
|
|
105
|
-
.description("Delete frames")
|
|
106
|
-
.argument("<frame-id...>", "Frame IDs")
|
|
107
|
-
.option("-y, --yes", "Confirm deletion", false)
|
|
108
|
-
.action(async (frameIds, options, command) => {
|
|
109
|
-
requireConfirmation(options.yes);
|
|
110
|
-
const project = projectId(command);
|
|
111
|
-
const client = await clientFor(command);
|
|
112
|
-
for (const frameId of frameIds) {
|
|
113
|
-
const id = designIdSchema.parse(frameId);
|
|
114
|
-
await client.frame.delete({ project, frame: id });
|
|
115
|
-
console.log(`Deleted ${id}.`);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
frame
|
|
119
|
-
.command("screenshot")
|
|
120
|
-
.description("Render frames and print their screenshot URLs")
|
|
121
|
-
.argument("<frame-id...>", "Frame IDs")
|
|
122
|
-
.action(async (frameIds, _options, command) => {
|
|
123
|
-
const project = projectId(command);
|
|
124
|
-
const client = await clientFor(command);
|
|
125
|
-
for (const frameId of frameIds) {
|
|
126
|
-
const id = designIdSchema.parse(frameId);
|
|
127
|
-
const result = await client.frame.screenshot({ project, frame: id });
|
|
128
|
-
console.log(`${id}\t${result.url}`);
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
program
|
|
132
|
-
.command("ls")
|
|
133
|
-
.description("List paths in the selected project filesystem")
|
|
134
|
-
.argument("[path]", "Absolute directory path", "/")
|
|
135
|
-
.action(async (path, _options, command) => {
|
|
136
|
-
const client = await clientFor(command);
|
|
137
|
-
const result = await client.filesystem.list({
|
|
138
|
-
project: projectId(command),
|
|
139
|
-
path,
|
|
140
|
-
});
|
|
141
|
-
for (const item of result.paths)
|
|
142
|
-
console.log(item);
|
|
143
|
-
});
|
|
144
|
-
program
|
|
145
|
-
.command("read")
|
|
146
|
-
.description("Read a project file")
|
|
147
|
-
.argument("<path>", "Absolute project path")
|
|
148
|
-
.action(async (path, _options, command) => {
|
|
149
|
-
const client = await clientFor(command);
|
|
150
|
-
const file = await client.filesystem.read({
|
|
151
|
-
project: projectId(command),
|
|
152
|
-
path,
|
|
153
|
-
});
|
|
154
|
-
if (file.type === "text") {
|
|
155
|
-
process.stdout.write(file.text);
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
console.log(file.url);
|
|
159
|
-
});
|
|
160
|
-
program
|
|
161
|
-
.command("write")
|
|
162
|
-
.description("Create or overwrite a text project file")
|
|
163
|
-
.argument("<path>", "Absolute project path")
|
|
164
|
-
.argument("[text]", "Inline text; omit or use - to read stdin")
|
|
165
|
-
.action(async (path, text, _options, command) => {
|
|
166
|
-
const client = await clientFor(command);
|
|
167
|
-
await client.filesystem.write({
|
|
168
|
-
project: projectId(command),
|
|
169
|
-
path,
|
|
170
|
-
text: text === undefined || text === "-" ? await readStdin() : text,
|
|
171
|
-
});
|
|
172
|
-
console.log(`Wrote ${path}.`);
|
|
173
|
-
});
|
|
174
|
-
program
|
|
175
|
-
.command("edit")
|
|
176
|
-
.description("Replace text that occurs exactly once in a project file")
|
|
177
|
-
.argument("<path>", "Absolute project path")
|
|
178
|
-
.argument("<old-text>", "Text that must occur exactly once")
|
|
179
|
-
.argument("<new-text>", "Replacement text")
|
|
180
|
-
.action(async (path, oldText, newText, _options, command) => {
|
|
181
|
-
const client = await clientFor(command);
|
|
182
|
-
await client.filesystem.edit({
|
|
183
|
-
project: projectId(command),
|
|
184
|
-
path,
|
|
185
|
-
oldText,
|
|
186
|
-
newText,
|
|
187
|
-
});
|
|
188
|
-
console.log(`Edited ${path}.`);
|
|
189
|
-
});
|
|
190
|
-
program
|
|
191
|
-
.command("delete")
|
|
192
|
-
.description("Delete a project file")
|
|
193
|
-
.argument("<path>", "Absolute project path")
|
|
194
|
-
.option("-y, --yes", "Confirm deletion", false)
|
|
195
|
-
.action(async (path, options, command) => {
|
|
196
|
-
requireConfirmation(options.yes);
|
|
197
|
-
const client = await clientFor(command);
|
|
198
|
-
await client.filesystem.delete({ project: projectId(command), path });
|
|
199
|
-
console.log(`Deleted ${path}.`);
|
|
200
|
-
});
|
|
20
|
+
registerProjectCommands(program);
|
|
21
|
+
registerFrameCommands(program);
|
|
22
|
+
registerFilesystemCommands(program);
|
|
201
23
|
return program;
|
|
202
24
|
}
|
|
203
|
-
async function createFrameInput(project, name, options) {
|
|
204
|
-
if (options.type === "glts") {
|
|
205
|
-
if (options.image !== undefined || options.asset !== undefined) {
|
|
206
|
-
throw new Error("GLTS frames do not accept --image or --asset");
|
|
207
|
-
}
|
|
208
|
-
if (options.size === undefined) {
|
|
209
|
-
throw new Error("GLTS frames require --size <width>x<height>");
|
|
210
|
-
}
|
|
211
|
-
return {
|
|
212
|
-
project,
|
|
213
|
-
name,
|
|
214
|
-
type: "glts",
|
|
215
|
-
...parseFrameSize(options.size),
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
if (options.type === "image") {
|
|
219
|
-
if (options.size !== undefined || options.asset !== undefined) {
|
|
220
|
-
throw new Error("Image frames do not accept --size or --asset");
|
|
221
|
-
}
|
|
222
|
-
if (options.image === undefined)
|
|
223
|
-
throw new Error("Image frames require --image");
|
|
224
|
-
return {
|
|
225
|
-
project,
|
|
226
|
-
name,
|
|
227
|
-
type: "image",
|
|
228
|
-
image: await readImageInput(options.image),
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
if (options.type === "market") {
|
|
232
|
-
if (options.size !== undefined || options.image !== undefined) {
|
|
233
|
-
throw new Error("Market frames do not accept --size or --image");
|
|
234
|
-
}
|
|
235
|
-
if (options.asset === undefined)
|
|
236
|
-
throw new Error("Market frames require --asset");
|
|
237
|
-
return {
|
|
238
|
-
project,
|
|
239
|
-
name,
|
|
240
|
-
type: "market",
|
|
241
|
-
asset: marketAssetSchema.parse(options.asset),
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
throw new Error("--type must be glts, image, or market");
|
|
245
|
-
}
|
|
246
|
-
async function clientFor(command) {
|
|
247
|
-
return getCliClient(apiOption(command));
|
|
248
|
-
}
|
|
249
|
-
function projectId(command) {
|
|
250
|
-
const value = command.optsWithGlobals().project;
|
|
251
|
-
if (typeof value !== "string") {
|
|
252
|
-
throw new Error("Select a project with -p, --project <project-id>.");
|
|
253
|
-
}
|
|
254
|
-
return designIdSchema.parse(value);
|
|
255
|
-
}
|
|
256
|
-
function requireConfirmation(confirmed) {
|
|
257
|
-
if (!confirmed)
|
|
258
|
-
throw new Error("Destructive commands require --yes.");
|
|
259
|
-
}
|
|
260
|
-
async function readStdin() {
|
|
261
|
-
const chunks = [];
|
|
262
|
-
for await (const chunk of process.stdin) {
|
|
263
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
264
|
-
}
|
|
265
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
266
|
-
}
|
|
267
25
|
function readPackageVersion() {
|
|
268
26
|
const manifest = createRequire(import.meta.url)("../package.json");
|
|
269
27
|
if (!manifest ||
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function frameImageUrl(viewUrl: string, frameId: string): string;
|
package/dist/image.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { type ImageInput } from "./v1/schemas.js";
|
|
2
|
+
export declare function readImageInput(value: string): Promise<ImageInput>;
|
package/dist/image.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { basename, extname } from "node:path";
|
|
3
|
-
import { imageUrlSchema } from "./v1/schemas.js";
|
|
3
|
+
import { imageInputSchema, imageUrlSchema, } from "./v1/schemas.js";
|
|
4
4
|
export async function readImageInput(value) {
|
|
5
5
|
if (/^https?:/i.test(value) || value.includes("://")) {
|
|
6
6
|
return imageUrlSchema.parse(value);
|
|
@@ -10,9 +10,7 @@ export async function readImageInput(value) {
|
|
|
10
10
|
if (mediaType === undefined) {
|
|
11
11
|
throw new Error("Local images must be PNG, JPEG, or WebP files");
|
|
12
12
|
}
|
|
13
|
-
return new File([await readFile(value)], basename(value), {
|
|
14
|
-
type: mediaType,
|
|
15
|
-
});
|
|
13
|
+
return imageInputSchema.parse(new File([await readFile(value)], basename(value), { type: mediaType }));
|
|
16
14
|
}
|
|
17
15
|
function mediaTypeForExtension(extension) {
|
|
18
16
|
if (extension === ".jpeg" || extension === ".jpg")
|