@mumulinya167/ccm-pet-assets 2.0.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 (178) hide show
  1. package/README.md +4 -0
  2. package/assets/calico/calico-collapsing.apng +0 -0
  3. package/assets/calico/calico-dozing.apng +0 -0
  4. package/assets/calico/calico-error.apng +0 -0
  5. package/assets/calico/calico-happy.apng +0 -0
  6. package/assets/calico/calico-idle-follow.svg +324 -0
  7. package/assets/calico/calico-idle.apng +0 -0
  8. package/assets/calico/calico-mini-alert.apng +0 -0
  9. package/assets/calico/calico-mini-crabwalk.apng +0 -0
  10. package/assets/calico/calico-mini-enter.apng +0 -0
  11. package/assets/calico/calico-mini-happy.apng +0 -0
  12. package/assets/calico/calico-mini-idle.apng +0 -0
  13. package/assets/calico/calico-mini-peek.apng +0 -0
  14. package/assets/calico/calico-mini-sleep.apng +0 -0
  15. package/assets/calico/calico-notification.apng +0 -0
  16. package/assets/calico/calico-react-drag.apng +0 -0
  17. package/assets/calico/calico-react-left.apng +0 -0
  18. package/assets/calico/calico-react-poke.apng +0 -0
  19. package/assets/calico/calico-sleeping.apng +0 -0
  20. package/assets/calico/calico-thinking.apng +0 -0
  21. package/assets/calico/calico-waking.apng +0 -0
  22. package/assets/calico/calico-working-building.apng +0 -0
  23. package/assets/calico/calico-working-carrying.apng +0 -0
  24. package/assets/calico/calico-working-conducting.apng +0 -0
  25. package/assets/calico/calico-working-juggling.apng +0 -0
  26. package/assets/calico/calico-working-sweeping.apng +0 -0
  27. package/assets/calico/calico-working-typing.apng +0 -0
  28. package/assets/calico/calico-yawning.apng +0 -0
  29. package/assets/calico.svg +324 -0
  30. package/assets/clawd/LICENSE +22 -0
  31. package/assets/clawd/clawd-about-hero.svg +202 -0
  32. package/assets/clawd/clawd-collapse-sleep.svg +247 -0
  33. package/assets/clawd/clawd-error.svg +94 -0
  34. package/assets/clawd/clawd-happy.svg +161 -0
  35. package/assets/clawd/clawd-headphones-groove.svg +194 -0
  36. package/assets/clawd/clawd-idle-bubble.svg +180 -0
  37. package/assets/clawd/clawd-idle-collapse.svg +101 -0
  38. package/assets/clawd/clawd-idle-doze.svg +72 -0
  39. package/assets/clawd/clawd-idle-follow.svg +64 -0
  40. package/assets/clawd/clawd-idle-living.svg +196 -0
  41. package/assets/clawd/clawd-idle-look.svg +115 -0
  42. package/assets/clawd/clawd-idle-reading.svg +252 -0
  43. package/assets/clawd/clawd-idle-yawn.svg +158 -0
  44. package/assets/clawd/clawd-mini-alert.svg +129 -0
  45. package/assets/clawd/clawd-mini-crabwalk.svg +76 -0
  46. package/assets/clawd/clawd-mini-enter-sleep.svg +64 -0
  47. package/assets/clawd/clawd-mini-enter.svg +114 -0
  48. package/assets/clawd/clawd-mini-happy.svg +124 -0
  49. package/assets/clawd/clawd-mini-idle.svg +83 -0
  50. package/assets/clawd/clawd-mini-peek.svg +82 -0
  51. package/assets/clawd/clawd-mini-sleep.svg +112 -0
  52. package/assets/clawd/clawd-mini-typing.svg +153 -0
  53. package/assets/clawd/clawd-notification.svg +438 -0
  54. package/assets/clawd/clawd-react-annoyed.svg +167 -0
  55. package/assets/clawd/clawd-react-double-jump.svg +229 -0
  56. package/assets/clawd/clawd-react-double.svg +108 -0
  57. package/assets/clawd/clawd-react-drag.svg +102 -0
  58. package/assets/clawd/clawd-react-left.svg +102 -0
  59. package/assets/clawd/clawd-react-right.svg +102 -0
  60. package/assets/clawd/clawd-sleeping.svg +118 -0
  61. package/assets/clawd/clawd-static-base.svg +21 -0
  62. package/assets/clawd/clawd-wake.svg +277 -0
  63. package/assets/clawd/clawd-working-building.svg +329 -0
  64. package/assets/clawd/clawd-working-carrying.svg +178 -0
  65. package/assets/clawd/clawd-working-debugger.svg +245 -0
  66. package/assets/clawd/clawd-working-juggling.svg +183 -0
  67. package/assets/clawd/clawd-working-sweeping.svg +248 -0
  68. package/assets/clawd/clawd-working-thinking.svg +501 -0
  69. package/assets/clawd/clawd-working-typing-boss.svg +307 -0
  70. package/assets/clawd/clawd-working-typing.svg +273 -0
  71. package/assets/clawd/clawd-working-ultrathink.svg +166 -0
  72. package/assets/clawd/clawd-working-wizard.svg +356 -0
  73. package/assets/clawd.svg +64 -0
  74. package/assets/cloudling/cloudling/cloudling-attention.svg +380 -0
  75. package/assets/cloudling/cloudling/cloudling-building.svg +667 -0
  76. package/assets/cloudling/cloudling/cloudling-carrying.svg +604 -0
  77. package/assets/cloudling/cloudling/cloudling-conducting.svg +616 -0
  78. package/assets/cloudling/cloudling/cloudling-dozing-to-sleeping.svg +578 -0
  79. package/assets/cloudling/cloudling/cloudling-dozing.svg +358 -0
  80. package/assets/cloudling/cloudling/cloudling-error.svg +428 -0
  81. package/assets/cloudling/cloudling/cloudling-idle-reading.svg +658 -0
  82. package/assets/cloudling/cloudling/cloudling-idle-to-dozing.svg +568 -0
  83. package/assets/cloudling/cloudling/cloudling-idle-to-sleeping.svg +811 -0
  84. package/assets/cloudling/cloudling/cloudling-idle.svg +255 -0
  85. package/assets/cloudling/cloudling/cloudling-juggling.svg +648 -0
  86. package/assets/cloudling/cloudling/cloudling-mini-alert.svg +390 -0
  87. package/assets/cloudling/cloudling/cloudling-mini-crabwalk.svg +335 -0
  88. package/assets/cloudling/cloudling/cloudling-mini-enter-roll-in.svg +226 -0
  89. package/assets/cloudling/cloudling/cloudling-mini-enter-sleep.svg +232 -0
  90. package/assets/cloudling/cloudling/cloudling-mini-happy.svg +371 -0
  91. package/assets/cloudling/cloudling/cloudling-mini-idle.svg +279 -0
  92. package/assets/cloudling/cloudling/cloudling-mini-peek.svg +328 -0
  93. package/assets/cloudling/cloudling/cloudling-mini-sleep.svg +260 -0
  94. package/assets/cloudling/cloudling/cloudling-mini-typing.svg +692 -0
  95. package/assets/cloudling/cloudling/cloudling-notification.svg +494 -0
  96. package/assets/cloudling/cloudling/cloudling-react-drag.svg +83 -0
  97. package/assets/cloudling/cloudling/cloudling-sleeping-to-idle.svg +555 -0
  98. package/assets/cloudling/cloudling/cloudling-sleeping.svg +500 -0
  99. package/assets/cloudling/cloudling/cloudling-sweeping.svg +292 -0
  100. package/assets/cloudling/cloudling/cloudling-thinking.svg +720 -0
  101. package/assets/cloudling/cloudling/cloudling-typing.svg +354 -0
  102. package/assets/cloudling/cloudling-attention.svg +380 -0
  103. package/assets/cloudling/cloudling-building.svg +667 -0
  104. package/assets/cloudling/cloudling-carrying.svg +604 -0
  105. package/assets/cloudling/cloudling-conducting.svg +616 -0
  106. package/assets/cloudling/cloudling-dozing-to-sleeping.svg +578 -0
  107. package/assets/cloudling/cloudling-dozing.svg +358 -0
  108. package/assets/cloudling/cloudling-error.svg +428 -0
  109. package/assets/cloudling/cloudling-idle-reading.svg +658 -0
  110. package/assets/cloudling/cloudling-idle-to-dozing.svg +568 -0
  111. package/assets/cloudling/cloudling-idle-to-sleeping.svg +811 -0
  112. package/assets/cloudling/cloudling-idle.svg +255 -0
  113. package/assets/cloudling/cloudling-juggling.svg +648 -0
  114. package/assets/cloudling/cloudling-mini-alert.svg +390 -0
  115. package/assets/cloudling/cloudling-mini-crabwalk.svg +335 -0
  116. package/assets/cloudling/cloudling-mini-enter-roll-in.svg +226 -0
  117. package/assets/cloudling/cloudling-mini-enter-sleep.svg +232 -0
  118. package/assets/cloudling/cloudling-mini-happy.svg +371 -0
  119. package/assets/cloudling/cloudling-mini-idle.svg +279 -0
  120. package/assets/cloudling/cloudling-mini-peek.svg +328 -0
  121. package/assets/cloudling/cloudling-mini-sleep.svg +260 -0
  122. package/assets/cloudling/cloudling-mini-typing.svg +692 -0
  123. package/assets/cloudling/cloudling-notification.svg +494 -0
  124. package/assets/cloudling/cloudling-react-drag.svg +83 -0
  125. package/assets/cloudling/cloudling-sleeping-to-idle.svg +555 -0
  126. package/assets/cloudling/cloudling-sleeping.svg +500 -0
  127. package/assets/cloudling/cloudling-sweeping.svg +292 -0
  128. package/assets/cloudling/cloudling-thinking.svg +720 -0
  129. package/assets/cloudling/cloudling-typing.svg +354 -0
  130. package/assets/cloudling.svg +255 -0
  131. package/assets/ghost-attention.svg +19 -0
  132. package/assets/ghost-carrying.svg +19 -0
  133. package/assets/ghost-collapsing.svg +19 -0
  134. package/assets/ghost-dozing.svg +23 -0
  135. package/assets/ghost-error.svg +19 -0
  136. package/assets/ghost-happy.svg +23 -0
  137. package/assets/ghost-idle-action1.svg +19 -0
  138. package/assets/ghost-idle-action2.svg +19 -0
  139. package/assets/ghost-idle-action3.svg +19 -0
  140. package/assets/ghost-idle.svg +11 -0
  141. package/assets/ghost-juggling.svg +19 -0
  142. package/assets/ghost-notification.svg +19 -0
  143. package/assets/ghost-react-double.svg +23 -0
  144. package/assets/ghost-react-drag.svg +23 -0
  145. package/assets/ghost-react-left.svg +19 -0
  146. package/assets/ghost-react-right.svg +19 -0
  147. package/assets/ghost-sleeping.svg +19 -0
  148. package/assets/ghost-sweeping.svg +19 -0
  149. package/assets/ghost-thinking.svg +19 -0
  150. package/assets/ghost-waking.svg +19 -0
  151. package/assets/ghost-working.svg +19 -0
  152. package/assets/ghost-yawning.svg +23 -0
  153. package/assets/ghost.svg +11 -0
  154. package/assets/robot-attention.svg +19 -0
  155. package/assets/robot-carrying.svg +19 -0
  156. package/assets/robot-collapsing.svg +19 -0
  157. package/assets/robot-dozing.svg +23 -0
  158. package/assets/robot-error.svg +19 -0
  159. package/assets/robot-happy.svg +23 -0
  160. package/assets/robot-idle-action1.svg +19 -0
  161. package/assets/robot-idle-action2.svg +19 -0
  162. package/assets/robot-idle-action3.svg +19 -0
  163. package/assets/robot-idle.svg +11 -0
  164. package/assets/robot-juggling.svg +19 -0
  165. package/assets/robot-notification.svg +19 -0
  166. package/assets/robot-react-double.svg +23 -0
  167. package/assets/robot-react-drag.svg +23 -0
  168. package/assets/robot-react-left.svg +19 -0
  169. package/assets/robot-react-right.svg +19 -0
  170. package/assets/robot-sleeping.svg +19 -0
  171. package/assets/robot-sweeping.svg +19 -0
  172. package/assets/robot-thinking.svg +19 -0
  173. package/assets/robot-waking.svg +19 -0
  174. package/assets/robot-working.svg +19 -0
  175. package/assets/robot-yawning.svg +23 -0
  176. package/assets/robot.svg +11 -0
  177. package/manifest.json +889 -0
  178. package/package.json +18 -0
