arkaos 5.4.0 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +2 -2
  2. package/THE-ARKAOS-GUIDE.md +1 -1
  3. package/VERSION +1 -1
  4. package/arka/SKILL.md +1 -1
  5. package/config/skills-provenance.yaml +27 -0
  6. package/departments/dev/skills/canvas-generative/SKILL.md +362 -0
  7. package/departments/dev/skills/canvas-generative/references/algorithms.md +934 -0
  8. package/departments/dev/skills/canvas-generative/references/genjutsu.LICENSE +21 -0
  9. package/departments/dev/skills/css-native/SKILL.md +353 -0
  10. package/departments/dev/skills/css-native/references/genjutsu.LICENSE +21 -0
  11. package/departments/dev/skills/css-native/references/modern-css.md +465 -0
  12. package/departments/dev/skills/framer-motion/SKILL.md +223 -0
  13. package/departments/dev/skills/framer-motion/references/api.md +312 -0
  14. package/departments/dev/skills/framer-motion/references/genjutsu.LICENSE +21 -0
  15. package/departments/dev/skills/threejs/SKILL.md +173 -0
  16. package/departments/dev/skills/threejs/references/genjutsu.LICENSE +21 -0
  17. package/departments/dev/skills/threejs/references/r3f.md +249 -0
  18. package/departments/dev/skills/threejs/references/scene-setup.md +404 -0
  19. package/departments/dev/skills/threejs/references/shaders.md +452 -0
  20. package/harness/codex/AGENTS.md +1 -1
  21. package/harness/copilot/copilot-instructions.md +1 -1
  22. package/harness/cursor/rules/arkaos.mdc +2 -2
  23. package/harness/gemini/GEMINI.md +1 -1
  24. package/harness/opencode/AGENTS.md +1 -1
  25. package/harness/opencode/agents/arka-architect-gabriel.md +1 -1
  26. package/harness/opencode/agents/arka-brand-director-valentina.md +1 -1
  27. package/harness/opencode/agents/arka-cfo-helena.md +1 -1
  28. package/harness/opencode/agents/arka-chief-of-staff-afonso.md +1 -1
  29. package/harness/opencode/agents/arka-community-strategist-beatriz.md +1 -1
  30. package/harness/opencode/agents/arka-content-strategist-rafael.md +1 -1
  31. package/harness/opencode/agents/arka-conversion-strategist-ines.md +1 -1
  32. package/harness/opencode/agents/arka-coo-sofia.md +1 -1
  33. package/harness/opencode/agents/arka-copy-director-eduardo.md +1 -1
  34. package/harness/opencode/agents/arka-cqo-marta.md +1 -1
  35. package/harness/opencode/agents/arka-cto-marco.md +1 -1
  36. package/harness/opencode/agents/arka-design-ops-lead-iris.md +1 -1
  37. package/harness/opencode/agents/arka-ecom-director-ricardo.md +1 -1
  38. package/harness/opencode/agents/arka-knowledge-director-clara.md +1 -1
  39. package/harness/opencode/agents/arka-leadership-director-rodrigo.md +1 -1
  40. package/harness/opencode/agents/arka-marketing-director-luna.md +1 -1
  41. package/harness/opencode/agents/arka-ops-lead-daniel.md +1 -1
  42. package/harness/opencode/agents/arka-pm-director-carolina.md +1 -1
  43. package/harness/opencode/agents/arka-revops-lead-vicente.md +1 -1
  44. package/harness/opencode/agents/arka-saas-strategist-tiago.md +1 -1
  45. package/harness/opencode/agents/arka-sales-director-miguel.md +1 -1
  46. package/harness/opencode/agents/arka-strategy-director-tomas.md +1 -1
  47. package/harness/opencode/agents/arka-tech-director-francisca.md +1 -1
  48. package/harness/opencode/agents/arka-tech-lead-paulo.md +1 -1
  49. package/harness/opencode/agents/arka-video-producer-simao.md +1 -1
  50. package/harness/zed/.rules +1 -1
  51. package/knowledge/commands-registry.json +1 -1
  52. package/knowledge/skills-manifest.json +61 -1
  53. package/package.json +1 -1
  54. package/pyproject.toml +1 -1
  55. package/scripts/marketplace_gen.py +1 -1
