kmcom-nuxt-layers 1.6.4 → 1.6.6

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 (165) hide show
  1. package/layers/core/app/.DS_Store +0 -0
  2. package/layers/core/app/assets/.DS_Store +0 -0
  3. package/layers/core/app/assets/css/main.css +2 -0
  4. package/layers/core/app/components/ErrorBoundary.vue +1 -1
  5. package/layers/core/nuxt.config.ts +0 -8
  6. package/layers/motion/app/plugins/locomotive-scroll.client.ts +14 -11
  7. package/layers/motion/app.config.ts +6 -0
  8. package/layers/shader/app/components/Pipeline/ACESTonemap.client.vue +14 -0
  9. package/layers/shader/app/components/Pipeline/AddBlend.client.vue +24 -0
  10. package/layers/shader/app/components/Pipeline/AgedFilm.client.vue +62 -0
  11. package/layers/shader/app/components/Pipeline/Aurora.client.vue +81 -0
  12. package/layers/shader/app/components/Pipeline/BilinearGradient.client.vue +46 -0
  13. package/layers/shader/app/components/Pipeline/BillowNoise.client.vue +42 -0
  14. package/layers/shader/app/components/Pipeline/BrightnessContrast.client.vue +25 -0
  15. package/layers/shader/app/components/Pipeline/CellularNoise.client.vue +41 -0
  16. package/layers/shader/app/components/Pipeline/ChannelMixer.client.vue +44 -0
  17. package/layers/shader/app/components/Pipeline/ChebyshevNoiseField.client.vue +60 -0
  18. package/layers/shader/app/components/Pipeline/Checkerboard.client.vue +41 -0
  19. package/layers/shader/app/components/Pipeline/ChromaticAberration.client.vue +52 -0
  20. package/layers/shader/app/components/Pipeline/ChromaticScreenWaves.client.vue +49 -0
  21. package/layers/shader/app/components/Pipeline/Circle.client.vue +41 -0
  22. package/layers/shader/app/components/Pipeline/Clouds.client.vue +60 -0
  23. package/layers/shader/app/components/Pipeline/ColorBurnBlend.client.vue +24 -0
  24. package/layers/shader/app/components/Pipeline/ColorDodgeBlend.client.vue +24 -0
  25. package/layers/shader/app/components/Pipeline/ColourRamp.client.vue +57 -0
  26. package/layers/shader/app/components/Pipeline/ComplexPlaneField.client.vue +77 -0
  27. package/layers/shader/app/components/Pipeline/ConicGradient.client.vue +40 -0
  28. package/layers/shader/app/components/Pipeline/CosinePalette.client.vue +71 -0
  29. package/layers/shader/app/components/Pipeline/CoverageAlpha.client.vue +18 -0
  30. package/layers/shader/app/components/Pipeline/Cross.client.vue +51 -0
  31. package/layers/shader/app/components/Pipeline/CurlNoise.client.vue +48 -0
  32. package/layers/shader/app/components/Pipeline/DarkenBlend.client.vue +24 -0
  33. package/layers/shader/app/components/Pipeline/DayNightCycle.client.vue +48 -0
  34. package/layers/shader/app/components/Pipeline/Desaturate.client.vue +21 -0
  35. package/layers/shader/app/components/Pipeline/DiagonalGradient.client.vue +38 -0
  36. package/layers/shader/app/components/Pipeline/DiamondGradient.client.vue +40 -0
  37. package/layers/shader/app/components/Pipeline/DifferenceBlend.client.vue +24 -0
  38. package/layers/shader/app/components/Pipeline/DivideBlend.client.vue +26 -0
  39. package/layers/shader/app/components/Pipeline/DomainWarpedNoise.client.vue +48 -0
  40. package/layers/shader/app/components/Pipeline/Dots.client.vue +44 -0
  41. package/layers/shader/app/components/Pipeline/DuoTone.client.vue +34 -0
  42. package/layers/shader/app/components/Pipeline/ExclusionBlend.client.vue +24 -0
  43. package/layers/shader/app/components/Pipeline/ExponentialFog.client.vue +46 -0
  44. package/layers/shader/app/components/Pipeline/Exposure.client.vue +23 -0
  45. package/layers/shader/app/components/Pipeline/FBMNoise.client.vue +50 -0
  46. package/layers/shader/app/components/Pipeline/FilmBurn.client.vue +49 -0
  47. package/layers/shader/app/components/Pipeline/FilmGrain.client.vue +69 -0
  48. package/layers/shader/app/components/Pipeline/FisheyeRay.client.vue +35 -0
  49. package/layers/shader/app/components/Pipeline/Flame.client.vue +51 -0
  50. package/layers/shader/app/components/Pipeline/FocalGradient.client.vue +43 -0
  51. package/layers/shader/app/components/Pipeline/Gamma.client.vue +23 -0
  52. package/layers/shader/app/components/Pipeline/GodRays.client.vue +64 -0
  53. package/layers/shader/app/components/Pipeline/GradientNoise.client.vue +42 -0
  54. package/layers/shader/app/components/Pipeline/Grain.client.vue +81 -0
  55. package/layers/shader/app/components/Pipeline/Grid.client.vue +43 -0
  56. package/layers/shader/app/components/Pipeline/Halation.client.vue +43 -0
  57. package/layers/shader/app/components/Pipeline/Halftone.client.vue +33 -0
  58. package/layers/shader/app/components/Pipeline/HardLightBlend.client.vue +24 -0
  59. package/layers/shader/app/components/Pipeline/Haze.client.vue +50 -0
  60. package/layers/shader/app/components/Pipeline/Hexagon.client.vue +42 -0
  61. package/layers/shader/app/components/Pipeline/Hue.client.vue +21 -0
  62. package/layers/shader/app/components/Pipeline/Invert.client.vue +14 -0
  63. package/layers/shader/app/components/Pipeline/LensFlare.client.vue +67 -0
  64. package/layers/shader/app/components/Pipeline/LightenBlend.client.vue +24 -0
  65. package/layers/shader/app/components/Pipeline/LinearGradient.client.vue +36 -0
  66. package/layers/shader/app/components/Pipeline/LinearGradient4.client.vue +67 -0
  67. package/layers/shader/app/components/Pipeline/LinearToSRGB.client.vue +25 -0
  68. package/layers/shader/app/components/Pipeline/Marble.client.vue +69 -0
  69. package/layers/shader/app/components/Pipeline/MixBlend.client.vue +45 -0
  70. package/layers/shader/app/components/Pipeline/MonochromeTint.client.vue +31 -0
  71. package/layers/shader/app/components/Pipeline/MultiplyBlend.client.vue +24 -0
  72. package/layers/shader/app/components/Pipeline/NoisyGradient.client.vue +54 -0
  73. package/layers/shader/app/components/Pipeline/NoisyGradientBlend.client.vue +116 -0
  74. package/layers/shader/app/components/Pipeline/OverlayBlend.client.vue +24 -0
  75. package/layers/shader/app/components/Pipeline/PaperTexture.client.vue +41 -0
  76. package/layers/shader/app/components/Pipeline/Polygon.client.vue +47 -0
  77. package/layers/shader/app/components/Pipeline/Posterise.client.vue +23 -0
  78. package/layers/shader/app/components/Pipeline/RadialGradient.client.vue +39 -0
  79. package/layers/shader/app/components/Pipeline/RayAutoOrbit.client.vue +37 -0
  80. package/layers/shader/app/components/Pipeline/RayMouseOrbit.client.vue +49 -0
  81. package/layers/shader/app/components/Pipeline/RayRotateX.client.vue +25 -0
  82. package/layers/shader/app/components/Pipeline/RayRotateY.client.vue +25 -0
  83. package/layers/shader/app/components/Pipeline/RayRotateZ.client.vue +25 -0
  84. package/layers/shader/app/components/Pipeline/RayTiltBasis.client.vue +41 -0
  85. package/layers/shader/app/components/Pipeline/RaymarchTunnel.client.vue +101 -0
  86. package/layers/shader/app/components/Pipeline/Rectangle.client.vue +47 -0
  87. package/layers/shader/app/components/Pipeline/ReinhardTonemap.client.vue +18 -0
  88. package/layers/shader/app/components/Pipeline/RidgedNoise.client.vue +41 -0
  89. package/layers/shader/app/components/Pipeline/Ring.client.vue +44 -0
  90. package/layers/shader/app/components/Pipeline/RingField.client.vue +69 -0
  91. package/layers/shader/app/components/Pipeline/RisographGrain.client.vue +74 -0
  92. package/layers/shader/app/components/Pipeline/RotatedGradientBlend.client.vue +61 -0
  93. package/layers/shader/app/components/Pipeline/SDFColourMask.client.vue +33 -0
  94. package/layers/shader/app/components/Pipeline/SDFRadialMask.client.vue +31 -0
  95. package/layers/shader/app/components/Pipeline/SRGBToLinear.client.vue +25 -0
  96. package/layers/shader/app/components/Pipeline/Saturation.client.vue +21 -0
  97. package/layers/shader/app/components/Pipeline/Scanlines.client.vue +36 -0
  98. package/layers/shader/app/components/Pipeline/ScreenBlend.client.vue +24 -0
  99. package/layers/shader/app/components/Pipeline/ShaderDebugger.client.vue +74 -0
  100. package/layers/shader/app/components/Pipeline/SimplexNoise.client.vue +49 -0
  101. package/layers/shader/app/components/Pipeline/SkyAtmosphere.client.vue +75 -0
  102. package/layers/shader/app/components/Pipeline/SoftLightBlend.client.vue +24 -0
  103. package/layers/shader/app/components/Pipeline/SolidColour.client.vue +21 -0
  104. package/layers/shader/app/components/Pipeline/SplitTone.client.vue +47 -0
  105. package/layers/shader/app/components/Pipeline/Star.client.vue +47 -0
  106. package/layers/shader/app/components/Pipeline/Starfield.client.vue +65 -0
  107. package/layers/shader/app/components/Pipeline/Stripes.client.vue +46 -0
  108. package/layers/shader/app/components/Pipeline/SubtractBlend.client.vue +24 -0
  109. package/layers/shader/app/components/Pipeline/TanhTonemap.client.vue +24 -0
  110. package/layers/shader/app/components/Pipeline/Threshold.client.vue +25 -0
  111. package/layers/shader/app/components/Pipeline/Tint.client.vue +33 -0
  112. package/layers/shader/app/components/Pipeline/Triangle.client.vue +42 -0
  113. package/layers/shader/app/components/Pipeline/UVAspectCorrect.client.vue +14 -0
  114. package/layers/shader/app/components/Pipeline/UVBreath.client.vue +36 -0
  115. package/layers/shader/app/components/Pipeline/UVBulge.client.vue +26 -0
  116. package/layers/shader/app/components/Pipeline/UVClamp.client.vue +18 -0
  117. package/layers/shader/app/components/Pipeline/UVColumnOffset.client.vue +28 -0
  118. package/layers/shader/app/components/Pipeline/UVFlipX.client.vue +14 -0
  119. package/layers/shader/app/components/Pipeline/UVFlipXY.client.vue +14 -0
  120. package/layers/shader/app/components/Pipeline/UVFlipY.client.vue +14 -0
  121. package/layers/shader/app/components/Pipeline/UVFractBand.client.vue +31 -0
  122. package/layers/shader/app/components/Pipeline/UVMousePull.client.vue +39 -0
  123. package/layers/shader/app/components/Pipeline/UVNoiseRotate.client.vue +54 -0
  124. package/layers/shader/app/components/Pipeline/UVNoiseWarp.client.vue +35 -0
  125. package/layers/shader/app/components/Pipeline/UVOrbit.client.vue +34 -0
  126. package/layers/shader/app/components/Pipeline/UVParallax.client.vue +39 -0
  127. package/layers/shader/app/components/Pipeline/UVPixelate.client.vue +21 -0
  128. package/layers/shader/app/components/Pipeline/UVPulse.client.vue +33 -0
  129. package/layers/shader/app/components/Pipeline/UVRipple.client.vue +34 -0
  130. package/layers/shader/app/components/Pipeline/UVRotate.client.vue +37 -0
  131. package/layers/shader/app/components/Pipeline/UVScale.client.vue +30 -0
  132. package/layers/shader/app/components/Pipeline/UVScroll.client.vue +27 -0
  133. package/layers/shader/app/components/Pipeline/UVScrollX.client.vue +21 -0
  134. package/layers/shader/app/components/Pipeline/UVScrollY.client.vue +21 -0
  135. package/layers/shader/app/components/Pipeline/UVShear.client.vue +25 -0
  136. package/layers/shader/app/components/Pipeline/UVSineWarpXY.client.vue +46 -0
  137. package/layers/shader/app/components/Pipeline/UVSwapAxes.client.vue +14 -0
  138. package/layers/shader/app/components/Pipeline/UVTile.client.vue +27 -0
  139. package/layers/shader/app/components/Pipeline/UVTwirl.client.vue +26 -0
  140. package/layers/shader/app/components/Pipeline/UVWarp.client.vue +30 -0
  141. package/layers/shader/app/components/Pipeline/VHSBleed.client.vue +58 -0
  142. package/layers/shader/app/components/Pipeline/ValueNoise.client.vue +41 -0
  143. package/layers/shader/app/components/Pipeline/Vibrance.client.vue +24 -0
  144. package/layers/shader/app/components/Pipeline/Vignette.client.vue +32 -0
  145. package/layers/shader/app/components/Pipeline/VoronoiEdges.client.vue +50 -0
  146. package/layers/shader/app/components/Pipeline/Water.client.vue +53 -0
  147. package/layers/shader/app/components/Pipeline/WaveBendLayer.client.vue +61 -0
  148. package/layers/shader/app/components/Pipeline/WaveColourLayer.client.vue +56 -0
  149. package/layers/shader/app/components/Pipeline/WhiteBalance.client.vue +40 -0
  150. package/layers/shader/app/components/Pipeline/Wood.client.vue +63 -0
  151. package/layers/shader/app/components/Shader/Canvas.vue +24 -0
  152. package/layers/shader/app/components/Shader/Pipeline.client.vue +56 -0
  153. package/layers/shader/app/components/Shader/PipelineContext.vue +18 -0
  154. package/layers/shader/app/composables/useCSSColourUniform.ts +41 -0
  155. package/layers/shader/app/composables/useCSSFloatUniform.ts +34 -0
  156. package/layers/shader/app/composables/useShaderPerf.ts +73 -0
  157. package/layers/shader/app/composables/useShaderPipeline.ts +99 -0
  158. package/layers/shader/app/composables/useSunDirectionUniform.ts +54 -0
  159. package/layers/shader/app/composables/useTSLNodes.ts +0 -1
  160. package/layers/shader/app/shaders/common/complex.ts +29 -0
  161. package/layers/shader/app/shaders/common/shapes.ts +4 -4
  162. package/layers/shader/app/shaders/common/uv.ts +2 -2
  163. package/layers/shader/app/utils/tsl/patterns.ts +1 -1
  164. package/layers/shader/app/utils/tsl/uv.ts +3 -3
  165. package/package.json +9 -7
