@call-me-sensei/toonlab 0.4.20 → 0.4.21

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/NPM-LIBRARY.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # ToonLab npm library guide
2
2
 
3
3
  This guide is included in `@call-me-sensei/toonlab`. It is the package-first
4
- reference for version 0.4.20 and does not require access to the source
4
+ reference for version 0.4.21 and does not require access to the source
5
5
  repository.
6
6
 
7
7
  ## Install
@@ -45,7 +45,7 @@ them:
45
45
  - `@call-me-sensei/toonlab/styles` for portable bundles, strict labels,
46
46
  auditing, and coordinated application;
47
47
  - `@call-me-sensei/toonlab/assetlib`, `/asset-policy`, and
48
- `/official-catalog` for policy-aware Library and Gallery discovery;
48
+ `/official-catalog` for policy-aware local/released-catalog integration;
49
49
  - `@call-me-sensei/toonlab/character` and `/loaders` for supported preview and
50
50
  host-model integration;
51
51
  - `@call-me-sensei/toonlab/runtime`, `/renderer`, `/lighting`, and
@@ -76,7 +76,9 @@ The npm artifact contains four supported layers:
76
76
  It does not contain the ToonLab website, Lab application UIs, examples,
77
77
  screenshots, models, textures, or other binary media. Optional public fixtures
78
78
  are identified by immutable `https://assets.toonlab.io` metadata rather than
79
- being bundled.
79
+ being bundled. Authored tree recipes are assets too: the reviewed public tree
80
+ collection remains in the Gallery and is discovered through MCP instead of
81
+ being hard-coded into the JavaScript runtime.
80
82
 
81
83
  ## Use a focused runtime
82
84
 
@@ -167,7 +169,7 @@ it uses the disk workspace passed through `--workspace`; no account is needed.
167
169
  "command": "npx",
168
170
  "args": [
169
171
  "-y",
170
- "@call-me-sensei/toonlab@0.4.20",
172
+ "@call-me-sensei/toonlab@0.4.21",
171
173
  "--workspace",
172
174
  "/absolute/path/to/your-game/.toonlab"
173
175
  ],
@@ -181,8 +183,11 @@ it uses the disk workspace passed through `--workspace`; no account is needed.
181
183
 
182
184
  This server provides the anime-game profile, runtime guide, live-Lab
183
185
  contracts, deterministic document creation and mutation, policy validation,
184
- public asset discovery, local Library operations, and optional provider-backed
185
- generation when the developer supplies server-side provider keys.
186
+ public Gallery search/retrieval, local Library operations, and optional
187
+ provider-backed generation when the developer supplies server-side provider
188
+ keys. Authored tree recipes are not bundled. Call `search_public_gallery` with
189
+ `{ query: "tree", type: "tree-recipe" }`, then use
190
+ `get_public_gallery_asset` to retrieve the reviewed portable recipe.
186
191
 
187
192
  ### ToonLab Pro MCP
188
193
 
package/README.md CHANGED
@@ -19,7 +19,7 @@ Use ToonLab as a focused runtime library (`@call-me-sensei/toonlab` on npm), as
19
19
  an asset/preset authoring workspace, or as an asset-discovery service—not as a
20
20
  one-shot world generator.
21
21
 
22
- ## Versioned expectation: 0.4.20
22
+ ## Versioned expectation: 0.4.21
23
23
 
24
24
  For a correctly constructed and semantically labeled scene, one strict Call Me
25
25
  Sensei bundle application is expected to establish the supported visual
@@ -310,10 +310,13 @@ npm run dev
310
310
  # In an MCP client, use the command/config shown at /settings/.
311
311
  ```
312
312
 
313
- The local server can search the built-in procedural catalog and public CC0
314
- sources, read your saved presets and exported files, generate seeded recipes,
315
- and import assets into the project. It uses stdio, requires no account or
316
- OAuth, and keeps work local. See [Local MCP and workspace](https://github.com/call-me-sensei/toonlab/blob/main/docs/mcp.md) and
313
+ The local server can search your workspace, the public ToonLab Gallery, the
314
+ released official catalog, and public CC0 sources; read saved presets and
315
+ exported files; generate approved seeded recipes; and import assets into the
316
+ project. Authored Gallery recipes are fetched with their public license and
317
+ provenance instead of being copied into the npm package. The server uses stdio,
318
+ requires no account or OAuth for public discovery, and keeps local work local.
319
+ See [Local MCP and workspace](https://github.com/call-me-sensei/toonlab/blob/main/docs/mcp.md) and
317
320
  [Local database and public asset releases](https://github.com/call-me-sensei/toonlab/blob/main/docs/local-database-and-public-assets.md).
318
321
 
319
322
  ## The labs
@@ -375,7 +378,7 @@ camera behavior, and game feel remain host-owned or pre-beta.
375
378
  | Toon character shading | `@call-me-sensei/toonlab/toon` | Modern anime character shader: cel bands with art-directed face lighting, skin-tone shadow management, shadow-color HSV control, scene/self/contact shadows, average-shadow smoothing, rim light (fresnel or screen-space depth), stylized + anisotropic hair highlights, eye catchlights, role-aware specular, source map routing (normal/AO/emissive/MatCap/ramp/detail), inverted-hull outlines, glitter, stickers, perspective removal, shell fur, dither fades — 23 settings groups, all preset-serializable. [Docs](https://github.com/call-me-sensei/toonlab/blob/main/docs/toon-shading.md) |
376
379
  | Environment shading | `@call-me-sensei/toonlab/environment` | Modern anime-style scene shader for texture packs, standard glTF, and untextured scenes: material-role classification, wrapped lighting, packed-map hints, window cutouts, sun/lamp rigs, time-of-day, six-direction ambient probe, planar floor reflections, BVH vertex-AO baking, height fog, cloud shadows. [Docs](https://github.com/call-me-sensei/toonlab/blob/main/docs/environment.md) |
377
380
  | Water | `@call-me-sensei/toonlab/water` | Focused water treatment for a host-authored water footprint, continuous shore, and closed seabed: Gerstner waves, absorption color, refraction/caustics/foam, ripples, splashes, wakes, kelp, underwater treatment, and CPU buoyancy sampling. It does not design the coast or underwater habitat. [Docs](https://github.com/call-me-sensei/toonlab/blob/main/docs/water.md) |
378
- | Vegetation | `@call-me-sensei/toonlab/vegetation` | Instanced grass and flower fields; the 12 named pre-species legacy trees; and a focused procedural broadleaf `BranchTree` with deterministic branching, five leaf silhouettes, caller-supplied leaf/bark textures, and a portable recipe. Package-generated Call Me Sensei trees preserve authored bark first and otherwise select a registered deterministic bark surface instead of a bare trunk. The 165-species research roster remains repository-only experimental work. Independent Tree, Grass, and Flower shader profiles share one semantic-role renderer family. [Docs](https://github.com/call-me-sensei/toonlab/blob/main/docs/vegetation-sky.md) |
381
+ | Vegetation | `@call-me-sensei/toonlab/vegetation` | Tree/grass/flower construction and shader runtimes, including the focused procedural broadleaf `BranchTree` with deterministic branching, five leaf silhouettes, caller-supplied leaf/bark textures, and a portable recipe. No authored tree asset collection is bundled or exported: reviewed tree recipes remain searchable Gallery assets delivered through MCP with their CC0 metadata. Package-generated trees preserve authored bark first and otherwise select a registered deterministic bark surface instead of a bare trunk. The experimental species roster remains repository-only. [Docs](https://github.com/call-me-sensei/toonlab/blob/main/docs/vegetation-sky.md) |
379
382
  | Rock shader | `@call-me-sensei/toonlab/rock-shader` | Detailed, versioned rock-material profiles with projected detail, distance tint, normal fading, striping, moss and optional top layers, plus explicit source-albedo and vertex-color/AO integration. Call Me Sensei is the default. Geometry generation remains separate in `rockgen`. [Docs](https://github.com/call-me-sensei/toonlab/blob/main/docs/rock-shader.md) |
380
383
  | Ground shader | `@call-me-sensei/toonlab/ground-shader` | Splat-weighted anime terrain with slope/cliff detail, shoreline and weather response, HDR sun/shade controls, safe shadow defaults, and flat-albedo ground-field output for vegetation adoption. [Docs](https://github.com/call-me-sensei/toonlab/blob/main/docs/ground-shader.md) |
381
384
  | Sky | `@call-me-sensei/toonlab/sky` | Reusable atmosphere, palette, sun, moon, stars, and god-ray settings authored by Sky Lab and Sky & Cloud Lab. Your application coordinates final lighting and scene state. [Docs](https://github.com/call-me-sensei/toonlab/blob/main/docs/sky.md) |
@@ -460,21 +463,21 @@ profile parameters for other authored styles. Its recipe-v3 LOD0/1/2 topology
460
463
  uses 40/14/6 primary blades and compensates retained stroke width so zooming
461
464
  does not make the underlying terrain appear to change color.
462
465
 
463
- The supported tree set is intentionally the proven pre-species surface.
464
- Use a named legacy silhouette directly:
466
+ ToonLab does not ship predefined tree assets in npm. Search reviewed public
467
+ tree recipes through MCP, then pass the retrieved portable recipe options to
468
+ the runtime:
465
469
 
466
470
  ```js
