@call-me-sensei/toonlab 0.4.19 → 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 +230 -0
- package/README.md +78 -36
- package/agents/PROMPTS.md +91 -0
- package/agents/README.md +9 -14
- package/agents/claude/CLAUDE.md +8 -0
- package/agents/codex/AGENTS.md +161 -0
- package/agents/cursor/toonlab.mdc +37 -0
- package/agents/references/mcp-asset-discovery.md +12 -9
- package/agents/references/runtime-entry-points.md +5 -4
- package/agents/skills/claude/asset-sourcing/SKILL.md +9 -6
- package/agents/skills/claude/rock-ground-shaders/SKILL.md +16 -0
- package/agents/skills/claude/vegetation-sky/SKILL.md +7 -4
- package/agents/skills/codex/asset-sourcing/SKILL.md +9 -6
- package/agents/skills/codex/rock-ground-shaders/SKILL.md +16 -0
- package/agents/skills/codex/vegetation-sky/SKILL.md +7 -4
- package/cli/toonlab.mjs +3 -2
- package/database/fal-patina.mjs +131 -0
- package/database/generation-service.mjs +108 -7
- package/database/migrations/0007_creation_revisions.sql +80 -0
- package/database/migrations/0008_creation_revision_hardening.sql +98 -0
- package/database/migrations/0009_creation_revision_delete_integrity.sql +11 -0
- package/database/migrations/0010_external_catalog_assets.sql +28 -0
- package/database/providers.mjs +195 -16
- package/database/repository.mjs +518 -70
- package/database/seeds/catalog/0003_2026-08-open-assets.sql +159086 -0
- package/database/seeds/catalog/0004_2026-08-c7-rocks.sql +39405 -0
- package/mcp/public-catalog.mjs +4 -52
- package/mcp/server.mjs +175 -12
- package/mcp/vite-plugin.mjs +78 -2
- package/package.json +32 -12
- package/scripts/generate-catalog-seed.mjs +34 -7
- package/src/asset-policy/catalogLicenses.js +64 -6
- package/src/assetlib/loadImported.js +15 -1
- package/src/catalog/officialCatalogAssetRuntime.js +13 -2
- package/src/catalog/officialCatalogLod.js +34 -1
- package/src/catalog/officialCatalogPlacement.js +29 -14
- package/src/character/animationRetarget.js +2 -0
- package/src/character/characterRig.js +6 -1
- package/src/character/characterRuntime.js +175 -7
- package/src/environment/environmentMaterialAdapter.js +30 -0
- package/src/environment/environmentPresets.js +90 -0
- package/src/environment/environmentSunShadowPass.js +42 -0
- package/src/environment/toonLabSurfaceLighting.js +108 -10
- package/src/environment/urbanPropMaterial.js +104 -2
- package/src/lighting/lightingSystem.js +65 -10
- package/src/renderer/index.js +7 -0
- package/src/renderer/styleComparison.js +985 -0
- package/src/rock-shader/index.js +5 -0
- package/src/rock-shader/rockGeometryDetail.js +701 -0
- package/src/rock-shader/rockMaterial.js +782 -39
- package/src/rock-shader/rockRegionRuntime.js +185 -0
- package/src/rock-shader/rockSemanticMaterialRuntime.js +337 -0
- package/src/rock-shader/rockShaderRuntime.js +354 -67
- package/src/rock-shader/rockShaderSettings.js +246 -16
- package/src/rock-shader/rockTangentIntegrity.js +136 -0
- package/src/rock-shader/rockTextureIntegrity.js +115 -0
- package/src/rockgen/index.js +1 -0
- package/src/rockgen/rockDocument.js +27 -3
- package/src/rockgen/surface/c7GeologySurface.js +372 -0
- package/src/shaders-tsl/water.js +8 -0
- package/src/sky/cloudShadow.js +25 -1
- package/src/sky/skySystem.js +9 -0
- package/src/styles/index.js +23 -0
- package/src/styles/neutralStylePresets.js +712 -0
- package/src/styles/sceneStyleRuntime.js +51 -4
- package/src/styles/styleAdapters.js +13 -2
- package/src/styles/styleBundle.js +1 -1
- package/src/texgen/evaluateTexture.js +30 -3
- package/src/texgen/textureGenerators.js +63 -31
- package/src/texgen/textureSettings.js +25 -8
- package/src/toon/toonSettings.js +96 -2
- package/src/vegetation/branchTree.js +327 -42
- package/src/vegetation/index.js +24 -2
- package/src/vegetation/scatter.js +372 -1
- package/src/vegetation/stylizedTree.js +312 -126
- package/src/vegetation/stylizedTreeFoliage.js +229 -7
- package/src/vegetation/treeSurfaceTextures.js +328 -0
- package/src/version.js +1 -1
- package/src/water/waterSettings.js +15 -7
- package/types/asset-policy/catalogLicenses.d.ts +2 -0
- package/types/asset-policy/index.d.ts +2 -0
- package/types/assetlib/loadImported.d.ts +3 -1
- package/types/catalog/officialCatalogLod.d.ts +63 -5
- package/types/character/characterRig.d.ts +59 -0
- package/types/character/characterRuntime.d.ts +1 -0
- package/types/environment/environmentMaterialAdapter.d.ts +3 -1
- package/types/environment/toonLabSurfaceLighting.d.ts +20 -3
- package/types/index.d.ts +89 -7
- package/types/lighting/lightingSystem.d.ts +7 -0
- package/types/react/index.d.ts +20 -0
- package/types/renderer/index.d.ts +1 -0
- package/types/renderer/styleComparison.d.ts +409 -0
- package/types/rock-shader/index.d.ts +5 -0
- package/types/rock-shader/rockGeometryDetail.d.ts +300 -0
- package/types/rock-shader/rockMaterial.d.ts +3 -0
- package/types/rock-shader/rockRegionRuntime.d.ts +86 -0
- package/types/rock-shader/rockSemanticMaterialRuntime.d.ts +29 -0
- package/types/rock-shader/rockShaderRuntime.d.ts +2 -0
- package/types/rock-shader/rockShaderSettings.d.ts +19 -5
- package/types/rock-shader/rockTangentIntegrity.d.ts +51 -0
- package/types/rock-shader/rockTextureIntegrity.d.ts +61 -0
- package/types/rockgen/index.d.ts +1 -0
- package/types/rockgen/rockDocument.d.ts +29 -1
- package/types/rockgen/surface/c7GeologySurface.d.ts +8 -0
- package/types/sky/cloudShadow.d.ts +1 -0
- package/types/styles/index.d.ts +1 -0
- package/types/styles/neutralStylePresets.d.ts +944 -0
- package/types/styles/sceneStyleRuntime.d.ts +20 -0
- package/types/vegetation/branchTree.d.ts +4 -0
- package/types/vegetation/index.d.ts +9 -7
- package/types/vegetation/scatter.d.ts +1 -0
- package/types/vegetation/stylizedTree.d.ts +1 -2
- package/types/vegetation/stylizedTreeFoliage.d.ts +1 -0
- package/types/version.d.ts +1 -1
- package/agents/references/geology-playbook.md +0 -118
- package/agents/skills/claude/karst-cliff-construction/SKILL.md +0 -258
- package/agents/skills/claude/outdoor-world/SKILL.md +0 -492
- package/agents/skills/claude/outdoor-world/references/coastal-landform-formation.md +0 -100
- package/agents/skills/codex/karst-cliff-construction/SKILL.md +0 -258
- package/agents/skills/codex/outdoor-world/SKILL.md +0 -492
- package/agents/skills/codex/outdoor-world/references/coastal-landform-formation.md +0 -100
- package/src/vegetation/legacyTreePresets.js +0 -120
- package/types/vegetation/legacyTreePresets.d.ts +0 -22
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# ToonLab Agent Guide
|
|
2
|
+
|
|
3
|
+
Use this file when helping a developer build an anime-style game, character,
|
|
4
|
+
or environment with ToonLab's runtime library in their own Three.js app.
|
|
5
|
+
|
|
6
|
+
## Anime-Game Product Contract
|
|
7
|
+
|
|
8
|
+
- ToonLab is specifically an anime-game toolkit, not a generic stylization
|
|
9
|
+
library. Read `agents/references/anime-art-direction.md` before making
|
|
10
|
+
visual, shader, material, or asset decisions.
|
|
11
|
+
- Load the selected bundle's `artDirection` and treat it as authoritative.
|
|
12
|
+
Call Me Sensei is the first-party coordinated reference.
|
|
13
|
+
- Never accept unintended photorealism, generic low-poly styling, undirected
|
|
14
|
+
cartoon rendering, or untreated PBR materials as a finished ToonLab result.
|
|
15
|
+
- If supported shaders or approved assets are insufficient, document the
|
|
16
|
+
customization and why your project needs it.
|
|
17
|
+
|
|
18
|
+
## Runtime Boundary
|
|
19
|
+
|
|
20
|
+
- `@call-me-sensei/toonlab` is primarily a runtime library for applying
|
|
21
|
+
Three.js shaders, stylized materials, focused vegetation/water, settings,
|
|
22
|
+
and preset documents to supplied scene content.
|
|
23
|
+
- Prefer public package imports such as `@call-me-sensei/toonlab/toon`,
|
|
24
|
+
`@call-me-sensei/toonlab/environment`, `@call-me-sensei/toonlab/water`,
|
|
25
|
+
`@call-me-sensei/toonlab/vegetation`, `@call-me-sensei/toonlab/post`,
|
|
26
|
+
`@call-me-sensei/toonlab/styles`,
|
|
27
|
+
`@call-me-sensei/toonlab/texgen`, and `@call-me-sensei/toonlab/rockgen`.
|
|
28
|
+
- Use `@call-me-sensei/toonlab/sky` and `/cloud` for the live Sky, Cloud, and
|
|
29
|
+
Sky & Cloud Lab contracts. Weather, Climate, ambient effects, and fauna are
|
|
30
|
+
not public package entry points in this release.
|
|
31
|
+
- Use `@call-me-sensei/toonlab/vegetation-shaders` and
|
|
32
|
+
`@call-me-sensei/toonlab/grass-palettes` when a consumer needs only those
|
|
33
|
+
focused contracts. They are the same bindings exposed by the vegetation and
|
|
34
|
+
root barrels.
|
|
35
|
+
- Keep root imports lightweight in examples. Import only the feature subpath a
|
|
36
|
+
developer needs.
|
|
37
|
+
- Public export status and product maturity are different. Toon,
|
|
38
|
+
Environment/Ground/Rock, Tree/Grass/Flower, Water on a host-authored
|
|
39
|
+
footprint/shore/bed, Post, Gallery/MCP asset workflows, loaders, portable
|
|
40
|
+
settings, Sky/Cloud, and qualified focused generators are the recommended
|
|
41
|
+
path. Whole terrain/biome/coast/cliff construction,
|
|
42
|
+
automatic set dressing, and cross-system world assembly are experimental.
|
|
43
|
+
- ToonLab's labs, sample assets, local manifests, screenshots, captures, and
|
|
44
|
+
app routes are examples. Do not treat them as part of the runtime API or copy
|
|
45
|
+
their local asset assumptions into user apps.
|
|
46
|
+
- The host app owns scene layout and major geometry, asset placement, cameras,
|
|
47
|
+
collision, asset loading, decoder hosting, storage, routing, persistence,
|
|
48
|
+
collaboration, and final art direction. Renderer construction stays
|
|
49
|
+
host-owned, while the public renderer helper and scene style runtime apply
|
|
50
|
+
reversible Call Me Sensei renderer, lighting, probe, and shadow settings.
|
|
51
|
+
- VFX, camera, game-feel, and renderer selection are not style-bundle domains.
|
|
52
|
+
Treat project-local implementations as custom adapters and record a gap when
|
|
53
|
+
they affect the selected look. Lighting is a supported bundle domain.
|
|
54
|
+
|
|
55
|
+
## Usage Rules
|
|
56
|
+
|
|
57
|
+
- Use the README and docs pages first; feature skills in `agents/skills/codex/`
|
|
58
|
+
are short usage guides for public runtime APIs.
|
|
59
|
+
- For existing-scene or multi-system integration, start with the `game-dev`
|
|
60
|
+
skill (boundary, frame-loop contract, task→skill routing). When sourcing or generating
|
|
61
|
+
assets, use the `asset-sourcing` skill. Feature-detect the connected ToonLab
|
|
62
|
+
OSS local and/or ToonLab Pro remote MCP surface before calling tools; their
|
|
63
|
+
overlapping tool names have different discovery roles and schemas.
|
|
64
|
+
First-party rock search must shortlist from meter dimensions and taxonomy;
|
|
65
|
+
do not load or render catalog GLBs merely to determine their size.
|
|
66
|
+
- Whole-world and natural-cliff construction skills are not shipped in the npm
|
|
67
|
+
package. Prefer bounded work on an existing terrain, asset family,
|
|
68
|
+
vegetation field, or water body.
|
|
69
|
+
- For any style-aware construction, use `style-presets` to record the intended
|
|
70
|
+
selector and distinguish neutral defaults from domains that already default
|
|
71
|
+
to Call Me Sensei. Before visual approval or reporting a package gap, use
|
|
72
|
+
`visual-verification` for readiness, isolation, multi-view, measurement, and
|
|
73
|
+
outlier checks. These routes apply to every agent integration, not only Codex
|
|
74
|
+
or Claude.
|
|
75
|
+
- ToonLab materials are TSL/NodeMaterial-first for Three.js WebGPU with WebGL2
|
|
76
|
+
fallback through the same TSL path. Do not guide developers toward raw GLSL,
|
|
77
|
+
`ShaderMaterial`, or classic WebGL-only forks for ToonLab features.
|
|
78
|
+
- Keep developer examples focused on documented settings, preset documents,
|
|
79
|
+
public constructors/helpers, and app-owned Three.js objects.
|
|
80
|
+
- Keep artifact scope explicit: Shader Labs author IP profiles, Asset Labs
|
|
81
|
+
author geometry/material data, and Water Lab authors a focused runtime
|
|
82
|
+
system preset with embedded shader controls. Sky, Cloud, and Sky & Cloud are
|
|
83
|
+
live focused authoring surfaces. Vegetation is one shared
|
|
84
|
+
implementation family with separate Tree, Grass, and Flower shader profiles;
|
|
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,
|
|
91
|
+
lighting, camera, interactions, and complete Sky/Cloud composition stay
|
|
92
|
+
host-owned or experimental.
|
|
93
|
+
- For style-bundle, multi-shader, or arbitrary-asset work, read
|
|
94
|
+
`agents/references/style-bundles.md` first. A bundle selects coordinated treatments;
|
|
95
|
+
explicit asset labels select rendering destinations; scene state selects
|
|
96
|
+
current conditions. Inventory and label every renderable root and material.
|
|
97
|
+
Every newly modeled, generated, or imported material slot requires a stable
|
|
98
|
+
ID and valid semantic role in the versioned material contract. A root domain
|
|
99
|
+
label is not sufficient for a multi-material asset; missing roles and mixed
|
|
100
|
+
atlases without a split or ID mask block strict handoff.
|
|
101
|
+
Character anatomy, clothing, held weapons, and worn hero accessories
|
|
102
|
+
normally route to toon; manufactured props, vegetation, rocks/debris,
|
|
103
|
+
water/sky, and VFX retain their owning runtimes. For an imported manufactured
|
|
104
|
+
prop, call `proposeManufacturedStyleTargetLabel()` first, review every issue,
|
|
105
|
+
provide explicit `materialOverrides` for unresolved slots, then call
|
|
106
|
+
`applyManufacturedStyleTargetLabelProposal()`. Never collapse assisted
|
|
107
|
+
results into the automatic success rate.
|
|
108
|
+
- Report unknown domains and material roles, mixed atlases without masks,
|
|
109
|
+
unsupported transparency, and custom-renderer exemptions. Never silently
|
|
110
|
+
infer a production-safe route from names, texture colors, or scene
|
|
111
|
+
parenting. Preserve asset identity and runtime conditions when changing
|
|
112
|
+
style.
|
|
113
|
+
- Local style bundle creation, validation, serialization, parsing, and
|
|
114
|
+
resolution are OSS `/styles` APIs and require no database. Hosted storage
|
|
115
|
+
and `fetchStyleBundle` are optional. Scene discovery remains explicit;
|
|
116
|
+
manufactured assets have a conservative proposal/review/apply labeling path,
|
|
117
|
+
and strict bundle application reconciles declared roles against live slots
|
|
118
|
+
before the first mutation.
|
|
119
|
+
- New bundles use `treeShader`, `grassShader`, `flowerShader`, and
|
|
120
|
+
`groundShader`. V1 `vegetationShader`, `tree`, `grass`, and `flowers`
|
|
121
|
+
selections are migration inputs only and never serialize into v2. Do not
|
|
122
|
+
serialize asset recipes, tree geometry, scatter, or live wind/weather
|
|
123
|
+
into shader slots.
|
|
124
|
+
- Treat `call_me_sensei` as the first-party reference bundle. Optimize the
|
|
125
|
+
canonical domain implementations for the best coordinated result and apply
|
|
126
|
+
the completion gate in `agents/references/style-bundles.md`. Assigning the same
|
|
127
|
+
style id to every slot is not completion, and compatibility flattening for
|
|
128
|
+
incomplete assets must not define the signature look.
|
|
129
|
+
- For sourcing or generation, read
|
|
130
|
+
`agents/references/mcp-asset-discovery.md` and
|
|
131
|
+
`agents/references/asset-sourcing-policy.md`. Reuse an
|
|
132
|
+
accepted asset first. If a procedural family has an approved stylized base
|
|
133
|
+
set and passed the reliability gate, generate directly and skip gallery
|
|
134
|
+
search. Otherwise use curated CC0, then properly attributed CC-BY; reserve
|
|
135
|
+
image-to-3D for a named remaining gap. Preserve base-set version, recipe,
|
|
136
|
+
seed, domain/material labels, license provenance, and the Call Me Sensei
|
|
137
|
+
verification result. Pro may author/review base sets; OSS consumes portable
|
|
138
|
+
artifacts without a database.
|
|
139
|
+
- For Sky/Cloud and Water work, `.settings` is the portable authored baseline and
|
|
140
|
+
`.renderedSettings` is the current composition. Give Lighting, Weather, and
|
|
141
|
+
other transient owners unique `setSceneOverrideLayer` ids and clear only the
|
|
142
|
+
same id; never export composed scene state.
|
|
143
|
+
- In a composed-world test, use the public Lighting bundle slot and scene style
|
|
144
|
+
runtime for the coordinated sun, sky probe, and shared shadow contract. Keep
|
|
145
|
+
host-owned Weather and any custom local-light adapters coordinated with Sky,
|
|
146
|
+
Water, and vegetation scene inputs.
|
|
147
|
+
- Sky documents contain exactly 46 portable art fields. Radius and compile-time
|
|
148
|
+
quality are not portable: named tiers use 2/3/4 cloud octaves, custom quality
|
|
149
|
+
accepts `{ cloudOctaves: 1..5 }`, and `sky.setQuality()` rebuilds the material.
|
|
150
|
+
Water quality is chosen when constructing/replacing `WaterSurface`, not via
|
|
151
|
+
`applySettings()`.
|
|
152
|
+
- Do not depend on ToonLab sample assets, generated manifests, or lab-only
|
|
153
|
+
model/environment discovery.
|
|
154
|
+
- Keep these downloadable resources focused on runtime usage.
|
|
155
|
+
|
|
156
|
+
## Developer Verification
|
|
157
|
+
|
|
158
|
+
- For consumer apps, run that app's normal typecheck, test, build, and visual
|
|
159
|
+
smoke checks.
|
|
160
|
+
- For generated rock assets, use a fixed seed and compare the output your app
|
|
161
|
+
expects.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: ToonLab developer guidance for Cursor agents
|
|
3
|
+
globs: "**/*"
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# ToonLab
|
|
8
|
+
|
|
9
|
+
- Treat `node_modules/@call-me-sensei/toonlab/agents/codex/AGENTS.md` as the
|
|
10
|
+
shared developer guide.
|
|
11
|
+
- Help developers stylize and populate supplied scenes with ToonLab runtime,
|
|
12
|
+
Gallery, and MCP workflows. Do not promise one-shot world construction.
|
|
13
|
+
- Prefer package subpath imports such as `@call-me-sensei/toonlab/toon`,
|
|
14
|
+
`@call-me-sensei/toonlab/water`,
|
|
15
|
+
`@call-me-sensei/toonlab/vegetation-shaders`,
|
|
16
|
+
`@call-me-sensei/toonlab/grass-palettes`, and
|
|
17
|
+
`@call-me-sensei/toonlab/rockgen`;
|
|
18
|
+
keep root imports lightweight.
|
|
19
|
+
- Use TSL/NodeMaterial patterns. Do not guide developers toward raw GLSL,
|
|
20
|
+
`ShaderMaterial`, or classic WebGL-only forks for ToonLab features.
|
|
21
|
+
- Treat ToonLab labs, sample assets, screenshots, captures, and generated
|
|
22
|
+
manifests as examples, not runtime API.
|
|
23
|
+
- Use `agents/skills/` as reference material for feature-specific workflows.
|
|
24
|
+
- Material metadata is a blocking modeling deliverable. Every modeled,
|
|
25
|
+
generated, converted, or imported renderable needs a stable target ID and
|
|
26
|
+
domain; every material slot needs a stable material ID and semantic role in
|
|
27
|
+
the versioned ToonLab material contract. A root-only label is incomplete for
|
|
28
|
+
a multi-material asset. Split incompatible atlas regions or supply an ID
|
|
29
|
+
mask. Strict mode blocks handoff; advisory mode reports the gap and must not
|
|
30
|
+
describe incomplete material coverage as bundle-ready.
|
|
31
|
+
- Treat Sky and Cloud as supported focused authoring/runtime systems. Whole
|
|
32
|
+
terrain/biome/coast/cliff formation remains outside the npm boundary.
|
|
33
|
+
- Before style-aware construction, read the matching `style-presets` skill.
|
|
34
|
+
Before approving screenshots or reporting a package gap use
|
|
35
|
+
`visual-verification`. The Claude and Codex copies are byte-identical, so
|
|
36
|
+
either directory is authoritative.
|
|
37
|
+
- Verify with the consumer app's normal checks.
|
|
@@ -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
|
-
`
|
|
14
|
-
`
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
45
|
-
|
|
46
|
-
`
|
|
47
|
-
|
|
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/`.
|
|
@@ -145,8 +145,8 @@ surface.assertReady({
|
|
|
145
145
|
Do not hand-author grass Y values, independently offset a shoreline water
|
|
146
146
|
plane, or infer readiness from `castShadow` flags when this contract applies.
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
The live `cloud` entry is a raymarched volumetric deck, not painted sources
|
|
149
|
+
placed by a composition. The
|
|
150
150
|
painted pipeline (`createCloudSourceDocument`, `createCloudCompositionDocument`,
|
|
151
151
|
`createCloudField`, `createCloudShaderSettings`) was replaced outright and no
|
|
152
152
|
longer exists. Parameters are six groups on a `toonlab/sky-params` document, and
|
|
@@ -166,8 +166,9 @@ sky.update(delta); // once per frame, before the scene pass
|
|
|
166
166
|
|
|
167
167
|
`applyPreset` fully replaces sky state: anything the object omits falls back to
|
|
168
168
|
the schema default rather than to what is on screen. `sky.toParams()` is its
|
|
169
|
-
inverse and round-trips. This
|
|
170
|
-
|
|
169
|
+
inverse and round-trips. This is a focused sky/cloud authoring and runtime
|
|
170
|
+
contract; final scene composition and art direction remain host
|
|
171
|
+
responsibilities.
|
|
171
172
|
|
|
172
173
|
Use the package README and exported functions/settings as the runtime source
|
|
173
174
|
of truth. Labs, internal comparison fixtures, local asset paths, and repository
|
|
@@ -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 `
|
|
54
|
-
`
|
|
55
|
-
`
|
|
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
|
-
-
|
|
199
|
-
|
|
200
|
-
|
|
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.
|
|
@@ -35,6 +35,22 @@ if (report.usedGeneratedTextures) {
|
|
|
35
35
|
setRockShaderSceneState(rockRoot, { waterLevel });
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
For the representative semantic hoodoo, validate before styling:
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
import { inspectRockRegionBindings } from '@call-me-sensei/toonlab/rock-shader';
|
|
42
|
+
|
|
43
|
+
const regions = inspectRockRegionBindings(hoodooRoot);
|
|
44
|
+
if (regions.totalVertices !== 134981) throw new Error('Unexpected hoodoo binding');
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The v1 binding is fixed to profile `hoodoo-caprock-normalized-height-v1` and
|
|
48
|
+
channels `base/shaft/neck/cap`. Do not apply it to another family. Use only
|
|
49
|
+
material styling (`detail: null`) on the annotated mesh; any position/topology
|
|
50
|
+
edit requires semantic regeneration and rebaking. Prove repeated
|
|
51
|
+
`applyRockShader()` / `restoreRockShader()` cycles restore exact material,
|
|
52
|
+
texture, geometry, index, attribute, shadow, and user-data identities.
|
|
53
|
+
|
|
38
54
|
`applyRockShader` sets `castShadow` and `receiveShadow` true by default.
|
|
39
55
|
Override them only for an explicit rendering reason. It reports whether
|
|
40
56
|
provided textures or first-party generated maps were used; never call the
|
|
@@ -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
|
-
-
|
|
84
|
-
|
|
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
|
|
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,
|
|
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 `
|
|
54
|
-
`
|
|
55
|
-
`
|
|
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
|
-
-
|
|
199
|
-
|
|
200
|
-
|
|
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.
|
|
@@ -35,6 +35,22 @@ if (report.usedGeneratedTextures) {
|
|
|
35
35
|
setRockShaderSceneState(rockRoot, { waterLevel });
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
For the representative semantic hoodoo, validate before styling:
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
import { inspectRockRegionBindings } from '@call-me-sensei/toonlab/rock-shader';
|
|
42
|
+
|
|
43
|
+
const regions = inspectRockRegionBindings(hoodooRoot);
|
|
44
|
+
if (regions.totalVertices !== 134981) throw new Error('Unexpected hoodoo binding');
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The v1 binding is fixed to profile `hoodoo-caprock-normalized-height-v1` and
|
|
48
|
+
channels `base/shaft/neck/cap`. Do not apply it to another family. Use only
|
|
49
|
+
material styling (`detail: null`) on the annotated mesh; any position/topology
|
|
50
|
+
edit requires semantic regeneration and rebaking. Prove repeated
|
|
51
|
+
`applyRockShader()` / `restoreRockShader()` cycles restore exact material,
|
|
52
|
+
texture, geometry, index, attribute, shadow, and user-data identities.
|
|
53
|
+
|
|
38
54
|
`applyRockShader` sets `castShadow` and `receiveShadow` true by default.
|
|
39
55
|
Override them only for an explicit rendering reason. It reports whether
|
|
40
56
|
provided textures or first-party generated maps were used; never call the
|
|
@@ -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
|
-
-
|
|
84
|
-
|
|
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
|
|
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,
|
|
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
|
package/cli/toonlab.mjs
CHANGED
|
@@ -36,7 +36,8 @@ function parse(argv) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
try {
|
|
39
|
-
const
|
|
39
|
+
const argv = process.argv.slice(2);
|
|
40
|
+
const { operation, options } = parse(argv);
|
|
40
41
|
const input = JSON.parse(await readFile(options.input, 'utf8'));
|
|
41
42
|
const result = runSceneStyleOperation(operation, input, options);
|
|
42
43
|
const json = `${JSON.stringify(result, null, options.pretty ? 2 : 0)}\n`;
|
|
@@ -47,6 +48,6 @@ try {
|
|
|
47
48
|
}
|
|
48
49
|
if (!result.ok) process.exitCode = 2;
|
|
49
50
|
} catch (error) {
|
|
50
|
-
process.stderr.write(`${error?.message ?? String(error)}\n`);
|
|
51
|
+
process.stderr.write(`${error?.code ? `${error.code}: ` : ''}${error?.message ?? String(error)}\n`);
|
|
51
52
|
process.exitCode = 1;
|
|
52
53
|
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export const PATINA_MATERIAL_MODEL = 'fal-ai/patina/material';
|
|
2
|
+
export const PATINA_MAP_TYPES = Object.freeze(['basecolor', 'normal', 'roughness', 'metalness', 'height']);
|
|
3
|
+
export const PATINA_RESOLUTIONS = Object.freeze(['1k', '2k', '4k', '8k']);
|
|
4
|
+
export const PATINA_IMAGE_SIZE_PRESETS = Object.freeze([
|
|
5
|
+
'square_hd',
|
|
6
|
+
'square',
|
|
7
|
+
'portrait_4_3',
|
|
8
|
+
'portrait_16_9',
|
|
9
|
+
'landscape_4_3',
|
|
10
|
+
'landscape_16_9',
|
|
11
|
+
]);
|
|
12
|
+
export const PATINA_UPSCALE_FACTORS = Object.freeze([0, 2, 4]);
|
|
13
|
+
|
|
14
|
+
export function patinaResolutionPlan(resolution = '2k') {
|
|
15
|
+
const normalized = String(resolution).toLowerCase();
|
|
16
|
+
if (normalized === '1k') return { imageSize: { width: 1024, height: 1024 }, upscaleFactor: 0 };
|
|
17
|
+
if (normalized === '2k') return { imageSize: { width: 2048, height: 2048 }, upscaleFactor: 0 };
|
|
18
|
+
if (normalized === '4k') return { imageSize: { width: 2048, height: 2048 }, upscaleFactor: 2 };
|
|
19
|
+
if (normalized === '8k') return { imageSize: { width: 2048, height: 2048 }, upscaleFactor: 4 };
|
|
20
|
+
throw new Error(`Fal Patina does not support resolution "${resolution}".`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function integer(value, fallback, min, max, label) {
|
|
24
|
+
const resolved = value === undefined ? fallback : Number(value);
|
|
25
|
+
if (!Number.isInteger(resolved) || resolved < min || resolved > max) {
|
|
26
|
+
throw new Error(`${label} must be an integer from ${min} to ${max}.`);
|
|
27
|
+
}
|
|
28
|
+
return resolved;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function imageSize(value, fallback) {
|
|
32
|
+
if (value === undefined || value === null || value === '') return fallback;
|
|
33
|
+
if (typeof value === 'string') {
|
|
34
|
+
if (!PATINA_IMAGE_SIZE_PRESETS.includes(value)) throw new Error('Invalid Fal Patina image_size preset.');
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
if (!value || typeof value !== 'object') throw new Error('image_size must be a preset or { width, height }.');
|
|
38
|
+
return {
|
|
39
|
+
width: integer(value.width, NaN, 256, 2048, 'image_size.width'),
|
|
40
|
+
height: integer(value.height, NaN, 256, 2048, 'image_size.height'),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Validate and translate ToonLab's camelCase request into Fal's wire schema. */
|
|
45
|
+
export function createPatinaInput(request = {}) {
|
|
46
|
+
const prompt = String(request.prompt ?? '').trim();
|
|
47
|
+
if (!prompt) throw new Error('A material prompt is required.');
|
|
48
|
+
const plan = patinaResolutionPlan(request.resolution ?? '2k');
|
|
49
|
+
const requestedMaps = request.maps === undefined ? PATINA_MAP_TYPES : request.maps;
|
|
50
|
+
if (!Array.isArray(requestedMaps)) throw new Error('maps must be an array.');
|
|
51
|
+
const maps = [...new Set(requestedMaps.map(String))];
|
|
52
|
+
if (maps.some((map) => !PATINA_MAP_TYPES.includes(map))) throw new Error('Fal Patina maps contain an unsupported map type.');
|
|
53
|
+
const tilingMode = request.tilingMode ?? request.tiling_mode ?? 'both';
|
|
54
|
+
if (!['both', 'horizontal', 'vertical'].includes(tilingMode)) throw new Error('tiling_mode must be both, horizontal, or vertical.');
|
|
55
|
+
const outputFormat = request.outputFormat ?? request.output_format ?? 'png';
|
|
56
|
+
if (!['png', 'jpeg', 'webp'].includes(outputFormat)) throw new Error('output_format must be png, jpeg, or webp.');
|
|
57
|
+
const upscaleFactor = request.upscaleFactor ?? request.upscale_factor ?? plan.upscaleFactor;
|
|
58
|
+
if (!PATINA_UPSCALE_FACTORS.includes(Number(upscaleFactor))) throw new Error('upscale_factor must be 0, 2, or 4.');
|
|
59
|
+
const input = {
|
|
60
|
+
prompt,
|
|
61
|
+
image_size: imageSize(request.imageSize ?? request.image_size, plan.imageSize),
|
|
62
|
+
num_inference_steps: integer(request.numInferenceSteps ?? request.num_inference_steps, 8, 1, 8, 'num_inference_steps'),
|
|
63
|
+
num_images: integer(request.numImages ?? request.num_images, 1, 1, 4, 'num_images'),
|
|
64
|
+
enable_prompt_expansion: (request.enablePromptExpansion ?? request.enable_prompt_expansion) !== false,
|
|
65
|
+
enable_safety_checker: (request.enableSafetyChecker ?? request.enable_safety_checker) !== false,
|
|
66
|
+
tiling_mode: tilingMode,
|
|
67
|
+
tile_size: integer(request.tileSize ?? request.tile_size, 128, 32, 256, 'tile_size'),
|
|
68
|
+
tile_stride: integer(request.tileStride ?? request.tile_stride, 64, 16, 128, 'tile_stride'),
|
|
69
|
+
maps,
|
|
70
|
+
upscale_factor: Number(upscaleFactor),
|
|
71
|
+
output_format: outputFormat,
|
|
72
|
+
};
|
|
73
|
+
const seed = request.seed;
|
|
74
|
+
if (seed !== undefined) input.seed = integer(seed, 0, -2147483648, 2147483647, 'seed');
|
|
75
|
+
const imageUrl = request.imageUrl ?? request.image_url;
|
|
76
|
+
const maskUrl = request.maskUrl ?? request.mask_url;
|
|
77
|
+
if (imageUrl) {
|
|
78
|
+
if (!/^https:\/\//i.test(String(imageUrl))) throw new Error('image_url must be a public HTTPS URL.');
|
|
79
|
+
input.image_url = String(imageUrl);
|
|
80
|
+
const strength = Number(request.strength ?? 0.6);
|
|
81
|
+
if (!Number.isFinite(strength) || strength < 0 || strength > 1) throw new Error('strength must be from 0 to 1.');
|
|
82
|
+
input.strength = strength;
|
|
83
|
+
}
|
|
84
|
+
if (maskUrl) {
|
|
85
|
+
if (!imageUrl) throw new Error('mask_url requires image_url.');
|
|
86
|
+
if (!/^https:\/\//i.test(String(maskUrl))) throw new Error('mask_url must be a public HTTPS URL.');
|
|
87
|
+
input.mask_url = String(maskUrl);
|
|
88
|
+
}
|
|
89
|
+
return input;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function normalizePatinaResult(data, input) {
|
|
93
|
+
if (!Array.isArray(data?.images)) throw new Error('Fal Patina returned no material images.');
|
|
94
|
+
const images = data.images.map((image) => {
|
|
95
|
+
if (!image || typeof image.url !== 'string' || !/^https:\/\//i.test(image.url)) {
|
|
96
|
+
throw new Error('Fal Patina returned an invalid material image URL.');
|
|
97
|
+
}
|
|
98
|
+
if (image.map_type != null && !PATINA_MAP_TYPES.includes(image.map_type)) {
|
|
99
|
+
throw new Error(`Fal Patina returned an unsupported map type: ${String(image.map_type)}.`);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
url: image.url,
|
|
103
|
+
mapType: image.map_type == null ? null : image.map_type,
|
|
104
|
+
contentType: typeof image.content_type === 'string' ? image.content_type : null,
|
|
105
|
+
name: typeof image.file_name === 'string' ? image.file_name : null,
|
|
106
|
+
byteSize: Number.isFinite(Number(image.file_size)) ? Number(image.file_size) : null,
|
|
107
|
+
width: Number.isFinite(Number(image.width)) ? Number(image.width) : null,
|
|
108
|
+
height: Number.isFinite(Number(image.height)) ? Number(image.height) : null,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
const variants = Number(input.num_images ?? 1);
|
|
112
|
+
const requestedMaps = Array.isArray(input.maps) ? input.maps : PATINA_MAP_TYPES;
|
|
113
|
+
const previewCount = images.filter((image) => image.mapType === null).length;
|
|
114
|
+
if (previewCount !== variants) {
|
|
115
|
+
throw new Error(`Fal Patina returned ${previewCount} previews; expected ${variants}.`);
|
|
116
|
+
}
|
|
117
|
+
for (const mapType of PATINA_MAP_TYPES) {
|
|
118
|
+
const count = images.filter((image) => image.mapType === mapType).length;
|
|
119
|
+
const expected = requestedMaps.includes(mapType) ? variants : 0;
|
|
120
|
+
if (count !== expected) {
|
|
121
|
+
throw new Error(`Fal Patina returned ${count} ${mapType} maps; expected ${expected}.`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
images,
|
|
126
|
+
input,
|
|
127
|
+
prompt: typeof data.prompt === 'string' ? data.prompt : input.prompt,
|
|
128
|
+
seed: Number.isFinite(Number(data.seed)) ? Number(data.seed) : null,
|
|
129
|
+
timings: data.timings && typeof data.timings === 'object' ? data.timings : null,
|
|
130
|
+
};
|
|
131
|
+
}
|