@@ -1,13 +1,16 @@
1
1
  <script setup lang="ts">
2
+ // @ts-nocheck - Three.js WebGPU types + TresJS renderer factory
2
3
  import { TresCanvas } from '@tresjs/core'
3
4
  import type { ToneMapping } from 'three'
4
5
  import {
5
6
  ACESFilmicToneMapping,
6
7
  CineonToneMapping,
8
+ Color,
7
9
  LinearToneMapping,
8
10
  ReinhardToneMapping,
9
11
  SRGBColorSpace,
10
12
  } from 'three'
13
+ import { WebGPURenderer } from 'three/webgpu'
11
14
 
12
15
  const props = withDefaults(
13
16
  defineProps<{
@@ -20,6 +23,8 @@ const props = withDefaults(
20
23
  preserveDrawingBuffer?: boolean
21
24
  powerPreference?: 'default' | 'high-performance' | 'low-power'
22
25
  windowSize?: boolean
26
+ /** Use WebGPURenderer (required for node materials / TSL shaders) */
27
+ webgpu?: boolean
23
28
  }>(),
24
29
  {
25
30
  clearColor: '#000000',
@@ -31,6 +36,7 @@ const props = withDefaults(
31
36
  preserveDrawingBuffer: false,
32
37
  powerPreference: 'high-performance',
33
38
  windowSize: true,
39
+ webgpu: false,
34
40
  }
35
41
  )
36
42
 
@@ -65,6 +71,23 @@ const maxDpr = computed(() => {
65
71
  return Math.min(window.devicePixelRatio, configDpr)
66
72
  })
67
73
 
74
+ // WebGPU renderer factory — TresJS v5 calls renderer.init() automatically
75
+ // when the returned object has isRenderer === true (three/webgpu Renderer base class)
76
+ const webgpuRendererFactory = props.webgpu
77
+ ? ({ canvas }: { canvas: any }) => {
78
+ const r = new WebGPURenderer({
79
+ canvas: unref(canvas),
80
+ antialias: props.antialias,
81
+ powerPreference: props.powerPreference,
82
+ })
83
+ r.setPixelRatio(Math.min(window.devicePixelRatio, maxDpr.value))
84
+ r.setClearColor(new Color(props.clearColor))
85
+ r.toneMapping = resolvedToneMapping.value
86
+ r.outputColorSpace = resolvedColorSpace.value
87
+ return r
88
+ }
89
+ : undefined
90
+
68
91
  function onReady(context: any) {
69
92
  emit('ready', context)
70
93
  }
@@ -82,6 +105,7 @@ function onReady(context: any) {
82
105
  :power-preference
83
106
  :window-size
84
107
  :dpr="maxDpr"
108
+ :renderer="webgpuRendererFactory"
85
109
  class="shader-canvas"
86
110
  @ready="onReady"
87
111
  >
@@ -0,0 +1,56 @@
1
+ <script setup lang="ts">
2
+ // @ts-nocheck - TSL types are complex
3
+ import { DoubleSide } from 'three'
4
+ import { MeshBasicNodeMaterial } from 'three/webgpu'
5
+ import { float, positionLocal, uv, vec3, vec4 } from 'three/tsl'
6
+
7
+ const { transparent = false } = defineProps<{
8
+ transparent?: boolean
9
+ }>()
10
+
11
+ const pipeline = useShaderPipelineContext()
12
+
13
+ const material = new MeshBasicNodeMaterial()
14
+ material.side = DoubleSide
15
+ if (transparent) {
16
+ material.transparent = true
17
+ }
18
+
19
+ // Starting node for the ray pipeline: centred screen coord → normalised ray direction.
20
+ // Ray transformer blocks (Phase 4) refine this — FisheyeRay, RayTiltBasis, etc.
21
+ const screenRayNode = vec3(uv().sub(0.5).mul(2.0), float(1.0)).normalize()
22
+
23
+ watch(
24
+ pipeline.version,
25
+ () => {
26
+ const uvStages = pipeline.stagesFor('uv')
27
+ const fragment = pipeline.stagesFor('fragment')
28
+ const vertex = pipeline.stagesFor('vertex')
29
+ const ray = pipeline.stagesFor('ray')
30
+
31
+ // UV chain — generators read pipeline.uvNode.value in their stage fn closures
32
+ pipeline.uvNode.value = uvStages.reduce((node, { fn }) => fn(node), uv())
33
+
34
+ // Ray chain — sky/tunnel generators read pipeline.rayNode.value in their closures
35
+ pipeline.rayNode.value = ray.reduce((node, { fn }) => fn(node), screenRayNode)
36
+
37
+ // Fragment chain
38
+ material.colorNode = fragment.reduce((node, { fn }) => fn(node), vec4(0, 0, 0, 1))
39
+ material.needsUpdate = true
40
+
41
+ if (vertex.length > 0) {
42
+ material.positionNode = vertex.reduce((node, { fn }) => fn(node), positionLocal)
43
+ material.needsUpdate = true
44
+ }
45
+ },
46
+ { immediate: true },
47
+ )
48
+
49
+ onUnmounted(() => {
50
+ try { material.dispose() } catch { /* renderer already torn down */ }
51
+ })
52
+ </script>
53
+
54
+ <template>
55
+ <primitive :object="material" attach="material" />
56
+ </template>
@@ -0,0 +1,18 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Provides a ShaderPipeline context to its slot via the standard Vue DOM renderer.
4
+ * Block components (which render <template />) must be placed inside this component,
5
+ * NOT inside TresCanvas — TresJS's custom renderer crashes on null VNodes.
6
+ *
7
+ * Usage:
8
+ * <ShaderPipelineContext>
9
+ * <div class="hidden"><PipelineBlock ... /></div> ← blocks in DOM
10
+ * <ShaderCanvas><TresMesh><ShaderPipeline /></TresMesh></ShaderCanvas>
11
+ * </ShaderPipelineContext>
12
+ */
13
+ useShaderPipeline()
14
+ </script>
15
+
16
+ <template>
17
+ <slot />
18
+ </template>
@@ -0,0 +1,41 @@
1
+ import { Color, Vector3 } from 'three'
2
+ import { uniform } from 'three/tsl'
3
+
4
+ /**
5
+ * Creates a TSL vec3 uniform that tracks a CSS custom property (colour).
6
+ * Syncs on mount and whenever :root's class or style changes (theme switches).
7
+ *
8
+ * @example
9
+ * const primaryNode = useCSSColourUniform('--ui-color-primary-500')
10
+ * // Use primaryNode directly inside useShaderStage closures.
11
+ */
12
+ export function useCSSColourUniform(varName: string) {
13
+ // @ts-ignore — TSL uniform types
14
+ const node = uniform(new Vector3(0, 0, 0))
15
+ const c = new Color()
16
+
17
+ function sync() {
18
+ const raw = getComputedStyle(document.documentElement)
19
+ .getPropertyValue(varName)
20
+ .trim()
21
+ if (!raw) return
22
+ try {
23
+ c.set(raw)
24
+ // @ts-ignore
25
+ node.value.set(c.r, c.g, c.b)
26
+ }
27
+ catch { /* unsupported format — leave previous value */ }
28
+ }
29
+
30
+ onMounted(() => {
31
+ sync()
32
+ const observer = new MutationObserver(sync)
33
+ observer.observe(document.documentElement, {
34
+ attributes: true,
35
+ attributeFilter: ['style', 'class'],
36
+ })
37
+ onUnmounted(() => observer.disconnect())
38
+ })
39
+
40
+ return node
41
+ }
@@ -0,0 +1,34 @@
1
+ import { uniform } from 'three/tsl'
2
+
3
+ /**
4
+ * Creates a TSL float uniform that tracks a numeric CSS custom property.
5
+ * Syncs on mount and whenever :root's class or style changes (theme switches).
6
+ *
7
+ * @example
8
+ * const spacingNode = useCSSFloatUniform('--spacing-scale')
9
+ */
10
+ export function useCSSFloatUniform(varName: string) {
11
+ // @ts-ignore — TSL uniform types
12
+ const node = uniform(0)
13
+
14
+ function sync() {
15
+ const raw = getComputedStyle(document.documentElement)
16
+ .getPropertyValue(varName)
17
+ .trim()
18
+ const val = parseFloat(raw)
19
+ // @ts-ignore
20
+ if (!isNaN(val)) node.value = val
21
+ }
22
+
23
+ onMounted(() => {
24
+ sync()
25
+ const observer = new MutationObserver(sync)
26
+ observer.observe(document.documentElement, {
27
+ attributes: true,
28
+ attributeFilter: ['style', 'class'],
29
+ })
30
+ onUnmounted(() => observer.disconnect())
31
+ })
32
+
33
+ return node
34
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * useShaderPerf — lightweight performance monitor for shader pipelines.
3
+ *
4
+ * Tracks frame time, frame rate, and stage count for a named pipeline.
5
+ * Designed to be used in development to identify bottlenecks.
6
+ *
7
+ * Usage:
8
+ * const perf = useShaderPerf('myScene')
9
+ * // call perf.tick() each frame (e.g. from useRenderLoop or requestAnimationFrame)
10
+ * // read perf.fps, perf.frameMs, perf.stageCount
11
+ */
12
+ export function useShaderPerf(label = 'pipeline') {
13
+ const fps = ref(0)
14
+ const frameMs = ref(0)
15
+ const stageCount = ref(0)
16
+
17
+ let lastTime = performance.now()
18
+ let frames = 0
19
+ let accumMs = 0
20
+ let rafId: number | null = null
21
+ let running = false
22
+
23
+ const pipeline = useShaderPipelineContext()
24
+
25
+ function tick() {
26
+ if (!running) return
27
+ const now = performance.now()
28
+ const delta = now - lastTime
29
+ lastTime = now
30
+
31
+ frames++
32
+ accumMs += delta
33
+
34
+ if (accumMs >= 500) {
35
+ fps.value = Math.round((frames / accumMs) * 1000)
36
+ frameMs.value = Math.round(accumMs / frames * 10) / 10
37
+ frames = 0
38
+ accumMs = 0
39
+ }
40
+
41
+ stageCount.value = pipeline
42
+ ? (pipeline.stagesFor('uv').length + pipeline.stagesFor('fragment').length)
43
+ : 0
44
+ rafId = requestAnimationFrame(tick)
45
+ }
46
+
47
+ function start() {
48
+ if (running) return
49
+ running = true
50
+ lastTime = performance.now()
51
+ rafId = requestAnimationFrame(tick)
52
+ }
53
+
54
+ function stop() {
55
+ running = false
56
+ if (rafId !== null) {
57
+ cancelAnimationFrame(rafId)
58
+ rafId = null
59
+ }
60
+ }
61
+
62
+ onMounted(start)
63
+ onUnmounted(stop)
64
+
65
+ if (import.meta.dev) {
66
+ watch([fps, frameMs, stageCount], ([f, ms, s]) => {
67
+ // eslint-disable-next-line no-console
68
+ console.debug(`[ShaderPerf:${label}] ${f}fps ${ms}ms/frame ${s} stages`)
69
+ })
70
+ }
71
+
72
+ return { fps, frameMs, stageCount, start, stop }
73
+ }
@@ -0,0 +1,99 @@
1
+ import type { InjectionKey, Ref, ShallowRef } from 'vue'
2
+ import type { TSLNode } from '../types'
3
+
4
+ export type ShaderStage = 'fragment' | 'vertex' | 'ray' | 'uv'
5
+
6
+ export interface ShaderStageEntry {
7
+ fn: (input: TSLNode) => TSLNode
8
+ order: number
9
+ stage: ShaderStage
10
+ }
11
+
12
+ export interface ShaderPipelineContext {
13
+ register: (fn: ShaderStageEntry['fn'], order?: number, stage?: ShaderStage) => void
14
+ unregister: (fn: ShaderStageEntry['fn']) => void
15
+ stagesFor: (stage: ShaderStage) => ShaderStageEntry[]
16
+ /** Increments on topology changes (add/remove) to trigger material recompilation */
17
+ version: Ref<number>
18
+ /** Current UV node — starts as uv(), updated by UV transformer blocks before generators run.
19
+ * Generators read this via useShaderPipelineContext().uvNode to get the transformed UV. */
20
+ uvNode: ShallowRef<TSLNode | null>
21
+ /** Built ray direction node — populated by ShaderPipeline after ray transformers reduce.
22
+ * Terminal generators (SkyAtmosphere, RaymarchTunnel, etc.) read this in Phase 4. */
23
+ rayNode: ShallowRef<TSLNode | null>
24
+ }
25
+
26
+ export const SHADER_PIPELINE_KEY: InjectionKey<ShaderPipelineContext> = Symbol('shader-pipeline')
27
+
28
+ /**
29
+ * Creates a shader pipeline context and provides it to child components.
30
+ * Call this inside <ShaderPipeline> — it provides the context that useShaderStage() injects.
31
+ */
32
+ export function useShaderPipeline(): ShaderPipelineContext {
33
+ const entries: ShaderStageEntry[] = []
34
+ const version = ref(0)
35
+ const uvNode = shallowRef<TSLNode | null>(null)
36
+ const rayNode = shallowRef<TSLNode | null>(null)
37
+
38
+ function register(fn: ShaderStageEntry['fn'], order = 0, stage: ShaderStage = 'fragment') {
39
+ entries.push({ fn, order, stage })
40
+ version.value++
41
+ }
42
+
43
+ function unregister(fn: ShaderStageEntry['fn']) {
44
+ const i = entries.findIndex(e => e.fn === fn)
45
+ if (i !== -1) {
46
+ entries.splice(i, 1)
47
+ version.value++
48
+ }
49
+ }
50
+
51
+ function stagesFor(stage: ShaderStage): ShaderStageEntry[] {
52
+ return entries.filter(e => e.stage === stage).sort((a, b) => a.order - b.order)
53
+ }
54
+
55
+ const context: ShaderPipelineContext = { register, unregister, stagesFor, version, uvNode, rayNode }
56
+ provide(SHADER_PIPELINE_KEY, context)
57
+ return context
58
+ }
59
+
60
+ /**
61
+ * Registers a stage function into the nearest parent ShaderPipeline.
62
+ * The function receives the current pipeline node and returns the transformed node.
63
+ *
64
+ * @param stageFn - Transform function: (input: TSLNode) => TSLNode
65
+ * @param order - Execution order within the stage (lower runs first)
66
+ * @param stage - 'fragment' (default), 'vertex', or 'ray'
67
+ *
68
+ * @example
69
+ * // Inside a block component's <script setup>:
70
+ * useShaderStage((_prev) => vec4(colorNode, 1.0), props.order ?? 0)
71
+ *
72
+ * // Vertex deformation:
73
+ * useShaderStage((pos) => pos.add(normalNode.mul(offset)), 0, 'vertex')
74
+ */
75
+ export function useShaderStage(
76
+ stageFn: ShaderStageEntry['fn'],
77
+ order = 0,
78
+ stage: ShaderStage = 'fragment',
79
+ ): void {
80
+ const pipeline = inject(SHADER_PIPELINE_KEY)
81
+ if (!pipeline) {
82
+ throw new Error('useShaderStage() must be called inside a <ShaderPipeline> component')
83
+ }
84
+ onMounted(() => pipeline.register(stageFn, order, stage))
85
+ onUnmounted(() => pipeline.unregister(stageFn))
86
+ }
87
+
88
+ /**
89
+ * Injects the pipeline context from a parent ShaderPipeline.
90
+ * Use this in advanced block components that need direct context access
91
+ * (e.g. terminal ray generators that read pipeline.rayNode).
92
+ */
93
+ export function useShaderPipelineContext(): ShaderPipelineContext {
94
+ const pipeline = inject(SHADER_PIPELINE_KEY)
95
+ if (!pipeline) {
96
+ throw new Error('useShaderPipelineContext() must be called inside a <ShaderPipeline> component')
97
+ }
98
+ return pipeline
99
+ }
@@ -0,0 +1,54 @@
1
+ import { Vector2 } from 'three'
2
+ import { float, time, uniform, vec3 } from 'three/tsl'
3
+
4
+ export interface SunDirectionOptions {
5
+ /** Animation speed (radians/second). Only used when useMouseY is false. */
6
+ speed?: number
7
+ /** Drive elevation from mouse Y instead of time. */
8
+ useMouseY?: boolean
9
+ }
10
+
11
+ /**
12
+ * Returns a live TSL vec3 node representing a normalised sun direction.
13
+ *
14
+ * Animated mode (default): elevation oscillates via sin(time * speed).
15
+ * Mouse mode: elevation tracks mouse Y position.
16
+ *
17
+ * The guide formula: rotX(vec3(0,0,1), angle * 5 + 0.5)
18
+ * → vec3(0, -sin(a), cos(a)) where a = angle * 5 + 0.5
19
+ *
20
+ * @example
21
+ * const sunDir = useSunDirectionUniform({ speed: 0.05 })
22
+ * // Pass sunDir directly to SkyAtmosphere or any shader that needs sun position.
23
+ */
24
+ export function useSunDirectionUniform(options: SunDirectionOptions = {}) {
25
+ const { speed = 0.05, useMouseY = false } = options
26
+
27
+ if (useMouseY) {
28
+ // @ts-ignore
29
+ const mouse = uniform(new Vector2(0.5, 0.5))
30
+
31
+ onMounted(() => {
32
+ const onMove = (e: MouseEvent) => {
33
+ // @ts-ignore
34
+ mouse.value.set(e.clientX / window.innerWidth, e.clientY / window.innerHeight)
35
+ }
36
+ window.addEventListener('mousemove', onMove)
37
+ onUnmounted(() => window.removeEventListener('mousemove', onMove))
38
+ })
39
+
40
+ // angle = -(mouseY - 0.5) — top of screen = sun high, bottom = sun low
41
+ const angle = mouse.y.oneMinus().sub(0.5)
42
+ const a = angle.mul(5).add(0.5)
43
+ // @ts-ignore
44
+ return vec3(float(0), a.sin().negate(), a.cos())
45
+ }
46
+
47
+ // Animated mode — pure TSL expression, no JS uniform needed
48
+ // @ts-ignore
49
+ const speedNode = uniform(speed)
50
+ const angle = time.mul(speedNode).sin().div(8).sub(0.2)
51
+ const a = angle.mul(5).add(0.5)
52
+ // @ts-ignore
53
+ return vec3(float(0), a.sin().negate(), a.cos())
54
+ }
@@ -64,7 +64,6 @@ export function useTSLNodes() {
64
64
  asin: TSL.asin,
65
65
  acos: TSL.acos,
66
66
  atan: TSL.atan,
67
- atan2: TSL.atan2,
68
67
 
69
68
  // Vector operations
70
69
  dot: TSL.dot,
@@ -0,0 +1,29 @@
1
+ // @ts-nocheck - TSL types are complex and not fully exported from three/tsl
2
+ /**
3
+ * Complex number arithmetic in TSL
4
+ * Both functions operate on vec2 (x = real, y = imaginary).
5
+ */
6
+ import { vec2, dot } from 'three/tsl'
7
+ import type { TSLNode } from '../types'
8
+
9
+ /**
10
+ * Complex division: a / b
11
+ * (a.x*b.x + a.y*b.y) / |b|² , (a.y*b.x - a.x*b.y) / |b|²
12
+ * Used in imaginary2 for the Möbius transformation (z-p)/(z-q).
13
+ */
14
+ export function complexDiv(a: TSLNode, b: TSLNode): TSLNode {
15
+ const denom = dot(b, b)
16
+ return vec2(
17
+ a.x.mul(b.x).add(a.y.mul(b.y)).div(denom),
18
+ a.y.mul(b.x).sub(a.x.mul(b.y)).div(denom),
19
+ )
20
+ }
21
+
22
+ /**
23
+ * Complex natural logarithm: ln(z)
24
+ * Produces (ln|z|, arg(z)) — magnitude and winding angle.
25
+ * Used in imaginary2 to extract the winding angle between two poles.
26
+ */
27
+ export function complexLog(z: TSLNode): TSLNode {
28
+ return vec2(z.length().log(), z.y.atan(z.x))
29
+ }
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import {
7
7
  abs,
8
- atan2,
8
+ atan,
9
9
  cos,
10
10
  float,
11
11
  floor,
@@ -281,7 +281,7 @@ export function star(
281
281
  const outer = typeof outerRadius === 'number' ? float(outerRadius) : outerRadius
282
282
 
283
283
  const p = uv.sub(c)
284
- const angle = atan2(p.y, p.x)
284
+ const angle = atan(p.y, p.x)
285
285
  const dist = length(p)
286
286
 
287
287
  const segmentAngle = float((Math.PI * 2) / points)
@@ -310,7 +310,7 @@ export function radialLines(
310
310
  const t = typeof thickness === 'number' ? float(thickness) : thickness
311
311
 
312
312
  const p = uv.sub(c)
313
- const angle = atan2(p.y, p.x).add(Math.PI)
313
+ const angle = atan(p.y, p.x).add(Math.PI)
314
314
  const segment = float((Math.PI * 2) / numLines)
315
315
 
316
316
  const foldedAngle = fract(angle.div(segment))
@@ -353,7 +353,7 @@ export function polygon(
353
353
  const rot = typeof rotation === 'number' ? float(rotation) : rotation
354
354
 
355
355
  const p = uv.sub(c)
356
- const angle = atan2(p.y, p.x).add(rot)
356
+ const angle = atan(p.y, p.x).add(rot)
357
357
  const dist = length(p)
358
358
 
359
359
  const segmentAngle = float((Math.PI * 2) / sides)
@@ -3,7 +3,7 @@
3
3
  * Modular TSL UV Manipulation Utilities
4
4
  * Provides composable UV transformation functions
5
5
  */
6
- import { abs, atan2, cos, float, floor, fract, length, pow, sin, smoothstep, vec2 } from 'three/tsl'
6
+ import { abs, atan, cos, float, floor, fract, length, pow, sin, smoothstep, vec2 } from 'three/tsl'
7
7
  import type { TSLNode } from '../types'
8
8
 
9
9
  // ============================================
@@ -96,7 +96,7 @@ export function toPolar(uv: TSLNode, center: TSLNode | [number, number] = [0.5,
96
96
  const centered = uv.sub(c)
97
97
 
98
98
  const radius = length(centered)
99
- const angle = atan2(centered.y, centered.x)
99
+ const angle = atan(centered.y, centered.x)
100
100
  .add(Math.PI)
101
101
  .div(Math.PI * 2)
102
102
 
@@ -126,7 +126,7 @@ export const radialLines = Fn(
126
126
  ([uv, center, count, width]: [TSLNode, TSLNode, TSLNode, TSLNode]) => {
127
127
  const centered = uv.sub(center)
128
128
  const angle = centered.y
129
- .atan2(centered.x)
129
+ .atan(centered.x)
130
130
  .div(Math.PI * 2)
131
131
  .add(0.5)
132
132
  const segment = fract(angle.mul(count))
@@ -1,4 +1,4 @@
1
- import { atan2, cos, float, Fn, fract, length, sin, vec2 } from 'three/tsl'
1
+ import { atan, cos, float, Fn, fract, length, sin, vec2 } from 'three/tsl'
2
2
  import type { TSLNode } from '../../types'
3
3
 
4
4
  /**
@@ -34,7 +34,7 @@ export const distortUV = Fn(([uv, noiseValue, strength]: [TSLNode, TSLNode, TSLN
34
34
  */
35
35
  export const polarUV = Fn(([uv, center]: [TSLNode, TSLNode]) => {
36
36
  const centered = uv.sub(center)
37
- const angle = atan2(centered.y, centered.x)
37
+ const angle = atan(centered.y, centered.x)
38
38
  .div(Math.PI * 2)
39
39
  .add(0.5)
40
40
  const radius = length(centered)
@@ -94,7 +94,7 @@ export const mirrorUV = Fn(([uv]: [TSLNode]) => {
94
94
  */
95
95
  export const kaleidoscopeUV = Fn(([uv, center, segments]: [TSLNode, TSLNode, TSLNode]) => {
96
96
  const centered = uv.sub(center)
97
- const angle = atan2(centered.y, centered.x)
97
+ const angle = atan(centered.y, centered.x)
98
98
  const radius = length(centered)
99
99
 
100
100
  const segmentAngle = float(Math.PI * 2).div(segments)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kmcom-nuxt-layers",
3
3
  "private": false,
4
- "version": "1.6.4",
4
+ "version": "1.6.6",
5
5
  "description": "Composable Nuxt 4 layers for building scalable Vue applications",
6
6
  "files": [
7
7
  "layers/*/nuxt.config.ts",
@@ -38,7 +38,7 @@
38
38
  "nuxt": "^4.4.2",
39
39
  "nuxt-studio": "^1.4.0",
40
40
  "pinia": "^3.0.4",
41
- "tailwindcss": "^4.2.2",
41
+ "tailwindcss": "^4.2.4",
42
42
  "three": "^0.183.2",
43
43
  "v-gsap-nuxt": ">=1.0.0",
44
44
  "zod": "^4.3.6"
@@ -98,6 +98,7 @@
98
98
  "@eslint/markdown": "^7.5.1",
99
99
  "@ianvs/prettier-plugin-sort-imports": "^4.7.1",
100
100
  "@iconify-json/lucide": "^1.2.102",
101
+ "@netlify/nuxt": "0.2.41",
101
102
  "@nuxt/eslint": "^1.15.2",
102
103
  "@nuxt/fonts": "^0.14.0",
103
104
  "@nuxt/image": "^2.0.0",
@@ -124,7 +125,7 @@
124
125
  "eslint-plugin-unicorn": "^64.0.0",
125
126
  "eslint-plugin-unused-imports": "^4.4.1",
126
127
  "eslint-plugin-vue": "^10.8.0",
127
- "npm-check-updates": "^19.6.3",
128
+ "npm-check-updates": "^21.0.3",
128
129
  "nuxt": "latest",
129
130
  "pinia": "^3.0.4",
130
131
  "playwright": "^1.58.2",
@@ -142,11 +143,11 @@
142
143
  "stylelint-config-tailwindcss": "^1.0.1",
143
144
  "stylelint-no-unsupported-browser-features": "^8.1.1",
144
145
  "stylelint-prettier": "^5.0.3",
145
- "tailwindcss": "^4.2.2",
146
- "turbo": "^2.8.13",
146
+ "tailwindcss": "^4.2.4",
147
+ "turbo": "^2.9.6",
147
148
  "typescript": "^6.0.2",
148
- "vite-plugin-checker": "^0.12.0",
149
- "vitest": "^4.0.18",
149
+ "vite-plugin-checker": "^0.13.0",
150
+ "vitest": "^4.1.5",
150
151
  "vue": "latest",
151
152
  "vue-tsc": "^3.2.6",
152
153
  "zod": "^4.3.6",
@@ -159,6 +160,7 @@
159
160
  "last 2 Edge major versions"
160
161
  ],
161
162
  "dependencies": {
163
+ "node-gyp": "^12.3.0",
162
164
  "skills": "^1.5.0"
163
165
  },
164
166
  "engines": {