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.
Files changed (181) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +21 -0
  3. package/README.md +99 -0
  4. package/THIRD_PARTY_NOTICES.md +25 -0
  5. package/dist/art-direction.d.ts +77 -0
  6. package/dist/art-direction.js +186 -0
  7. package/dist/art-direction.js.map +1 -0
  8. package/dist/artifacts.d.ts +24 -0
  9. package/dist/artifacts.js +49 -0
  10. package/dist/artifacts.js.map +1 -0
  11. package/dist/assets.d.ts +16 -0
  12. package/dist/assets.js +96 -0
  13. package/dist/assets.js.map +1 -0
  14. package/dist/benchmark.d.ts +76 -0
  15. package/dist/benchmark.js +118 -0
  16. package/dist/benchmark.js.map +1 -0
  17. package/dist/cli.d.ts +2 -0
  18. package/dist/cli.js +300 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/commands.d.ts +5315 -0
  21. package/dist/commands.js +591 -0
  22. package/dist/commands.js.map +1 -0
  23. package/dist/components.d.ts +20 -0
  24. package/dist/components.js +98 -0
  25. package/dist/components.js.map +1 -0
  26. package/dist/construction.d.ts +53 -0
  27. package/dist/construction.js +80 -0
  28. package/dist/construction.js.map +1 -0
  29. package/dist/critique.d.ts +44 -0
  30. package/dist/critique.js +45 -0
  31. package/dist/critique.js.map +1 -0
  32. package/dist/detail.d.ts +5 -0
  33. package/dist/detail.js +52 -0
  34. package/dist/detail.js.map +1 -0
  35. package/dist/document.d.ts +64 -0
  36. package/dist/document.js +56 -0
  37. package/dist/document.js.map +1 -0
  38. package/dist/export.d.ts +405 -0
  39. package/dist/export.js +344 -0
  40. package/dist/export.js.map +1 -0
  41. package/dist/font-inspection.d.ts +8 -0
  42. package/dist/font-inspection.js +34 -0
  43. package/dist/font-inspection.js.map +1 -0
  44. package/dist/graphics.d.ts +9 -0
  45. package/dist/graphics.js +72 -0
  46. package/dist/graphics.js.map +1 -0
  47. package/dist/history-storage.d.ts +10 -0
  48. package/dist/history-storage.js +59 -0
  49. package/dist/history-storage.js.map +1 -0
  50. package/dist/index.d.ts +27 -0
  51. package/dist/index.js +28 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/layout.d.ts +18 -0
  54. package/dist/layout.js +155 -0
  55. package/dist/layout.js.map +1 -0
  56. package/dist/loader.d.ts +9 -0
  57. package/dist/loader.js +108 -0
  58. package/dist/loader.js.map +1 -0
  59. package/dist/mcp.d.ts +4 -0
  60. package/dist/mcp.js +223 -0
  61. package/dist/mcp.js.map +1 -0
  62. package/dist/merge.d.ts +6 -0
  63. package/dist/merge.js +49 -0
  64. package/dist/merge.js.map +1 -0
  65. package/dist/native.d.ts +3 -0
  66. package/dist/native.js +6 -0
  67. package/dist/native.js.map +1 -0
  68. package/dist/paint.d.ts +9 -0
  69. package/dist/paint.js +331 -0
  70. package/dist/paint.js.map +1 -0
  71. package/dist/path-edit.d.ts +20 -0
  72. package/dist/path-edit.js +30 -0
  73. package/dist/path-edit.js.map +1 -0
  74. package/dist/pdf-output.d.ts +21 -0
  75. package/dist/pdf-output.js +297 -0
  76. package/dist/pdf-output.js.map +1 -0
  77. package/dist/pipeline.d.ts +39 -0
  78. package/dist/pipeline.js +227 -0
  79. package/dist/pipeline.js.map +1 -0
  80. package/dist/print-scene.d.ts +490 -0
  81. package/dist/print-scene.js +55 -0
  82. package/dist/print-scene.js.map +1 -0
  83. package/dist/production-workflow.d.ts +251 -0
  84. package/dist/production-workflow.js +335 -0
  85. package/dist/production-workflow.js.map +1 -0
  86. package/dist/program-worker.d.ts +1 -0
  87. package/dist/program-worker.js +31 -0
  88. package/dist/program-worker.js.map +1 -0
  89. package/dist/program.d.ts +85 -0
  90. package/dist/program.js +246 -0
  91. package/dist/program.js.map +1 -0
  92. package/dist/project-config.d.ts +91 -0
  93. package/dist/project-config.js +187 -0
  94. package/dist/project-config.js.map +1 -0
  95. package/dist/project.d.ts +897 -0
  96. package/dist/project.js +347 -0
  97. package/dist/project.js.map +1 -0
  98. package/dist/raster-edit.d.ts +61 -0
  99. package/dist/raster-edit.js +91 -0
  100. package/dist/raster-edit.js.map +1 -0
  101. package/dist/raster-studio.d.ts +84 -0
  102. package/dist/raster-studio.js +236 -0
  103. package/dist/raster-studio.js.map +1 -0
  104. package/dist/render.d.ts +79 -0
  105. package/dist/render.js +437 -0
  106. package/dist/render.js.map +1 -0
  107. package/dist/rich-text.d.ts +10 -0
  108. package/dist/rich-text.js +56 -0
  109. package/dist/rich-text.js.map +1 -0
  110. package/dist/schema.d.ts +1912 -0
  111. package/dist/schema.js +571 -0
  112. package/dist/schema.js.map +1 -0
  113. package/dist/spatial.d.ts +10 -0
  114. package/dist/spatial.js +29 -0
  115. package/dist/spatial.js.map +1 -0
  116. package/dist/studio-reference.d.ts +153 -0
  117. package/dist/studio-reference.js +39 -0
  118. package/dist/studio-reference.js.map +1 -0
  119. package/dist/svg.d.ts +5 -0
  120. package/dist/svg.js +315 -0
  121. package/dist/svg.js.map +1 -0
  122. package/dist/techniques.d.ts +74 -0
  123. package/dist/techniques.js +164 -0
  124. package/dist/techniques.js.map +1 -0
  125. package/dist/verify.d.ts +36 -0
  126. package/dist/verify.js +277 -0
  127. package/dist/verify.js.map +1 -0
  128. package/dist/viewport.d.ts +3 -0
  129. package/dist/viewport.js +71 -0
  130. package/dist/viewport.js.map +1 -0
  131. package/dist/workflow.d.ts +53 -0
  132. package/dist/workflow.js +66 -0
  133. package/dist/workflow.js.map +1 -0
  134. package/docs/agent-art-workflow.md +201 -0
  135. package/docs/agent-setup.md +83 -0
  136. package/docs/atelier.md +117 -0
  137. package/docs/cli-reference.md +61 -0
  138. package/docs/decisions/001-runtime-and-renderer.md +35 -0
  139. package/docs/decisions/002-composable-scenes-and-history.md +28 -0
  140. package/docs/decisions/003-editing-refinement-and-agent-validation.md +45 -0
  141. package/docs/decisions/004-print-production-and-projects.md +19 -0
  142. package/docs/decisions/005-programmable-digital-art.md +15 -0
  143. package/docs/decisions/006-staged-2d-art-production.md +13 -0
  144. package/docs/decisions/007-foundation-first-art-direction.md +13 -0
  145. package/docs/decisions/008-agent-onboarding-and-trial-lessons.md +48 -0
  146. package/docs/engine-overview.md +174 -0
  147. package/docs/evolution.md +179 -0
  148. package/docs/getting-started.md +107 -0
  149. package/docs/next-milestone.md +61 -0
  150. package/docs/previews/a3-production.json +36 -0
  151. package/docs/previews/benchmark.json +42 -0
  152. package/docs/previews/landscape.png +0 -0
  153. package/docs/previews/performance.json +24 -0
  154. package/docs/previews/poster.png +0 -0
  155. package/docs/production.md +107 -0
  156. package/docs/releasing.md +60 -0
  157. package/docs/scene-format.md +126 -0
  158. package/docs/studio.md +108 -0
  159. package/examples/hello/README.md +14 -0
  160. package/examples/hello/refine.json +10 -0
  161. package/examples/hello/render.mjs +9 -0
  162. package/examples/hello/scene.json +19 -0
  163. package/licenses/DM-Serif-Display-OFL.txt +93 -0
  164. package/licenses/Inter-OFL.txt +93 -0
  165. package/package.json +84 -0
  166. package/schemas/authoring.schema.json +2177 -0
  167. package/schemas/brief.schema.json +57 -0
  168. package/schemas/command.schema.json +3154 -0
  169. package/schemas/construction.schema.json +159 -0
  170. package/schemas/critique.schema.json +101 -0
  171. package/schemas/document.schema.json +41 -0
  172. package/schemas/export.schema.json +89 -0
  173. package/schemas/fragment.schema.json +1861 -0
  174. package/schemas/patch.schema.json +3177 -0
  175. package/schemas/pipeline.schema.json +205 -0
  176. package/schemas/production.schema.json +452 -0
  177. package/schemas/program.schema.json +152 -0
  178. package/schemas/project.schema.json +115 -0
  179. package/schemas/raster-read.schema.json +42 -0
  180. package/schemas/raster-write.schema.json +75 -0
  181. package/schemas/scene.schema.json +2014 -0
