@pixel-point/toolcraft 0.0.9 → 0.0.12
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 +42 -9
- package/package.json +2 -1
- package/src/cli.mjs +4 -2
- package/src/cli.test.mjs +5 -2
- package/src/generate.mjs +14 -6
- package/src/generate.test.mjs +33 -2
- package/templates/runtime/contracts/component-contracts.test.ts +175 -36
- package/templates/runtime/contracts/component-contracts.ts +90 -45
- package/templates/runtime/contracts/decision-contracts.test.ts +33 -1
- package/templates/runtime/contracts/decision-contracts.ts +19 -6
- package/templates/runtime/export/export.test.ts +63 -0
- package/templates/runtime/export/export.ts +55 -0
- package/templates/runtime/index.ts +1 -0
- package/templates/runtime/react/canvas-shell.test.tsx +58 -4
- package/templates/runtime/react/canvas-shell.tsx +31 -9
- package/templates/runtime/react/controls-panel.test.tsx +478 -27
- package/templates/runtime/react/controls-panel.tsx +71 -26
- package/templates/runtime/react/runtime-public-api.test.tsx +1 -1
- package/templates/runtime/react/settings-transfer.test.ts +4 -0
- package/templates/runtime/react/settings-transfer.ts +6 -1
- package/templates/runtime/react/timeline-panel.test.tsx +14 -0
- package/templates/runtime/react/timeline-panel.tsx +44 -7
- package/templates/runtime/react/toolcraft-app.integration.test.tsx +9 -1
- package/templates/runtime/react/toolcraft-app.test.tsx +112 -3
- package/templates/runtime/react/toolcraft-app.tsx +56 -37
- package/templates/runtime/schema/define-toolcraft.test.ts +225 -173
- package/templates/runtime/schema/define-toolcraft.ts +117 -247
- package/templates/runtime/schema/runtime-targets.ts +21 -0
- package/templates/runtime/schema/types.ts +41 -0
- package/templates/runtime/state/create-template-state.test.ts +156 -0
- package/templates/runtime/state/create-template-state.ts +38 -8
- package/templates/runtime/state/media-defaults.ts +105 -0
- package/templates/runtime/state/persistence.test.ts +58 -0
- package/templates/runtime/state/persistence.ts +105 -1
- package/templates/runtime/state/reducer.test.ts +280 -4
- package/templates/runtime/state/reducer.ts +195 -9
- package/templates/runtime/state/timeline-loop.test.ts +71 -0
- package/templates/runtime/state/timeline-loop.ts +35 -0
- package/templates/runtime/state/types.ts +27 -0
- package/templates/runtime/testing/performance.test.ts +657 -2
- package/templates/runtime/testing/performance.ts +789 -49
- package/templates/starter/AGENTS.md +22 -16
- package/templates/starter/docs/toolcraft/README.md +8 -4
- package/templates/starter/docs/toolcraft/acceptance-testing.md +41 -8
- package/templates/starter/docs/toolcraft/agent-worklog.md +1 -0
- package/templates/starter/docs/toolcraft/assembly-workflow.md +51 -20
- package/templates/starter/docs/toolcraft/component-rules.md +58 -37
- package/templates/starter/docs/toolcraft/decision-contract.md +2 -0
- package/templates/starter/docs/toolcraft/performance.md +30 -10
- package/templates/starter/docs/toolcraft/schema-reference.md +134 -33
- package/templates/starter/docs/toolcraft/workflow.md +5 -2
- package/templates/starter/e2e/app-browser-acceptance.spec.ts +3 -3
- package/templates/starter/e2e/app-performance.spec.ts +57 -4
- package/templates/starter/e2e/performance-helpers.ts +45 -0
- package/templates/starter/index.html +1 -0
- package/templates/starter/package.json +3 -2
- package/templates/starter/playwright.config.ts +1 -1
- package/templates/starter/scripts/check-toolcraft-docs.mjs +1 -0
- package/templates/starter/scripts/run-vite-on-free-port.mjs +82 -16
- package/templates/starter/scripts/toolcraft-port.mjs +178 -0
- package/templates/starter/scripts/toolcraft-port.test.mjs +147 -0
- package/templates/starter/src/app/starter-acceptance.test.ts +2573 -313
- package/templates/starter/src/app/starter-acceptance.ts +978 -81
- package/templates/starter/src/app/starter-performance.test.ts +130 -8
- package/templates/starter/src/app/starter-performance.ts +5 -0
- package/templates/starter/src/app/starter-schema.test.ts +32 -7
- package/templates/starter/src/app/starter-schema.ts +6 -2
- package/templates/starter/vite.config.ts +58 -2
- package/templates/ui/components/controls/actions/actions-control.tsx +46 -3
- package/templates/ui/components/controls/color/palette-control.tsx +34 -4
- package/templates/ui/components/controls/file-drop/file-drop-control.tsx +186 -14
- package/templates/ui/components/controls/file-drop/index.ts +6 -1
- package/templates/ui/components/controls/index.ts +2 -0
- package/templates/ui/components/controls/range-slider/range-slider-value.ts +19 -5
- package/templates/ui/components/controls/select/select-control.tsx +4 -26
- package/templates/ui/components/controls/vector/vector-control.tsx +25 -4
- package/templates/ui/components/panel/panel-actions.tsx +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
# Toolcraft CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Create standalone Toolcraft apps from the command line.
|
|
4
|
+
|
|
5
|
+
Toolcraft is a Pixel Point starter kit and UI/runtime library for building
|
|
6
|
+
personal creative design tools with AI. It gives generated apps a ready canvas,
|
|
7
|
+
control panel, export flow, AI workflow instructions, and verification checks so
|
|
8
|
+
you can focus prompts on the visual idea instead of rebuilding common creative
|
|
9
|
+
tool plumbing.
|
|
10
|
+
|
|
11
|
+
You use Toolcraft when a custom tool is faster or clearer than forcing a broad
|
|
12
|
+
design app to fit one specific job: procedural graphics, image effects, branded
|
|
13
|
+
asset generators, animation controllers, shader experiments, or quick visual
|
|
14
|
+
tools for client work.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
4
17
|
|
|
5
18
|
```bash
|
|
6
19
|
npx @pixel-point/toolcraft create
|
|
@@ -12,14 +25,20 @@ After dependencies are installed, Toolcraft installs the required workflow skill
|
|
|
12
25
|
in a batch through the `skills` CLI. The skill installer uses the same agent,
|
|
13
26
|
scope, and installation prompts as `npx skills add`.
|
|
14
27
|
|
|
15
|
-
|
|
28
|
+
Example:
|
|
16
29
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
```bash
|
|
31
|
+
npx @pixel-point/toolcraft create my-ascii-tool
|
|
32
|
+
cd my-ascii-tool
|
|
33
|
+
pnpm dev
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then open the generated folder in Codex, Claude Code, Cursor, or another AI
|
|
37
|
+
coding agent and prompt for the creative output you want, for example:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
Build an app that applies an ASCII effect to an uploaded image.
|
|
41
|
+
```
|
|
23
42
|
|
|
24
43
|
Scripted usage:
|
|
25
44
|
|
|
@@ -50,5 +69,19 @@ Local tarball test, matching the published package layout:
|
|
|
50
69
|
cd cli
|
|
51
70
|
npm pack --pack-destination /tmp
|
|
52
71
|
cd ..
|
|
53
|
-
TOOLCRAFT_SKIP_INSTALL=1 TOOLCRAFT_SKIP_SKILLS=1 npm exec --package /tmp/pixel-point-toolcraft-0.0.
|
|
72
|
+
TOOLCRAFT_SKIP_INSTALL=1 TOOLCRAFT_SKIP_SKILLS=1 npm exec --package /tmp/pixel-point-toolcraft-0.0.10.tgz -- toolcraft create /tmp/toolcraft-pack-exec-test --name pack-exec-test --yes --force
|
|
54
73
|
```
|
|
74
|
+
|
|
75
|
+
## Learn more
|
|
76
|
+
|
|
77
|
+
- [How to craft personal design tools with AI](https://pixelpoint.io/blog/how-to-craft-personal-design-tools-with-toolcraft/)
|
|
78
|
+
- [Toolcraft YouTube tutorial](https://youtu.be/-QlmkGZLzFo)
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
Toolcraft is distributed under the Toolcraft Designer License in `LICENSE.md`.
|
|
83
|
+
Designer client work is permitted under that license. Using AI coding assistants
|
|
84
|
+
or agents such as Codex, Claude, ChatGPT, Cursor, or similar tools to work on
|
|
85
|
+
generated apps is permitted. Platform, generator, AI software product,
|
|
86
|
+
app-builder, website-builder, template-marketplace, and resale uses require a
|
|
87
|
+
separate commercial license from Pixel Point.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixel-point/toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@clack/prompts": "^1.6.0",
|
|
27
|
+
"cross-spawn": "^7.0.6",
|
|
27
28
|
"skills": "^1.5.12"
|
|
28
29
|
}
|
|
29
30
|
}
|
package/src/cli.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
1
|
import { createRequire } from "node:module";
|
|
3
2
|
import fs from "node:fs/promises";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
import readline from "node:readline/promises";
|
|
6
5
|
import { fileURLToPath } from "node:url";
|
|
7
6
|
|
|
7
|
+
import spawn from "cross-spawn";
|
|
8
8
|
import {
|
|
9
9
|
cancel,
|
|
10
10
|
confirm,
|
|
@@ -364,7 +364,7 @@ function runCommand(command, args, options = {}, context = {}) {
|
|
|
364
364
|
const child = spawn(command, args, {
|
|
365
365
|
cwd: options.cwd,
|
|
366
366
|
env: options.env,
|
|
367
|
-
shell:
|
|
367
|
+
shell: options.shell ?? false,
|
|
368
368
|
stdio: options.stdio ?? "inherit",
|
|
369
369
|
});
|
|
370
370
|
|
|
@@ -439,6 +439,7 @@ async function installToolcraftSkills(parsedOptions, targetDir, context = {}) {
|
|
|
439
439
|
...process.env,
|
|
440
440
|
...(context.env ?? {}),
|
|
441
441
|
},
|
|
442
|
+
shell: false,
|
|
442
443
|
stdio: "inherit",
|
|
443
444
|
},
|
|
444
445
|
context,
|
|
@@ -493,6 +494,7 @@ export async function runCreateCommand(parsedOptions, context = {}) {
|
|
|
493
494
|
...process.env,
|
|
494
495
|
...(context.env ?? {}),
|
|
495
496
|
},
|
|
497
|
+
shell: false,
|
|
496
498
|
stdio: "inherit",
|
|
497
499
|
},
|
|
498
500
|
context,
|
package/src/cli.test.mjs
CHANGED
|
@@ -259,7 +259,7 @@ describe("runToolcraftCli", () => {
|
|
|
259
259
|
throwOnError: true,
|
|
260
260
|
toolcraftSkillsSourceDir: skillsSourceDir,
|
|
261
261
|
async runCommand(command, args, options) {
|
|
262
|
-
installs.push({ args, command, cwd: options.cwd });
|
|
262
|
+
installs.push({ args, command, cwd: options.cwd, shell: options.shell });
|
|
263
263
|
},
|
|
264
264
|
});
|
|
265
265
|
|
|
@@ -268,11 +268,13 @@ describe("runToolcraftCli", () => {
|
|
|
268
268
|
args: ["install"],
|
|
269
269
|
command: "pnpm",
|
|
270
270
|
cwd: path.join(tempRoot, "install-app"),
|
|
271
|
+
shell: false,
|
|
271
272
|
},
|
|
272
273
|
{
|
|
273
274
|
args: [skillsCliPath, "add", skillsSourceDir, "--skill", "*", "--yes"],
|
|
274
275
|
command: process.execPath,
|
|
275
276
|
cwd: path.join(tempRoot, "install-app"),
|
|
277
|
+
shell: false,
|
|
276
278
|
},
|
|
277
279
|
]);
|
|
278
280
|
assert.match(stdout.text, /Installing dependencies with pnpm/);
|
|
@@ -308,7 +310,7 @@ describe("runToolcraftCli", () => {
|
|
|
308
310
|
throwOnError: true,
|
|
309
311
|
toolcraftSkillsSourceDir: skillsSourceDir,
|
|
310
312
|
async runCommand(command, args, options) {
|
|
311
|
-
skillsCommands.push({ args, command, cwd: options.cwd });
|
|
313
|
+
skillsCommands.push({ args, command, cwd: options.cwd, shell: options.shell });
|
|
312
314
|
},
|
|
313
315
|
},
|
|
314
316
|
);
|
|
@@ -331,6 +333,7 @@ describe("runToolcraftCli", () => {
|
|
|
331
333
|
],
|
|
332
334
|
command: process.execPath,
|
|
333
335
|
cwd: path.join(tempRoot, "skills-options-app"),
|
|
336
|
+
shell: false,
|
|
334
337
|
},
|
|
335
338
|
]);
|
|
336
339
|
});
|
package/src/generate.mjs
CHANGED
|
@@ -32,6 +32,10 @@ function escapeHtmlText(value) {
|
|
|
32
32
|
.replaceAll(">", ">");
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
function escapeHtmlAttribute(value) {
|
|
36
|
+
return escapeHtmlText(value).replaceAll('"', """);
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
async function readJson(filePath) {
|
|
36
40
|
return JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
37
41
|
}
|
|
@@ -114,12 +118,16 @@ async function restoreGeneratedGitignore(targetDir) {
|
|
|
114
118
|
async function writeGeneratedProjectTitle(targetDir, title) {
|
|
115
119
|
const indexPath = path.join(targetDir, "index.html");
|
|
116
120
|
const source = await fs.readFile(indexPath, "utf8");
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
const escapedTitle = escapeHtmlText(title);
|
|
122
|
+
const escapedTitleAttribute = escapeHtmlAttribute(title);
|
|
123
|
+
const withTitle = source.replace(/<title>.*?<\/title>/, `<title>${escapedTitle}</title>`);
|
|
124
|
+
const identityMeta = `<meta name="toolcraft-app-title" content="${escapedTitleAttribute}" />`;
|
|
125
|
+
const identityMetaPattern = /<meta\b(?=[^>]*\bname=["']toolcraft-app-title["'])[^>]*>/i;
|
|
126
|
+
const nextSource = identityMetaPattern.test(withTitle)
|
|
127
|
+
? withTitle.replace(identityMetaPattern, identityMeta)
|
|
128
|
+
: withTitle.replace(/<head>/i, `<head>\n ${identityMeta}`);
|
|
129
|
+
|
|
130
|
+
if (!nextSource.includes(`<title>${escapedTitle}</title>`) || !nextSource.includes(identityMeta)) {
|
|
123
131
|
throw new Error(`Unable to update generated app title in ${indexPath}.`);
|
|
124
132
|
}
|
|
125
133
|
|
package/src/generate.test.mjs
CHANGED
|
@@ -93,14 +93,18 @@ describe("generateToolcraft", () => {
|
|
|
93
93
|
packageJson.scripts.test,
|
|
94
94
|
"node scripts/check-toolcraft-docs.mjs && node scripts/check-toolcraft-integrity.mjs && node --test scripts/*.test.mjs && vitest run src --passWithNoTests",
|
|
95
95
|
);
|
|
96
|
-
assert.equal(
|
|
96
|
+
assert.equal(
|
|
97
|
+
packageJson.scripts["test:browser"],
|
|
98
|
+
'playwright install chromium && playwright test --grep-invert "browser perf:"',
|
|
99
|
+
);
|
|
97
100
|
assert.equal(
|
|
98
101
|
packageJson.scripts["test:browser:perf"],
|
|
99
|
-
"playwright install chromium && playwright test
|
|
102
|
+
"playwright install chromium && playwright test --grep \"browser perf:\" --workers=1 --pass-with-no-tests",
|
|
100
103
|
);
|
|
101
104
|
assert.equal(packageJson.scripts["verify:quick"], "pnpm ai:check && pnpm test");
|
|
102
105
|
assert.equal(packageJson.scripts["verify:ui"], "pnpm test:browser");
|
|
103
106
|
assert.equal(packageJson.scripts["verify:perf"], "pnpm test:browser:perf");
|
|
107
|
+
assert.equal(packageJson.scripts["verify:perf:playwright"], "pnpm test:browser:perf");
|
|
104
108
|
assert.equal(
|
|
105
109
|
packageJson.scripts["verify:final"],
|
|
106
110
|
"pnpm ai:check && pnpm test && pnpm build && pnpm test:browser",
|
|
@@ -108,6 +112,7 @@ describe("generateToolcraft", () => {
|
|
|
108
112
|
|
|
109
113
|
const indexHtmlSource = await fs.readFile(path.join(targetDir, "index.html"), "utf8");
|
|
110
114
|
assert.match(indexHtmlSource, /<title>Generated App<\/title>/);
|
|
115
|
+
assert.match(indexHtmlSource, /<meta name="toolcraft-app-title" content="Generated App" \/>/);
|
|
111
116
|
|
|
112
117
|
assert.ok(await fs.stat(path.join(targetDir, "playwright.config.ts")));
|
|
113
118
|
const playwrightConfigSource = await fs.readFile(
|
|
@@ -158,6 +163,9 @@ describe("generateToolcraft", () => {
|
|
|
158
163
|
"utf8",
|
|
159
164
|
);
|
|
160
165
|
assert.match(appPerformanceSource, /defineToolcraftPerformance/);
|
|
166
|
+
assert.match(appPerformanceSource, /preferredRunner:\s*"agent-browser"/);
|
|
167
|
+
assert.match(appPerformanceSource, /fallbackRunner:\s*"playwright"/);
|
|
168
|
+
assert.match(appPerformanceSource, /agent-browser-unavailable/);
|
|
161
169
|
assert.doesNotMatch(appPerformanceSource, /function\s+validateToolcraftPerformanceCoverage/);
|
|
162
170
|
assert.doesNotMatch(
|
|
163
171
|
appPerformanceSource,
|
|
@@ -337,4 +345,27 @@ describe("generateToolcraft", () => {
|
|
|
337
345
|
/Target directory is not empty/,
|
|
338
346
|
);
|
|
339
347
|
});
|
|
348
|
+
|
|
349
|
+
it("writes the app identity marker when the generated title matches the template title", async () => {
|
|
350
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "toolcraft-cli-"));
|
|
351
|
+
tempRoots.push(tempRoot);
|
|
352
|
+
|
|
353
|
+
await generateToolcraft({
|
|
354
|
+
cwd: tempRoot,
|
|
355
|
+
force: true,
|
|
356
|
+
name: "toolcraft-app-template",
|
|
357
|
+
targetDir: "toolcraft-app-template",
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const indexHtmlSource = await fs.readFile(
|
|
361
|
+
path.join(tempRoot, "toolcraft-app-template", "index.html"),
|
|
362
|
+
"utf8",
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
assert.match(indexHtmlSource, /<title>Toolcraft App Template<\/title>/);
|
|
366
|
+
assert.match(
|
|
367
|
+
indexHtmlSource,
|
|
368
|
+
/<meta name="toolcraft-app-title" content="Toolcraft App Template" \/>/,
|
|
369
|
+
);
|
|
370
|
+
});
|
|
340
371
|
});
|