467
- import {
468
- LEGACY_TREE_IDS,
469
- createLegacyTree,
470
- } from '@call-me-sensei/toonlab/vegetation';
471
+ import { StylizedTree } from '@call-me-sensei/toonlab/vegetation';
471
472
 
472
- console.log(LEGACY_TREE_IDS); // straight, leaning, see-through, ...
473
- const legacyTree = createLegacyTree('golden-gingko', {
473
+ // treeRecipe is the document returned by get_public_gallery_asset after
474
+ // search_public_gallery({ query: 'tree', type: 'tree-recipe' }).
475
+ const tree = new StylizedTree({
476
+ ...treeRecipe.options,
474
477
  trunkMap: barkTexture,
475
478
  vegetationShader: { preset: 'call_me_sensei' },
476
479
  });
477
- scene.add(legacyTree);
480
+ scene.add(tree);
478
481
  ```
479
482
 
480
483
  For a configurable recursive branch-type broadleaf, use the focused wrapper:
@@ -495,7 +498,8 @@ scene.add(tree);
495
498
  shapes. Botanical species generation is not a public package claim.
496
499
 
497
500
  Water, sky, flowers, trees, grass, and splashes keep procedural defaults, while
498
- legacy trees and BranchTree may use caller-owned leaf and bark textures. The
501
+ Gallery tree recipes, `StylizedTree`, and `BranchTree` may use caller-owned
502
+ leaf and bark textures. The
499
503
  Call Me Sensei tree path resolves bark in this order: explicit
500
504
  `trunkSurfaceProfile`, authored `trunkMap`, then the registered
501
505
  `call-me-sensei-bark-v1` fallback. Use `getTreeSurfaceProfileOptions()` when a
@@ -698,9 +702,11 @@ Ready-to-paste supported prompts are included in
698
702
  Two servers, and they compose:
699
703
 
700
704
  **Local (free, no account).** The stdio server included in this package
701
- searches the built-in procedural catalog and public CC0 sources, reads your
702
- saved presets and lab exports, generates seeded recipes, and imports assets
703
- into a disk-backed `.toonlab/` workspace:
705
+ searches local/project content, reviewed public Gallery recipes, the released
706
+ official catalog, and public CC0 sources; reads saved presets and Lab exports;
707
+ generates approved seeded recipes; and imports assets into a disk-backed
708
+ `.toonlab/` workspace. Use `search_public_gallery` for authored assets such as
709
+ the public tree collection—those recipes are not npm built-ins:
704
710
 
705
711
  ```json
706
712
  {
package/agents/PROMPTS.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # ToonLab agent prompts
2
2
 
3
3
  These prompts are included with the npm package. They use only the supported
4
- 0.4.20 runtime, agent skills, and MCP workflows.
4
+ 0.4.21 runtime, agent skills, and MCP workflows.
5
5
 
6
6
  ## Existing-scene integration
7
7
 
@@ -55,11 +55,15 @@ document.
55
55
 
56
56
  ```text
57
57
  Using the ToonLab vegetation-sky skill, create the requested tree, shrub,
58
- grass, or groundcover with the public vegetation runtime. Keep species/recipe,
59
- seed, geometry, palette, and placement separate from the Tree/Grass/Flower
60
- shader profile. Emit stable modeled-part and material-role labels, preserve
61
- the recipe for regeneration, and verify LOD and shadow behavior. Do not use the
62
- repository-only experimental species engine.
58
+ grass, or groundcover. For an existing authored tree, search the public Gallery
59
+ through MCP first and retrieve its portable recipe; the npm package contains
60
+ tree construction/shader capability but no predefined tree asset collection.
61
+ Author a new `StylizedTree` or `BranchTree` only when requested or when accepted
62
+ discovery does not close the role. Keep recipe, seed, geometry, palette, and
63
+ placement separate from the Tree/Grass/Flower shader profile. Emit stable
64
+ modeled-part and material-role labels, preserve the recipe for regeneration,
65
+ and verify LOD and shadow behavior. Do not use the repository-only experimental
66
+ species engine.
63
67
  ```
64
68
 
65
69
  ## Rock generation and shader separation
@@ -82,10 +82,12 @@ or environment with ToonLab's runtime library in their own Three.js app.
82
82
  system preset with embedded shader controls. Sky, Cloud, and Sky & Cloud are
83
83
  live focused authoring surfaces. Vegetation is one shared
84
84
  implementation family with separate Tree, Grass, and Flower shader profiles;
85
- their Generation Labs own geometry/material data. The stable public package
86
- trees are the 12 named pre-species legacy presets plus the generic
87
- `BranchTree`; repository species work is experimental and must not be
88
- presented as supported. Ground is a separate shader domain. Scene weather,
85
+ their Generation Labs own geometry/material data. The public package ships
86
+ tree construction and shader capability, not predefined tree assets. Search
87
+ reviewed authored tree recipes through MCP; use `StylizedTree` or
88
+ `BranchTree` to instantiate or deliberately author them. Repository species
89
+ work is experimental and must not be presented as supported. Ground is a
90
+ separate shader domain. Scene weather,
89
91
  lighting, camera, interactions, and complete Sky/Cloud composition stay
90
92
  host-owned or experimental.
91
93
  - For style-bundle, multi-shader, or arbitrary-asset work, read
@@ -10,11 +10,12 @@ Inspect the connected server's tool names before calling anything. Do not
10
10
  assume the developer has ToonLab Pro, and do not assume a local OSS checkout.
11
11
 
12
12
  - **ToonLab OSS local MCP** exposes `get_workspace_info`,
13
- `search_cc0_assets`, `get_cc0_asset`, and `import_cc0_asset`. Its
14
- `search_assets` searches built-in procedural entries, the complete official
15
- R2-backed Gallery catalog, and the disk-backed project/library/workspace.
16
- Use `source: 'official'` for released assets and follow `nextOffset` until
17
- null.
13
+ `search_public_gallery`, `get_public_gallery_asset`, `search_cc0_assets`,
14
+ `get_cc0_asset`, and `import_cc0_asset`. Its `search_assets` searches
15
+ approved built-in generator/settings entries, the local released official
16
+ catalog, and the disk-backed project/library/workspace. Authored assets such
17
+ as reviewed tree recipes are searched through `search_public_gallery` rather
18
+ than copied into npm.
18
19
  - **ToonLab Pro remote MCP** exposes `get_runtime_guide`,
19
20
  `search_public_gallery`, and `get_toonlab_asset`. Its `search_assets`
20
21
  searches indexed external open assets; first-party rocks/trees and community
@@ -41,10 +42,12 @@ the surface the developer actually has and report which surface was used.
41
42
  1. Call `list_my_creations` with the task's text and exact tag filters, then
42
43
  `search_assets` with `source: 'library'`,
43
44
  `'workspace'`, `'workspace-storage'`, or `'builtin'` as appropriate. If no
44
- accepted local entry closes the role, call it with `source: 'official'` to
45
- search the released Gallery through the local database. Follow
46
- `nextOffset` until null so all matching official assets remain reachable.
47
- 2. Call local `get_asset` for the complete recipe/file descriptor.
45
+ accepted local entry closes the role, call `search_public_gallery` for
46
+ authored public assets and follow `nextOffset` until null. Use
47
+ `type: 'tree-recipe'` for reviewed trees. Use `search_assets` with
48
+ `source: 'official'` only for the installed released catalog.
49
+ 2. Call local `get_public_gallery_asset` for a selected Gallery document or
50
+ `get_asset` for a local/released-catalog record.
48
51
  3. When policy permits external CC0, call `search_cc0_assets`, then
49
52
  `get_cc0_asset`; call `import_cc0_asset` only after selection to download
50
53
  bytes plus their provenance manifest into `.toonlab/imports/`.
@@ -50,9 +50,11 @@ scene-kit role.
50
50
  2. **Reuse what exists.** In OSS, earlier imports, saved presets, and exports
51
51
  live in `.toonlab/`; call local `list_my_creations` and `search_assets` with
52
52
  `source: 'workspace'`, `'workspace-storage'`, `'library'`, or `'builtin'`.
53
- If those do not close the role, use local `search_assets` with
54
- `source: 'official'` for the complete released Gallery catalog and follow
55
- `nextOffset` until null.
53
+ If those do not close the role, use local `search_public_gallery` and follow
54
+ `nextOffset` until null; use `type: 'tree-recipe'` for authored trees and
55
+ retrieve a finalist with `get_public_gallery_asset`. Use local
56
+ `search_assets({ source: 'official' })` only for the installed released
57
+ catalog.
56
58
  In Pro, call cloud `list_my_creations`, then `search_public_gallery`; use
57
59
  `source: 'toonlab'` plus the appropriate catalog for first-party assets and
58
60
  follow `nextOffset`. For rocks on either surface, shortlist from positive
@@ -195,9 +197,10 @@ table, as the source of truth):
195
197
 
196
198
  - Seamless PBR material sets (brick, wood, ground …) → ambientCG.
197
199
  - Photoscanned props, HDRIs, high-end texture sets → Poly Haven.
198
- - Stylized/toon-ready models, vegetation, buildings → the built-in
199
- procedural catalog first only for families that passed the base-set
200
- reliability gate; otherwise compare curated external candidates.
200
+ - Authored stylized/toon-ready models and vegetation → the public Gallery
201
+ first; retrieve their portable document and provenance through MCP. Use a
202
+ built-in procedural family only when it passed the base-set reliability gate
203
+ and the task calls for new generation rather than an existing asset.
201
204
  - Stylized *seamless textures* → `@call-me-sensei/toonlab/texgen` in code
202
205
  (60+ presets); the catalog has no texture entries, so this route is a
203
206
  package import, not an MCP call.
@@ -80,8 +80,11 @@ User guidance:
80
80
  create the pass after all writer meshes exist, call its `update()` every frame before grass, require
81
81
  `writerCount > 0` and `ready`, and call `invalidate()` after repainting a
82
82
  writer without a transform change.
83
- - In the public package, use `LEGACY_TREE_PRESETS` and `createLegacyTree()` for the 12
84
- supported pre-species silhouettes. Use `BranchTree` when the developer wants
83
+ - The public package contains tree construction and shader capability, not a
84
+ predefined tree asset collection. For an authored tree, use the connected
85
+ MCP's `search_public_gallery` and `get_public_gallery_asset` tools, then
86
+ instantiate its portable recipe with `StylizedTree`. Use `BranchTree` when
87
+ the developer explicitly wants
85
88
  recursive branching, five broadleaf silhouettes, leaf color/texture plus a
86
89
  portable lit/shadow/crown palette, independent leaf `coverageScale`, bark
87
90
  color/texture, authored trunk bend/twist/taper, roots, portable documents,
@@ -108,11 +111,11 @@ User guidance:
108
111
  `GRASS_SHADER_PROFILE`, and `FLOWER_SHADER_PROFILE` through the bundle's
109
112
  `treeShader`, `grassShader`, and `flowerShader` slots. They share a renderer
110
113
  family and semantic material contract, but serialize and apply independently.
111
- Keep asset albedo and legacy/BranchTree geometry/material inputs separate from all
114
+ Keep asset albedo and Gallery-recipe/BranchTree geometry/material inputs separate from all
112
115
  three shader treatments.
113
116
  - Grass palettes update base, tip, and shadow tint together; they do not change
114
117
  shadow strength, wind, or current weather.
115
- - Use the public versioned Grass, vegetation-shader, legacy/BranchTree/flower recipe, and Sky
118
+ - Use the public versioned Grass, vegetation-shader, BranchTree/flower recipe, and Sky
116
119
  preset document APIs for portable data; do not copy lab-local validators.
117
120
  - Treat Sky as one integrated World System, not a separate Shader Lab. Its
118
121
  shader, sun, clouds, stars, and procedural motion form one system preset with
@@ -50,9 +50,11 @@ scene-kit role.
50
50
  2. **Reuse what exists.** In OSS, earlier imports, saved presets, and exports
51
51
  live in `.toonlab/`; call local `list_my_creations` and `search_assets` with
52
52
  `source: 'workspace'`, `'workspace-storage'`, `'library'`, or `'builtin'`.
53
- If those do not close the role, use local `search_assets` with
54
- `source: 'official'` for the complete released Gallery catalog and follow
55
- `nextOffset` until null.
53
+ If those do not close the role, use local `search_public_gallery` and follow
54
+ `nextOffset` until null; use `type: 'tree-recipe'` for authored trees and
55
+ retrieve a finalist with `get_public_gallery_asset`. Use local
56
+ `search_assets({ source: 'official' })` only for the installed released
57
+ catalog.
56
58
  In Pro, call cloud `list_my_creations`, then `search_public_gallery`; use
57
59
  `source: 'toonlab'` plus the appropriate catalog for first-party assets and
58
60
  follow `nextOffset`. For rocks on either surface, shortlist from positive
@@ -195,9 +197,10 @@ table, as the source of truth):
195
197
 
196
198
  - Seamless PBR material sets (brick, wood, ground …) → ambientCG.
197
199
  - Photoscanned props, HDRIs, high-end texture sets → Poly Haven.
198
- - Stylized/toon-ready models, vegetation, buildings → the built-in
199
- procedural catalog first only for families that passed the base-set
200
- reliability gate; otherwise compare curated external candidates.
200
+ - Authored stylized/toon-ready models and vegetation → the public Gallery
201
+ first; retrieve their portable document and provenance through MCP. Use a
202
+ built-in procedural family only when it passed the base-set reliability gate
203
+ and the task calls for new generation rather than an existing asset.
201
204
  - Stylized *seamless textures* → `@call-me-sensei/toonlab/texgen` in code
202
205
  (60+ presets); the catalog has no texture entries, so this route is a
203
206
  package import, not an MCP call.
@@ -80,8 +80,11 @@ User guidance:
80
80
  create the pass after all writer meshes exist, call its `update()` every frame before grass, require
81
81
  `writerCount > 0` and `ready`, and call `invalidate()` after repainting a
82
82
  writer without a transform change.
83
- - In the public package, use `LEGACY_TREE_PRESETS` and `createLegacyTree()` for the 12
84
- supported pre-species silhouettes. Use `BranchTree` when the developer wants
83
+ - The public package contains tree construction and shader capability, not a
84
+ predefined tree asset collection. For an authored tree, use the connected
85
+ MCP's `search_public_gallery` and `get_public_gallery_asset` tools, then
86
+ instantiate its portable recipe with `StylizedTree`. Use `BranchTree` when
87
+ the developer explicitly wants
85
88
  recursive branching, five broadleaf silhouettes, leaf color/texture plus a
86
89
  portable lit/shadow/crown palette, independent leaf `coverageScale`, bark
87
90
  color/texture, authored trunk bend/twist/taper, roots, portable documents,
@@ -108,11 +111,11 @@ User guidance:
108
111
  `GRASS_SHADER_PROFILE`, and `FLOWER_SHADER_PROFILE` through the bundle's
109
112
  `treeShader`, `grassShader`, and `flowerShader` slots. They share a renderer
110
113
  family and semantic material contract, but serialize and apply independently.
111
- Keep asset albedo and legacy/BranchTree geometry/material inputs separate from all
114
+ Keep asset albedo and Gallery-recipe/BranchTree geometry/material inputs separate from all
112
115
  three shader treatments.
113
116
  - Grass palettes update base, tip, and shadow tint together; they do not change
114
117
  shadow strength, wind, or current weather.
115
- - Use the public versioned Grass, vegetation-shader, legacy/BranchTree/flower recipe, and Sky
118
+ - Use the public versioned Grass, vegetation-shader, BranchTree/flower recipe, and Sky
116
119
  preset document APIs for portable data; do not copy lab-local validators.
117
120
  - Treat Sky as one integrated World System, not a separate Shader Lab. Its
118
121
  shader, sun, clouds, stars, and procedural motion form one system preset with
@@ -25,62 +25,14 @@ function entry(input) {
25
25
  });