@@ -0,0 +1,107 @@
1
+ # Make your first image
2
+
3
+ Rtistree runs locally on Node.js 22 or newer. It needs no GPU or model API key.
4
+ Start in an empty directory:
5
+
6
+ ```sh
7
+ mkdir my-art
8
+ cd my-art
9
+ npm init -y
10
+ npm install https://github.com/Coly010/rtistree/releases/download/v0.5.0/rtistree-0.5.0.tgz
11
+ ```
12
+
13
+ This installs the checked release tarball from GitHub. npm registry publication is pending;
14
+ the installed package and `graphics` executable are the same.
15
+
16
+ ## Render a scene
17
+
18
+ Save this as `scene.json`:
19
+
20
+ ```json
21
+ {
22
+ "version": 1,
23
+ "canvas": { "width": 640, "height": 400, "background": "#14251f" },
24
+ "layers": [
25
+ {
26
+ "id": "disc",
27
+ "type": "vector",
28
+ "bounds": [360, 60, 200, 200],
29
+ "shape": { "type": "ellipse", "fill": "#95b85c" }
30
+ },
31
+ {
32
+ "id": "title",
33
+ "type": "text",
34
+ "bounds": [48, 275, 544, 80],
35
+ "content": "Hello, Rtistree.",
36
+ "style": { "font": "inter", "size": 44, "weight": "bold", "colour": "#edf3e8" }
37
+ }
38
+ ]
39
+ }
40
+ ```
41
+
42
+ Or copy the same scene from `node_modules/rtistree/examples/hello/scene.json`.
43
+
44
+ ```sh
45
+ npx graphics render scene.json -o hello.png
46
+ npx graphics inspect scene.json
47
+ ```
48
+
49
+ Open `hello.png`: a green disc and a title on a dark background. An adjacent evidence JSON
50
+ records the scene hash, dimensions, fonts and renderer/runtime information.
51
+
52
+ ## Change one part
53
+
54
+ Save this as `refine.json`:
55
+
56
+ ```json
57
+ {
58
+ "reason": "Brighten the disc while preserving the title and background",
59
+ "commands": [
60
+ {
61
+ "type": "applyRasterOperation",
62
+ "target": "disc",
63
+ "operation": { "type": "brightness", "bounds": [360, 60, 200, 200], "amount": 0.15 }
64
+ }
65
+ ]
66
+ }
67
+ ```
68
+
69
+ ```sh
70
+ npx graphics apply scene.json refine.json
71
+ npx graphics render scene.json -o brighter.png
72
+ npx graphics undo scene.json
73
+ npx graphics render scene.json -o restored.png
74
+ ```
75
+
76
+ The disc becomes brighter. Undo restores the first image. Changes are journaled beside
77
+ the scene; the original authoring file remains intact. On the same runtime and platform,
78
+ `restored.png` should have the same bytes as `hello.png`.
79
+
80
+ ## Export a portable project
81
+
82
+ ```sh
83
+ npx graphics export scene.json -o portable/scene.json
84
+ npx graphics render portable/scene.json -o portable.png
85
+ ```
86
+
87
+ The export includes referenced assets and fonts. Keep the exported directory together.
88
+
89
+ ## Use the SDK
90
+
91
+ Save this as `render.mjs` beside `scene.json`:
92
+
93
+ ```js
94
+ import { Project } from 'rtistree';
95
+ import { writeFile } from 'node:fs/promises';
96
+
97
+ const project = await Project.open('./scene.json');
98
+ const result = await project.render();
99
+ await writeFile('sdk-output.png', result.png);
100
+ ```
101
+
102
+ Run `node render.mjs`. The SDK and CLI use the same engine.
103
+
104
+ ## Next steps
105
+
106
+ Read [agent setup](agent-setup.md) to connect an MCP client, or the [scene format](scene-format.md)
107
+ to add layers, text, assets and masks. For generated painting programs, use the [studio guide](studio.md).
@@ -0,0 +1,61 @@
1
+ # Proposed next milestone: print documents and coherent projects
2
+
3
+ Status: delivered as the v0.3 milestone. This document retains the original proposal; see [the production guide](production.md) and [ADR 004](decisions/004-print-production-and-projects.md) for the implemented scope, selected backends and explicit limits.
4
+
5
+ ## Recommended order
6
+
7
+ 1. Introduce a project manifest, physical document model, and `graphics project new` together.
8
+ 2. Add JPEG, TIFF and physically sized PDF exports, with colour management and output validation.
9
+ 3. Preserve text and vector geometry in PDF; add practical compositing, selection and path tools.
10
+ 4. Run a more demanding generated-asset integration trial, including subject extraction or seamless extension, and print the result through the new export pipeline.
11
+
12
+ The first generated-asset fitting trial is already recorded in [the example](../examples/generated-asset-trial/README.md). It uses one built-in image-generation call followed by deterministic Rtistree composition. It establishes asset ingestion, aspect-preserving placement, editable surrounding content and exact preservation of the placed photograph. It does not establish seamless photographic outpainting, segmentation or print readiness.
13
+
14
+ ## Project creation
15
+
16
+ Proposed syntax (not current commands):
17
+
18
+ ```sh
19
+ graphics project new alpine --size A3 --orientation landscape --ppi 300 --bleed 3mm --output-dir output
20
+ graphics render alpine --preset screen
21
+ graphics export alpine --preset print
22
+ graphics preflight alpine --preset print
23
+ ```
24
+
25
+ Create a project manifest describing the scene entry point, output directory and export presets; a scene referencing layer fragments; `assets/`, `fonts/`, `profiles/`, `layers/`, `history/`, and the designated output directory. Include a useful starter scene, schema references, a short project README, and ignore rules for generated artifacts. Relative paths resolve consistently from the project root. Creating a project must not overwrite an existing one. Existing scene-file commands remain usable.
26
+
27
+ The output setting must apply to CLI renders, MCP renders, region inspections, exports and reports. The existing `export` command means a portable scene bundle; preserve that meaning explicitly, for example as `--format project`, while adding image/document export options.
28
+
29
+ ## Physical dimensions and print
30
+
31
+ Keep physical trim dimensions authoritative, independently of pixel dimensions. Support mm, inches and points, bleed, safe margins and a raster sampling resolution. A3 landscape has a 420 by 297 mm trim size. Pixel dimensions necessarily round at a requested ppi; that rounding must not change the PDF's physical trim size. Keep PDF page dimensions at full precision and test read-back within a stated tolerance, such as 0.01 mm.
32
+
33
+ Record the PDF MediaBox, TrimBox and BleedBox correctly; actually render artwork into the bleed. Offer crop marks as an explicit option outside trim. Report effective ppi of placed source images and whether the requested output resamples them. The file can encode exact size, but downstream printer/viewer scaling remains outside the engine's control.
34
+
35
+ The current schema caps canvas dimensions at 4,096 pixels and the renderer holds full-canvas layer surfaces. A3 at 300 ppi is about 4,961 by 3,508 pixels before bleed. Print therefore needs a bounded-memory strategy and expanded dimension support, not merely a new encoder or a higher schema limit. Tiled rendering must retain context for filters and match reference crops.
36
+
37
+ ## Colour and formats
38
+
39
+ CMYK requires a characterised output condition. Store the printer/paper ICC profile as a hashed project asset, distinguish profile assignment from conversion, record rendering intent, and embed the matching output profile. An RGB working preview with an explicit conversion at export is a sensible first stage; it is not a native CMYK editing engine. Preserve imported profile information and define input conversion explicitly.
40
+
41
+ For PDF, the output intent describes the printing condition; it does not by itself convert every object to CMYK. A CMYK-only preset must also inspect the emitted image and vector colour spaces. PDF/X-4 is a useful eventual target and permits colour-managed content beyond CMYK; claim compliance only after independent validation. See the [PDF Association's requirements](https://pdfa.org/technical-side-and-requirements-of-pdfx/).
42
+
43
+ JPEG should expose quality, chroma subsampling, explicit alpha flattening and ICC metadata. TIFF should offer lossless compression, resolution tags and RGB/CMYK output. Writing an 8-bit render into a higher-bit-depth container would not provide a higher-precision editing pipeline. PNG remains the convenient lossless screen output.
44
+
45
+ Sharp/libvips is a strong candidate for raster encoding and ICC conversion: its documented API includes JPEG/TIFF and profile transformation/attachment. Evaluate the exact pinned build before selecting it. PDF needs a separate writer capable of physical page boxes and appropriate colour objects. See [Sharp output documentation](https://sharp.pixelplumbing.com/api-output/).
46
+
47
+ A flattened raster PDF is a useful first delivery, with its resolution limits made explicit. The production typography milestone should preserve vector paths and embedded/subset fonts wherever supported, rasterizing effects or groups that require it. It should then address K-only small black text, rich-black policy, total ink coverage and soft proofing. Spot colours and overprint can follow a specific print use case.
48
+
49
+ Export acceptance checks should reopen actual files to verify format signatures, dimensions, resolution tags, profiles and channels. PDF checks additionally inspect physical boxes, font embedding and image placement resolution. Preflight reports should reference the scene hash, actual output hash, profile hash and export settings. Lossy JPEG needs tolerance-based decoded comparison rather than equality with source pixels.
50
+
51
+ ## Editing features worth adding
52
+
53
+ Prioritise operations that help an agent integrate and revise real artwork:
54
+
55
+ - Source crop rectangles, focal-point placement, reusable linked assets, perspective transforms and editable clipping paths.
56
+ - Mask algebra, feather/grow/shrink controls, colour-range selection, and a way to import externally generated segmentation masks.
57
+ - Levels, curves, white balance and reusable adjustment stacks; then clone/heal tools for local repairs.
58
+ - Path booleans, editable control points, stroke joins/caps/dashes, SVG import/export, alignment and distribution.
59
+ - Rich text runs, tracking, kerning controls, paragraph styles and stronger font diagnostics.
60
+
61
+ The generated-image trial exposed one small authoring weakness: the folio used spaces for alignment. Separate aligned objects or tab stops would make that layout more robust. A larger next trial should test a masked photographic subject, matched shadows/colour, and multiple aspect ratios. PSD/AI round-tripping, a GUI toolbox, advanced liquify and automatic image tracing can wait until a concrete workflow justifies their complexity.
@@ -0,0 +1,36 @@
1
+ {
2
+ "trim_mm": [420, 297],
3
+ "pixels": [5031, 3579],
4
+ "density": 300,
5
+ "channels": 4,
6
+ "pdf": {
7
+ "format": "pdf",
8
+ "pages": [
9
+ {
10
+ "media": {
11
+ "x": 0,
12
+ "y": 0,
13
+ "width": 1243.559055,
14
+ "height": 894.897638
15
+ },
16
+ "trim": {
17
+ "x": 26.503937,
18
+ "y": 26.503937,
19
+ "width": 1190.551181,
20
+ "height": 841.889764
21
+ },
22
+ "bleed": {
23
+ "x": 18,
24
+ "y": 18,
25
+ "width": 1207.559055,
26
+ "height": 858.897638
27
+ }
28
+ }
29
+ ],
30
+ "images": [],
31
+ "embedded_fonts": ["/CZZZZZ+DMSerifDisplay-Regular"],
32
+ "profile_hashes": ["sha256:156e7c14f244cfc4ed83a755ca4803d80e15dd249b40fae82cb127d3902e15c7"],
33
+ "output_intents": 1
34
+ },
35
+ "status": "pass"
36
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "before": {
3
+ "status": "fail",
4
+ "score": 0.9375,
5
+ "issues": [
6
+ {
7
+ "id": "issue-1",
8
+ "category": "composition",
9
+ "severity": "medium",
10
+ "region": [755, 610, 56, 84],
11
+ "message": "Rendered region luma is outside the requested range.",
12
+ "measured": 0.4914004455115222,
13
+ "expected": 0.52
14
+ }
15
+ ]
16
+ },
17
+ "after": {
18
+ "status": "pass",
19
+ "score": 1
20
+ },
21
+ "iterations": 1,
22
+ "locality": {
23
+ "changed_pixels": 4704,
24
+ "outside_changed_pixels": 0,
25
+ "changed_bounds": [755, 610, 56, 84],
26
+ "scopes": [[755, 610, 56, 84]]
27
+ },
28
+ "reopened_identical": true,
29
+ "undo_identical": true,
30
+ "redo_identical": true,
31
+ "export_identical": true,
32
+ "renderer": {
33
+ "name": "rtistree-skia",
34
+ "version": "0.2.0",
35
+ "backend": "@napi-rs/canvas@1.0.8",
36
+ "node": "v24.14.0",
37
+ "platform": "darwin",
38
+ "arch": "arm64"
39
+ },
40
+ "scene_hash": "sha256:80c2bd4d6de618e31d54f5b547ad72682713a7f0241ff51a15b1639bb1347149",
41
+ "png_hash": "sha256:65ba24cef903f57275c743ec04acc37f0fc38d9fe848b7bdcc7970298fbb0bde"
42
+ }
Binary file
@@ -0,0 +1,24 @@
1
+ {
2
+ "scene": [1024, 1024],
3
+ "layers": 16,
4
+ "cold_ms": 165.842292,
5
+ "warm_ms": 92.88699999999997,
6
+ "incremental_ms": 84.64695799999998,
7
+ "fresh_ms": 89.01799999999997,
8
+ "incremental_statistics": {
9
+ "rasterized_layers": 1,
10
+ "cached_layers": 15,
11
+ "cache_bytes": 71303168
12
+ },
13
+ "viewport_ms": 1.833167000000003,
14
+ "full_crop_ms": 4.616415999999958,
15
+ "viewport_statistics": {
16
+ "rasterized_layers": 16,
17
+ "cached_layers": 0,
18
+ "cache_bytes": 72351744,
19
+ "viewport_pixels": 16384
20
+ },
21
+ "incremental_identical": true,
22
+ "region_identical": true,
23
+ "note": "Single local measurements, including PNG encoding; not a throughput guarantee."
24
+ }
Binary file
@@ -0,0 +1,107 @@
1
+ # Projects, print production and editing in v0.3
2
+
3
+ ## Start a project
4
+
5
+ ```sh
6
+ graphics project new alpine --size A3 --orientation landscape --ppi 300 --bleed 3mm --output-dir output
7
+ graphics render alpine --preset screen
8
+ graphics preflight alpine --preset print
9
+ ```
10
+
11
+ `project new` accepts A3, A4, A5, Letter, or a custom pair such as `160x100` (mm) or `8.5inx11in`. It creates `rtistree.yaml`, a scene with layer fragments, assets/fonts/profiles/history directories, local JSON Schemas, a README and a designated output directory. The destination must be new. Existing scene-file commands remain available; opening a configured entry-point scene also discovers its project settings.
12
+
13
+ Generated projects start with an sRGB screen preset and a CMYK print preset. Before using the print preset, copy your printer/paper ICC file into `profiles/` and set `presets.print.profile`. Set `profile_hash` to its SHA-256 digest to pin it. CMYK export rejects missing, invalid, changed or non-CMYK profiles. The demo profile is for exercising the pipeline, not a recommendation for a printing process.
14
+
15
+ Project paths are local to the project root. CLI/MCP renders and artwork exports use `output_dir`; CLI verification and preflight reports use it too. History and critique records remain persistent project state. Explicit CLI `--output` paths override artifact destinations.
16
+
17
+ ## Physical document model
18
+
19
+ The scene can contain:
20
+
21
+ ```yaml
22
+ document:
23
+ width: 160
24
+ height: 100
25
+ unit: mm
26
+ ppi: 300
27
+ bleed: 3
28
+ safe_margin: 6
29
+ ```
30
+
31
+ Units are `mm`, `in`, or `pt`; bleed and safe margin use the same unit. Canvas coordinates remain design coordinates. The physical trim dimensions are authoritative and must have substantially the same aspect ratio as the canvas (ratio tolerance 0.002). PDF page boxes use full-precision physical dimensions; rounded raster dimensions do not redefine the trim. Export checks read the PDF back within 0.01 mm of the declared size.
32
+
33
+ Print rendering applies an outer affine transform, preserving source crops, object-relative paint, direct pixels and palette grids. Effects and mask feathering are scaled to maintain their physical appearance. Unbounded procedural backgrounds extend through the bleed. Other artwork must be authored to extend beyond trim when needed. Crop marks are optional and sit outside the bleed.
34
+
35
+ The renderer accepts edges up to 8,192 pixels. Print preflight also enforces a 32-megapixel output ceiling and a surface budget based on nesting and retained mask dependencies. Uncached composition releases completed layer surfaces rather than retaining every layer. This supports A3 at 300 ppi: 5,031 by 3,579 pixels with 3 mm bleed, as recorded in [the A3 check](previews/a3-production.json). It is not an unlimited-memory or general tiled renderer.
36
+
37
+ ## Formats and colour
38
+
39
+ ```sh
40
+ graphics export alpine --preset print --format pdf
41
+ graphics export alpine --preset print --format tiff
42
+ graphics export alpine --preset screen --format jpeg
43
+ graphics export alpine --format svg -o alpine/output/artwork.svg
44
+ graphics proof alpine --preset print
45
+ graphics export alpine --format project -o /tmp/alpine-copy/scene.json
46
+ ```
47
+
48
+ Known artwork extensions are inferred for `export` and full `render`. Region renders remain PNG. A filename extension must agree with the actual format. Portable scene export keeps its original meaning with `--format project` or a JSON/YAML destination, and copies configured print profiles/presets when exporting a project.
49
+
50
+ | Format | Implemented output |
51
+ | ------- | -------------------------------------------------------------------------------------------------------------------------------------- |
52
+ | PNG | Lossless sRGB, alpha, ICC profile |
53
+ | JPEG | sRGB or CMYK, quality 1–100, 4:4:4 or 4:2:0, explicit flattening background |
54
+ | TIFF | 8-bit sRGB or CMYK, lossless LZW, resolution tags and ICC profile |
55
+ | PDF 1.7 | Exact physical boxes, optional crop marks, sRGB or ICC-managed CMYK, embedded/subset fonts, vector paths and gradients where supported |
56
+ | SVG | Vector geometry and text outlines; losslessly embedded raster fallbacks for compositing/effects |
57
+
58
+ The pinned Sharp/libvips encoder uses **perceptual** ICC conversion. This is the only accepted rendering intent, and it is recorded in evidence. Imported profiled rasters and TIFFs are explicitly converted to the sRGB working space. Original source bytes remain pinned and unchanged.
59
+
60
+ For CMYK PDF, raster image streams contain four-channel samples and reference the embedded ICC profile; vector colours are converted through that profile and the document has the corresponding output intent. Pure `#000000` vector text/shapes use K-only black by default; set `black: profile` to use the profile conversion. This policy does not rewrite black inside photographic assets.
61
+
62
+ Hybrid PDF retains supported top-level text, paths, shapes, gradients and isolation-free groups. Effects, masks and context-dependent compositing use raster fallbacks at the document resolution. `mode: raster` explicitly produces a flattened page. Gradient endpoints are converted to the output colour space and interpolated there, so PDF gradients may differ slightly from a per-pixel converted screen raster.
63
+
64
+ `proof` produces an sRGB PNG after a round trip through the chosen CMYK profile. It is useful for inspecting gamut changes, and does not simulate a calibrated monitor or paper white. TIFF/JPEG ink reporting measures decoded CMYK values. PDF ink reporting samples constituent raster/vector colours; it is not a RIP-based analysis of transparency/overprint interactions. Over-limit ink and low effective ppi appear as warnings.
65
+
66
+ **PDF/X compliance is not asserted.** Spot colours, overprint, a high-bit-depth working pipeline and printer-specific certification remain future work. Export evidence identifies these limits and never labels an ordinary PDF as certified PDF/X.
67
+
68
+ ## Preflight and evidence
69
+
70
+ Preflight checks physical geometry, render capacity, output profiles, font glyph coverage, text overflow, safe margins and effective image resolution including crop/fit placement. Perspective placements report that their resolution estimate is pre-warp. Low-resolution sources and safe-margin crossings are warnings; invalid geometry/profiles and missing glyphs are errors. Preflight exits 2 for errors and persists its report.
71
+
72
+ Each artwork export has a `.evidence.json` containing the scene hash, output hash, resolved settings, ICC hash, backend versions and inspection of the actual encoded bytes. Raster inspection reads format, dimensions, channels, profile and density. PDF inspection reads page boxes, image colour-space declarations, embedded font descriptors and profile streams. This complements visual proof inspection; it does not replace press validation.
73
+
74
+ ## New editing operations
75
+
76
+ Commands are available through `apply` in the CLI, SDK and MCP. See generated command/patch schemas for complete inputs.
77
+
78
+ | Command or field | Behaviour |
79
+ | ---------------------------------------- | ---------------------------------------------------------------------------------- |
80
+ | `setDocument` | Set or clear physical document settings in history |
81
+ | `setCrop` | Set a source-pixel rectangle and optional normalised focal point |
82
+ | `setPerspective` | Map an image into four convex local corners using inverse projective sampling |
83
+ | `setAffine` | Set an invertible six-number local affine matrix |
84
+ | `setPath` | Edit move/line/quadratic/cubic/close segments with explicit control points |
85
+ | `booleanPath` | Union, intersect, subtract or XOR sibling vector operands into a new editable path |
86
+ | `setTextRuns` | Styled text spans with colour and regular/bold weight |
87
+ | `setParagraphStyle`, `useParagraphStyle` | Reuse and update named text styles |
88
+ | `setAdjustmentStack` | Replace a non-destructive operation stack with locality validation |
89
+
90
+ Multiple image layers can reference one registered asset. Replacing an asset registration with a new immutable source updates those linked uses; edits to already-pinned bytes are still rejected. Existing alignment and distribution commands remain available.
91
+
92
+ Shapes support stroke caps, joins, dash arrays and dash offsets. Text styles support tracking and kerning control. Rich runs must concatenate exactly to `content`; `setTextRuns` maintains both. Paragraph styles define shared font/size/colour/spacing values. Custom font files should represent the intended face; a weight flag does not select another face inside a custom font collection.
93
+
94
+ Masks now support editable SVG-style paths, image alpha/luminance/colour-range selections, and union/intersection/subtraction/XOR. Combined masks use one coordinate space; children inherit the outer space and reference size. The processing order is selection/algebra, inversion, grow/shrink, then feathering. Image masks reference an asset directly, making external segmentation masks usable without a visible mask layer.
95
+
96
+ New scoped operations are levels, piecewise-linear RGB/channel curves, white balance, clone and heal. Cloning samples a frozen copy of the target using `source_offset`. Healing adds a local mean-colour correction to that cloned texture; this is a deterministic colour-matched clone, not a Poisson or learned healing algorithm. All operate non-destructively through the existing history and scoped-pixel checks.
97
+
98
+ ## SVG interchange and trial
99
+
100
+ ```sh
101
+ graphics import-svg drawing.svg -o drawing.scene.json
102
+ graphics export drawing.scene.json --format svg -o drawing.svg
103
+ ```
104
+
105
+ The importer supports static paths, rectangles, circles, ellipses, lines, polygons, polylines, groups, transforms and basic stroke/fill styling. Unsupported elements/attributes, remote resources, scripts and entities are rejected rather than silently dropped. Text, filters and arbitrary CSS are not imported. Exported text is outlined; complex export fallbacks are embedded images. Full round-trip equivalence for arbitrary SVG is not promised.
106
+
107
+ The [print trial](../examples/print-trial/README.md) generates a ceramic vase/leaf photograph, extracts its silhouette using the new masks, adds a contact shadow and colour adjustment, adapts it to two aspect ratios, and exports CMYK PDF/TIFF/JPEG. The source, exact prompt, edits, critiques, final scenes and output evidence are retained.
@@ -0,0 +1,60 @@
1
+ # Release process
2
+
3
+ The repository is `Coly010/rtistree`, the npm package is `rtistree`, and the website's
4
+ canonical origin is `https://rtistree.dev`. The package is MIT licensed to Colum Ferry.
5
+
6
+ ## Validate a release
7
+
8
+ 1. Update `package.json`, its lockfile and `CHANGELOG.md` together. Keep the website version
9
+ label and package instructions aligned with the release.
10
+ 2. Run `npm ci`, `npm run check`, `npm run format:check` and `npm run package:check`.
11
+ 3. Run `npm ci --prefix website` and `npm run build --prefix website`.
12
+ 4. Review the tarball inventory, documentation commands and example sources. The package
13
+ intentionally includes only the small hello example; larger trials are repository assets.
14
+ 5. Commit the validated source and tag the matching version, for example `v0.5.0`.
15
+
16
+ ## First npm publication
17
+
18
+ The owner must authenticate with `npm login` and complete any npm 2FA challenge.
19
+ Confirm `npm whoami` is the intended owner. Build and inspect the package before publishing:
20
+
21
+ ```sh
22
+ npm pack
23
+ npm publish rtistree-0.5.0.tgz --access public
24
+ ```
25
+
26
+ Do not republish a version number. If a command's outcome is uncertain, check the npm
27
+ registry before retrying. Never commit npm tokens or configure long-lived tokens in workflows.
28
+
29
+ ## Subsequent releases with trusted publishing
30
+
31
+ In the npm package settings, configure a GitHub Actions trusted publisher:
32
+
33
+ - Owner: `Coly010`
34
+ - Repository: `rtistree`
35
+ - Workflow filename: `release.yml`
36
+
37
+ After configuring the npm trusted publisher, set the GitHub repository Actions variable
38
+ `NPM_TRUSTED_PUBLISHING_ENABLED` to `true`. Without it, release validation runs but npm
39
+ publication is skipped. This permits the initial GitHub tarball release before npm sign-in.
40
+
41
+ The release workflow checks the tag against the package version, validates the engine,
42
+ website and packaged installation, then publishes the same checked tarball with provenance.
43
+ It runs on publication of a GitHub release. Configure trust before publishing the next release.
44
+ For the bootstrap release, publish npm first and let the workflow skip an already-published version.
45
+
46
+ See https://docs.npmjs.com/trusted-publishers/ for current npm account requirements.
47
+
48
+ ## Website
49
+
50
+ The website is an Astro/Starlight static build in `website/`. `docs/` is the source of truth;
51
+ the content sync script generates Starlight pages, remaps repository links and copies curated
52
+ assets. Generated files are ignored by Git but included when preparing a standalone Sites source snapshot.
53
+
54
+ `npm run build --prefix website` produces `website/dist`. This is portable static output.
55
+ The Sites project ID is in `website/.openai/hosting.json`; never replace it on redeployment.
56
+ Use the Sites publishing flow to save and deploy the validated source and static archive.
57
+ The `website.yml` workflow validates and uploads a deployable static artifact for each change.
58
+
59
+ Custom-domain DNS must use the exact records returned by the hosting provider. Do not infer
60
+ an apex A record from a preview hostname. Confirm domain and TLS status after the DNS change.
@@ -0,0 +1,126 @@
1
+ # Scene format v1
2
+
3
+ The examples in this document are the implemented format. The original proposal is exploratory; unsupported fields are rejected, not silently interpreted. Generate machine-readable schemas with `npm run schema` or `graphics schema`.
4
+
5
+ The [production guide](production.md) describes v0.3 physical documents, exports, affine and perspective transforms, extended masks, adjustments, path commands and typography.
6
+
7
+ The [studio guide](studio.md) describes v0.4 programmable assets and dense editing. Assets may carry a `recipe: {source, hash}` reference to a frozen program manifest. Promoted regions accept `composite: replace|over` (default replacement). Programs are executed explicitly and baked; scenes never execute referenced recipes while rendering.
8
+
9
+ ## Files and assets
10
+
11
+ Root documents contain `version: 1`, `canvas`, `layers` and/or `include`, optional `assets`, `metadata`, and `verification: {rules: [...]}`. Fragments contain `include`, `assets`, `fonts`, `components` and `layers`. JSON and YAML can be mixed. There can be at most 128 included documents and 256 total layers.
12
+
13
+ ```yaml
14
+ assets:
15
+ portrait:
16
+ type: image # or generated-image; provenance category only
17
+ source: ./assets/portrait.png
18
+ hash: sha256:... # optional on input; checked when supplied
19
+ ```
20
+
21
+ PNG, JPEG, WebP and TIFF assets are supported. ICC-tagged input is normalised to sRGB. The separate `import-svg` command converts a closed static geometry subset into scene layers. No URL loading or external resource resolution is permitted. Asset paths must resolve inside the root scene directory, including after symlink resolution. File size is limited to 64 MiB and decoded dimensions to 32 megapixels. Large rasters belong in binary image files, not YAML arrays.
22
+
23
+ ## Coordinates and layout
24
+
25
+ `canvas` uses integer `width` and `height`, each from 1 to 8192, `colour_space: srgb`, and a `#RRGGBB` or `#RRGGBBAA` background. A conservative total-surface memory budget can reject large scenes with many retained layers. Physical document dimensions are separate from these design pixels.
26
+
27
+ Geometry uses `bounds: [x, y, width, height]` **relative to the parent**. Omitted bounds fill the parent's content area. A layer may override dimensions using numeric `width`/`height` or percentages, and `anchor` can be `top-left`, `center`, `top-right`, `bottom-left`, or `bottom-right`. Rotation is in degrees about the box center; scale is a positive `[x, y]` pair. Parent transforms apply to descendants.
28
+
29
+ ```yaml
30
+ - id: product-row
31
+ type: group
32
+ bounds: [40, 40, 944, 500]
33
+ layout: { type: horizontal, padding: 24, gap: 32, align: center }
34
+ children:
35
+ - id: left
36
+ type: vector
37
+ width: 50%
38
+ height: 320
39
+ shape: { type: rectangle, fill: '#f2ce94', radius: 8 }
40
+ - id: right
41
+ type: vector
42
+ width: 50%
43
+ height: 320
44
+ shape: { type: ellipse, fill: '#638379' }
45
+ ```
46
+
47
+ Flow layouts support horizontal/vertical directions, padding, gaps and cross-axis start/center/end alignment. Percentages on the flow axis use space remaining after padding and gaps. Flow layout overrides child positions and anchors. Direct move/resize/alignment commands require an absolute-layout parent; change the authoring layout to control flow sizing. An optional `aspect_ratio` derives height from width when height is omitted. Positive `grow` weights share remaining flow space; `justify` supports start, center, end and space-between. Automatic asset dimensions and arbitrary constraints are not implemented.
48
+
49
+ **Masks, paint paths, raster scopes, pixel locations and palette tiles default to canvas coordinates.** Set `space: layer` and a `reference_size` to attach them to an object. Commands capture the reference size automatically; movement, rotation and resizing then transform the edit. This explicit separation permits exact locality measurements. Layer masks use the rendered alpha of the target layer for `semantic-object`; hidden targets have empty alpha.
50
+
51
+ ## Layers
52
+
53
+ Common fields: `id`, `name`, `role`, `visible`, `opacity`, `blend_mode`, `z`, `bounds`, `transform`, `mask`, `effects`, `operations`, `tiles`. Children are embedded layer objects on a `group`, not string references. Positive `z` paints later among siblings. Equal values preserve declaration order.
54
+
55
+ | Type | Required or relevant fields |
56
+ | ----------------------- | ----------------------------------------------------------------------------------------- |
57
+ | group | `children`, optional `layout` |
58
+ | vector | `shape` |
59
+ | text | `content`, optional `style` |
60
+ | asset / generated-asset | `source` asset ID; `fit: contain`, `cover`, or `stretch` |
61
+ | procedural | `generator` |
62
+ | raster | optional `source` asset ID, `tiles`, `operations`; initially transparent without a source |
63
+ | adjustment | `operations`, optional mask and opacity; operates on already composed siblings |
64
+
65
+ Shapes are `rectangle` (optional radius), `ellipse`, or `path` with SVG path data in `d`. All require `fill`; transparent fill is `#00000000`. Optional `stroke` and `stroke_width` add an outline. Paths use local coordinates in the layer's box; they are not automatically normalized to that box.
66
+
67
+ Text is retained as semantic content. It wraps at whitespace and respects explicit newlines. `style` supports `font: inter|display`, `size`, `weight: regular|bold`, `colour`, `line_height` as a multiplier, and `align: left|center|right`. `display` uses DM Serif Display regular; its bold variant is not supplied. Text is clipped to its box, and the verifier reports overflow. Bundled Latin fonts provide reproducible typography; a custom font can be registered in the scene fonts map and selected by its ID.
68
+
69
+ Generators: `solid` (`colour`); `gradient` (`from`, `to`, `angle`, with 0° horizontal and 90° vertical); `noise` (`seed`, `amount`, optional base `colour`). Every random generator requires an integer seed. Noise is sampled in a stable row-major order.
70
+
71
+ Blend modes: normal, multiply, screen, overlay, darken, lighten, difference, soft-light, hard-light. Compositing and colour adjustments use Skia's sRGB path; this is not a linear-light color grading engine.
72
+
73
+ ## Effects, masks and raster operations
74
+
75
+ Layer effects currently support `{type: blur, radius: 8}`. Rendering order is geometry/children → effects → palette tiles → raster operations → promoted regions → layer mask → opacity → sibling blending. Adjustment layers apply scoped operations to the existing composite, then mix the result by their mask and opacity.
76
+
77
+ Mask types:
78
+
79
+ ```yaml
80
+ mask: { type: rectangle, bounds: [100, 100, 200, 200], feather: 8 }
81
+ ```
82
+
83
+ The exact mask type token is **`rectangle`**, alongside `ellipse`, `polygon` (`points: [[x,y], ...]`), and `semantic-object` (`target: layer-id`). All accept `feather` in pixels. The feather is applied before intersection with a raster operation's hard bounds. Semantic mask references are checked for dependency cycles, including parent/child cycles. Adjustment layers cannot be semantic mask targets.
84
+
85
+ Every raster operation requires a `bounds` rectangle and accepts an optional `mask`. Bounds use half-open canvas pixel coordinates: a pixel at `(x,y)` is in scope when `left <= x < right` and `top <= y < bottom`. Integer bounds are recommended. Filters and soft strokes are hard-clipped to this region, so provide enough padding for a smooth falloff.
86
+
87
+ | Operation | Parameters |
88
+ | ------------- | --------------------------------------------------------------- |
89
+ | fill | `colour`; source-over fill |
90
+ | paintStroke | `path`, `radius`, `colour`, optional `opacity`, `hardness` |
91
+ | eraseStroke | `path`, `radius`, optional `opacity`, `hardness`; removes alpha |
92
+ | brightness | `amount` from −1 to 1; additive channel offset |
93
+ | contrast | `amount` from −1 to 1; contrast around the channel midpoint |
94
+ | saturation | `amount` from −1 to 1; −1 gives monochrome |
95
+ | hueShift | `degrees` from −360 to 360 |
96
+ | noise | `amount` from 0 to 1, explicit integer `seed` |
97
+ | blur | `radius`; samples full layer context, changes only scope |
98
+ | colourReplace | `from`, `to`, optional `tolerance` from 0 to 1 |
99
+ | setPixels | `pixels: [{x, y, colour}, ...]`; maximum 4096 sparse pixels |
100
+
101
+ Brush hardness is implemented by a deterministic Gaussian blur of the stroke. Colour adjustments preserve alpha. `setPixels` replaces RGBA values. Pixels outside bounds are ignored; feathered masks blend premultiplied colors. For transactional edits, the final composite is compared before and after; any changed pixel outside the command's scope rejects the whole transaction. Direct `applyCommand` is a pure structural helper; use `Project.apply` to enforce the rendered locality guarantee.
102
+
103
+ ## Palette tiles
104
+
105
+ Tiles have canvas bounds, a single-character palette, and rows of keys. Rows must have equal widths, every key must exist, and grids are limited to 256 × 256. Scaling uses nearest-neighbor sampling.
106
+
107
+ ```yaml
108
+ - id: detail
109
+ type: raster
110
+ tiles:
111
+ - bounds: [320, 280, 32, 32]
112
+ palette: { A: '#00000000', B: '#f2be81', C: '#b75c40' }
113
+ pixels: ['AABB', 'ABBC', 'BBCC', 'BCCB']
114
+ ```
115
+
116
+ `replaceTile` replaces a tile with exactly matching bounds or appends a new one. Transparent tile pixels composite over underlying layer content. Palette tiles remain compact grids. Separately, `promoteRegion`, `updateRegion` and `demoteRegion` manage patches with 1–4× resolution and optional external raster sources; see [evolution](evolution.md).
117
+
118
+ ## Commands and verification
119
+
120
+ `apply` accepts `{reason, expected_hash?, commands: [...]}`. Commands include add/remove/move/resize layer, set opacity/blend/text/style, group siblings, align/distribute siblings, apply effect, apply raster operation, and replace tile. See `graphics schema --kind command` for exact fields. Removing referenced mask or verification targets is rejected unless the resulting scene is valid. Grouping can change compositing with interleaved siblings; it is an explicit structural edit without a locality promise.
121
+
122
+ Verification always checks rendered text overflow. Optional rules cover safe-area, text-overflow, text-equals, required-role, contrast, no-overlap and region-luma. Luma is a mean weighted sRGB channel value in [0,1], not perceptual luminance. `contrast` uses declared foreground and explicit background colours. `pixel-contrast` and `visible-area` use counterfactual rendered samples through `verifyRendered` / `Project.verify`. Geometry checks use transformed bounding boxes, not exact silhouettes. A heatmap marks issue bounds. All reports state these limitations.
123
+
124
+ `render-region` requires an integer rectangle fully inside the canvas and equals an exact crop of the full render. `draft` halves both output dimensions after rendering; preview and final currently use the same full-quality pipeline. PNG output includes renderer evidence in a sidecar. Canonical scene hashes omit timestamps; history includes timestamps for audit purposes without influencing pixel output.
125
+
126
+ The [evolution guide](evolution.md) documents the expanded command surface, coordinates, components, fonts, adaptive regions, critiques, rebase, compaction, and agent trials.