@@ -0,0 +1,358 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg id="stage" xmlns="http://www.w3.org/2000/svg" viewBox="-6 -6 36 36" preserveAspectRatio="xMidYMid meet">
3
+ <g id="__cloudling-export-shims" opacity="0" pointer-events="none" style="display:none">
4
+ <g id="control-grid"></g>
5
+ <g id="readout"></g>
6
+ </g>
7
+ <script type="application/ecmascript"><![CDATA[
8
+ const CLOUD_D = "M9.064 3.344a4.578 4.578 0 012.285-.312c1 .115 1.891.54 2.673 1.275.01.01.024.017.037.021a.09.09 0 00.043 0 4.55 4.55 0 013.046.275l.047.022.116.057a4.581 4.581 0 012.188 2.399c.209.51.313 1.041.315 1.595a4.24 4.24 0 01-.134 1.223.123.123 0 00.03.115c.594.607.988 1.33 1.183 2.17.289 1.425-.007 2.71-.887 3.854l-.136.166a4.548 4.548 0 01-2.201 1.388.123.123 0 00-.081.076c-.191.551-.383 1.023-.740 1.494-.9 1.187-2.222 1.846-3.711 1.838-1.187-.006-2.239-.44-3.157-1.302a.107.107 0 00-.105-.024c-.388.125-.78.143-1.204.138a4.441 4.441 0 01-1.945-.466 4.544 4.544 0 01-1.61-1.335c-.152-.202-.303-.392-.414-.617a5.81 5.81 0 01-.37-.961 4.582 4.582 0 01-.014-2.298.124.124 0 00.006-.056.085.085 0 00-.027-.048 4.467 4.467 0 01-1.034-1.651 3.896 3.896 0 01-.251-1.192 5.189 5.189 0 01.141-1.6c.337-1.112.982-1.985 1.933-2.618.212-.141.413-.251.601-.33.215-.089.43-.164.646-.227a.098.098 0 00.065-.066 4.51 4.51 0 01.829-1.615 4.535 4.535 0 011.837-1.388z";
9
+
10
+ const SOFT_CLOUD_POINTS = [
11
+ {x:9.064,y:3.344},{x:9.825,y:3.106},{x:10.616,y:3.004},{x:11.413,y:3.040},
12
+ {x:12.192,y:3.210},{x:12.928,y:3.518},{x:13.599,y:3.948},{x:14.255,y:4.291},
13
+ {x:15.046,y:4.188},{x:15.843,y:4.224},{x:16.621,y:4.398},{x:17.357,y:4.706},
14
+ {x:18.029,y:5.134},{x:18.617,y:5.673},{x:19.103,y:6.306},{x:19.471,y:7.013},
15
+ {x:19.715,y:7.772},{x:19.812,y:8.564},{x:19.784,y:9.361},{x:19.797,y:10.105},
16
+ {x:20.287,y:10.735},{x:20.656,y:11.442},{x:20.897,y:12.202},{x:20.995,y:12.994},
17
+ {x:20.964,y:13.791},{x:20.791,y:14.569},{x:20.480,y:15.304},{x:20.052,y:15.977},
18
+ {x:19.516,y:16.567},{x:18.882,y:17.052},{x:18.174,y:17.418},{x:17.539,y:17.808},
19
+ {x:17.231,y:18.545},{x:16.805,y:19.218},{x:16.264,y:19.805},{x:15.633,y:20.292},
20
+ {x:14.925,y:20.660},{x:14.163,y:20.895},{x:13.372,y:20.996},{x:12.575,y:20.964},
21
+ {x:11.796,y:20.793},{x:11.061,y:20.483},{x:10.392,y:20.049},{x:9.736,y:19.714},
22
+ {x:8.946,y:19.811},{x:8.148,y:19.777},{x:7.370,y:19.606},{x:6.634,y:19.299},
23
+ {x:5.962,y:18.868},{x:5.377,y:18.326},{x:4.885,y:17.698},{x:4.522,y:16.989},
24
+ {x:4.283,y:16.228},{x:4.182,y:15.437},{x:4.220,y:14.640},{x:4.179,y:13.907},
25
+ {x:3.696,y:13.273},{x:3.332,y:12.563},{x:3.092,y:11.803},{x:3.003,y:11.010},
26
+ {x:3.038,y:10.213},{x:3.202,y:9.433},{x:3.514,y:8.699},{x:3.944,y:8.027},
27
+ {x:4.484,y:7.440},{x:5.114,y:6.951},{x:5.822,y:6.586},{x:6.445,y:6.191},
28
+ {x:6.752,y:5.455},{x:7.185,y:4.785},{x:7.723,y:4.197},{x:8.356,y:3.711},
29
+ ];
30
+
31
+ const BASE = {
32
+ slump: 0.55,
33
+ topSag: 1.42,
34
+ shoulderSag: 0.34,
35
+ spread: 0.82,
36
+ floorY: 19.92,
37
+ floorFlatten: 0.56,
38
+ bottomSquish: 0.82,
39
+ leftLean: -0.18,
40
+ bodyTx: 0,
41
+ bodyTy: 1.15,
42
+ bodyRot: -1.5,
43
+ bodyScale: 1.04,
44
+ eyeY: 13.0,
45
+ eyeGap: 6.12,
46
+ eyeW: 2.70,
47
+ eyeH: 1.28,
48
+ eyeTilt: 7.7,
49
+ eyeStroke: 0.80,
50
+ eyeOpacity: 0.92,
51
+ haloY: 20.62,
52
+ haloRx: 10.8,
53
+ haloRy: 1.28,
54
+ haloOpacity: 0.34,
55
+ glowOpacity: 0.18,
56
+ };
57
+
58
+ const LOOP = {
59
+ period: 5.8,
60
+ breathAmp: 0.038,
61
+ pathBreath: 0.055,
62
+ nodCenter: 0.67,
63
+ nodWidth: 0.17,
64
+ nodDepth: 0.38,
65
+ nodRot: -0.85,
66
+ };
67
+
68
+ const CONTROL_CONFIG = [
69
+ { key: 'period', label: '周期', min: 3.8, max: 10, step: 0.1, unit: 's' },
70
+ { key: 'breathAmp', label: '呼吸', min: 0, max: 0.08, step: 0.001 },
71
+ { key: 'pathBreath', label: '轮廓起伏', min: 0, max: 0.12, step: 0.001 },
72
+ { key: 'nodCenter', label: '点头位置', min: 0.25, max: 0.90, step: 0.01 },
73
+ { key: 'nodWidth', label: '点头宽度', min: 0.08, max: 0.28, step: 0.01 },
74
+ { key: 'nodDepth', label: '点头下沉', min: 0, max: 0.9, step: 0.01 },
75
+ { key: 'nodRot', label: '点头旋转', min: -2.5, max: 0.5, step: 0.05, unit: 'deg' },
76
+ ];
77
+
78
+ const stage = document.getElementById('stage');
79
+ const readout = document.getElementById('readout');
80
+ const controlGrid = document.getElementById('control-grid');
81
+ let playing = true;
82
+ let startMs = performance.now();
83
+ let pausedT = 0;
84
+ let lowPowerPaused = false;
85
+ let rafId = 0;
86
+
87
+ function scheduleLoop() {
88
+ if (!lowPowerPaused && !rafId) rafId = requestAnimationFrame(loop);
89
+ }
90
+
91
+ window.__clawdSetLowPowerPaused = paused => {
92
+ const next = !!paused;
93
+ if (lowPowerPaused === next) return;
94
+ lowPowerPaused = next;
95
+ if (next) {
96
+ pausedT = currentTime(performance.now());
97
+ playing = false;
98
+ if (rafId) cancelAnimationFrame(rafId);
99
+ rafId = 0;
100
+ return;
101
+ }
102
+ playing = true;
103
+ startMs = performance.now() - pausedT * 1000;
104
+ scheduleLoop();
105
+ };
106
+
107
+ function clamp01(v) { return Math.max(0, Math.min(1, v)); }
108
+ function lerp(a, b, t) { return a + (b - a) * t; }
109
+ function smoothstep01(x) {
110
+ const t = clamp01(x);
111
+ return t * t * (3 - 2 * t);
112
+ }
113
+ function loopDistance(a, b) {
114
+ return Math.abs(((a - b + 0.5) % 1 + 1) % 1 - 0.5);
115
+ }
116
+ function loopBump(phase, center, width) {
117
+ const d = loopDistance(phase, center);
118
+ if (d >= width) return 0;
119
+ return 1 - smoothstep01(d / width);
120
+ }
121
+
122
+ function catmullRomClosedPath(points, tension = 1) {
123
+ const q = (n) => n.toFixed(3);
124
+ let d = `M ${q(points[0].x)} ${q(points[0].y)}`;
125
+ const n = points.length;
126
+ for (let i = 0; i < n; i++) {
127
+ const p0 = points[(i - 1 + n) % n];
128
+ const p1 = points[i];
129
+ const p2 = points[(i + 1) % n];
130
+ const p3 = points[(i + 2) % n];
131
+ const c1x = p1.x + (p2.x - p0.x) * tension / 6;
132
+ const c1y = p1.y + (p2.y - p0.y) * tension / 6;
133
+ const c2x = p2.x - (p3.x - p1.x) * tension / 6;
134
+ const c2y = p2.y - (p3.y - p1.y) * tension / 6;
135
+ d += ` C ${q(c1x)} ${q(c1y)} ${q(c2x)} ${q(c2y)} ${q(p2.x)} ${q(p2.y)}`;
136
+ }
137
+ return `${d} Z`;
138
+ }
139
+
140
+ function makeDozeCloudPath(amount) {
141
+ const mix = clamp01(amount);
142
+ if (mix <= 0.0001) return CLOUD_D;
143
+ const slump = mix;
144
+ const points = SOFT_CLOUD_POINTS.map((pt) => {
145
+ const dx = pt.x - 12;
146
+ const distX = Math.abs(dx);
147
+ const sign = dx === 0 ? 0 : Math.sign(dx);
148
+ const topWeight = Math.pow(clamp01((8.2 - pt.y) / 5.2), 0.95);
149
+ const crownCenter = clamp01(1 - distX / 6.6);
150
+ const shoulderWeight = clamp01((pt.y - 6.0) / 8.5) * clamp01((18.6 - pt.y) / 10.5);
151
+ const lowerWeight = Math.pow(clamp01((pt.y - 15.4) / 5.8), 0.78);
152
+ const sideWeight = clamp01((distX - 2.2) / 7.5) * clamp01((20.2 - pt.y) / 16.2);
153
+
154
+ const topSag = slump * topWeight * BASE.topSag * (0.70 + crownCenter * 0.42);
155
+ const shoulderSag = slump * shoulderWeight * BASE.shoulderSag;
156
+ const floorCurve = BASE.floorY + Math.pow(distX / 10, 2) * 0.46 - crownCenter * 0.04;
157
+ const floorPull = (floorCurve - pt.y) * lowerWeight * BASE.floorFlatten * slump;
158
+ const sideOut = sign * slump * BASE.spread * (0.18 + sideWeight * 0.76 + lowerWeight * 0.42);
159
+ const bottomInhale = lowerWeight * BASE.bottomSquish * slump * -0.34;
160
+ const lean = BASE.leftLean * slump * (0.15 + topWeight * 0.50 + shoulderWeight * 0.30);
161
+
162
+ return { x: pt.x + sideOut + lean, y: pt.y + topSag + shoulderSag + floorPull + bottomInhale };
163
+ });
164
+ return catmullRomClosedPath(points, lerp(0.95, 0.68, mix));
165
+ }
166
+
167
+ function defs(cloudD) {
168
+ return `
169
+ <linearGradient id="body-grad" x1="12" y1="2" x2="12" y2="22" gradientUnits="userSpaceOnUse">
170
+ <stop offset="0" stop-color="#93c5fd"/>
171
+ <stop offset="0.48" stop-color="#f5f6fa"/>
172
+ <stop offset="1" stop-color="#dde2ec"/>
173
+ </linearGradient>
174
+ <linearGradient id="eye-grad" x1="12" y1="9" x2="12" y2="15" gradientUnits="userSpaceOnUse">
175
+ <stop offset="0" stop-color="#B1A7FF"/>
176
+ <stop offset="0.55" stop-color="#7A9DFF"/>
177
+ <stop offset="1" stop-color="#3941FF"/>
178
+ </linearGradient>
179
+ <radialGradient id="hi-grad" cx="0.43" cy="0.22" r="0.62">
180
+ <stop offset="0" stop-color="#93c5fd" stop-opacity="0.82"/>
181
+ <stop offset="0.58" stop-color="#93c5fd" stop-opacity="0.28"/>
182
+ <stop offset="1" stop-color="#93c5fd" stop-opacity="0"/>
183
+ </radialGradient>
184
+ <radialGradient id="halo-grad" cx="50%" cy="50%" r="50%">
185
+ <stop offset="0" stop-color="#7A9DFF" stop-opacity="0.55"/>
186
+ <stop offset="0.54" stop-color="#3941FF" stop-opacity="0.24"/>
187
+ <stop offset="1" stop-color="#3941FF" stop-opacity="0"/>
188
+ </radialGradient>
189
+ <filter id="drop" x="-45%" y="-45%" width="190%" height="190%">
190
+ <feDropShadow class="cloud-drop-shadow" dx="0" dy="0.72" stdDeviation="0.92" flood-color="#3941FF" flood-opacity="0.35"/>
191
+ </filter>
192
+ <filter id="edge-blur" x="-40%" y="-40%" width="180%" height="180%">
193
+ <feGaussianBlur stdDeviation="0.16"/>
194
+ </filter>
195
+ <filter id="halo-blur" x="-80%" y="-120%" width="260%" height="340%">
196
+ <feGaussianBlur stdDeviation="0.42"/>
197
+ </filter>
198
+ <clipPath id="cloud-clip"><path d="${cloudD}"/></clipPath>
199
+ `;
200
+ }
201
+
202
+ function sleepyEyePath(cx, cy, w, h, tiltDeg, flip) {
203
+ const x1 = cx - w / 2;
204
+ const x2 = cx + w / 2;
205
+ const mid = cy + h * 0.56;
206
+ const c1x = cx - w * 0.18;
207
+ const c2x = cx + w * 0.18;
208
+ const tilt = flip * tiltDeg * 0.018;
209
+ return `M ${x1.toFixed(2)} ${(cy - tilt).toFixed(2)} C ${c1x.toFixed(2)} ${mid.toFixed(2)} ${c2x.toFixed(2)} ${mid.toFixed(2)} ${x2.toFixed(2)} ${(cy + tilt).toFixed(2)}`;
210
+ }
211
+
212
+ function makeEyes(frame) {
213
+ const lcx = 12 - BASE.eyeGap / 2;
214
+ const rcx = 12 + BASE.eyeGap / 2;
215
+ const cy = BASE.eyeY + frame.eyeDrop;
216
+ const h = BASE.eyeH + frame.eyeSoft;
217
+ const opacity = BASE.eyeOpacity * frame.eyeOpacity;
218
+ return `
219
+ <g fill="none" stroke="url(#eye-grad)" stroke-width="${(BASE.eyeStroke + frame.eyeStrokeAdd).toFixed(2)}" stroke-linecap="round" stroke-linejoin="round" opacity="${opacity.toFixed(3)}">
220
+ <path d="${sleepyEyePath(lcx, cy, BASE.eyeW, h, BASE.eyeTilt, -1)}"/>
221
+ <path d="${sleepyEyePath(rcx, cy, BASE.eyeW, h, BASE.eyeTilt, 1)}"/>
222
+ </g>
223
+ `;
224
+ }
225
+
226
+ function computeFrame(t) {
227
+ const phase = (t / LOOP.period) % 1;
228
+ const inhale = (1 - Math.cos(phase * Math.PI * 2)) / 2;
229
+ const exhale = 1 - inhale;
230
+ const nod = loopBump(phase, LOOP.nodCenter, LOOP.nodWidth);
231
+ const microFloat = Math.sin(phase * Math.PI * 4 + 0.45) * 0.06;
232
+ const pathSlump = clamp01(BASE.slump - inhale * LOOP.pathBreath + nod * 0.045);
233
+ const bodyTy = BASE.bodyTy - inhale * LOOP.breathAmp * 4.6 + nod * LOOP.nodDepth + microFloat * 0.18;
234
+ const bodyRot = BASE.bodyRot + inhale * 0.22 + nod * LOOP.nodRot;
235
+ const sx = BASE.bodyScale + inhale * LOOP.breathAmp * 0.95 + nod * 0.014;
236
+ const sy = BASE.bodyScale + inhale * LOOP.breathAmp * 1.36 - nod * 0.024;
237
+ return {
238
+ phase,
239
+ inhale,
240
+ nod,
241
+ pathSlump,
242
+ bodyTy,
243
+ bodyRot,
244
+ sx,
245
+ sy,
246
+ eyeDrop: -inhale * 0.12 + nod * 0.18,
247
+ eyeSoft: inhale * 0.12 + nod * 0.10,
248
+ eyeStrokeAdd: inhale * 0.04 + nod * 0.03,
249
+ eyeOpacity: clamp01(1 + inhale * 0.04 - nod * 0.04),
250
+ haloOpacity: BASE.haloOpacity + exhale * 0.045 + nod * 0.055,
251
+ haloY: BASE.haloY + exhale * 0.08 + nod * 0.18,
252
+ haloRx: BASE.haloRx + exhale * 0.44 + nod * 0.32,
253
+ haloRy: BASE.haloRy + exhale * 0.08 + nod * 0.08,
254
+ };
255
+ }
256
+
257
+ function renderFrame(t) {
258
+ const frame = computeFrame(t);
259
+ const cloudD = makeDozeCloudPath(frame.pathSlump);
260
+ const bodyTransform = `translate(${BASE.bodyTx.toFixed(3)} ${frame.bodyTy.toFixed(3)}) translate(12 12) rotate(${frame.bodyRot.toFixed(3)}) scale(${frame.sx.toFixed(4)} ${frame.sy.toFixed(4)}) translate(-12 -12)`;
261
+ stage.innerHTML = `
262
+ <style>
263
+ .edge-rim-tight { stroke-opacity: .9; }
264
+ .edge-rim-soft { stroke-opacity: .75; }
265
+ .edge-contact { stroke-opacity: .9; }
266
+ .wrap {
267
+ width: min(980px, 100%);
268
+ display: grid;
269
+ gap: 18px;
270
+ }
271
+ .header {
272
+ display: flex;
273
+ justify-content: space-between;
274
+ gap: 14px;
275
+ align-items: end;
276
+ }
277
+ #stage {
278
+ width: min(540px, 82vw);
279
+ aspect-ratio: 1;
280
+ display: grid;
281
+ place-items: center;
282
+ }
283
+ .grid {
284
+ display: grid;
285
+ grid-template-columns: repeat(4, minmax(120px, 1fr));
286
+ gap: 10px 14px;
287
+ }
288
+ .field {
289
+ display: grid;
290
+ grid-template-columns: 1fr auto;
291
+ gap: 6px;
292
+ align-items: center;
293
+ color: rgba(255,255,255,.58);
294
+ font-size: 12px;
295
+ }
296
+ .field input { grid-column: 1 / -1; }
297
+ output {
298
+ min-width: 46px;
299
+ text-align: right;
300
+ color: rgba(255,255,255,.70);
301
+ font-size: 12px;
302
+ }
303
+ @media (prefers-color-scheme: light) {
304
+ .edge-rim-tight { stroke-opacity: .95; }
305
+ .edge-rim-soft { stroke-opacity: .85; }
306
+ .edge-contact { stroke-opacity: .95; }
307
+ .cloud-drop-shadow { flood-opacity: .13; }
308
+ output, .field { color: rgba(21,33,63,.62); }
309
+ .cloud-drop-shadow { flood-opacity: .13; }
310
+ .food-cloud-shadow { flood-opacity: .28; flood-color: #5060FF; }
311
+ .food-cloud > path {
312
+ stroke: #5870E8 !important;
313
+ stroke-width: 1.2 !important;
314
+ stroke-opacity: .9 !important;
315
+ vector-effect: non-scaling-stroke !important;
316
+ }
317
+ }
318
+ </style>
319
+ <defs>
320
+ ${defs(cloudD)}
321
+ <path id="cloud-shape" d="${cloudD}"/>
322
+ </defs>
323
+ <g id="halo-layer">
324
+ <ellipse cx="12" cy="${frame.haloY.toFixed(2)}" rx="${frame.haloRx.toFixed(2)}" ry="${frame.haloRy.toFixed(2)}" fill="url(#halo-grad)" opacity="${frame.haloOpacity.toFixed(3)}" filter="url(#halo-blur)"/>
325
+ <ellipse cx="12" cy="${(frame.haloY + 0.05).toFixed(2)}" rx="${(frame.haloRx * 0.68).toFixed(2)}" ry="${(frame.haloRy * 0.46).toFixed(2)}" fill="#7A9DFF" opacity="${BASE.glowOpacity}" filter="url(#halo-blur)"/>
326
+ </g>
327
+ <g transform="${bodyTransform}">
328
+ <g id="edge-system">
329
+ <use class="edge-contact" href="#cloud-shape" fill="none" stroke="#7d8aa7" stroke-width="1.00" stroke-linejoin="round" transform="translate(0 0.16)" filter="url(#edge-blur)"/>
330
+ <use class="edge-rim-soft" href="#cloud-shape" fill="none" stroke="#7A9DFF" stroke-width="0.88" stroke-linejoin="round" filter="url(#edge-blur)"/>
331
+ <use class="edge-rim-tight" href="#cloud-shape" fill="none" stroke="#aab3c6" stroke-width="0.40" stroke-linejoin="round"/>
332
+ </g>
333
+ <g filter="url(#drop)">
334
+ <use href="#cloud-shape" fill="url(#body-grad)"/>
335
+ <rect x="1" y="2.2" width="22" height="10.4" fill="url(#hi-grad)" clip-path="url(#cloud-clip)" opacity=".88"/>
336
+ </g>
337
+ ${makeEyes(frame)}
338
+ </g>
339
+ `;
340
+ readout.innerHTML = `<span>phase: <b>${frame.phase.toFixed(2)}</b></span><span>slump ${(frame.pathSlump * 100).toFixed(0)}%</span><span>inhale ${(frame.inhale * 100).toFixed(0)}%</span><span>nod ${(frame.nod * 100).toFixed(0)}%</span><span>rot ${frame.bodyRot.toFixed(2)}deg</span>`;
341
+ }
342
+
343
+ function currentTime(now) {
344
+ if (!playing) return pausedT;
345
+ return (now - startMs) / 1000;
346
+ }
347
+
348
+ function loop(now) {
349
+ rafId = 0;
350
+ if (lowPowerPaused) return;
351
+ renderFrame(currentTime(now));
352
+ scheduleLoop();
353
+ }
354
+
355
+ renderFrame(0);
356
+ scheduleLoop();
357
+ ]]></script>
358
+ </svg>