@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,494 @@
1
+ <svg id="svg" xmlns="http://www.w3.org/2000/svg" viewBox="-6 -6 36 36" preserveAspectRatio="xMidYMid meet">
2
+ <style><![CDATA[
3
+ .statusbar {
4
+ font-size: 10px;
5
+ font-family: ui-monospace, monospace;
6
+ color: #7A9DFF;
7
+ background: #1f1f26;
8
+ border-radius: 8px;
9
+ padding: 6px 14px;
10
+ min-width: 760px;
11
+ text-align: center;
12
+ letter-spacing: 0.05em;
13
+ }
14
+ .statusbar .phase-A { color: #7A9DFF; }
15
+ .statusbar .phase-B { color: #FFA56B; font-weight: 700; }
16
+ .statusbar .phase-C { color: #B1A7FF; }
17
+ .edge-rim-tight { stroke-opacity: .9; }
18
+ .edge-rim-soft { stroke-opacity: .75; }
19
+ .edge-contact { stroke-opacity: .9; }
20
+ @media (prefers-color-scheme: light) {
21
+ .statusbar { background: #fff; color: #3941FF; }
22
+ .edge-rim-tight { stroke-opacity: .95; }
23
+ .edge-rim-soft { stroke-opacity: .85; }
24
+ .edge-contact { stroke-opacity: .95; }
25
+ .cloud-drop-shadow { flood-opacity: .13; }
26
+ .food-cloud-shadow { flood-opacity: .28; flood-color: #5060FF; }
27
+ .food-cloud > path {
28
+ stroke: #5870E8 !important;
29
+ stroke-width: 1.2 !important;
30
+ stroke-opacity: .9 !important;
31
+ vector-effect: non-scaling-stroke !important;
32
+ }
33
+ }
34
+ ]]></style>
35
+ <defs>
36
+ <linearGradient id="bg-grad" x1="12" y1="2" x2="12" y2="22" gradientUnits="userSpaceOnUse">
37
+ <stop offset="0" stop-color="#93c5fd"/>
38
+ <stop offset="0.5" stop-color="#60a5fa"/>
39
+ <stop offset="1" stop-color="#3b82f6"/>
40
+ </linearGradient>
41
+ <radialGradient id="hg-grad" cx="0.45" cy="0.25" r="0.55">
42
+ <stop offset="0" stop-color="#93c5fd" stop-opacity="0.9"/>
43
+ <stop offset="1" stop-color="#93c5fd" stop-opacity="0"/>
44
+ </radialGradient>
45
+ <linearGradient id="eye-grad" x1="0" y1="0" x2="0" y2="24" gradientUnits="userSpaceOnUse">
46
+ <stop offset="0" stop-color="#B1A7FF"/>
47
+ <stop offset="0.5" stop-color="#7A9DFF"/>
48
+ <stop offset="1" stop-color="#3941FF"/>
49
+ </linearGradient>
50
+ <linearGradient id="signal-grad" x1="-2" y1="12" x2="26" y2="12" gradientUnits="userSpaceOnUse">
51
+ <stop offset="0" stop-color="#B1A7FF" stop-opacity="0.10"/>
52
+ <stop offset="0.22" stop-color="#8EA7FF" stop-opacity="0.92"/>
53
+ <stop offset="0.78" stop-color="#7A9DFF" stop-opacity="0.92"/>
54
+ <stop offset="1" stop-color="#3941FF" stop-opacity="0.10"/>
55
+ </linearGradient>
56
+ <linearGradient id="alert-grad" x1="19" y1="4" x2="5" y2="22" gradientUnits="userSpaceOnUse">
57
+ <stop offset="0" stop-color="#FFD36E" stop-opacity="0.98"/>
58
+ <stop offset="0.36" stop-color="#FFA56B" stop-opacity="0.9"/>
59
+ <stop offset="0.72" stop-color="#8EA7FF" stop-opacity="0.74"/>
60
+ <stop offset="1" stop-color="#3941FF" stop-opacity="0.18"/>
61
+ </linearGradient>
62
+ <filter id="cloud-shadow" x="-40%" y="-40%" width="180%" height="180%">
63
+ <feDropShadow class="cloud-drop-shadow" dx="0" dy="0.6" stdDeviation="0.8" flood-color="#3941FF" flood-opacity="0.35"/>
64
+ </filter>
65
+ <filter id="edge-blur" x="-40%" y="-40%" width="180%" height="180%">
66
+ <feGaussianBlur stdDeviation="0.16"/>
67
+ </filter>
68
+ <filter id="pulse-glow" x="-50%" y="-50%" width="200%" height="200%">
69
+ <feGaussianBlur stdDeviation="0.22" result="b"/>
70
+ <feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
71
+ </filter>
72
+ <filter id="alert-glow" x="-80%" y="-80%" width="260%" height="260%">
73
+ <feGaussianBlur stdDeviation="0.38" result="b"/>
74
+ <feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
75
+ </filter>
76
+ <clipPath id="cloud-clip">
77
+ <path 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-.74 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"/>
78
+ </clipPath>
79
+ </defs>
80
+
81
+ <!-- 脉冲圈层 (在 cloudling 后面) -->
82
+ <g id="pulses-layer"></g>
83
+
84
+ <!-- cloudling 主体 -->
85
+ <g id="g-translate">
86
+ <g id="g-rotate">
87
+ <g id="g-squash">
88
+ <g id="edge-system">
89
+ <path class="edge-contact" 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-.74 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" fill="none" stroke="#7d8aa7" stroke-width="1.4" stroke-linejoin="round" transform="translate(0 0.18)" filter="url(#edge-blur)"/>
90
+ <path class="edge-rim-soft" 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-.74 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" fill="none" stroke="#7A9DFF" stroke-width="1.2" stroke-linejoin="round" filter="url(#edge-blur)"/>
91
+ <path class="edge-rim-tight" 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-.74 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" fill="none" stroke="#aab3c6" stroke-width="0.8" stroke-linejoin="round"/>
92
+ </g>
93
+ <g id="cloud-body" filter="url(#cloud-shadow)">
94
+ <path 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-.74 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" fill="url(#bg-grad)"/>
95
+ <rect x="1" y="2" width="22" height="11" fill="url(#hg-grad)" clip-path="url(#cloud-clip)"/>
96
+ </g>
97
+ <g id="rim-flash-layer"></g>
98
+ <!-- 眼睛层 (counter-rotate 容器) -->
99
+ <g id="eyes-counter">
100
+ <g id="eye-left" transform="translate(9 12)"></g>
101
+ <g id="eye-right" transform="translate(15 12)"></g>
102
+ </g>
103
+ </g>
104
+ </g>
105
+ </g>
106
+ <g id="__cloudling-export-shims" opacity="0" pointer-events="none" style="display:none">
107
+ <g id="params"></g>
108
+ <g id="statusbar"></g>
109
+ </g>
110
+
111
+ <script type="application/ecmascript"><![CDATA[
112
+ /* ============================================================
113
+ Cloudling notification — ABC 三段式 + 老式闹铃
114
+ ============================================================ */
115
+
116
+ const COLOR = '#7A9DFF';
117
+ const EYE_GRAD = 'url(#eye-grad)';
118
+
119
+ // ===== 默认参数 (推荐值) =====
120
+ const DEFAULTS = {
121
+ totalPeriod: 2600, // ms 总周期
122
+ ratioA: 0.22, // A 段占比
123
+ ratioB: 0.50, // B 段占比 (C = 1 - A - B)
124
+ shakeAngle: 14, // ° 摇晃幅度
125
+ shakeFreqHz: 7, // Hz 摇晃频率
126
+ shakeJumpY: 0.46, // px 跟随微跳幅度
127
+ squashStrength: 0.040, // 摇晃顶点 squash (1 - x)
128
+ scaleMicro: 0.034, // micro 顶点撑大幅度
129
+ scaleMacro: 0.060, // macro B 段整气幅度
130
+ idleBreathAmp: 0.02, // idle 微呼吸幅度 (0.96 ↔ 1.00 → ±0.02)
131
+ idleBreathPeriod: 5000, // ms idle 呼吸周期 (跟 idle 锁定一致)
132
+ pulseLifetimeA: 760, // ms A 段预亮生命
133
+ pulseLifetimeB: 520, // ms B 段冲击弧生命
134
+ pulseScaleEnd: 1.54, // 终值 scale
135
+ pulseStrokeWidth: 0.76, // 通知弧描边宽度
136
+ pulseGlow: 0.36, // glow stdDeviation
137
+ pulseStartRadius: 8.9, // 弧出生时半径
138
+ pulseFadeIn: 35, // ms 弧入场 fade in (opacity 0→max)
139
+ eyeWidenAmount: 0.22, // capsule rx +22%
140
+ eyeWidenDuration: 100, // ms 瞪大持续时间
141
+ counterRotate: true, // 眼睛是否抵消 rotate
142
+ };
143
+ let P = { ...DEFAULTS };
144
+
145
+ // ===== params UI =====
146
+ const PARAM_DEFS = [
147
+ { group: '节奏 / RHYTHM' },
148
+ { key: 'totalPeriod', label: '总周期 (ms)', min: 1500, max: 4000, step: 100 },
149
+ { key: 'ratioA', label: 'A 段占比 (蓄势)', min: 0.10, max: 0.50, step: 0.02 },
150
+ { key: 'ratioB', label: 'B 段占比 (发作)', min: 0.20, max: 0.70, step: 0.02 },
151
+ { group: '摇晃 / SHAKE' },
152
+ { key: 'shakeAngle', label: '摇晃幅度 (±°)', min: 4, max: 25, step: 1 },
153
+ { key: 'shakeFreqHz', label: '摇晃频率 (Hz)', min: 3, max: 10, step: 0.5 },
154
+ { key: 'shakeJumpY', label: '微跳幅度 (px)', min: 0, max: 2, step: 0.1 },
155
+ { key: 'squashStrength', label: 'squash 强度 (顶点压)', min: 0, max: 0.12, step: 0.01 },
156
+ { group: '呼吸 / BREATH (整体 scale)' },
157
+ { key: 'scaleMicro', label: 'micro 顶点撑大 (B)', min: 0, max: 0.10, step: 0.005 },
158
+ { key: 'scaleMacro', label: 'macro 整气 (B 段)', min: 0, max: 0.15, step: 0.005 },
159
+ { key: 'idleBreathAmp', label: 'idle 呼吸幅度 (A/C)', min: 0, max: 0.05, step: 0.005 },
160
+ { key: 'idleBreathPeriod',label: 'idle 呼吸周期 (ms)', min: 2000, max: 8000, step: 200 },
161
+ { group: '通知弧 / PULSE' },
162
+ { key: 'pulseLifetimeA', label: 'A 段缓弧生命 (ms)', min: 500, max: 1500, step: 50 },
163
+ { key: 'pulseLifetimeB', label: 'B 段密集弧生命 (ms)', min: 250, max: 1000, step: 50 },
164
+ { key: 'pulseScaleEnd', label: '弧终值 scale', min: 1.15, max: 1.9, step: 0.05 },
165
+ { key: 'pulseStrokeWidth',label: '弧描边粗细', min: 0.35, max: 1.6, step: 0.05 },
166
+ { key: 'pulseGlow', label: 'glow stdDeviation', min: 0.1, max: 1.5, step: 0.05 },
167
+ { key: 'pulseStartRadius',label: '弧出生半径', min: 8.8, max: 12, step: 0.2 },
168
+ { key: 'pulseFadeIn', label: '弧入场 fade in (ms)', min: 0, max: 250, step: 10 },
169
+ { group: '眼睛 / EYES' },
170
+ { key: 'eyeWidenAmount', label: '瞪大幅度 (+rx %)', min: 0, max: 0.5, step: 0.02 },
171
+ { key: 'eyeWidenDuration',label: '瞪大持续 (ms)', min: 50, max: 300, step: 10 },
172
+ { key: 'counterRotate', label: '眼睛 counter-rotate', type: 'cb' },
173
+ ];
174
+
175
+ const paramsEl = document.getElementById('params');
176
+ function buildParams() {
177
+ paramsEl.innerHTML = '';
178
+ for (const def of PARAM_DEFS) {
179
+ if (def.group) {
180
+ const h = document.createElement('h3');
181
+ h.textContent = def.group;
182
+ paramsEl.appendChild(h);
183
+ continue;
184
+ }
185
+ const lab = document.createElement('label');
186
+ if (def.type === 'cb') {
187
+ lab.className = 'cb';
188
+ lab.innerHTML = `
189
+ <input type="checkbox" data-key="${def.key}" ${P[def.key] ? 'checked' : ''}/>
190
+ <span>${def.label}</span>
191
+ `;
192
+ lab.querySelector('input').onchange = e => { P[def.key] = e.target.checked; };
193
+ } else {
194
+ lab.innerHTML = `
195
+ <span>${def.label}</span>
196
+ <input type="range" data-key="${def.key}" min="${def.min}" max="${def.max}" step="${def.step}" value="${P[def.key]}"/>
197
+ <span class="val" data-val="${def.key}">${P[def.key]}</span>
198
+ `;
199
+ const range = lab.querySelector('input');
200
+ const val = lab.querySelector('[data-val]');
201
+ range.oninput = e => {
202
+ P[def.key] = parseFloat(e.target.value);
203
+ val.textContent = (def.step < 1) ? P[def.key].toFixed(2) : P[def.key];
204
+ };
205
+ }
206
+ paramsEl.appendChild(lab);
207
+ }
208
+ }
209
+
210
+ // ===== 眼睛绘制 =====
211
+ function setEyeCapsule(g, widenFactor = 1, heightFactor = 1, tilt = 0) {
212
+ const w = 2.55 * widenFactor;
213
+ const h = 4.85 * heightFactor;
214
+ const rx = Math.min(w / 2, 1.25);
215
+ g.innerHTML = `<rect x="${(-w/2).toFixed(3)}" y="${(-h/2).toFixed(3)}" width="${w.toFixed(3)}" height="${h.toFixed(3)}" rx="${rx.toFixed(3)}" fill="${EYE_GRAD}" stroke="#fff" stroke-width="0.08" stroke-opacity="0.55" transform="rotate(${tilt.toFixed(3)})"/>`;
216
+ }
217
+ function setEyeGT(g) {
218
+ // > chevron 向右
219
+ const w = 1.6, h = 1.7;
220
+ g.innerHTML = `<path d="M ${-w/2} ${-h} L ${w/2} 0 L ${-w/2} ${h}" fill="none" stroke="${EYE_GRAD}" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>`;
221
+ }
222
+ function setEyeLT(g) {
223
+ // < chevron 向左
224
+ const w = 1.6, h = 1.7;
225
+ g.innerHTML = `<path d="M ${w/2} ${-h} L ${-w/2} 0 L ${w/2} ${h}" fill="none" stroke="${EYE_GRAD}" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>`;
226
+ }
227
+
228
+ const eyeL = document.getElementById('eye-left');
229
+ const eyeR = document.getElementById('eye-right');
230
+ const eyesCounter = document.getElementById('eyes-counter');
231
+ const gTrans = document.getElementById('g-translate');
232
+ const gRotate = document.getElementById('g-rotate');
233
+ const gSquash = document.getElementById('g-squash');
234
+ const pulsesLayer = document.getElementById('pulses-layer');
235
+ const rimFlashLayer = document.getElementById('rim-flash-layer');
236
+ const cloudPathD = document.querySelector('#cloud-clip path').getAttribute('d');
237
+
238
+ // ===== 脉冲池 =====
239
+ let pulses = []; // { bornT (perf ms), lifetime, scaleEnd, type }
240
+ let pulseIdCounter = 0;
241
+
242
+ function emitPulse(type, now) {
243
+ pulses.push({
244
+ id: ++pulseIdCounter,
245
+ bornT: now,
246
+ lifetime: type === 'A' ? P.pulseLifetimeA : P.pulseLifetimeB,
247
+ scaleEnd: P.pulseScaleEnd,
248
+ type
249
+ });
250
+ }
251
+
252
+ // 动态 glow filter
253
+ const glowBlur = document.querySelector('#pulse-glow feGaussianBlur');
254
+ function updateGlowStdDev() {
255
+ glowBlur.setAttribute('stdDeviation', P.pulseGlow);
256
+ }
257
+
258
+ function polarPoint(cx, cy, r, deg) {
259
+ const a = deg * Math.PI / 180;
260
+ return [cx + Math.cos(a) * r, cy + Math.sin(a) * r];
261
+ }
262
+
263
+ function arcPath(cx, cy, r, startDeg, endDeg) {
264
+ const [x0, y0] = polarPoint(cx, cy, r, startDeg);
265
+ const [x1, y1] = polarPoint(cx, cy, r, endDeg);
266
+ const largeArc = Math.abs(endDeg - startDeg) > 180 ? 1 : 0;
267
+ const sweep = endDeg >= startDeg ? 1 : 0;
268
+ return `M${x0.toFixed(3)} ${y0.toFixed(3)} A${r.toFixed(3)} ${r.toFixed(3)} 0 ${largeArc} ${sweep} ${x1.toFixed(3)} ${y1.toFixed(3)}`;
269
+ }
270
+
271
+ function renderPulses(now) {
272
+ pulses = pulses.filter(p => now - p.bornT < p.lifetime);
273
+ let html = '';
274
+ for (const p of pulses) {
275
+ const ageMs = now - p.bornT;
276
+ const t = ageMs / p.lifetime;
277
+ const scale = 1 + (p.scaleEnd - 1) * easeOutCubic(t);
278
+ const r = P.pulseStartRadius * scale;
279
+ const baseOp = p.type === 'A' ? 0.34 : 0.82;
280
+ const fadeIn = P.pulseFadeIn > 0 ? Math.min(1, ageMs / P.pulseFadeIn) : 1;
281
+ const fadeOut = Math.pow(1 - t, p.type === 'A' ? 1.22 : 1.55);
282
+ const opacity = baseOp * fadeIn * fadeOut;
283
+ const wobble = p.type === 'B' ? ((p.id % 3) - 1) * 7 : 0;
284
+ const strokeW = P.pulseStrokeWidth * (p.type === 'A' ? 0.62 : 1);
285
+ const segments = p.type === 'A'
286
+ ? [[-70, -34], [-12, 18], [34, 56]]
287
+ : [[-122, -86], [-48, 14], [42, 78], [130, 166], [190, 216]];
288
+
289
+ for (let i = 0; i < segments.length; i++) {
290
+ const [a0, a1] = segments[i];
291
+ const d = arcPath(12, 12, r + (i % 2) * 0.26, a0 + wobble, a1 + wobble);
292
+ const segOp = opacity * (1 - i * 0.085);
293
+ const stroke = (p.type === 'B' && i < 2) ? 'url(#alert-grad)' : 'url(#signal-grad)';
294
+ html += `<path d="${d}" fill="none" stroke="${stroke}" stroke-width="${strokeW.toFixed(3)}" stroke-linecap="round" stroke-opacity="${segOp.toFixed(3)}" filter="url(#pulse-glow)"/>`;
295
+ }
296
+ }
297
+ pulsesLayer.innerHTML = html;
298
+ }
299
+
300
+ function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); }
301
+ function easeInOutCubic(t) { return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; }
302
+ function clamp(v, a, b) { return Math.max(a, Math.min(b, v)); }
303
+ function expPulse(x, center, width) {
304
+ const d = (x - center) / width;
305
+ return Math.exp(-d * d);
306
+ }
307
+
308
+ function renderAlertLayer(phase, phaseTms, tA, tB, tC, shakePeak, bEnvelope) {
309
+ let prep = 0, ping = 0, fade = 0;
310
+ if (phase === 'A') {
311
+ prep = easeInOutCubic(clamp(phaseTms / tA, 0, 1));
312
+ fade = 0.48 + prep * 0.36;
313
+ } else if (phase === 'B') {
314
+ const b = clamp(phaseTms / tB, 0, 1);
315
+ const ping1 = expPulse(b, 0.10, 0.055);
316
+ const ping2 = expPulse(b, 0.54, 0.070);
317
+ ping = Math.max(ping1, ping2, shakePeak * bEnvelope * 0.34);
318
+ fade = 0.92;
319
+ } else {
320
+ const c = clamp(phaseTms / tC, 0, 1);
321
+ fade = (1 - easeOutCubic(c)) * 0.56;
322
+ }
323
+
324
+ const rimOp = clamp(ping * 0.58 + prep * 0.12, 0, 0.68);
325
+ const rimW = 0.28 + ping * 0.42;
326
+ rimFlashLayer.innerHTML = rimOp > 0.015
327
+ ? `<path d="${cloudPathD}" fill="none" stroke="url(#alert-grad)" stroke-width="${rimW.toFixed(3)}" stroke-linejoin="round" stroke-opacity="${rimOp.toFixed(3)}" filter="url(#alert-glow)"/>`
328
+ : '';
329
+ }
330
+
331
+ // ===== 主循环 =====
332
+ let t0 = performance.now();
333
+ let lastCycleT = -1;
334
+ let emittedA = false;
335
+ let emittedBPeaks = 0;
336
+ let cycleCount = 0;
337
+ let paused = false;
338
+
339
+ let pauseAccumT = 0;
340
+ let pauseStart = 0;
341
+
342
+ const sb = document.getElementById('statusbar');
343
+
344
+ function tick() {
345
+ if (paused) {
346
+ requestAnimationFrame(tick);
347
+ return;
348
+ }
349
+ const now = performance.now();
350
+ const tInCycle = ((now - t0) % P.totalPeriod);
351
+
352
+ // cycle wrap detection
353
+ if (tInCycle < lastCycleT) {
354
+ cycleCount++;
355
+ emittedA = false;
356
+ emittedBPeaks = 0;
357
+ }
358
+ lastCycleT = tInCycle;
359
+
360
+ // 段时长 (动态从当前 P 算)
361
+ const tA = P.totalPeriod * P.ratioA;
362
+ const tB = P.totalPeriod * P.ratioB;
363
+ const tC = P.totalPeriod - tA - tB;
364
+
365
+ let phase, phaseTms;
366
+ if (tInCycle < tA) {
367
+ phase = 'A'; phaseTms = tInCycle;
368
+ } else if (tInCycle < tA + tB) {
369
+ phase = 'B'; phaseTms = tInCycle - tA;
370
+ } else {
371
+ phase = 'C'; phaseTms = tInCycle - tA - tB;
372
+ }
373
+
374
+ // ===== 脉冲发射 =====
375
+ // A 段: 在段开头发 1 圈 (缓)
376
+ if (phase === 'A' && !emittedA) {
377
+ emitPulse('A', now);
378
+ emittedA = true;
379
+ }
380
+ // B 段: 摇晃顶点 = 圈出生 (每半周期一个顶点)
381
+ if (phase === 'B') {
382
+ const tBSec = phaseTms / 1000;
383
+ const peakInterval = 1 / (2 * P.shakeFreqHz); // 半周期 = 一个顶点间隔
384
+ // 顶点位于 (k + 0.25) * (1/freq) = (2k+0.5) * peakInterval, k=0,1,2,...
385
+ // 简化: 等间隔发射, 第一发在 peakInterval / 2 (即 1/(4*freq))
386
+ const expectedPeaks = Math.floor((tBSec - peakInterval / 2) / peakInterval) + 1;
387
+ while (emittedBPeaks < expectedPeaks && emittedBPeaks * peakInterval + peakInterval / 2 < (tB / 1000)) {
388
+ if (emittedBPeaks % 3 === 0) emitPulse('B', now);
389
+ emittedBPeaks++;
390
+ }
391
+ }
392
+
393
+ // ===== 摇晃 / 形变 / 呼吸 =====
394
+ let rotate = 0, jumpY = 0;
395
+ let burstX = 1, burstY = 1; // B 段额外 scale (在 idle 呼吸基础上叠加)
396
+ let shakePeak = 0;
397
+ let bEnvelope = 0;
398
+
399
+ if (phase === 'B') {
400
+ const tBSec = phaseTms / 1000;
401
+ const angularFreq = 2 * Math.PI * P.shakeFreqHz;
402
+ // 进场 envelope: 前 0.15s 内从 0 -> 1, ease in
403
+ const envIn = Math.min(1, tBSec / 0.15);
404
+ // 出场 envelope: 最后 0.2s ease out
405
+ const tBTotal = tB / 1000;
406
+ const envOut = Math.min(1, (tBTotal - tBSec) / 0.2);
407
+ const env = Math.min(envIn, envOut);
408
+ const sinV = Math.sin(angularFreq * tBSec);
409
+ const absSin = Math.abs(sinV);
410
+ shakePeak = absSin;
411
+ bEnvelope = env;
412
+ rotate = P.shakeAngle * sinV * env;
413
+ // 微跳: abs(sin) 的 2 倍频, 每次顶点跳一下
414
+ jumpY = -P.shakeJumpY * Math.abs(Math.sin(angularFreq * tBSec / 2 + Math.PI / 4)) * env;
415
+ // micro 撑大: 跟摇晃顶点同步, abs(sin) 在顶点为 1
416
+ const microFactor = 1 + P.scaleMicro * absSin * env;
417
+ // macro 整气: B 段一次大吸气-呼气, sin(π * t/T), 中点峰值
418
+ const macroFactor = 1 + P.scaleMacro * Math.sin(Math.PI * Math.min(1, Math.max(0, tBSec / tBTotal)));
419
+ // squash 顶点压 (scaleY 减一点)
420
+ const squashFactor = 1 - P.squashStrength * absSin * env;
421
+ burstX = microFactor * macroFactor;
422
+ burstY = microFactor * macroFactor * squashFactor;
423
+ } else if (phase === 'C') {
424
+ // C 段: 摇晃用衰减振荡 ease out
425
+ const tCSec = phaseTms / 1000;
426
+ const tCTotal = tC / 1000;
427
+ const decay = Math.max(0, 1 - tCSec / tCTotal);
428
+ const decayEased = decay * decay;
429
+ const angularFreq = 2 * Math.PI * P.shakeFreqHz * 0.7;
430
+ rotate = P.shakeAngle * decayEased * 0.5 * Math.sin(angularFreq * (tB / 1000 + tCSec));
431
+ jumpY = 0;
432
+ }
433
+
434
+ // ===== idle 呼吸 base (跨 cycle 连续, 不重置) =====
435
+ // 中心值 1 - amp, 上下 ±amp, 范围 [1-2*amp, 1] (跟 idle 锁定 0.96-1.00 一致)
436
+ const breathPhase = (now / P.idleBreathPeriod) * 2 * Math.PI;
437
+ const baseBreath = (1 - P.idleBreathAmp) + P.idleBreathAmp * Math.sin(breathPhase);
438
+ // 注意: 上面公式范围 [1-2*amp, 1], idle 锁定 0.96-1.00 即 amp=0.02
439
+
440
+ const finalScaleX = baseBreath * burstX;
441
+ const finalScaleY = baseBreath * burstY;
442
+
443
+ // ===== 应用 transform =====
444
+ gTrans.setAttribute('transform', `translate(0 ${jumpY.toFixed(3)})`);
445
+ gRotate.setAttribute('transform', `rotate(${rotate.toFixed(3)} 12 12)`);
446
+ gSquash.setAttribute('transform', `translate(12 12) scale(${finalScaleX.toFixed(4)} ${finalScaleY.toFixed(4)}) translate(-12 -12)`);
447
+
448
+ // ===== 眼睛 counter-rotate =====
449
+ if (P.counterRotate) {
450
+ eyesCounter.setAttribute('transform', `rotate(${(-rotate).toFixed(3)} 12 12)`);
451
+ } else {
452
+ eyesCounter.removeAttribute('transform');
453
+ }
454
+
455
+ // ===== 眼睛 swap =====
456
+ let widenFactor = 1;
457
+ let eyeHeight = 1;
458
+ let eyeTiltL = 0;
459
+ let eyeTiltR = 0;
460
+ if (phase === 'A') {
461
+ const remainA = tA - phaseTms;
462
+ if (remainA < P.eyeWidenDuration) {
463
+ // 瞪大
464
+ const widen01 = 1 - remainA / P.eyeWidenDuration;
465
+ widenFactor = 1 + P.eyeWidenAmount * widen01;
466
+ }
467
+ } else if (phase === 'B') {
468
+ widenFactor = 1 + P.eyeWidenAmount * 0.92 + shakePeak * bEnvelope * 0.070;
469
+ eyeHeight = 1.04 + shakePeak * bEnvelope * 0.040;
470
+ eyeTiltL = -3.0 * bEnvelope;
471
+ eyeTiltR = 3.0 * bEnvelope;
472
+ } else if (phase === 'C') {
473
+ // C 段开头 capsule 重新渐入 (微小入场)
474
+ const fadeIn = Math.min(1, phaseTms / 120);
475
+ widenFactor = 1 + P.eyeWidenAmount * (1 - fadeIn) * 0.5; // 收回时残留
476
+ }
477
+
478
+ setEyeCapsule(eyeL, widenFactor, eyeHeight, eyeTiltL);
479
+ setEyeCapsule(eyeR, widenFactor, eyeHeight, eyeTiltR);
480
+
481
+ // ===== 渲染脉冲 =====
482
+ updateGlowStdDev();
483
+ renderPulses(now);
484
+ renderAlertLayer(phase, phaseTms, tA, tB, tC, shakePeak, bEnvelope);
485
+
486
+ // ===== 状态栏 =====
487
+ sb.innerHTML = `phase: <span class="phase-${phase}">${phase}</span> · cycle: ${cycleCount} · pulses: ${pulses.length} · t: ${tInCycle.toFixed(0)}ms / ${P.totalPeriod}`;
488
+
489
+ requestAnimationFrame(tick);
490
+ }
491
+
492
+ requestAnimationFrame(tick);
493
+ ]]></script>
494
+ </svg>
@@ -0,0 +1,83 @@
1
+ <svg id="cloudling" xmlns="http://www.w3.org/2000/svg" viewBox="-6 -6 36 36" preserveAspectRatio="xMidYMid meet">
2
+ <style><![CDATA[
3
+ .edge-rim-tight { stroke-opacity: .9; }
4
+ .edge-rim-soft { stroke-opacity: .75; }
5
+ .edge-contact { stroke-opacity: .9; }
6
+ .drag-wobble {
7
+ transform-origin: 12px 12px;
8
+ animation: drag-wobble var(--wobble-period, 0.9s) infinite alternate ease-in-out;
9
+ }
10
+ @keyframes drag-wobble {
11
+ 0% { transform: rotate(calc(-1 * var(--wobble-deg, 11deg))); }
12
+ 100% { transform: rotate(var(--wobble-deg, 11deg)); }
13
+ }
14
+ .drag-breath {
15
+ transform-origin: 12px 12px;
16
+ animation: drag-breath var(--breath-period, 5s) infinite ease-in-out;
17
+ }
18
+ @keyframes drag-breath {
19
+ 0%, 100% { transform: scale(var(--breath-low, 0.96)); }
20
+ 50% { transform: scale(1); }
21
+ }
22
+ @media (prefers-color-scheme: light) {
23
+ .edge-rim-tight { stroke-opacity: .95; }
24
+ .edge-rim-soft { stroke-opacity: .85; }
25
+ .edge-contact { stroke-opacity: .95; }
26
+ .cloud-drop-shadow { flood-opacity: .13; }
27
+ .food-cloud-shadow { flood-opacity: .28; flood-color: #5060FF; }
28
+ .food-cloud > path {
29
+ stroke: #5870E8 !important;
30
+ stroke-width: 1.2 !important;
31
+ stroke-opacity: .9 !important;
32
+ vector-effect: non-scaling-stroke !important;
33
+ }
34
+ }
35
+ ]]></style>
36
+ <defs>
37
+ <linearGradient id="body-grad" x1="12" y1="2" x2="12" y2="22" gradientUnits="userSpaceOnUse">
38
+ <stop offset="0" stop-color="#93c5fd"/>
39
+ <stop offset="0.5" stop-color="#60a5fa"/>
40
+ <stop offset="1" stop-color="#3b82f6"/>
41
+ </linearGradient>
42
+ <linearGradient id="eye-grad" x1="12" y1="3" x2="12" y2="21" gradientUnits="userSpaceOnUse">
43
+ <stop offset="0" stop-color="#B1A7FF"/>
44
+ <stop offset="0.5" stop-color="#7A9DFF"/>
45
+ <stop offset="1" stop-color="#3941FF"/>
46
+ </linearGradient>
47
+ <radialGradient id="hi-grad" cx="0.45" cy="0.25" r="0.55">
48
+ <stop offset="0" stop-color="#93c5fd" stop-opacity="0.9"/>
49
+ <stop offset="1" stop-color="#93c5fd" stop-opacity="0"/>
50
+ </radialGradient>
51
+ <filter id="drop" x="-40%" y="-40%" width="180%" height="180%">
52
+ <feDropShadow class="cloud-drop-shadow" id="drop-shadow" dx="0" dy="0.6" stdDeviation="0.8" flood-color="#3941FF" flood-opacity="0.35"/>
53
+ </filter>
54
+ <filter id="edge-blur" x="-40%" y="-40%" width="180%" height="180%">
55
+ <feGaussianBlur stdDeviation="0.16"/>
56
+ </filter>
57
+ <path id="cloud-shape" 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-.74 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"/>
58
+ <clipPath id="cloud-clip">
59
+ <use href="#cloud-shape"/>
60
+ </clipPath>
61
+ </defs>
62
+
63
+ <!-- 外层 wobble (自旋摇晃, pivot 中心) -->
64
+ <g id="wobble-layer" class="drag-wobble">
65
+ <!-- 内层 breath (呼吸, pivot 中心) -->
66
+ <g id="breath-layer" class="drag-breath">
67
+ <g id="edge-system">
68
+ <use class="edge-contact" href="#cloud-shape" fill="none" stroke="#7d8aa7" stroke-width="1.4" stroke-linejoin="round" transform="translate(0 0.18)" filter="url(#edge-blur)"/>
69
+ <use class="edge-rim-soft" href="#cloud-shape" fill="none" stroke="#7A9DFF" stroke-width="1.2" stroke-linejoin="round" filter="url(#edge-blur)"/>
70
+ <use class="edge-rim-tight" href="#cloud-shape" fill="none" stroke="#aab3c6" stroke-width="0.8" stroke-linejoin="round"/>
71
+ </g>
72
+ <g filter="url(#drop)">
73
+ <use href="#cloud-shape" fill="url(#body-grad)"/>
74
+ <rect x="1" y="2" width="22" height="11" fill="url(#hi-grad)" clip-path="url(#cloud-clip)"/>
75
+ </g>
76
+ <g id="eyes-gt-lt" fill="none" stroke="url(#eye-grad)" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round">
77
+ <path d="M 8.20 10.30 L 9.80 12.00 L 8.20 13.70"/>
78
+ <path d="M 15.80 10.30 L 14.20 12.00 L 15.80 13.70"/>
79
+ </g>
80
+ </g>
81
+ </g>
82
+
83
+ </svg>