26
26
  }
27
27
 
28
- const CURATED_PLANT_RECIPES = Object.freeze([
29
- entry({
30
- cluster: 'vegetation',
31
- description: 'Mid-size broadleaf starting recipe for anime environments.',
32
- id: 'tree/broadleaf/sensei',
33
- kind: 'recipe',
34
- label: 'Sensei broadleaf',
35
- recipe: {
36
- options: { seed: 3, size: 3.2 },
37
- schema: 'treeRecipe',
38
- type: 'tree',
39
- version: 1,
40
- },
41
- spawn: 'createPlantFromRecipe(entry.recipe)',
42
- tags: ['tree', 'vegetation', 'anime'],
43
- thumbnail: thumbnailFor('tree/broadleaf/sensei'),
44
- }),
45
- entry({
46
- cluster: 'vegetation',
47
- description: 'Ground-hugging foliage mass for yards and path edges.',
48
- id: 'tree/bush/hedge',
49
- kind: 'recipe',
50
- label: 'Hedge bush',
51
- recipe: {
52
- options: { seed: 5, size: 1.2 },
53
- schema: 'treeRecipe',
54
- type: 'bush',
55
- version: 1,
56
- },
57
- spawn: 'createPlantFromRecipe(entry.recipe)',
58
- tags: ['bush', 'vegetation', 'anime'],
59
- thumbnail: thumbnailFor('tree/bush/hedge'),
60
- }),
61
- entry({
62
- cluster: 'vegetation',
63
- description: 'Flowering plant with daisy heads.',
64
- id: 'tree/flower/daisy',
65
- kind: 'recipe',
66
- label: 'Daisy flower',
67
- recipe: {
68
- options: { seed: 9, size: 0.5, species: 'daisy' },
69
- schema: 'treeRecipe',
70
- type: 'flower',
71
- version: 1,
72
- },
73
- spawn: 'createPlantFromRecipe(entry.recipe)',
74
- tags: ['flower', 'vegetation', 'anime'],
75
- thumbnail: thumbnailFor('tree/flower/daisy'),
76
- }),
77
- ]);
78
-
79
28
  let cached = null;
80
29
 