@@ -0,0 +1,312 @@
1
+ # Framer Motion — API Reference
2
+
3
+ > `motion` v11+ — `import { motion, AnimatePresence, ... } from "motion/react"`
4
+
5
+ ## motion component — Props
6
+
7
+ ### Animation
8
+
9
+ | Prop | Type | Description |
10
+ |------|------|-------------|
11
+ | `initial` | `Target \| VariantLabel \| false` | Initial state. `false` = no animation on mount (starts directly from `animate`) |
12
+ | `animate` | `Target \| VariantLabel` | Target state. Change = animates automatically |
13
+ | `exit` | `Target \| VariantLabel` | Exit animation (requires parent `AnimatePresence`) |
14
+ | `transition` | `Transition` | Transition config (duration, ease, type, delay...) |
15
+ | `variants` | `Variants` | Map of labels to targets. Propagate to motion children |
16
+
17
+ ### Gesture props
18
+
19
+ | Prop | Type | Description |
20
+ |------|------|-------------|
21
+ | `whileHover` | `Target \| VariantLabel` | Animates during hover |
22
+ | `whileTap` | `Target \| VariantLabel` | Animates during press/tap |
23
+ | `whileFocus` | `Target \| VariantLabel` | Animates during focus |
24
+ | `whileDrag` | `Target \| VariantLabel` | Animates during drag |
25
+ | `whileInView` | `Target \| VariantLabel` | Animates when visible in the viewport |
26
+
27
+ ### Drag
28
+
29
+ | Prop | Type | Description |
30
+ |------|------|-------------|
31
+ | `drag` | `boolean \| "x" \| "y"` | Enables drag. `true` = both axes |
32
+ | `dragConstraints` | `{ top, left, right, bottom } \| RefObject` | Drag limits. Ref = a parent element |
33
+ | `dragElastic` | `number \| { top, left, right, bottom }` | Elasticity outside constraints (0-1, default 0.35) |
34
+ | `dragMomentum` | `boolean` | Inertia after release (default true) |
35
+ | `dragSnapToOrigin` | `boolean` | Returns to initial position after release |
36
+ | `dragTransition` | `InertiaTransition` | Inertia transition config |
37
+ | `dragPropagation` | `boolean` | Propagates drag to draggable parents |
38
+ | `onDrag` | `(event, info: PanInfo) => void` | Callback during drag |
39
+ | `onDragStart` | `(event, info: PanInfo) => void` | Callback at drag start |
40
+ | `onDragEnd` | `(event, info: PanInfo) => void` | Callback at drag end |
41
+ | `onDirectionLock` | `(axis: "x" \| "y") => void` | Callback when direction is locked |
42
+
43
+ ### Layout
44
+
45
+ | Prop | Type | Description |
46
+ |------|------|-------------|
47
+ | `layout` | `boolean \| "position" \| "size" \| "preserve-aspect"` | Automatically animates layout changes |
48
+ | `layoutId` | `string` | Shared layout — animates between components sharing the same layoutId |
49
+ | `layoutDependency` | `any` | Forces a layout recalculation when this value changes |
50
+ | `layoutScroll` | `boolean` | Compensates scroll offset in layout calculations |
51
+ | `onLayoutAnimationStart` | `() => void` | Callback at layout animation start |
52
+ | `onLayoutAnimationComplete` | `() => void` | Callback at layout animation end |
53
+
54
+ ### Lifecycle callbacks
55
+
56
+ | Prop | Type | Description |
57
+ |------|------|-------------|
58
+ | `onAnimationStart` | `(definition) => void` | When an animation starts |
59
+ | `onAnimationComplete` | `(definition) => void` | When an animation ends |
60
+ | `onUpdate` | `(latest: ResolvedValues) => void` | On each frame during animation |
61
+ | `onViewportEnter` | `(entry) => void` | When the element enters the viewport |
62
+ | `onViewportLeave` | `(entry) => void` | When the element leaves the viewport |
63
+
64
+ ### Style (MotionValue-compatible)
65
+
66
+ The `style` prop accepts `MotionValue` in addition to normal values:
67
+
68
+ ```tsx
69
+ const x = useMotionValue(0);
70
+ <motion.div style={{ x, opacity: 0.5 }} />
71
+ ```
72
+
73
+ **Independent transforms** supported in `style`: `x`, `y`, `z`, `rotateX`, `rotateY`, `rotateZ`, `scale`, `scaleX`, `scaleY`, `skewX`, `skewY`.
74
+
75
+ ## AnimatePresence — Props
76
+
77
+ | Prop | Type | Description |
78
+ |------|------|-------------|
79
+ | `mode` | `"sync" \| "wait" \| "popLayout"` | Enter/exit behavior. Default `"sync"` |
80
+ | `initial` | `boolean` | `false` = disables initial animations of children on first render |
81
+ | `onExitComplete` | `() => void` | Callback when all exit animations are finished |
82
+ | `custom` | `any` | Value passed to dynamic `exit` variants |
83
+ | `presenceAffectsLayout` | `boolean` | Does the exiting element still affect layout (default true) |
84
+
85
+ ### Mode details
86
+
87
+ - **`sync`** (default) — enter and exit simultaneously
88
+ - **`wait`** — waits for exit to finish before starting enter (page transitions)
89
+ - **`popLayout`** — removes the element from flow immediately via `position: absolute`, starts enter during exit
90
+
91
+ ## Transition — Configuration
92
+
93
+ ```tsx
94
+ // Spring (default for physical values)
95
+ transition={{ type: "spring", stiffness: 300, damping: 30, mass: 1 }}
96
+
97
+ // Tween (default for opacity, color)
98
+ transition={{ type: "tween", duration: 0.3, ease: "easeInOut" }}
99
+
100
+ // Inertia (for drag momentum)
101
+ transition={{ type: "inertia", velocity: 200, power: 0.8 }}
102
+
103
+ // Per-value
104
+ transition={{
105
+ x: { type: "spring", stiffness: 300 },
106
+ opacity: { duration: 0.2 },
107
+ }}
108
+
109
+ // Orchestration in variants
110
+ transition={{
111
+ staggerChildren: 0.08,
112
+ delayChildren: 0.2,
113
+ staggerDirection: -1, // reverse
114
+ when: "beforeChildren", // or "afterChildren"
115
+ }}
116
+ ```
117
+
118
+ ### Ease presets
119
+
120
+ `"linear"`, `"easeIn"`, `"easeOut"`, `"easeInOut"`, `"circIn"`, `"circOut"`, `"circInOut"`, `"backIn"`, `"backOut"`, `"backInOut"`, `"anticipate"`
121
+
122
+ Cubic bezier: `ease: [0.25, 0.1, 0.25, 1.0]`
123
+
124
+ ## Hooks
125
+
126
+ ### useMotionValue
127
+
128
+ ```tsx
129
+ const x = useMotionValue(0);
130
+ // Set without re-render
131
+ x.set(100);
132
+ // Read current value
133
+ x.get(); // 100
134
+ // Bind to style
135
+ <motion.div style={{ x }} />
136
+ ```
137
+
138
+ Creates a reactive value that updates the DOM without React re-render. Foundation of all other hooks.
139
+
140
+ ### useTransform
141
+
142
+ ```tsx
143
+ // Map a value to another
144
+ const opacity = useTransform(x, [-200, 0, 200], [0, 1, 0]);
145
+
146
+ // Transform with a function
147
+ const rounded = useTransform(x, (latest) => Math.round(latest));
148
+
149
+ // Combine multiple values
150
+ const combined = useTransform([x, y], ([latestX, latestY]) => latestX + latestY);
151
+ ```
152
+
153
+ Derives a `MotionValue` from one or more others. Recalculated on each source change.
154
+
155
+ ### useSpring
156
+
157
+ ```tsx
158
+ const springX = useSpring(motionValue, {
159
+ stiffness: 300, // default 100
160
+ damping: 30, // default 10
161
+ mass: 1, // default 1
162
+ restSpeed: 0.01,
163
+ restDelta: 0.01,
164
+ });
165
+
166
+ // With initial value (number)
167
+ const spring = useSpring(0);
168
+ spring.set(100); // animates to 100 with spring physics
169
+ ```
170
+
171
+ Creates a spring that follows a `MotionValue` or a number. Ideal for smooth scroll indicators, cursor followers.
172
+
173
+ ### useScroll
174
+
175
+ ```tsx
176
+ // Page scroll
177
+ const { scrollX, scrollY, scrollXProgress, scrollYProgress } = useScroll();
178
+
179
+ // Target element scroll
180
+ const ref = useRef(null);
181
+ const { scrollYProgress } = useScroll({
182
+ target: ref,
183
+ offset: ["start end", "end start"],
184
+ // offset format: [targetEdge containerEdge]
185
+ // "start end" = when the top of target reaches the bottom of the container
186
+ });
187
+
188
+ // Specific container scroll
189
+ const { scrollYProgress } = useScroll({ container: scrollRef });
190
+ ```
191
+
192
+ Returns 4 `MotionValue`: absolute position (scrollX/Y) and normalized 0-1 progress (scrollXProgress/YProgress).
193
+
194
+ **Offset keywords**: `"start"`, `"center"`, `"end"`, or a number/percentage (`0.5`, `"100px"`).
195
+
196
+ ### useAnimate
197
+
198
+ ```tsx
199
+ const [scope, animate] = useAnimate();
200
+
201
+ // Imperative animation
202
+ await animate(scope.current, { x: 100, opacity: 1 }, { duration: 0.5 });
203
+
204
+ // Selectors within the scope
205
+ await animate("li", { opacity: 1 }, { delay: stagger(0.1) });
206
+
207
+ // Sequence
208
+ await animate([
209
+ [scope.current, { x: 100 }],
210
+ ["li", { opacity: 1 }, { delay: stagger(0.1) }],
211
+ ]);
212
+
213
+ <div ref={scope}>
214
+ <li>...</li>
215
+ </div>
216
+ ```
217
+
218
+ Imperative API for complex animations, sequences, and dynamic orchestrations. Alternative to variants when logic is too dynamic.
219
+
220
+ ### useInView
221
+
222
+ ```tsx
223
+ const ref = useRef(null);
224
+ const isInView = useInView(ref, {
225
+ once: true, // triggers only once
226
+ amount: 0.5, // 50% visible (default "some")
227
+ margin: "-100px", // rootMargin (like IntersectionObserver)
228
+ });
229
+
230
+ // Usage with animate
231
+ <motion.div
232
+ ref={ref}
233
+ initial={{ opacity: 0, y: 50 }}
234
+ animate={isInView ? { opacity: 1, y: 0 } : {}}
235
+ />
236
+ ```
237
+
238
+ Wrapper around `IntersectionObserver`. Returns a reactive boolean.
239
+
240
+ ### useReducedMotion
241
+
242
+ ```tsx
243
+ const prefersReduced = useReducedMotion();
244
+
245
+ <motion.div
246
+ animate={prefersReduced ? { opacity: 1 } : { opacity: 1, y: 0, scale: 1 }}
247
+ transition={prefersReduced ? { duration: 0 } : { type: "spring" }}
248
+ />
249
+ ```
250
+
251
+ Respects `prefers-reduced-motion`. **Always use it** for non-essential animations. Returns `true` if the user has enabled the system preference.
252
+
253
+ ### useMotionValueEvent
254
+
255
+ ```tsx
256
+ const x = useMotionValue(0);
257
+
258
+ useMotionValueEvent(x, "change", (latest) => {
259
+ console.log("x changed to", latest);
260
+ });
261
+
262
+ useMotionValueEvent(x, "animationStart", () => { /* ... */ });
263
+ useMotionValueEvent(x, "animationComplete", () => { /* ... */ });
264
+ useMotionValueEvent(x, "animationCancel", () => { /* ... */ });
265
+ ```
266
+
267
+ Subscribes to a MotionValue without re-render. Auto-cleanup on unmount. Prefer over `onChange` (deprecated).
268
+
269
+ ## Utilities
270
+
271
+ ### stagger
272
+
273
+ ```tsx
274
+ import { stagger } from "motion/react";
275
+
276
+ animate("li", { opacity: 1 }, { delay: stagger(0.1) });
277
+ animate("li", { opacity: 1 }, { delay: stagger(0.1, { startDelay: 0.2 }) });
278
+ animate("li", { opacity: 1 }, { delay: stagger(0.1, { from: "center" }) });
279
+ // from: number | "first" | "center" | "last"
280
+ ```
281
+
282
+ ### animate (standalone)
283
+
284
+ ```tsx
285
+ import { animate } from "motion/react";
286
+
287
+ // Animate a value
288
+ animate(0, 100, {
289
+ duration: 1,
290
+ onUpdate: (latest) => el.style.opacity = latest,
291
+ });
292
+
293
+ // Animate an element directly (outside React)
294
+ animate(element, { opacity: 1 }, { duration: 0.5 });
295
+ ```
296
+
297
+ ### scroll (standalone)
298
+
299
+ ```tsx
300
+ import { scroll } from "motion";
301
+
302
+ // Callback based on scroll progress
303
+ scroll((progress) => {
304
+ el.style.opacity = progress;
305
+ });
306
+
307
+ // Bind to an element's scroll
308
+ scroll(
309
+ (progress) => { /* ... */ },
310
+ { target: element, offset: ["start end", "end start"] }
311
+ );
312
+ ```
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Adrien Thevon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,173 @@
1
+ ---
2
+ name: dev/threejs
3
+ description: >
4
+ Real-time 3D in the browser with three.js and React Three Fiber: modern
5
+ setup (import maps, WebGL vs WebGPU renderer choice, TSL), the
6
+ scene-camera-renderer wiring, geometries, materials and lights, loaders,
7
+ disposal discipline against GPU memory leaks, performance budgets — and
8
+ the R3F layer (hooks, drei, postprocessing) for React projects.
9
+ TRIGGER: "three.js", "threejs", "react three fiber", "r3f", "webgl",
10
+ "webgpu", "shader", "GLSL", "cena 3D", "3D interativo no browser",
11
+ "drei", "/dev threejs".
12
+ SKIP: a pre-rendered 3D world scrubbed by scroll (no real-time engine)
13
+ -> dev/scroll-world; generative 2D canvas -> dev/canvas-generative;
14
+ generating a 3D asset (GLB mesh) from an image -> the higgsfield-generate
15
+ skill; React motion without 3D -> dev/framer-motion.
16
+ allowed-tools: [Read, Write, Edit, Bash, Grep, Glob]
17
+ metadata:
18
+ origin: community
19
+ source: https://github.com/AThevon/genjutsu
20
+ license: MIT
21
+ ---
22
+
23
+ <!-- arka:kb-first-prefix begin -->
24
+ > **KB-first:** query `mcp__obsidian__search_notes` (and
25
+ > `mcp__graphify__query_graph` when configured) and cite `[[wikilinks]]`
26
+ > or graph nodes — or declare the gap — BEFORE any external research.
27
+ > Full doctrine: `arka/SKILL.md` (KB-First Research).
28
+ <!-- arka:kb-first-prefix end -->
29
+
30
+ # Three.js — `/dev threejs`
31
+
32
+ > **Agent:** Diana (Frontend Dev) | **Framework:** three.js (MIT) + React Three Fiber
33
+ > Core authored from the three.js project's own LLM guidance
34
+ > (`docs/llms.txt`, MIT); the R3F layer derives from genjutsu (MIT) — see
35
+ > `references/`.
36
+
37
+ ## Setup — the modern shape
38
+
39
+ Import maps, never legacy CDN script tags. Pin one version for `three`
40
+ and `three/addons/` together — mixing versions across the two specifiers
41
+ is the classic silent breaker:
42
+
43
+ ```html
44
+ <script type="importmap">
45
+ { "imports": {
46
+ "three": "https://cdn.jsdelivr.net/npm/three@<version>/build/three.module.js",
47
+ "three/webgpu": "https://cdn.jsdelivr.net/npm/three@<version>/build/three.webgpu.js",
48
+ "three/tsl": "https://cdn.jsdelivr.net/npm/three@<version>/build/three.tsl.js",
49
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@<version>/examples/jsm/" } }
50
+ </script>
51
+ ```
52
+
53
+ `WebGPURenderer` is not exported from `three` — it lives in the
54
+ `three/webgpu` entry, and it is async: `await renderer.init()` before the
55
+ first frame. The TSL node functions live in `three/tsl`, not in
56
+ `three/webgpu`. Omit either entry and the import fails at runtime.
57
+
58
+ In a bundled project, `npm i three` and import the same specifiers. Check
59
+ the installed version before writing code — the API moves, and the
60
+ project's lockfile outranks your memory of it.
61
+
62
+ **Renderer choice.** `WebGLRenderer` is the default and safe everywhere.
63
+ `WebGPURenderer` (with TSL node materials) is the forward path but needs
64
+ feature detection and a WebGL fallback; choose it only when the project
65
+ targets browsers you control or ships the fallback. Never mix GLSL
66
+ `ShaderMaterial` into a WebGPU pipeline — that renderer wants TSL
67
+ `NodeMaterial` classes.
68
+
69
+ ## The irreducible core
70
+
71
+ Scene, camera, renderer, loop — everything else hangs off these:
72
+
73
+ ```js
74
+ const scene = new THREE.Scene();
75
+ const camera = new THREE.PerspectiveCamera(75, w / h, 0.1, 100);
76
+ const renderer = new THREE.WebGLRenderer({ antialias: true });
77
+ renderer.setSize(w, h);
78
+ renderer.setPixelRatio(Math.min(devicePixelRatio, 2)); // cap DPR: 3x buys nothing visible and burns fill rate
79
+ renderer.setAnimationLoop(tick); // not a hand-rolled rAF: in XR it swaps to the session's loop; outside XR it is rAF with correct teardown via setAnimationLoop(null)
80
+ ```
81
+
82
+ On resize: update `camera.aspect`, call `camera.updateProjectionMatrix()`,
83
+ then `renderer.setSize`. Forgetting the middle step is why "resize
84
+ stretches everything".
85
+
86
+ **Animate with elapsed time, not the frame count.** A per-frame delta
87
+ keeps speed identical at 30 and 144 fps; per-frame increments do not.
88
+ Use `THREE.Timer` (`timer.update(); timer.getDelta()`) — `THREE.Clock`
89
+ is deprecated since r183 and warns at runtime.
90
+
91
+ ## Disposal — GPU memory does not garbage-collect
92
+
93
+ Removing a mesh from the scene frees nothing on the GPU. Every geometry,
94
+ material, texture and render target you create, you `dispose()` when its
95
+ life ends — and a material's textures are disposed separately from the
96
+ material. For a whole subtree, collect first, dispose once: `traverse`
97
+ visits Groups and Lights that have no material, and shared resources must
98
+ not be disposed per-mesh:
99
+
100
+ ```js
101
+ function disposeSubtree(root) {
102
+ const geometries = new Set(), materials = new Set(), textures = new Set();
103
+ root.traverse((obj) => {
104
+ if (!obj.isMesh && !obj.isPoints && !obj.isLine && !obj.isSprite) return;
105
+ // Sprites share one engine-owned quad geometry app-wide: never dispose it
106
+ if (obj.geometry && !obj.isSprite) geometries.add(obj.geometry);
107
+ const mats = Array.isArray(obj.material) ? obj.material : [obj.material];
108
+ for (const m of mats) {
109
+ if (!m) continue;
110
+ materials.add(m);
111
+ for (const v of Object.values(m)) if (v?.isTexture) textures.add(v);
112
+ // ShaderMaterial textures live in uniforms, not on the material —
113
+ // and a uniform's value can be an ARRAY of textures (atlas shape)
114
+ if (m.uniforms) for (const u of Object.values(m.uniforms)) {
115
+ const vals = Array.isArray(u?.value) ? u.value : [u?.value];
116
+ for (const v of vals) if (v?.isTexture) textures.add(v);
117
+ }
118
+ }
119
+ });
120
+ textures.forEach((t) => t.dispose());
121
+ materials.forEach((m) => m.dispose());
122
+ geometries.forEach((g) => g.dispose());
123
+ }
124
+ ```
125
+
126
+ This covers the shapes meshes ordinarily carry — geometries, material
127
+ maps, scalar and array texture uniforms. When a resource is shared
128
+ with meshes OUTSIDE the subtree — a texture atlas, a cached material —
129
+ exclude it: ownership is yours to model, and dispose belongs to the owner.
130
+
131
+ A single-page app that mounts and unmounts scenes without this leaks
132
+ until the tab dies. This is the number-one three.js production bug.
133
+
134
+ ## Performance budget
135
+
136
+ - **Draw calls first.** Hundreds of meshes sharing one geometry+material
137
+ belong in an `InstancedMesh`; thousands of distinct static meshes merge
138
+ via `BufferGeometryUtils.mergeGeometries`.
139
+ - Reuse geometries and materials across meshes; clone only when a
140
+ property must diverge.
141
+ - Textures: compressed formats (KTX2/Basis) for anything big, and never
142
+ larger than the screen area they cover; mipmaps come from the loader or
143
+ `generateMipmaps`, with no power-of-two constraint in WebGL 2.
144
+ - Lights are per-fragment cost: prefer one directional + ambient or an
145
+ environment map over a constellation of point lights. Bake what does
146
+ not move.
147
+ - Shadows are a render pass per caster: tight `shadow.camera` frustum,
148
+ modest map sizes, `castShadow` only where it is visible.
149
+
150
+ ## Loading models
151
+
152
+ `GLTFLoader` (with `DRACOLoader` or `KTX2Loader` when the asset uses
153
+ them) from `three/addons/`. Load asynchronously, add a loading state,
154
+ and dispose the loader's intermediates. glTF is the format; converting
155
+ from anything else happens in the asset pipeline, not at runtime.
156
+
157
+ ## React Three Fiber
158
+
159
+ For React projects, R3F replaces the imperative wiring: the component
160
+ tree IS the scene graph, `useFrame` is the loop, drei supplies the
161
+ controls/loaders/staging you would otherwise hand-write. The full layer —
162
+ hooks discipline, drei picks, postprocessing, and the Do-Nots (from
163
+ setState in useFrame to forgotten dispose) — lives
164
+ in [references/r3f.md](references/r3f.md), with canvas/scene wiring in
165
+ [references/scene-setup.md](references/scene-setup.md) and GLSL/TSL
166
+ patterns in [references/shaders.md](references/shaders.md).
167
+
168
+ ## Output
169
+
170
+ A working scene module (or R3F component tree) with the render loop,
171
+ resize handling and disposal wired, a stated performance budget (draw
172
+ calls, texture memory, DPR cap), and the renderer choice justified in one
173
+ line. Anything WebGPU/TSL states its fallback.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Adrien Thevon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.