rtistree 0.5.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/CHANGELOG.md +15 -0
- package/LICENSE +21 -0
- package/README.md +99 -0
- package/THIRD_PARTY_NOTICES.md +25 -0
- package/dist/art-direction.d.ts +77 -0
- package/dist/art-direction.js +186 -0
- package/dist/art-direction.js.map +1 -0
- package/dist/artifacts.d.ts +24 -0
- package/dist/artifacts.js +49 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/assets.d.ts +16 -0
- package/dist/assets.js +96 -0
- package/dist/assets.js.map +1 -0
- package/dist/benchmark.d.ts +76 -0
- package/dist/benchmark.js +118 -0
- package/dist/benchmark.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +300 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +5315 -0
- package/dist/commands.js +591 -0
- package/dist/commands.js.map +1 -0
- package/dist/components.d.ts +20 -0
- package/dist/components.js +98 -0
- package/dist/components.js.map +1 -0
- package/dist/construction.d.ts +53 -0
- package/dist/construction.js +80 -0
- package/dist/construction.js.map +1 -0
- package/dist/critique.d.ts +44 -0
- package/dist/critique.js +45 -0
- package/dist/critique.js.map +1 -0
- package/dist/detail.d.ts +5 -0
- package/dist/detail.js +52 -0
- package/dist/detail.js.map +1 -0
- package/dist/document.d.ts +64 -0
- package/dist/document.js +56 -0
- package/dist/document.js.map +1 -0
- package/dist/export.d.ts +405 -0
- package/dist/export.js +344 -0
- package/dist/export.js.map +1 -0
- package/dist/font-inspection.d.ts +8 -0
- package/dist/font-inspection.js +34 -0
- package/dist/font-inspection.js.map +1 -0
- package/dist/graphics.d.ts +9 -0
- package/dist/graphics.js +72 -0
- package/dist/graphics.js.map +1 -0
- package/dist/history-storage.d.ts +10 -0
- package/dist/history-storage.js +59 -0
- package/dist/history-storage.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +18 -0
- package/dist/layout.js +155 -0
- package/dist/layout.js.map +1 -0
- package/dist/loader.d.ts +9 -0
- package/dist/loader.js +108 -0
- package/dist/loader.js.map +1 -0
- package/dist/mcp.d.ts +4 -0
- package/dist/mcp.js +223 -0
- package/dist/mcp.js.map +1 -0
- package/dist/merge.d.ts +6 -0
- package/dist/merge.js +49 -0
- package/dist/merge.js.map +1 -0
- package/dist/native.d.ts +3 -0
- package/dist/native.js +6 -0
- package/dist/native.js.map +1 -0
- package/dist/paint.d.ts +9 -0
- package/dist/paint.js +331 -0
- package/dist/paint.js.map +1 -0
- package/dist/path-edit.d.ts +20 -0
- package/dist/path-edit.js +30 -0
- package/dist/path-edit.js.map +1 -0
- package/dist/pdf-output.d.ts +21 -0
- package/dist/pdf-output.js +297 -0
- package/dist/pdf-output.js.map +1 -0
- package/dist/pipeline.d.ts +39 -0
- package/dist/pipeline.js +227 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/print-scene.d.ts +490 -0
- package/dist/print-scene.js +55 -0
- package/dist/print-scene.js.map +1 -0
- package/dist/production-workflow.d.ts +251 -0
- package/dist/production-workflow.js +335 -0
- package/dist/production-workflow.js.map +1 -0
- package/dist/program-worker.d.ts +1 -0
- package/dist/program-worker.js +31 -0
- package/dist/program-worker.js.map +1 -0
- package/dist/program.d.ts +85 -0
- package/dist/program.js +246 -0
- package/dist/program.js.map +1 -0
- package/dist/project-config.d.ts +91 -0
- package/dist/project-config.js +187 -0
- package/dist/project-config.js.map +1 -0
- package/dist/project.d.ts +897 -0
- package/dist/project.js +347 -0
- package/dist/project.js.map +1 -0
- package/dist/raster-edit.d.ts +61 -0
- package/dist/raster-edit.js +91 -0
- package/dist/raster-edit.js.map +1 -0
- package/dist/raster-studio.d.ts +84 -0
- package/dist/raster-studio.js +236 -0
- package/dist/raster-studio.js.map +1 -0
- package/dist/render.d.ts +79 -0
- package/dist/render.js +437 -0
- package/dist/render.js.map +1 -0
- package/dist/rich-text.d.ts +10 -0
- package/dist/rich-text.js +56 -0
- package/dist/rich-text.js.map +1 -0
- package/dist/schema.d.ts +1912 -0
- package/dist/schema.js +571 -0
- package/dist/schema.js.map +1 -0
- package/dist/spatial.d.ts +10 -0
- package/dist/spatial.js +29 -0
- package/dist/spatial.js.map +1 -0
- package/dist/studio-reference.d.ts +153 -0
- package/dist/studio-reference.js +39 -0
- package/dist/studio-reference.js.map +1 -0
- package/dist/svg.d.ts +5 -0
- package/dist/svg.js +315 -0
- package/dist/svg.js.map +1 -0
- package/dist/techniques.d.ts +74 -0
- package/dist/techniques.js +164 -0
- package/dist/techniques.js.map +1 -0
- package/dist/verify.d.ts +36 -0
- package/dist/verify.js +277 -0
- package/dist/verify.js.map +1 -0
- package/dist/viewport.d.ts +3 -0
- package/dist/viewport.js +71 -0
- package/dist/viewport.js.map +1 -0
- package/dist/workflow.d.ts +53 -0
- package/dist/workflow.js +66 -0
- package/dist/workflow.js.map +1 -0
- package/docs/agent-art-workflow.md +201 -0
- package/docs/agent-setup.md +83 -0
- package/docs/atelier.md +117 -0
- package/docs/cli-reference.md +61 -0
- package/docs/decisions/001-runtime-and-renderer.md +35 -0
- package/docs/decisions/002-composable-scenes-and-history.md +28 -0
- package/docs/decisions/003-editing-refinement-and-agent-validation.md +45 -0
- package/docs/decisions/004-print-production-and-projects.md +19 -0
- package/docs/decisions/005-programmable-digital-art.md +15 -0
- package/docs/decisions/006-staged-2d-art-production.md +13 -0
- package/docs/decisions/007-foundation-first-art-direction.md +13 -0
- package/docs/decisions/008-agent-onboarding-and-trial-lessons.md +48 -0
- package/docs/engine-overview.md +174 -0
- package/docs/evolution.md +179 -0
- package/docs/getting-started.md +107 -0
- package/docs/next-milestone.md +61 -0
- package/docs/previews/a3-production.json +36 -0
- package/docs/previews/benchmark.json +42 -0
- package/docs/previews/landscape.png +0 -0
- package/docs/previews/performance.json +24 -0
- package/docs/previews/poster.png +0 -0
- package/docs/production.md +107 -0
- package/docs/releasing.md +60 -0
- package/docs/scene-format.md +126 -0
- package/docs/studio.md +108 -0
- package/examples/hello/README.md +14 -0
- package/examples/hello/refine.json +10 -0
- package/examples/hello/render.mjs +9 -0
- package/examples/hello/scene.json +19 -0
- package/licenses/DM-Serif-Display-OFL.txt +93 -0
- package/licenses/Inter-OFL.txt +93 -0
- package/package.json +84 -0
- package/schemas/authoring.schema.json +2177 -0
- package/schemas/brief.schema.json +57 -0
- package/schemas/command.schema.json +3154 -0
- package/schemas/construction.schema.json +159 -0
- package/schemas/critique.schema.json +101 -0
- package/schemas/document.schema.json +41 -0
- package/schemas/export.schema.json +89 -0
- package/schemas/fragment.schema.json +1861 -0
- package/schemas/patch.schema.json +3177 -0
- package/schemas/pipeline.schema.json +205 -0
- package/schemas/production.schema.json +452 -0
- package/schemas/program.schema.json +152 -0
- package/schemas/project.schema.json +115 -0
- package/schemas/raster-read.schema.json +42 -0
- package/schemas/raster-write.schema.json +75 -0
- package/schemas/scene.schema.json +2014 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.5.0 — Initial public alpha
|
|
4
|
+
|
|
5
|
+
- Deterministic JSON/YAML scenes, CPU Skia rendering and portable project exports.
|
|
6
|
+
- CLI, TypeScript SDK and MCP server with inspection, focused edits and undo/redo.
|
|
7
|
+
- Seeded painting programs, replayable recipes, dependency pipelines and staged art production.
|
|
8
|
+
- Versioned art-direction guidance shipped through MCP initialization, resources, CLI and SDK.
|
|
9
|
+
- Figure, prop and locomotion checks informed by recorded asset-trial feedback.
|
|
10
|
+
- Technical, functional and visual review criteria kept separate.
|
|
11
|
+
- MIT license, installation examples, documentation website and package installation checks.
|
|
12
|
+
|
|
13
|
+
Pre-1.0 APIs may change. Byte-identical rendering is tested on the same pinned runtime
|
|
14
|
+
and platform; cross-platform byte identity is not promised. The engine does not include
|
|
15
|
+
an LLM or an automatic artistic-quality judge.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Colum Ferry
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Rtistree
|
|
2
|
+
|
|
3
|
+
**Programmable graphics for AI agents.** Create reproducible artwork, inspect the result,
|
|
4
|
+
refine individual parts, and export usable assets with built-in art-direction guidance.
|
|
5
|
+
|
|
6
|
+
[Documentation](https://rtistree.dev/docs/getting-started/) · [Examples](https://rtistree.dev/examples/) · [Contributing](CONTRIBUTING.md)
|
|
7
|
+
|
|
8
|
+
Rtistree is a local Node.js graphics engine with a CLI, TypeScript SDK and MCP server.
|
|
9
|
+
JSON/YAML scenes retain structure; seeded painting programs bake to replayable raster assets.
|
|
10
|
+
Edits have history and undo. PNG, JPEG, TIFF, PDF, SVG and portable projects are outputs.
|
|
11
|
+
Rendering uses native CPU Skia and requires no model API key, browser or GPU.
|
|
12
|
+
|
|
13
|
+
**Public alpha · v0.5.0 · MIT.** APIs may change before 1.0. Technical checks do not judge artistic quality.
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
## Install and make your first image
|
|
18
|
+
|
|
19
|
+
Requires Node.js 22 or newer. Linux and macOS are covered by CI.
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
mkdir my-art
|
|
23
|
+
cd my-art
|
|
24
|
+
npm init -y
|
|
25
|
+
npm install https://github.com/Coly010/rtistree/releases/download/v0.5.0/rtistree-0.5.0.tgz
|
|
26
|
+
mkdir hello
|
|
27
|
+
cp node_modules/rtistree/examples/hello/scene.json hello/
|
|
28
|
+
cp node_modules/rtistree/examples/hello/refine.json hello/
|
|
29
|
+
npx graphics render hello/scene.json -o hello.png
|
|
30
|
+
npx graphics apply hello/scene.json hello/refine.json
|
|
31
|
+
npx graphics render hello/scene.json -o brighter.png
|
|
32
|
+
npx graphics undo hello/scene.json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
See the [complete quickstart](docs/getting-started.md) for the scene source, SDK example and export.
|
|
36
|
+
`npx graphics --help` lists commands. Structured output is JSON. Verification failures exit 2;
|
|
37
|
+
invalid input and other failures exit 1.
|
|
38
|
+
|
|
39
|
+
## Use with an agent
|
|
40
|
+
|
|
41
|
+
Read the included guidance before creating artwork:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
npx graphics art-guide
|
|
45
|
+
npx graphics serve /absolute/path/to/hello/scene.json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The second command starts the MCP server over stdio. See [agent setup](docs/agent-setup.md)
|
|
49
|
+
for a client configuration and SDK integration.
|
|
50
|
+
|
|
51
|
+
MCP clients receive concise instructions during initialization and can read the full guide
|
|
52
|
+
through `studioHelp` or `rtistree://guides/art-direction`. CLI agents use `graphics art-guide`;
|
|
53
|
+
SDK hosts can expose `rtistreeAgentInstructions` and `artDirectionGuide`.
|
|
54
|
+
|
|
55
|
+
The guide covers representative samples, silhouettes, connected anatomy, prop perspective,
|
|
56
|
+
ground contact and separate technical, functional and visual acceptance. Availability does
|
|
57
|
+
not guarantee that every client supplies the guidance or every model follows it.
|
|
58
|
+
|
|
59
|
+
## Learn the engine
|
|
60
|
+
|
|
61
|
+
- [Getting started](docs/getting-started.md) and [CLI reference](docs/cli-reference.md)
|
|
62
|
+
- [Agent setup](docs/agent-setup.md) and [art-direction workflow](docs/agent-art-workflow.md)
|
|
63
|
+
- [Scene format](docs/scene-format.md) and [programmable studio](docs/studio.md)
|
|
64
|
+
- [Staged production](docs/atelier.md) and [print/export](docs/production.md)
|
|
65
|
+
- [Recorded asset trial](examples/warden-asset-trial/README.md)
|
|
66
|
+
- [Architecture and evidence](docs/engine-overview.md), [changelog](CHANGELOG.md), [release process](docs/releasing.md)
|
|
67
|
+
|
|
68
|
+
The npm package includes a small runnable starter, documentation and schemas. Larger trials
|
|
69
|
+
live in the GitHub repository; clone it to reproduce them.
|
|
70
|
+
|
|
71
|
+
## Development
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
git clone https://github.com/Coly010/rtistree.git
|
|
75
|
+
cd rtistree
|
|
76
|
+
npm ci
|
|
77
|
+
npm run check
|
|
78
|
+
npm run demo
|
|
79
|
+
npm run package:check
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For website development and contribution guidance, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
83
|
+
|
|
84
|
+
## Limits and trust
|
|
85
|
+
|
|
86
|
+
Byte-identical replay is tested on the same pinned runtime and platform; cross-platform
|
|
87
|
+
Skia byte identity is not promised. The working renderer is 8-bit sRGB. Print exports do
|
|
88
|
+
not claim PDF/X certification, spot colours or overprint.
|
|
89
|
+
|
|
90
|
+
Programs and recipes execute trusted JavaScript. The VM is **not a security sandbox**;
|
|
91
|
+
normal rendering only reads baked assets. See [SECURITY.md](SECURITY.md).
|
|
92
|
+
|
|
93
|
+
Rtistree includes neither an LLM nor an automatic visual critic. An agent or human must
|
|
94
|
+
inspect the images and author honest reviews.
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
Copyright © 2026 Colum Ferry. [MIT](LICENSE). Fonts and dependencies retain their own
|
|
99
|
+
licenses; see [third-party notices](THIRD_PARTY_NOTICES.md).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
Rtistree source is MIT licensed. Dependencies and third-party assets retain their own licenses.
|
|
4
|
+
|
|
5
|
+
The following direct runtime dependencies are installed separately by npm; their packages include upstream notices.
|
|
6
|
+
|
|
7
|
+
| Package | Version | License |
|
|
8
|
+
| ---------------------------- | ------- | ---------- |
|
|
9
|
+
| @fontsource/dm-serif-display | 5.3.0 | OFL-1.1 |
|
|
10
|
+
| @fontsource/inter | 5.3.0 | OFL-1.1 |
|
|
11
|
+
| @modelcontextprotocol/sdk | 1.30.0 | MIT |
|
|
12
|
+
| @napi-rs/canvas | 1.0.8 | MIT |
|
|
13
|
+
| fast-xml-parser | 5.11.1 | MIT |
|
|
14
|
+
| fontkit | 2.0.4 | MIT |
|
|
15
|
+
| pdf-lib | 1.17.1 | MIT |
|
|
16
|
+
| pdfkit | 0.20.2 | MIT |
|
|
17
|
+
| sharp | 0.35.4 | Apache-2.0 |
|
|
18
|
+
| yaml | 2.9.0 | ISC |
|
|
19
|
+
| zod | 4.5.4 | MIT |
|
|
20
|
+
|
|
21
|
+
Inter and DM Serif Display are redistributed under SIL OFL 1.1. Full copyright and license texts are in `licenses/`. Website font copies retain the Inter notice. Fontsource distributes these fonts; the font licenses are distinct from package code licenses.
|
|
22
|
+
|
|
23
|
+
Native Skia, sharp/libvips and their transitive libraries retain their upstream licenses and notices in the distributed dependency packages. Preserve those notices when redistributing a bundled application.
|
|
24
|
+
|
|
25
|
+
Recorded trials may reference outside artwork or generated inputs; those references do not grant rights to the referenced material. The website gallery uses the locally authored procedural Verdigris Watch assets.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/** Ship this compact instruction with an agent's tool context; the guide is available separately. */
|
|
2
|
+
export declare const artDirectionResourceUri = "rtistree://guides/art-direction";
|
|
3
|
+
export declare const rtistreeAgentInstructions: string;
|
|
4
|
+
/** Machine-readable practice guide, returned by CLI/MCP studioHelp as well as the SDK. */
|
|
5
|
+
export declare const artDirectionGuide: {
|
|
6
|
+
version: string;
|
|
7
|
+
agent_instructions: string;
|
|
8
|
+
purpose: string;
|
|
9
|
+
sequence: {
|
|
10
|
+
step: string;
|
|
11
|
+
deliverable: string;
|
|
12
|
+
stop: string;
|
|
13
|
+
}[];
|
|
14
|
+
subject_checks: {
|
|
15
|
+
articulated_figures: {
|
|
16
|
+
construction: string;
|
|
17
|
+
inspect: string;
|
|
18
|
+
blocking_examples: string[];
|
|
19
|
+
};
|
|
20
|
+
constructed_props: {
|
|
21
|
+
construction: string;
|
|
22
|
+
inspect: string;
|
|
23
|
+
blocking_examples: string[];
|
|
24
|
+
};
|
|
25
|
+
locomotion: {
|
|
26
|
+
construction: string;
|
|
27
|
+
inspect: string;
|
|
28
|
+
functional_checks: string[];
|
|
29
|
+
limitation: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
acceptance: {
|
|
33
|
+
technical: string;
|
|
34
|
+
functional: string;
|
|
35
|
+
visual: string;
|
|
36
|
+
reporting: string;
|
|
37
|
+
partial_feedback: string;
|
|
38
|
+
unavailable_observation: string;
|
|
39
|
+
};
|
|
40
|
+
trial_lessons: {
|
|
41
|
+
case: string;
|
|
42
|
+
missed: string;
|
|
43
|
+
correction: string;
|
|
44
|
+
remaining: string;
|
|
45
|
+
}[];
|
|
46
|
+
starter_plan: {
|
|
47
|
+
id: string;
|
|
48
|
+
brief: string;
|
|
49
|
+
stages: ({
|
|
50
|
+
id: string;
|
|
51
|
+
goal: string;
|
|
52
|
+
minimum_alternatives: number;
|
|
53
|
+
require_references: boolean;
|
|
54
|
+
criteria: {
|
|
55
|
+
id: string;
|
|
56
|
+
description: string;
|
|
57
|
+
}[];
|
|
58
|
+
} | {
|
|
59
|
+
minimum_alternatives?: undefined;
|
|
60
|
+
require_references?: undefined;
|
|
61
|
+
id: string;
|
|
62
|
+
goal: string;
|
|
63
|
+
criteria: {
|
|
64
|
+
id: string;
|
|
65
|
+
description: string;
|
|
66
|
+
}[];
|
|
67
|
+
})[];
|
|
68
|
+
};
|
|
69
|
+
review_order: string[];
|
|
70
|
+
calibration: {
|
|
71
|
+
fail: string;
|
|
72
|
+
revise: string;
|
|
73
|
+
ready: string;
|
|
74
|
+
};
|
|
75
|
+
protocol: string;
|
|
76
|
+
instructions: string;
|
|
77
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/** Ship this compact instruction with an agent's tool context; the guide is available separately. */
|
|
2
|
+
export const artDirectionResourceUri = 'rtistree://guides/art-direction';
|
|
3
|
+
export const rtistreeAgentInstructions = [
|
|
4
|
+
'Rtistree renders deterministic graphics; successful rendering, verification, replay or export does not establish artistic quality.',
|
|
5
|
+
`Before creating or substantially revising artwork, read studioHelp or the ${artDirectionResourceUri} resource (CLI: graphics art-guide; SDK: artDirectionGuide).`,
|
|
6
|
+
'Start with a representative sample before expanding an asset family. Explore distinct silhouettes, establish named connected landmarks and shared perspective guides, then inspect grayscale construction before adding detail.',
|
|
7
|
+
'Define subject-specific checks: trace shoulder–elbow–wrist attachments and equipment ownership; construct prop surfaces and fittings in one perspective; inspect animation in playback against ground contact, including travel direction, joint bend and foot lift.',
|
|
8
|
+
'Inspect actual rendered images at intended display size and full size. A contact sheet or unique frame hashes cannot establish correct motion. If vision or playback is unavailable, state that the corresponding visual review is incomplete.',
|
|
9
|
+
'Fix structural defects before adding texture. Preserve the parent, state the correction hypothesis, compare images and retain failed reviews. User rejection overrides an earlier favourable agent assessment; partial praise approves only the stated aspect.',
|
|
10
|
+
'Use production plan/capture/review/select/advance for enforced review gates. Report technical correctness, functional checks and visual acceptance separately. Do not infer a human review or require a new human approval unless the user or agreed plan requires one.',
|
|
11
|
+
'These instructions support judgment; they cannot certify taste, truthful observation or natural motion. Low-level rendering remains usable without a production session.',
|
|
12
|
+
].join('\n');
|
|
13
|
+
/** Machine-readable practice guide, returned by CLI/MCP studioHelp as well as the SDK. */
|
|
14
|
+
export const artDirectionGuide = {
|
|
15
|
+
version: 'foundation-first/2',
|
|
16
|
+
agent_instructions: rtistreeAgentInstructions,
|
|
17
|
+
purpose: 'Improve decisions through observable revision. This process cannot guarantee artistic excellence and does not supply an automatic critic.',
|
|
18
|
+
sequence: [
|
|
19
|
+
{
|
|
20
|
+
step: 'scope',
|
|
21
|
+
deliverable: 'A brief naming the medium, target display size, reference standard and representative sample. For an asset family, establish one character and one representative prop before producing variants or a full animation set.',
|
|
22
|
+
stop: 'Do not multiply an unproven construction across many assets.',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
step: 'study',
|
|
26
|
+
deliverable: 'Reference log with source, observation and intended application. Separate anatomical facts from invented design.',
|
|
27
|
+
stop: 'If you cannot describe the relevant structure, study it before drawing.',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
step: 'explore',
|
|
31
|
+
deliverable: 'At least three materially different gesture/silhouette candidates. Vary action, balance, overlap and negative space, not just scale or position.',
|
|
32
|
+
stop: 'Do not mistake shared prompt elements for equivalent artistic quality.',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
step: 'construct',
|
|
36
|
+
deliverable: 'Named landmarks, connected guide curves, overlapping masses and a single stated view/pose.',
|
|
37
|
+
stop: 'Block disconnected joints, unreadable appendages, accidental tangencies and contradictory perspective.',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
step: 'value',
|
|
41
|
+
deliverable: 'Plain-background grayscale study: large light/shadow masses, one light direction, controlled edges. Inspect at thumbnail size, full size and mirrored.',
|
|
42
|
+
stop: 'Do not add texture, scenery or effects to distract from a weak drawing.',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
step: 'revise',
|
|
46
|
+
deliverable: 'Name the defect, predict a structural correction, save the parent, make the edit, compare the actual images and record improved/unchanged/worse with visible evidence.',
|
|
47
|
+
stop: 'If two structural revisions do not improve the drawing, change the construction or pose; do not accumulate detail.',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
step: 'review',
|
|
51
|
+
deliverable: 'Criterion-specific verdicts and blocking issues, supported by observed images and functional checks. Use independent or human judgment when required by the user or agreed plan; do not invent an approval requirement.',
|
|
52
|
+
stop: 'Do not self-certify professional quality. Reviewer identity is declared, not authenticated by this local tool.',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
subject_checks: {
|
|
56
|
+
articulated_figures: {
|
|
57
|
+
construction: 'Name shoulder, elbow, wrist, hip, knee and ankle landmarks; connect contours and equipment to those landmarks. State which hand holds each object and the intended front-to-back order.',
|
|
58
|
+
inspect: 'Trace each complete limb, including hidden portions, and check balance, silhouette, joint bend and equipment clearance at full and target size. A shared landmark graph can still encode a bad pose.',
|
|
59
|
+
blocking_examples: [
|
|
60
|
+
'A sword elbow crosses the torso beneath the shield arm to reach an overhead weapon.',
|
|
61
|
+
'Disconnected helmet planes make its dome, visor and jaw impossible to read as one form.',
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
constructed_props: {
|
|
65
|
+
construction: 'Use a shared perspective grid or projection for adjoining faces, lid curves, planks, bands, hinges and latch. Draw visible portions according to surface overlap.',
|
|
66
|
+
inspect: 'Check shared corners, edge convergence or parallel directions, surface attachment and occlusion before grain, scratches or rivets. Distinct shaded faces are insufficient evidence of consistent perspective.',
|
|
67
|
+
blocking_examples: [
|
|
68
|
+
'Chest lid and body follow unrelated angles.',
|
|
69
|
+
'The far side of a metal hoop is drawn over the near lid surface and reads as a floating handle.',
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
locomotion: {
|
|
73
|
+
construction: 'Declare facing, travel direction, ground plane, cycle duration, stance/recovery phases and intended stride distance. Derive drawings and functional checks from the same rig definition.',
|
|
74
|
+
inspect: 'Watch a complete loop against ground markers, then step through contact, passing and recovery frames. Check feet, knees, body rise, arm overlap and loop continuity; also inspect at intended game size.',
|
|
75
|
+
functional_checks: [
|
|
76
|
+
'During planted stance, foot velocity relative to the body plus body velocity should be approximately zero in world space.',
|
|
77
|
+
'For ordinary forward walking, the recovering foot travels forward relative to the body while lifted; planted feet remain on the declared ground.',
|
|
78
|
+
'Check reachability, segment lengths, joint angles and foot clearance across the cycle, not just the exported frames. Declare any intentional stretch or stylisation.',
|
|
79
|
+
'Bind checks to the actual rendered rig or frozen source. Verify frame order, duration, pivots and loop closure; uniqueness of PNG hashes is only a file property.',
|
|
80
|
+
],
|
|
81
|
+
limitation: 'Correct contact direction and fixed bone lengths do not establish natural movement. Excessive knee flexion and foot lift still need visual correction. Other gaits require their own criteria.',
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
acceptance: {
|
|
85
|
+
technical: 'Schema validity, reproducible bytes, alpha margins and correct export/atlas coordinates.',
|
|
86
|
+
functional: 'Subject-specific geometry or behaviour checks, such as contact direction and rig continuity.',
|
|
87
|
+
visual: 'Observed construction, gesture, perspective, readability, materials and motion against the brief.',
|
|
88
|
+
reporting: 'Report each axis with its evidence and unresolved issues. A pass on one axis never implies the others. Preserve user feedback verbatim or faithfully summarised, tied to the candidate and aspect reviewed.',
|
|
89
|
+
partial_feedback: 'Walking forward confirms direction; much better confirms relative improvement. Neither implies approval of every asset or production readiness.',
|
|
90
|
+
unavailable_observation: 'Mark unobserved images or motion as unreviewed. Do not fabricate viewing evidence or substitute mechanical test results.',
|
|
91
|
+
},
|
|
92
|
+
trial_lessons: [
|
|
93
|
+
{
|
|
94
|
+
case: 'Verdigris Watch: reversed walk despite eight distinct frames',
|
|
95
|
+
missed: 'Lifted feet moved backward and planted feet moved forward relative to the body.',
|
|
96
|
+
correction: 'Separate contact and recovery, inspect ground-relative motion, and test the same function baked into the sprites.',
|
|
97
|
+
remaining: 'Direction improved before knee bend, helmet and arm construction were satisfactory.',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
case: 'Verdigris Watch: chest and guard construction',
|
|
101
|
+
missed: 'A common palette and successful renders concealed contradictory prop perspective and crossed limb chains.',
|
|
102
|
+
correction: 'Rebuild the chest on one guide grid; hide occluded hoop sections; move the sword elbow outside the torso; simplify connected helmet shells.',
|
|
103
|
+
remaining: 'The user described revision 4 as much better; this establishes improvement, not blanket professional-quality certification.',
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
// A valid production plan template. Replace the brief and adapt criteria to the actual subject.
|
|
107
|
+
starter_plan: {
|
|
108
|
+
id: 'representative-asset-study',
|
|
109
|
+
brief: 'Replace with the requested subject, medium, reference standard and target display size before starting this plan.',
|
|
110
|
+
stages: [
|
|
111
|
+
{
|
|
112
|
+
id: 'exploration',
|
|
113
|
+
goal: 'Compare materially different constructions of one representative sample.',
|
|
114
|
+
minimum_alternatives: 3,
|
|
115
|
+
require_references: true,
|
|
116
|
+
criteria: [
|
|
117
|
+
{
|
|
118
|
+
id: 'silhouette',
|
|
119
|
+
description: 'Action, negative spaces and subject are readable at target size.',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: 'construction',
|
|
123
|
+
description: 'Landmarks, attachments, balance and perspective are coherent.',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: 'sample-review',
|
|
129
|
+
goal: 'Resolve construction and applicable functional defects before expanding the asset family.',
|
|
130
|
+
criteria: [
|
|
131
|
+
{
|
|
132
|
+
id: 'structure',
|
|
133
|
+
description: 'Grayscale and full-size inspection find no unresolved structural blockers.',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: 'function',
|
|
137
|
+
description: 'Subject-specific behaviour and geometry were checked; for static art, name the applicable static checks.',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'appearance',
|
|
141
|
+
description: 'The actual images and any animation were observed at intended size against the brief.',
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: 'family-review',
|
|
147
|
+
goal: 'Review expanded assets and exports without substituting mechanical success for visual acceptance.',
|
|
148
|
+
criteria: [
|
|
149
|
+
{
|
|
150
|
+
id: 'consistency',
|
|
151
|
+
description: 'Variants preserve the approved construction, style, scale and attachments.',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: 'function',
|
|
155
|
+
description: 'Variants and any full animation loop retain the applicable functional checks.',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: 'visual',
|
|
159
|
+
description: 'Each delivered asset has been visually inspected; remaining failures are explicit.',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: 'technical',
|
|
163
|
+
description: 'Replay, alpha, frame metadata and applicable exports have been checked.',
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
review_order: [
|
|
170
|
+
'gesture and balance',
|
|
171
|
+
'silhouette and negative space',
|
|
172
|
+
'construction and anatomy',
|
|
173
|
+
'perspective and overlap',
|
|
174
|
+
'value grouping and lighting',
|
|
175
|
+
'edge hierarchy',
|
|
176
|
+
'only then materials and marks',
|
|
177
|
+
],
|
|
178
|
+
calibration: {
|
|
179
|
+
fail: 'Recognizable subject but stiff, inconsistent, flat, or structurally implausible. Prompt compliance does not rescue it.',
|
|
180
|
+
revise: 'A promising construction with identifiable problems; retain as a work in progress, not a finished success.',
|
|
181
|
+
ready: 'Foundation remains convincing without colour, texture, scenery or effects; no structural blockers remain, and the intended quality standard has been met by the required reviewer.',
|
|
182
|
+
},
|
|
183
|
+
protocol: 'production plans can require reference observations, minimum alternatives and human review per stage. Capture revisions with parent and hypothesis; reviews must compare against that parent. Blocking issues always prevent selection and advance, regardless of numerical scores. Reviews are retained in review_history; changes to ratings do not erase prior criticism.',
|
|
184
|
+
instructions: 'Use named 2D construction guides through art.guides({points,paths}) to make structural changes propagate to connected paths. Define subject-specific anatomy in the artwork, not in engine commands. Open every candidate image before reviewing it. Returned paths and hashes do not establish that a reviewer actually looked at it.',
|
|
185
|
+
};
|
|
186
|
+
//# sourceMappingURL=art-direction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"art-direction.js","sourceRoot":"","sources":["../src/art-direction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Scene } from './schema.js';
|
|
2
|
+
import type { RenderResult } from './render.js';
|
|
3
|
+
export declare function writeArtifact(file: string, data: string | Uint8Array): Promise<void>;
|
|
4
|
+
export declare function writeRender(file: string, result: RenderResult): Promise<{
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
colour_space: 'srgb';
|
|
8
|
+
quality: string;
|
|
9
|
+
region?: import("./schema.js").Bounds;
|
|
10
|
+
layer?: string;
|
|
11
|
+
png_hash: string;
|
|
12
|
+
excluded_layers?: string[];
|
|
13
|
+
canvas_transform?: import("./layout.js").Matrix;
|
|
14
|
+
image: string;
|
|
15
|
+
evidence: string;
|
|
16
|
+
statistics: {
|
|
17
|
+
rasterized_layers: number;
|
|
18
|
+
cached_layers: number;
|
|
19
|
+
cache_bytes: number;
|
|
20
|
+
viewport_pixels?: number;
|
|
21
|
+
};
|
|
22
|
+
}>;
|
|
23
|
+
/** Write a fresh baseline with immutable local assets, without any history. */
|
|
24
|
+
export declare function writeSceneBundle(input: Scene, root: string, file: string): Promise<void>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, extname, join } from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { localAssetPath, sha256 } from './assets.js';
|
|
5
|
+
import { copyRecipe } from './program.js';
|
|
6
|
+
export async function writeArtifact(file, data) {
|
|
7
|
+
await mkdir(dirname(file), { recursive: true });
|
|
8
|
+
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
9
|
+
await writeFile(temporary, data);
|
|
10
|
+
await rename(temporary, file);
|
|
11
|
+
}
|
|
12
|
+
export async function writeRender(file, result) {
|
|
13
|
+
if (extname(file).toLowerCase() !== '.png')
|
|
14
|
+
throw new Error('writeRender writes PNG only; use artwork export for other formats');
|
|
15
|
+
await writeArtifact(file, result.png);
|
|
16
|
+
await writeArtifact(`${file}.evidence.json`, JSON.stringify(result.evidence, null, 2) + '\n');
|
|
17
|
+
return {
|
|
18
|
+
image: file,
|
|
19
|
+
evidence: `${file}.evidence.json`,
|
|
20
|
+
...result.evidence.render,
|
|
21
|
+
statistics: result.statistics,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Write a fresh baseline with immutable local assets, without any history. */
|
|
25
|
+
export async function writeSceneBundle(input, root, file) {
|
|
26
|
+
const scene = structuredClone(input);
|
|
27
|
+
for (const asset of Object.values(scene.assets)) {
|
|
28
|
+
if (asset.recipe)
|
|
29
|
+
asset.recipe = await copyRecipe(root, asset.recipe, dirname(file));
|
|
30
|
+
const source = await localAssetPath(root, asset.source), bytes = await readFile(source), hash = sha256(bytes);
|
|
31
|
+
if (asset.hash && asset.hash !== hash)
|
|
32
|
+
throw new Error('Asset changed before export');
|
|
33
|
+
const relative = join('assets', `${hash.slice(7)}${extname(source).toLowerCase()}`);
|
|
34
|
+
await writeArtifact(join(dirname(file), relative), bytes);
|
|
35
|
+
asset.source = relative;
|
|
36
|
+
asset.hash = hash;
|
|
37
|
+
}
|
|
38
|
+
for (const font of Object.values(scene.fonts ?? {})) {
|
|
39
|
+
const source = await localAssetPath(root, font.source), bytes = await readFile(source), hash = sha256(bytes);
|
|
40
|
+
if (font.hash && font.hash !== hash)
|
|
41
|
+
throw new Error('Font changed before export');
|
|
42
|
+
const path = join('fonts', `${hash.slice(7)}${extname(source)}`);
|
|
43
|
+
await writeArtifact(join(dirname(file), path), bytes);
|
|
44
|
+
font.source = path;
|
|
45
|
+
font.hash = hash;
|
|
46
|
+
}
|
|
47
|
+
await writeArtifact(file, JSON.stringify(scene, null, 2) + '\n');
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=artifacts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../src/artifacts.ts"],"names":[],"mappings":"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"}
|
package/dist/assets.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Image } from './native.js';
|
|
2
|
+
import type { Scene } from './schema.js';
|
|
3
|
+
export declare function sha256(data: string | Uint8Array): string;
|
|
4
|
+
export declare function canonical(value: unknown): string;
|
|
5
|
+
export declare const sceneHash: (scene: Scene) => string;
|
|
6
|
+
export declare function localAssetPath(root: string, source: string): Promise<string>;
|
|
7
|
+
export declare function registerFonts(): Promise<Record<string, string>>;
|
|
8
|
+
export interface LoadedAssets {
|
|
9
|
+
images: Map<string, Image>;
|
|
10
|
+
hashes: Record<string, string>;
|
|
11
|
+
}
|
|
12
|
+
export declare function loadAssets(scene: Scene, root: string): Promise<LoadedAssets>;
|
|
13
|
+
export declare function registerProjectFonts(scene: Scene, root: string): Promise<{
|
|
14
|
+
hashes: Record<string, string>;
|
|
15
|
+
aliases: Record<string, string>;
|
|
16
|
+
}>;
|
package/dist/assets.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import sharp from 'sharp';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { readFile, realpath } from 'node:fs/promises';
|
|
4
|
+
import { dirname, isAbsolute, relative, resolve, sep } from 'node:path';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
import { GlobalFonts, loadImage } from './native.js';
|
|
7
|
+
export function sha256(data) {
|
|
8
|
+
return `sha256:${createHash('sha256').update(data).digest('hex')}`;
|
|
9
|
+
}
|
|
10
|
+
export function canonical(value) {
|
|
11
|
+
if (Array.isArray(value))
|
|
12
|
+
return `[${value.map(canonical).join(',')}]`;
|
|
13
|
+
if (value !== null && typeof value === 'object')
|
|
14
|
+
return `{${Object.entries(value)
|
|
15
|
+
.filter(([, v]) => v !== undefined)
|
|
16
|
+
.sort(([a], [b]) => a.localeCompare(b, 'en'))
|
|
17
|
+
.map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`)
|
|
18
|
+
.join(',')}}`;
|
|
19
|
+
return JSON.stringify(value);
|
|
20
|
+
}
|
|
21
|
+
export const sceneHash = (scene) => sha256(canonical(scene));
|
|
22
|
+
export async function localAssetPath(root, source) {
|
|
23
|
+
if (isAbsolute(source) || /^[a-z]+:/i.test(source))
|
|
24
|
+
throw new Error('Assets must be project-relative local files');
|
|
25
|
+
const base = await realpath(root), path = await realpath(resolve(base, source));
|
|
26
|
+
const rel = relative(base, path);
|
|
27
|
+
if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel))
|
|
28
|
+
throw new Error(`Asset escapes project: ${source}`);
|
|
29
|
+
return path;
|
|
30
|
+
}
|
|
31
|
+
let fontPromise;
|
|
32
|
+
export function registerFonts() {
|
|
33
|
+
return (fontPromise ??= (async () => {
|
|
34
|
+
const require = createRequire(import.meta.url);
|
|
35
|
+
const fonts = [
|
|
36
|
+
['inter', '@fontsource/inter', 'inter-latin-400-normal.woff'],
|
|
37
|
+
['inter-bold', '@fontsource/inter', 'inter-latin-700-normal.woff'],
|
|
38
|
+
['display', '@fontsource/dm-serif-display', 'dm-serif-display-latin-400-normal.woff'],
|
|
39
|
+
];
|
|
40
|
+
const hashes = {};
|
|
41
|
+
for (const [name, pkg, file] of fonts) {
|
|
42
|
+
const path = resolve(dirname(require.resolve(`${pkg}/package.json`)), 'files', file);
|
|
43
|
+
const bytes = await readFile(path);
|
|
44
|
+
if (!GlobalFonts.register(bytes, `Rtistree-${name}`))
|
|
45
|
+
throw new Error(`Could not load bundled font: ${name}`);
|
|
46
|
+
hashes[name] = sha256(bytes);
|
|
47
|
+
}
|
|
48
|
+
return hashes;
|
|
49
|
+
})());
|
|
50
|
+
}
|
|
51
|
+
export async function loadAssets(scene, root) {
|
|
52
|
+
const images = new Map(), hashes = {};
|
|
53
|
+
for (const [id, asset] of Object.entries(scene.assets)) {
|
|
54
|
+
const path = await localAssetPath(root, asset.source), bytes = await readFile(path);
|
|
55
|
+
if (bytes.length > 64 * 1024 * 1024)
|
|
56
|
+
throw new Error(`Asset exceeds 64 MiB: ${id}`);
|
|
57
|
+
// External resources and embedded SVG fonts break the closed-input determinism contract.
|
|
58
|
+
const png = bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
|
|
59
|
+
const jpeg = bytes[0] === 255 && bytes[1] === 216;
|
|
60
|
+
const webp = bytes.toString('ascii', 0, 4) === 'RIFF' && bytes.toString('ascii', 8, 12) === 'WEBP';
|
|
61
|
+
const tiff = bytes.toString('ascii', 0, 2) === 'II' || bytes.toString('ascii', 0, 2) === 'MM';
|
|
62
|
+
if (!png && !jpeg && !webp && !tiff)
|
|
63
|
+
throw new Error(`Asset ${id}: only PNG, JPEG, WebP and TIFF are supported`);
|
|
64
|
+
const hash = sha256(bytes);
|
|
65
|
+
if (asset.hash && asset.hash !== hash)
|
|
66
|
+
throw new Error(`Asset hash mismatch: ${id}`);
|
|
67
|
+
const metadata = await sharp(bytes, { limitInputPixels: 32 * 1024 * 1024 }).metadata();
|
|
68
|
+
const image = await loadImage(metadata.icc || tiff ? await sharp(bytes).toColourspace('srgb').png().toBuffer() : bytes);
|
|
69
|
+
if (image.width * image.height > 32 * 1024 * 1024)
|
|
70
|
+
throw new Error(`Asset dimensions too large: ${id}`);
|
|
71
|
+
images.set(id, image);
|
|
72
|
+
hashes[id] = hash;
|
|
73
|
+
}
|
|
74
|
+
return { images, hashes };
|
|
75
|
+
}
|
|
76
|
+
const projectFonts = new Set();
|
|
77
|
+
export async function registerProjectFonts(scene, root) {
|
|
78
|
+
const hashes = {}, aliases = {};
|
|
79
|
+
for (const [id, font] of Object.entries(scene.fonts ?? {})) {
|
|
80
|
+
const bytes = await readFile(await localAssetPath(root, font.source)), hash = sha256(bytes);
|
|
81
|
+
if (bytes.length > 20 * 1024 * 1024)
|
|
82
|
+
throw new Error('Font exceeds 20 MiB');
|
|
83
|
+
if (font.hash && font.hash !== hash)
|
|
84
|
+
throw new Error(`Font hash mismatch: ${id}`);
|
|
85
|
+
const alias = `Rtistree-custom-${hash.slice(7)}`;
|
|
86
|
+
if (!projectFonts.has(hash)) {
|
|
87
|
+
if (!GlobalFonts.register(bytes, alias))
|
|
88
|
+
throw new Error(`Invalid font: ${id}`);
|
|
89
|
+
projectFonts.add(hash);
|
|
90
|
+
}
|
|
91
|
+
hashes[`custom:${id}`] = hash;
|
|
92
|
+
aliases[id] = alias;
|
|
93
|
+
}
|
|
94
|
+
return { hashes, aliases };
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"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"}
|