81
30
  export function publicMcpCatalogEntries() {
82
31
  if (cached) return cached;
83
- const entries = [...CURATED_PLANT_RECIPES];
32
+ // Authored vegetation recipes are Gallery assets, not package built-ins.
33
+ // The packaged MCP discovers them from the public Gallery so provenance,
34
+ // license, review state, and immutable documents stay attached.
35
+ const entries = [];
84
36
 
85
37
  for (const option of getRockgenPresetOptions()) {
86
38
  const id = `rock/${slug(option.value)}`;
package/mcp/server.mjs CHANGED
@@ -82,6 +82,8 @@ const SERVER_NAME = 'toonlab-oss';
82
82
  const SERVER_VERSION = JSON.parse(
83
83
  readFileSync(new URL('../package.json', import.meta.url), 'utf8'),
84
84
  ).version;
85
+ const PUBLIC_GALLERY_ENDPOINT = process.env.TOONLAB_PUBLIC_GALLERY_URL
86
+ ?? 'https://toonlab.io/api/v1/creations';
85
87
  const LATEST_PROTOCOL_VERSION = '2025-11-25';
86
88
  const SUPPORTED_PROTOCOL_VERSIONS = new Set([
87
89
  LATEST_PROTOCOL_VERSION,
@@ -289,6 +291,112 @@ function normalizeDiscovery(entry, summary, { domain = '', policy = null } = {})
289
291
  };
290
292
  }
291
293
 
294
+ function publicGalleryUrl(path = '') {
295
+ let endpoint;
296
+ try {
297
+ endpoint = new URL(PUBLIC_GALLERY_ENDPOINT);
298
+ } catch {
299
+ throw new Error('TOONLAB_PUBLIC_GALLERY_URL must be an absolute HTTP(S) URL.');
300
+ }
301
+ if (!['http:', 'https:'].includes(endpoint.protocol)) {
302
+ throw new Error('TOONLAB_PUBLIC_GALLERY_URL must use HTTP(S).');
303
+ }
304
+ endpoint.search = '';
305
+ endpoint.hash = '';
306
+ if (!path) return endpoint;
307
+ endpoint.pathname = `${endpoint.pathname.replace(/\/$/u, '')}/${encodeURIComponent(path)}`;
308
+ return endpoint;
309
+ }
310
+
311
+ function absoluteGalleryReference(reference) {
312
+ if (typeof reference !== 'string' || !reference.trim()) return null;
313
+ return new URL(reference, publicGalleryUrl()).href;
314
+ }
315
+
316
+ async function fetchPublicGalleryJson(url) {
317
+ let response;
318
+ try {
319
+ response = await fetch(url, {
320
+ headers: { accept: 'application/json' },
321
+ signal: AbortSignal.timeout(15_000),
322
+ });
323
+ } catch (cause) {
324
+ throw new Error(`ToonLab public Gallery request failed: ${cause?.message ?? String(cause)}`);
325
+ }
326
+ if (!response.ok) {
327
+ throw new Error(`ToonLab public Gallery returned HTTP ${response.status}.`);
328
+ }
329
+ try {
330
+ return await response.json();
331
+ } catch {
332
+ throw new Error('ToonLab public Gallery returned invalid JSON.');
333
+ }
334
+ }
335
+
336
+ function normalizePublicGalleryItem(item, args = {}) {
337
+ const id = String(item?.id ?? '').trim();
338
+ if (!id) throw new Error('ToonLab public Gallery returned an item without an id.');
339
+ const type = String(item.type ?? 'creation');
340
+ const entry = {
341
+ attribution: item.attribution ?? null,
342
+ author: item.author ?? null,
343
+ cluster: type,
344
+ description: item.description ?? null,
345
+ documentUrl: absoluteGalleryReference(item.download ?? item.documentUrl),
346
+ id,
347
+ kind: type,
348
+ label: String(item.label ?? id),
349
+ license: item.license ?? null,
350
+ source: 'gallery',
351
+ sourceClass: 'toonlab-gallery',
352
+ tags: Array.isArray(item.tags) ? item.tags.map(String) : [],
353
+ thumbnail: absoluteGalleryReference(item.thumbnail),
354
+ updatedAt: item.updated_at ?? null,
355
+ url: absoluteGalleryReference(item.url ?? `/c/${item.slug ?? id}`),
356
+ };
357
+ return normalizeDiscovery(entry, entry, args);
358
+ }
359
+
360
+ async function searchPublicGallery(args = {}) {
361
+ const limit = limitValue(args.limit, 20, 60);
362
+ const offset = Math.max(0, Math.floor(Number(args.offset) || 0));
363
+ const endpoint = publicGalleryUrl();
364
+ endpoint.searchParams.set('limit', String(limit));
365
+ endpoint.searchParams.set('offset', String(offset));
366
+ if (String(args.query ?? '').trim()) endpoint.searchParams.set('q', String(args.query).trim());
367
+ if (String(args.type ?? '').trim()) endpoint.searchParams.set('type', String(args.type).trim());
368
+ const response = await fetchPublicGalleryJson(endpoint);
369
+ if (!Array.isArray(response?.items)) {
370
+ throw new Error('ToonLab public Gallery response is missing items.');
371
+ }
372
+ const items = response.items.map((item) => normalizePublicGalleryItem(item, args));
373
+ return {
374
+ count: items.length,
375
+ items,
376
+ limit,
377
+ nextOffset: items.length === limit ? offset + items.length : null,
378
+ offset,
379
+ source: 'toonlab-public-gallery',
380
+ };
381
+ }
382
+
383
+ async function getPublicGalleryAsset(args = {}) {
384
+ const id = String(args.id ?? '').trim();
385
+ if (!id) throw new Error('A public Gallery asset id or slug is required.');
386
+ const item = await fetchPublicGalleryJson(publicGalleryUrl(id));
387
+ const normalized = normalizePublicGalleryItem({
388
+ ...item,
389
+ download: item.documentUrl ?? null,
390
+ thumbnail: item.thumbnail ?? null,
391
+ url: item.url ?? `/c/${item.slug ?? id}`,
392
+ }, args);
393
+ return {
394
+ ...normalized,
395
+ document: item.document ?? null,
396
+ files: Array.isArray(item.files) ? item.files : [],
397
+ };
398
+ }
399
+
292
400
  function summarizeCatalogEntry(entry) {
293
401
  const metadata = entry.metadata && typeof entry.metadata === 'object' && !Array.isArray(entry.metadata)
294
402
  ? entry.metadata
@@ -989,7 +1097,7 @@ const TOOLS = [
989
1097
  },
990
1098
  {
991
1099
  annotations: { readOnlyHint: true },
992
- description: 'Search built-in procedural assets, the paginated complete official Gallery catalog, saved library entries, lab presets, and files on disk. Official rock summaries include compact dimensions and taxonomy metadata; follow nextOffset until null.',
1100
+ description: 'Search approved built-in generator/settings entries, the local released official catalog, saved library entries, lab presets, and files on disk. Official rock summaries include compact dimensions and taxonomy metadata; follow nextOffset until null.',
993
1101
  inputSchema: {
994
1102
  additionalProperties: false,
995
1103
  properties: {
@@ -1008,6 +1116,40 @@ const TOOLS = [
1008
1116
  name: 'search_assets',
1009
1117
  title: 'Search ToonLab assets',
1010
1118
  },
1119
+ {
1120
+ annotations: { openWorldHint: true, readOnlyHint: true },
1121
+ description: 'Search public ToonLab Gallery creations with their license, attribution, tags, thumbnail, and immutable portable-document URL. Authored tree recipes live here rather than in npm; use type="tree-recipe" and follow nextOffset until null.',
1122
+ inputSchema: {
1123
+ additionalProperties: false,
1124
+ properties: {
1125
+ domain: { type: 'string' },
1126
+ limit: { maximum: 60, minimum: 1, type: 'integer' },
1127
+ offset: { minimum: 0, type: 'integer' },
1128
+ policy: { type: 'object' },
1129
+ query: { type: 'string' },
1130
+ type: { type: 'string' },
1131
+ },
1132
+ type: 'object',
1133
+ },
1134
+ name: 'search_public_gallery',
1135
+ title: 'Search the ToonLab public Gallery',
1136
+ },
1137
+ {
1138
+ annotations: { openWorldHint: true, readOnlyHint: true },
1139
+ description: 'Get one public ToonLab Gallery creation by publication id or slug, including its complete portable document and immutable public files.',
1140
+ inputSchema: {
1141
+ additionalProperties: false,
1142
+ properties: {
1143
+ domain: { type: 'string' },
1144
+ id: { type: 'string' },
1145
+ policy: { type: 'object' },
1146
+ },
1147
+ required: ['id'],
1148
+ type: 'object',
1149
+ },
1150
+ name: 'get_public_gallery_asset',
1151
+ title: 'Get a ToonLab public Gallery asset',
1152
+ },
1011
1153
  {
1012
1154
  annotations: { readOnlyHint: true },
1013
1155
  description: 'Get a complete asset, recipe, preset, library entry, or workspace file descriptor by id.',
@@ -1415,6 +1557,8 @@ async function callTool(name, args = {}) {
1415
1557
  case 'get_lab_features': return getLabFeatures(args.lab);
1416
1558
  case 'create_lab_document': return createLabDocumentTool(args);
1417
1559
  case 'search_assets': return searchAssets(args);
1560
+ case 'search_public_gallery': return searchPublicGallery(args);
1561
+ case 'get_public_gallery_asset': return getPublicGalleryAsset(args);
1418
1562
  case 'get_asset': return getAsset(args);
1419
1563
  case 'list_my_creations': return listMyCreations(args);
1420
1564
  case 'get_my_creation': return getMyCreation(args);
@@ -1486,6 +1630,7 @@ async function callTool(name, args = {}) {
1486
1630
  available: [
1487
1631
  'Generate deterministic recipes from the stable public ToonLab catalog entries.',
1488
1632
  'Save and retrieve editable lab documents through the shared .toonlab workspace.',
1633
+ 'Search and retrieve authored public Gallery recipes with license and provenance.',
1489
1634
  'Search and import CC0 assets from Poly Haven and ambientCG.',
1490
1635
  'Use each recipe\'s spawn snippet to integrate it into a Three.js project.',
1491
1636
  ],
@@ -1536,7 +1681,7 @@ async function handleRequest(message) {
1536
1681
  jsonrpc: '2.0',
1537
1682
  result: {
1538
1683
  capabilities: { resources: {}, tools: {} },
1539
- instructions: 'ToonLab builds anime-style games, characters, and environments for Three.js. Call list_live_labs and get_lab_features when authoring a Lab artifact, use create_lab_document for a valid starter, and prefer mutate_lab_creation over hand-editing structural arrays. Official Rock details include a source-GLB starter. This OSS server may edit any non-secret creation or lab-state entry in its local workspace; deletions require explicit confirmation. Load the selected style bundle first. If no asset-sourcing policy exists, ask the developer, then continue with library-first advisory discovery while recording the unresolved decision. Search the project and ToonLab libraries before public sources; generate or hand-author only when policy permits. Validate every candidate, preserve provenance, evaluate anime-fit in scene, and record a gap before adding a custom shader, texture, model, or adapter. When local provider keys are configured, generate_ai_asset exposes image generation, Meshy 7 image/multiview-to-3D, and Text -> selected image model -> Meshy 7 through MCP.',
1684
+ instructions: 'ToonLab builds anime-style games, characters, and environments for Three.js. Call list_live_labs and get_lab_features when authoring a Lab artifact, use create_lab_document for a valid starter, and prefer mutate_lab_creation over hand-editing structural arrays. Official Rock details include a source-GLB starter. This OSS server may edit any non-secret creation or lab-state entry in its local workspace; deletions require explicit confirmation. Load the selected style bundle first. If no asset-sourcing policy exists, ask the developer, then continue with library-first advisory discovery while recording the unresolved decision. Search the project and local library first, then search_public_gallery for reviewed authored assets such as tree recipes, before external sources. Generate or hand-author only when policy permits. Validate every candidate, preserve provenance, evaluate anime-fit in scene, and record a gap before adding a custom shader, texture, model, or adapter. When local provider keys are configured, generate_ai_asset exposes image generation, Meshy 7 image/multiview-to-3D, and Text -> selected image model -> Meshy 7 through MCP.',
1540
1685
  protocolVersion: SUPPORTED_PROTOCOL_VERSIONS.has(requested) ? requested : LATEST_PROTOCOL_VERSION,
1541
1686
  serverInfo: { name: SERVER_NAME, version: SERVER_VERSION },
1542
1687
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@call-me-sensei/toonlab",
3
- "version": "0.4.20",
3
+ "version": "0.4.21",
4
4
  "description": "Anime-style game toolkit for Three.js with cel shading, vegetation, rocks, water, sky, clouds, procedural textures, and portable Lab artifacts",
5
5
  "license": "MIT",
6
6
  "author": "Hyperbond Studio PTE. LTD. (Call Me Sensei)",
@@ -347,7 +347,6 @@
347
347
  "verify:quality-integration": "node scripts/verify-quality-budget-integration.mjs && node scripts/verify-packaged-quality-budget-integration.mjs",
348
348
  "verify:vegetation-shader": "node scripts/verify-vegetation-shader.mjs",
349
349
  "verify:lab-styles": "node scripts/verify-lab-style-boundaries.mjs",
350
- "verify:legacy-trees": "node scripts/verify-legacy-trees.mjs",
351
350
  "verify:branch-tree": "node scripts/verify-branch-tree.mjs",
352
351
  "verify:tree-doodle": "node scripts/verify-tree-doodle.mjs",
353
352
  "verify:tree-woody": "node scripts/verify-tree-woody-engine.mjs",
@@ -365,7 +364,7 @@
365
364
  "verify:collision-package": "node scripts/verify-packaged-world-collision.mjs",
366
365
  "verify:official-catalog": "node scripts/verify-official-catalog-provider.mjs && node scripts/verify-official-catalog-runtime.mjs && node scripts/verify-official-catalog-lod.mjs && node scripts/verify-official-catalog-placement.mjs && node scripts/verify-playground-official-catalog.mjs && node scripts/verify-packaged-official-catalog.mjs",
367
366
  "verify:system-style": "node scripts/verify-system-style-policy.mjs",
368
- "verify:release": "npm run verify:system-style && npm run verify:style-contract && npm run verify:style-labels-package && npm run verify:collision-package && npm run verify:scene-collision && npm run verify:official-catalog && npm run verify:public-labs && npm run verify:live-labs && npm run verify:rock-shader && npm run verify:rock-generation-lab && npm run verify:rock-lab-lifecycle && npm run verify:ground-shader && npm run verify:manufactured-materials && npm run verify:material-labeling:agent && npm run verify:material-labeling:mcp && npm run verify:water-lab && npm run verify:water && npm run verify:vegetation-labs && npm run verify:texgen && npm run verify:cloud-shader && npm run verify:sky-cloud-lab && npm run verify:world-quality && npm run verify:scatter && npm run verify:legacy-trees && npm run verify:branch-tree && npm run verify:grass-package && npm run verify:character-package && npm run verify:locomotion-state && npm run verify:ground-stabilizer && npm run verify:water-interaction && npm run verify:walkable-character && npm run verify:playground-walkable && npm run verify:renderer-configuration && npm run verify:scene-scheduler && npm run verify:scene-surface && npm run verify:shared-shadow-receivers && npm run verify:scene-quality && npm run verify:quality-integration && npm run verify:lab-asset-provenance && npm run verify:api && npm run verify:types && npm run verify:mcp && npm run verify:skills && npm run verify:docs && npm run verify:local-database && npm run verify:package",
367
+ "verify:release": "npm run verify:system-style && npm run verify:style-contract && npm run verify:style-labels-package && npm run verify:collision-package && npm run verify:scene-collision && npm run verify:official-catalog && npm run verify:public-labs && npm run verify:live-labs && npm run verify:rock-shader && npm run verify:rock-generation-lab && npm run verify:rock-lab-lifecycle && npm run verify:ground-shader && npm run verify:manufactured-materials && npm run verify:material-labeling:agent && npm run verify:material-labeling:mcp && npm run verify:water-lab && npm run verify:water && npm run verify:vegetation-labs && npm run verify:texgen && npm run verify:cloud-shader && npm run verify:sky-cloud-lab && npm run verify:world-quality && npm run verify:scatter && npm run verify:branch-tree && npm run verify:grass-package && npm run verify:character-package && npm run verify:locomotion-state && npm run verify:ground-stabilizer && npm run verify:water-interaction && npm run verify:walkable-character && npm run verify:playground-walkable && npm run verify:renderer-configuration && npm run verify:scene-scheduler && npm run verify:scene-surface && npm run verify:shared-shadow-receivers && npm run verify:scene-quality && npm run verify:quality-integration && npm run verify:lab-asset-provenance && npm run verify:api && npm run verify:types && npm run verify:mcp && npm run verify:skills && npm run verify:docs && npm run verify:local-database && npm run verify:package",
369
368
  "verify:generation": "node scripts/verify-generation.mjs",
370
369
  "verify:post": "node scripts/verify-post.mjs",
371
370
  "verify:camera": "node scripts/verify-camera.mjs",
@@ -374,7 +373,7 @@
374
373
  "verify:soundscape": "node scripts/verify-soundscape.mjs",
375
374
  "verify:game-feel": "node scripts/verify-game-feel.mjs",
376
375
  "verify:world-quality": "node scripts/verify-world-quality.mjs",
377
- "verify:all": "npm run verify:system-style && npm run verify:product-namespace && npm run verify:generation && npm run verify:post && npm run verify:camera && npm run verify:motion && npm run verify:biome && npm run verify:soundscape && npm run verify:game-feel && npm run verify:world-quality && npm run verify:rock-shader && npm run verify:ground-shader && npm run verify:rockgen && npm run verify:rock-generation-lab && npm run verify:pathgen && npm run verify:propgen && npm run verify:buildinggen && npm run verify:villagegen && npm run verify:fauna && npm run verify:ambientfx && npm run verify:vfxgen && npm run verify:water && npm run verify:landscape && npm run verify:lighting && npm run verify:lighting-lab && npm run verify:live-labs && npm run verify:lab-preview && npm run verify:weather && npm run verify:climate && npm run verify:atmospheric-condition-lab && npm run verify:sky && npm run verify:cloud-shader && npm run verify:sky-cloud-lab && npm run verify:environment-cycle && npm run verify:manufactured-materials && npm run verify:ground-field && npm run verify:grass && npm run verify:grass-package && npm run verify:character-package && npm run verify:vegetation-shader && npm run verify:lab-styles && npm run verify:scatter && npm run verify:legacy-trees && npm run verify:branch-tree && npm run verify:collision-package && npm run verify:scene-collision && npm run verify:official-catalog && npm run verify:api && npm run verify:package && npm run verify:catalog && npm run verify:assetlib && npm run verify:mcp && npm run verify:local-generation",
376
+ "verify:all": "npm run verify:system-style && npm run verify:product-namespace && npm run verify:generation && npm run verify:post && npm run verify:camera && npm run verify:motion && npm run verify:biome && npm run verify:soundscape && npm run verify:game-feel && npm run verify:world-quality && npm run verify:rock-shader && npm run verify:ground-shader && npm run verify:rockgen && npm run verify:rock-generation-lab && npm run verify:pathgen && npm run verify:propgen && npm run verify:buildinggen && npm run verify:villagegen && npm run verify:fauna && npm run verify:ambientfx && npm run verify:vfxgen && npm run verify:water && npm run verify:landscape && npm run verify:lighting && npm run verify:lighting-lab && npm run verify:live-labs && npm run verify:lab-preview && npm run verify:weather && npm run verify:climate && npm run verify:atmospheric-condition-lab && npm run verify:sky && npm run verify:cloud-shader && npm run verify:sky-cloud-lab && npm run verify:environment-cycle && npm run verify:manufactured-materials && npm run verify:ground-field && npm run verify:grass && npm run verify:grass-package && npm run verify:character-package && npm run verify:vegetation-shader && npm run verify:lab-styles && npm run verify:scatter && npm run verify:branch-tree && npm run verify:collision-package && npm run verify:scene-collision && npm run verify:official-catalog && npm run verify:api && npm run verify:package && npm run verify:catalog && npm run verify:assetlib && npm run verify:mcp && npm run verify:local-generation",
378
377
  "verify:catalog": "node scripts/verify-catalog.mjs",
379
378
  "verify:assetlib": "node scripts/verify-assetlib.mjs",
380
379
  "verify:mcp": "node scripts/verify-mcp.mjs",
@@ -4,9 +4,29 @@ export * from './grassClump.js';
4
4
  export * from './callMeSenseiGrass.js';
5
5
  export * from './stylizedFlowers.js';
6
6
  export * from './flowerSpecies.js';
7
- export * from './stylizedTree.js';
7
+ // Public tree construction/runtime surface. Authored tree recipes are assets
8
+ // and are discovered through ToonLab MCP; showcase/example recipe arrays are
9
+ // deliberately not re-exported from the npm package.
10
+ export {
11
+ DEFAULT_STYLIZED_TREE_SETTINGS,
12
+ STYLIZED_TREE_SETTING_FIELD_SCHEMA,
13
+ STYLIZED_TREE_SETTING_GROUPS,
14
+ TREE_RECIPE_SCHEMA,
15
+ TREE_RECIPE_VERSION,
16
+ TREE_TRUNK_STYLES,
17
+ StylizedTree,
18
+ createBranchTubeGeometry,
19
+ createBranchingTreeSkeleton,
20
+ createStylizedTreeSettings,
21
+ createTreeSkeleton,
22
+ createTreeTrunkGeometry,
23
+ getStylizedTreePresetOptions,
24
+ polarProfileFromOutline,
25
+ registerStylizedTreePreset,
26
+ resolvePadPruning,
27
+ serializableTreeOptions,
28
+ } from './stylizedTree.js';
8
29
  export * from './branchTree.js';
9
- export * from './legacyTreePresets.js';
10
30
  export * from './stylizedTreeFoliage.js';
11
31
  export * from './stylizedBush.js';
12
32
  export * from './stylizedFlower.js';
@@ -247,7 +247,12 @@ export function scatterGrassAround({
247
247
  * straight to StylizedTree for silhouette variation:
248
248
  *
249
249
  * for (const p of scatterForest({ center, radius: 80, spacing: 9, heightAt, mask })) {
250
- * const tree = new StylizedTree({ preset: 'call_me_sensei', seed: p.seed, size: 3.2 });
250
+ * const tree = new StylizedTree({
251
+ * ...galleryTreeRecipe.options,
252
+ * seed: p.seed,
253
+ * size: 3.2,
254
+ * vegetationShader: { preset: 'call_me_sensei' },
255
+ * });
251
256
  * tree.position.set(p.x, p.y, p.z);
252
257
  * scene.add(tree);
253
258
  * }
@@ -1334,88 +1334,6 @@ export const TREE_TRUNK_STYLES = Object.freeze({
1334
1334
  height: 1.8, radiusBottom: 0.24 },
1335
1335
  });
1336
1336
 
1337
- // Ready-made example recipes, ordered least → most complex configuration.
1338
- // Used by Tree Lab and the playground scene's showcase row; each is a
1339
- // complete options object for `new StylizedTree(...)`.
1340
- export const STYLIZED_TREE_EXAMPLES = Object.freeze([
1341
- // 1. Baseline: straight trunk, default crown, one flat color.
1342
- { seed: 3, size: 1.7, canopyColor: 0x4da258, leafDensity: 1,
1343
- trunk: TREE_TRUNK_STYLES.straight },
1344
- // 2. Leaning trunk, same simple crown.
1345
- { seed: 8, size: 1.8, canopyColor: 0x54a85e, leafDensity: 1,
1346
- trunk: TREE_TRUNK_STYLES.leaning },
1347
- // 3. Slight see-through: gap pockets open, branches peek through.
1348
- { seed: 5, size: 1.9, canopyColor: 0x5eb063, leafDensity: 0.85,
1349
- trunk: TREE_TRUNK_STYLES.leaning },
1350
- // 4. Curved trunk + its own irregular crown layout.
1351
- { seed: 11, size: 2.0, canopyColor: 0x58ab5c, leafDensity: 0.95,
1352
- trunk: TREE_TRUNK_STYLES.curved },
1353
- // 5. Color picked from a list, per-seed (forest variation from one spec).
1354
- { seed: 17, size: 2.0, canopyColor: [0x4da258, 0x7fb84e, 0x9cbf46], leafDensity: 0.95,
1355
- trunk: TREE_TRUNK_STYLES.curved },
1356
- // 6. Wide-and-shallow crown (X reach 1.6, Z reach 0.7).
1357
- { seed: 9, size: 2.0, canopyColor: 0x6db54f, leafDensity: 0.95,
1358
- canopyWidth: 1.6, canopyDepth: 0.7, trunk: TREE_TRUNK_STYLES.leaning },
1359
- // 7. Autumn blend: seeded mix between two colors.
1360
- { seed: 21, size: 2.1, canopyColor: { from: 0xe8a33c, to: 0xd96f29 }, leafDensity: 0.9,
1361
- trunk: TREE_TRUNK_STYLES.curved },
1362
- // 8. Gnarled old tree: knotted growth, sparser crown shows the wood.
1363
- { seed: 14, size: 2.0, canopyColor: 0x8f9e44, leafDensity: 0.72,
1364
- trunk: TREE_TRUNK_STYLES.gnarled },
1365
- // 9. Bonsai: corkscrew twist, flat wide pads, HSL-range blossom color.
1366
- { seed: 26, size: 1.7, leafDensity: 0.8, canopyWidth: 1.4, canopyDepth: 1.2,
1367
- canopyColor: { hue: [0.9, 1.0], saturation: [0.45, 0.6], lightness: [0.62, 0.72] },
1368
- trunk: TREE_TRUNK_STYLES.bonsai },
1369
- // 10. The Liyue golden gingko: fat-based serpentine trunk (bow right,
1370
- // top swept hard left, S locked by leanOffset), extra-wide crown,
1371
- // pinned pale-gold highlight tone. bendDirection 0 keeps the S in the
1372
- // X-Y plane so the silhouette reads head-on in the showcase row.
1373
- { seed: 12, size: 2.4, canopyColor: 0xf5c531, canopyPalette: { crown: 0xffe98a },
1374
- leafDensity: 0.95, canopyWidth: 1.5,
1375
- skeleton: { radialSegments: 10 },
1376
- trunk: { ...TREE_TRUNK_STYLES.swooping, bend: 0.5, lean: 0.95,
1377
- bendDirection: 0, height: 2.0, radiusBottom: 0.28 } },
1378
- // 11. Sumeru-style: long bare pale limbs reaching out of the crown with
1379
- // violet leaf bushes only at the branch ends (leafPlacement 'tips').
1380
- { seed: 31, size: 2.3, pale: true, canopyColor: 0x8578e6,
1381
- canopyPalette: { crown: 0xbdb2ff },
1382
- leafDensity: 0.9, canopyWidth: 1.45, leafPlacement: 'tips',
1383
- trunkReceiveShadow: false,
1384
- skeleton: { attractionCount: 70, influenceRadius: 1.35 },
1385
- trunk: { ...TREE_TRUNK_STYLES.curved, bend: 0.3, lean: 0.35, gnarl: 0.45,
1386
- height: 1.9, radiusBottom: 0.26 } },
1387
- // 12. MASSIVE climbable Sumeru tree: thick bare limbs long and low enough
1388
- // to stand or sit on (scenes read `climbable: true` and collide the
1389
- // wood as a trimesh instead of a trunk capsule), sparse skeleton so
1390
- // the pale limbs stay on show, foliage clouds only at the limb ends.
1391
- { seed: 46, size: 4.0, pale: true, climbable: true,
1392
- canopyColor: 0x8578e6, canopyPalette: { crown: 0xbdb2ff },
1393
- leafDensity: 0.92, canopyWidth: 1.75, canopyDepth: 1.2,
1394
- leafPlacement: 'tips', trunkReceiveShadow: false,
1395
- skeleton: { attractionCount: 55, influenceRadius: 1.7, killRadius: 0.55,
1396
- segmentLength: 0.36, attractionReach: 0.95, radialSegments: 14,
1397
- tipRadius: 0.05, minLimbRadius: 0.04, maxNodes: 130 },
1398
- canopy: { cardsPerCluster: 12, clusterRadius: 0.62 },
1399
- trunk: { ...TREE_TRUNK_STYLES.leaning, bend: 0.24, lean: 0.42,
1400
- height: 1.4, radiusBottom: 0.48 } },
1401
- ]);
1402
-
1403
- // Centered X offsets for a showcase row: cumulative spacing from each tree's
1404
- // approximate crown footprint, so a massive example doesn't swallow its
1405
- // neighbors the way fixed spacing would.
1406
- export function layoutTreeRow(configs, { margin = 1.6 } = {}) {
1407
- const footprints = configs.map((config) =>
1408
- (config.size ?? 1) * (config.canopyWidth ?? 1) * 2.3 + 1.4);
1409
- const offsets = [];
1410
- let cursor = 0;
1411
- footprints.forEach((footprint, index) => {
1412
- if (index > 0) cursor += (footprints[index - 1] + footprint) / 2 + margin;
1413
- offsets.push(cursor);
1414
- });
1415
- const center = cursor / 2;
1416
- return offsets.map((offset) => offset - center);
1417
- }
1418
-
1419
1337
  // Recipe documents: a plant serialized as { schema, version, type, options }.
1420
1338
  // The options are exactly what the constructor takes, so a recipe rebuilds
1421
1339
  // the identical plant (generation is deterministic per seed). Defined here —
@@ -1667,37 +1585,15 @@ export const DEFAULT_STYLIZED_TREE_SETTINGS = Object.freeze({
1667
1585
  }),
1668
1586
  });
1669
1587
 
1670
- // Named tree presets (grouped partial settings): 'default' is the baseline;
1671
- // 'call_me_sensei' is the studio-managed signature look, curated and updated
1672
- // over releases. Community presets register alongside them via
1673
- // registerStylizedTreePreset().
1588
+ // Named runtime settings: npm ships only the neutral baseline. Authored tree
1589
+ // recipes live in the public Gallery and may be registered explicitly after
1590
+ // MCP discovery when an application wants name-based selection.
1674
1591
  const stylizedTreePresetRegistry = new Map([
1675
1592
  ['default', Object.freeze({
1676
1593
  description: 'Baseline stylized tree.',
1677
1594
  label: 'Default',
1678
1595
  settings: Object.freeze({}),
1679
1596
  })],
1680
- ['call_me_sensei', Object.freeze({
1681
- description: 'Studio-managed signature tree, curated by Call Me Sensei and updated over releases. Open broadleaf with visible branching: leaf clusters sit at the branch tips instead of filling a solid shell, so the limb structure reads through the crown.',
1682
- label: 'Call Me Sensei',
1683
- settings: Object.freeze({
1684
- skeleton: Object.freeze({ attractionCount: 55, influenceRadius: 1.35 }),
1685
- tree: Object.freeze({
1686
- canopyWidth: 1.35,
1687
- leafDensity: 1.15,
1688
- leafPlacement: 'tips',
1689
- trunkColor: Object.freeze([0.58, 0.36, 0.2]),
1690
- }),
1691
- trunk: Object.freeze({
1692
- ...TREE_TRUNK_STYLES.curved,
1693
- bend: 0.24,
1694
- gnarl: 0.22,
1695
- height: 1.9,
1696
- lean: 0.24,
1697
- radiusBottom: 0.2,
1698
- }),
1699
- }),
1700
- })],
1701
1597
  ]);
1702
1598
 
1703
1599
  /**
package/src/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Single runtime source of truth for package consumers and ToonLab Pro/MCP.
2
2
  // Keep this value synchronized with package.json during release preparation.
3
- export const TOONLAB_VERSION = '0.4.20';
3
+ export const TOONLAB_VERSION = '0.4.21';
package/types/index.d.ts CHANGED
@@ -299,8 +299,6 @@ export const HERO_CLOUD_RECIPE_VERSION: any;
299
299
  export const HUMANOID_ROLES: any;
300
300
  export const LEAF_SHAPE_PRESETS: any;
301
301
  export const LEGACY_STYLE_BUNDLE_SCHEMA_VERSION: any;
302
- export const LEGACY_TREE_IDS: any;
303
- export const LEGACY_TREE_PRESETS: any;
304
302
  export const LIGHTING_LOOK_DOCUMENT_TYPE: any;
305
303
  export const LIGHTING_LOOK_PRESETS: any;
306
304
  export const LIGHTING_LOOK_SCHEMA_VERSION: any;
@@ -505,7 +503,6 @@ export const STYLE_TARGET_DOMAINS: any;
505
503
  export const STYLE_TARGET_LABEL_KEY: any;
506
504
  export const STYLE_TARGET_LABEL_SCHEMA_VERSION: any;
507
505
  export const STYLIZED_FOREST_IMPOSTOR_QUALITY: any;
508
- export const STYLIZED_TREE_EXAMPLES: any;
509
506
  export const STYLIZED_TREE_SETTING_FIELD_SCHEMA: any;
510
507
  export const STYLIZED_TREE_SETTING_GROUPS: any;
511
508
  export const SUN_COLOR_FIELD: any;
@@ -869,7 +866,6 @@ export function createHeroCloudWeatherTexture(...args: any[]): any;
869
866
  export function createImportedVegetationMaterial(...args: any[]): any;
870
867
  export function createIndirectLightSettings(...args: any[]): any;
871
868
  export function createLeafSpriteTexture(...args: any[]): any;
872
- export function createLegacyTree(...args: any[]): any;
873
869
  export function createLightArtisticSettings(...args: any[]): any;
874
870
  export function createLightColor(...args: any[]): any;
875
871
  export function createLightCookie(...args: any[]): any;
@@ -1098,8 +1094,6 @@ export function getGeometryTriangleCount(...args: any[]): any;
1098
1094
  export function getGrassPresetOptions(...args: any[]): any;
1099
1095
  export function getGroundShaderPresetOptions(...args: any[]): any;
1100
1096
  export function getHeroCloudPreviewCenter(...args: any[]): any;
1101
- export function getLegacyTreePreset(...args: any[]): any;
1102
- export function getLegacyTreePresetOptions(...args: any[]): any;
1103
1097
  export function getLightFixtureGeneratorFamilyOptions(...args: any[]): any;
1104
1098
  export function getLightFixtureOptions(...args: any[]): any;
1105
1099
  export function getLightingPresetOptions(...args: any[]): any;
@@ -1159,7 +1153,6 @@ export function isVegetationSharedShaderGroup(...args: any[]): any;
1159
1153
  export function isWindowCutoutMaterial(...args: any[]): any;
1160
1154
  export function labelManufacturedStyleTarget(...args: any[]): any;
1161
1155
  export function labelStyleTarget(...args: any[]): any;
1162
- export function layoutTreeRow(...args: any[]): any;
1163
1156
  export function listFirstPartyStyleBundles(...args: any[]): any;
1164
1157
  export function listSurfaceMaterialModes(...args: any[]): any;
1165
1158
  export function loadAsync(...args: any[]): any;
@@ -39,12 +39,9 @@ export const GRASS_SHADER_PROFILE: any;
39
39
  export const GRASS_SHADER_PROFILE_DOCUMENT_TYPE: any;
40
40
  export const GRASS_SHADER_PROFILE_SCHEMA_VERSION: any;
41
41
  export const LEAF_SHAPE_PRESETS: any;
42
- export const LEGACY_TREE_IDS: any;
43
- export const LEGACY_TREE_PRESETS: any;
44
42
  export const NEEDLE_LEAF_SHAPES: any;
45
43
  export class RetainedGrassClumpField { constructor(...args: any[]); [key: string]: any; }
46
44
  export const STYLIZED_FOREST_IMPOSTOR_QUALITY: any;
47
- export const STYLIZED_TREE_EXAMPLES: any;
48
45
  export const STYLIZED_TREE_SETTING_FIELD_SCHEMA: any;
49
46
  export const STYLIZED_TREE_SETTING_GROUPS: any;
50
47
  export class StylizedBush { constructor(...args: any[]); [key: string]: any; }
@@ -133,7 +130,6 @@ export function createGrassShaderProfileSettings(...args: any[]): any;
133
130
  export function createGrassShaderSettings(...args: any[]): any;
134
131
  export function createImportedVegetationMaterial(...args: any[]): any;
135
132
  export function createLeafSpriteTexture(...args: any[]): any;
136
- export function createLegacyTree(...args: any[]): any;
137
133
  export function createNoisePatchMask(...args: any[]): any;
138
134
  export function createOrganLeafSpriteTexture(...args: any[]): any;
139
135
  export function createSlopeMask(...args: any[]): any;
@@ -159,8 +155,6 @@ export function deriveCanopyPalette(...args: any[]): any;
159
155
  export function disposeExportGroup(...args: any[]): any;
160
156
  export function getFlowerPresetOptions(...args: any[]): any;
161
157
  export function getGrassPresetOptions(...args: any[]): any;
162
- export function getLegacyTreePreset(...args: any[]): any;
163
- export function getLegacyTreePresetOptions(...args: any[]): any;
164
158
  export function getStylizedTreePresetOptions(...args: any[]): any;
165
159
  export function getTreeSurfaceProfileOptions(...args: any[]): any;
166
160
  export function getVegetationMaterialContract(...args: any[]): any;
@@ -169,7 +163,6 @@ export function getVegetationShaderScopeExcludedFields(...args: any[]): any;
169
163
  export function getVegetationShaderScopeFieldSchema(...args: any[]): any;
170
164
  export function getVegetationShaderScopeSettingGroups(...args: any[]): any;
171
165
  export function isVegetationSharedShaderGroup(...args: any[]): any;
172
- export function layoutTreeRow(...args: any[]): any;
173
166
  export function loadCallMeSenseiGrassClump(...args: any[]): any;
174
167
  export function loadCompiledTreeAsset(...args: any[]): any;
175
168
  export function matchGrassColorPalette(...args: any[]): any;
@@ -1,6 +1,5 @@
1
1
  // Source inference fallback: public names retained with permissive signatures.
2
2
  export const DEFAULT_STYLIZED_TREE_SETTINGS: any;
3
- export const STYLIZED_TREE_EXAMPLES: any;
4
3
  export const STYLIZED_TREE_SETTING_FIELD_SCHEMA: any;
5
4
  export const STYLIZED_TREE_SETTING_GROUPS: any;
6
5
  export class StylizedTree { constructor(...args: any[]); [key: string]: any; }
@@ -13,7 +12,6 @@ export function createStylizedTreeSettings(...args: any[]): any;
13
12
  export function createTreeSkeleton(...args: any[]): any;
14
13
  export function createTreeTrunkGeometry(...args: any[]): any;
15
14
  export function getStylizedTreePresetOptions(...args: any[]): any;
16
- export function layoutTreeRow(...args: any[]): any;
17
15
  export function polarProfileFromOutline(...args: any[]): any;
18
16
  export function registerStylizedTreePreset(...args: any[]): any;
19
17
  export function resolvePadPruning(...args: any[]): any;
@@ -1 +1 @@
1
- export const TOONLAB_VERSION: "0.4.20";
1
+ export const TOONLAB_VERSION: "0.4.21";
@@ -1,120 +0,0 @@
1
- import {
2
- STYLIZED_TREE_EXAMPLES,
3
- StylizedTree,
4
- TREE_RECIPE_SCHEMA,
5
- TREE_RECIPE_VERSION,
6
- serializableTreeOptions,
7
- } from './stylizedTree.js';
8
-
9
- const LEGACY_TREE_DEFINITIONS = Object.freeze([
10
- Object.freeze({ id: 'straight', label: 'Straight' }),
11
- Object.freeze({ id: 'leaning', label: 'Leaning' }),
12
- Object.freeze({ id: 'see-through', label: 'See-through' }),
13
- Object.freeze({ id: 'curved', label: 'Curved' }),
14
- Object.freeze({ id: 'forest-mix', label: 'Forest Mix' }),
15
- Object.freeze({ id: 'wide-crown', label: 'Wide Crown' }),
16
- Object.freeze({ id: 'autumn-blend', label: 'Autumn Blend' }),
17
- Object.freeze({ id: 'gnarled', label: 'Gnarled' }),
18
- Object.freeze({ id: 'bonsai', label: 'Bonsai' }),
19
- Object.freeze({ id: 'golden-gingko', label: 'Golden Gingko' }),
20
- Object.freeze({ id: 'sumeru-tips', label: 'Sumeru Tips' }),
21
- Object.freeze({ id: 'massive-sumeru', label: 'Massive Sumeru' }),
22
- ]);
23
-
24
- function clone(value) {
25
- if (Array.isArray(value)) return value.map(clone);
26
- if (value && typeof value === 'object') {
27
- return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, clone(entry)]));
28
- }
29
- return value;
30
- }
31
-
32
- function deepFreeze(value) {
33
- if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
34
- for (const entry of Object.values(value)) deepFreeze(entry);
35
- return Object.freeze(value);
36
- }
37
-
38
- function legacyOptions(example) {
39
- // `pale` and `climbable` are old showcase-scene hints, not tree generator
40
- // inputs. Preserve them as document metadata without feeding them into the
41
- // stable constructor surface.
42
- const { pale = false, climbable = false, ...options } = example;
43
- return {
44
- metadata: { climbable: Boolean(climbable), pale: Boolean(pale) },
45
- options: serializableTreeOptions(options),
46
- };
47
- }
48
-
49
- /**
50
- * The twelve pre-species ToonLab trees, now named and versioned explicitly.
51
- *
52
- * This is the supported legacy set that existed before the repository's
53
- * botanical species research. It contains only generic stylized silhouettes;
54
- * none of the documents claims a botanical species identity.
55
- */
56
- export const LEGACY_TREE_PRESETS = deepFreeze(LEGACY_TREE_DEFINITIONS.map((definition, index) => {
57
- const normalized = legacyOptions(STYLIZED_TREE_EXAMPLES[index]);
58
- return {
59
- builtIn: true,
60
- id: definition.id,
61
- label: definition.label,
62
- metadata: normalized.metadata,
63
- options: normalized.options,
64
- schema: TREE_RECIPE_SCHEMA,
65
- type: 'tree',
66
- version: TREE_RECIPE_VERSION,
67
- };
68
- }));
69
-
70
- export const LEGACY_TREE_IDS = Object.freeze(LEGACY_TREE_PRESETS.map(({ id }) => id));
71
-
72
- /** Returns one immutable legacy recipe document, or null. */
73
- export function getLegacyTreePreset(id) {
74
- return LEGACY_TREE_PRESETS.find((preset) => preset.id === id) ?? null;
75
- }
76
-
77
- /** Lists the stable legacy IDs and labels without duplicating recipe data. */
78
- export function getLegacyTreePresetOptions() {
79
- return LEGACY_TREE_PRESETS.map(({ id, label, metadata }) => ({
80
- id,
81
- label,
82
- metadata: { ...metadata },
83
- }));
84
- }
85
-
86
- /**
87
- * Creates one tree from the supported pre-species set.
88
- *
89
- * Overrides remain the ordinary StylizedTree contract, including `leafShape`,
90
- * `trunkMap`, `foliage.leafMap`, and `vegetationShader`. Nested generator and
91
- * material groups merge over the selected recipe instead of replacing it.
92
- */
93
- export function createLegacyTree(id, overrides = {}) {
94
- const preset = getLegacyTreePreset(id);
95
- if (!preset) throw new Error(`Unknown legacy tree preset "${String(id)}".`);
96
- const base = clone(preset.options);
97
- const source = overrides && typeof overrides === 'object' && !Array.isArray(overrides)
98
- ? overrides
99
- : {};
100
- const tree = new StylizedTree({
101
- ...base,
102
- ...source,
103
- canopy: { ...(base.canopy ?? {}), ...(source.canopy ?? {}) },
104
- foliage: { ...(base.foliage ?? {}), ...(source.foliage ?? {}) },
105
- skeleton: { ...(base.skeleton ?? {}), ...(source.skeleton ?? {}) },
106
- tree: {
107
- ...(base.tree ?? {}),
108
- ...(source.tree ?? {}),
109
- trunkReceiveShadow: source.tree?.trunkReceiveShadow ?? true,
110
- },
111
- trunk: { ...(base.trunk ?? {}), ...(source.trunk ?? {}) },
112
- });
113
- tree.name = `LegacyTree:${preset.id}`;
114
- tree.userData.legacyTree = {
115
- id: preset.id,
116
- label: preset.label,
117
- recipeVersion: preset.version,
118
- };
119
- return tree;
120
- }
@@ -1,22 +0,0 @@
1
- /** Returns one immutable legacy recipe document, or null. */
2
- export function getLegacyTreePreset(id: any): any;
3
- /** Lists the stable legacy IDs and labels without duplicating recipe data. */
4
- export function getLegacyTreePresetOptions(): any;
5
- /**
6
- * Creates one tree from the supported pre-species set.
7
- *
8
- * Overrides remain the ordinary StylizedTree contract, including `leafShape`,
9
- * `trunkMap`, `foliage.leafMap`, and `vegetationShader`. Nested generator and
10
- * material groups merge over the selected recipe instead of replacing it.
11
- */
12
- export function createLegacyTree(id: any, overrides?: {}): StylizedTree;
13
- /**
14
- * The twelve pre-species ToonLab trees, now named and versioned explicitly.
15
- *
16
- * This is the supported legacy set that existed before the repository's
17
- * botanical species research. It contains only generic stylized silhouettes;
18
- * none of the documents claims a botanical species identity.
19
- */
20
- export const LEGACY_TREE_PRESETS: any;
21
- export const LEGACY_TREE_IDS: any;
22
- import { StylizedTree } from './stylizedTree.js';