@webeach/gl-circular-progress 0.2.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 (86) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +118 -0
  3. package/README.ru.md +120 -0
  4. package/docs/en/CircularProgressFire.md +105 -0
  5. package/docs/en/CircularProgressLiquid.md +106 -0
  6. package/lib/cjs/classes/BaseCircularProgress/BaseCircularProgress.js +151 -0
  7. package/lib/cjs/classes/BaseCircularProgress/BaseCircularProgress.js.map +1 -0
  8. package/lib/cjs/classes/BaseCircularProgress/constants.js +27 -0
  9. package/lib/cjs/classes/BaseCircularProgress/constants.js.map +1 -0
  10. package/lib/cjs/classes/BaseCircularProgress/index.js +8 -0
  11. package/lib/cjs/classes/BaseCircularProgress/index.js.map +1 -0
  12. package/lib/cjs/classes/CircularProgressFire/CircularProgressFire.js +299 -0
  13. package/lib/cjs/classes/CircularProgressFire/CircularProgressFire.js.map +1 -0
  14. package/lib/cjs/classes/CircularProgressFire/index.js +8 -0
  15. package/lib/cjs/classes/CircularProgressFire/index.js.map +1 -0
  16. package/lib/cjs/classes/CircularProgressFire/shaders/fragment.frag.js +6 -0
  17. package/lib/cjs/classes/CircularProgressFire/shaders/fragment.frag.js.map +1 -0
  18. package/lib/cjs/classes/CircularProgressFire/shaders/vertex.vert.js +6 -0
  19. package/lib/cjs/classes/CircularProgressFire/shaders/vertex.vert.js.map +1 -0
  20. package/lib/cjs/classes/CircularProgressLiquid/CircularProgressLiquid.js +306 -0
  21. package/lib/cjs/classes/CircularProgressLiquid/CircularProgressLiquid.js.map +1 -0
  22. package/lib/cjs/classes/CircularProgressLiquid/index.js +8 -0
  23. package/lib/cjs/classes/CircularProgressLiquid/index.js.map +1 -0
  24. package/lib/cjs/classes/CircularProgressLiquid/shaders/fragment.frag.js +6 -0
  25. package/lib/cjs/classes/CircularProgressLiquid/shaders/fragment.frag.js.map +1 -0
  26. package/lib/cjs/classes/CircularProgressLiquid/shaders/vertex.vert.js +6 -0
  27. package/lib/cjs/classes/CircularProgressLiquid/shaders/vertex.vert.js.map +1 -0
  28. package/lib/cjs/index.js +10 -0
  29. package/lib/cjs/index.js.map +1 -0
  30. package/lib/cjs/utils/hexToCss/hexToCss.js +22 -0
  31. package/lib/cjs/utils/hexToCss/hexToCss.js.map +1 -0
  32. package/lib/esm/classes/BaseCircularProgress/BaseCircularProgress.js +149 -0
  33. package/lib/esm/classes/BaseCircularProgress/BaseCircularProgress.js.map +1 -0
  34. package/lib/esm/classes/BaseCircularProgress/constants.js +22 -0
  35. package/lib/esm/classes/BaseCircularProgress/constants.js.map +1 -0
  36. package/lib/esm/classes/BaseCircularProgress/index.js +2 -0
  37. package/lib/esm/classes/BaseCircularProgress/index.js.map +1 -0
  38. package/lib/esm/classes/CircularProgressFire/CircularProgressFire.js +297 -0
  39. package/lib/esm/classes/CircularProgressFire/CircularProgressFire.js.map +1 -0
  40. package/lib/esm/classes/CircularProgressFire/index.js +2 -0
  41. package/lib/esm/classes/CircularProgressFire/index.js.map +1 -0
  42. package/lib/esm/classes/CircularProgressFire/shaders/fragment.frag.js +4 -0
  43. package/lib/esm/classes/CircularProgressFire/shaders/fragment.frag.js.map +1 -0
  44. package/lib/esm/classes/CircularProgressFire/shaders/vertex.vert.js +4 -0
  45. package/lib/esm/classes/CircularProgressFire/shaders/vertex.vert.js.map +1 -0
  46. package/lib/esm/classes/CircularProgressLiquid/CircularProgressLiquid.js +304 -0
  47. package/lib/esm/classes/CircularProgressLiquid/CircularProgressLiquid.js.map +1 -0
  48. package/lib/esm/classes/CircularProgressLiquid/index.js +2 -0
  49. package/lib/esm/classes/CircularProgressLiquid/index.js.map +1 -0
  50. package/lib/esm/classes/CircularProgressLiquid/shaders/fragment.frag.js +4 -0
  51. package/lib/esm/classes/CircularProgressLiquid/shaders/fragment.frag.js.map +1 -0
  52. package/lib/esm/classes/CircularProgressLiquid/shaders/vertex.vert.js +4 -0
  53. package/lib/esm/classes/CircularProgressLiquid/shaders/vertex.vert.js.map +1 -0
  54. package/lib/esm/index.js +3 -0
  55. package/lib/esm/index.js.map +1 -0
  56. package/lib/esm/utils/hexToCss/hexToCss.js +20 -0
  57. package/lib/esm/utils/hexToCss/hexToCss.js.map +1 -0
  58. package/lib/types/classes/BaseCircularProgress/BaseCircularProgress.d.ts +113 -0
  59. package/lib/types/classes/BaseCircularProgress/BaseCircularProgress.d.ts.map +1 -0
  60. package/lib/types/classes/BaseCircularProgress/constants.d.ts +21 -0
  61. package/lib/types/classes/BaseCircularProgress/constants.d.ts.map +1 -0
  62. package/lib/types/classes/BaseCircularProgress/index.d.ts +3 -0
  63. package/lib/types/classes/BaseCircularProgress/index.d.ts.map +1 -0
  64. package/lib/types/classes/BaseCircularProgress/types.d.ts +40 -0
  65. package/lib/types/classes/BaseCircularProgress/types.d.ts.map +1 -0
  66. package/lib/types/classes/CircularProgressFire/CircularProgressFire.d.ts +98 -0
  67. package/lib/types/classes/CircularProgressFire/CircularProgressFire.d.ts.map +1 -0
  68. package/lib/types/classes/CircularProgressFire/index.d.ts +3 -0
  69. package/lib/types/classes/CircularProgressFire/index.d.ts.map +1 -0
  70. package/lib/types/classes/CircularProgressFire/types.d.ts +12 -0
  71. package/lib/types/classes/CircularProgressFire/types.d.ts.map +1 -0
  72. package/lib/types/classes/CircularProgressLiquid/CircularProgressLiquid.d.ts +99 -0
  73. package/lib/types/classes/CircularProgressLiquid/CircularProgressLiquid.d.ts.map +1 -0
  74. package/lib/types/classes/CircularProgressLiquid/index.d.ts +3 -0
  75. package/lib/types/classes/CircularProgressLiquid/index.d.ts.map +1 -0
  76. package/lib/types/classes/CircularProgressLiquid/types.d.ts +13 -0
  77. package/lib/types/classes/CircularProgressLiquid/types.d.ts.map +1 -0
  78. package/lib/types/index.d.ts +3 -0
  79. package/lib/types/index.d.ts.map +1 -0
  80. package/lib/types/types/core.d.ts +4 -0
  81. package/lib/types/types/core.d.ts.map +1 -0
  82. package/lib/types/utils/hexToCss/hexToCss.d.ts +16 -0
  83. package/lib/types/utils/hexToCss/hexToCss.d.ts.map +1 -0
  84. package/lib/types/utils/hexToCss/index.d.ts +2 -0
  85. package/lib/types/utils/hexToCss/index.d.ts.map +1 -0
  86. package/package.json +121 -0
@@ -0,0 +1,304 @@
1
+ import { hexToCss } from '../../utils/hexToCss/hexToCss.js';
2
+ import { BaseCircularProgress } from '../BaseCircularProgress/BaseCircularProgress.js';
3
+ import fragmentShaderSource from './shaders/fragment.frag.js';
4
+ import vertexShaderSource from './shaders/vertex.vert.js';
5
+
6
+ /**
7
+ * A circular progress indicator with a liquid/water effect.
8
+ * Uses WebGL 2.0 for rendering.
9
+ *
10
+ * @example
11
+ * const instance = new CircularProgressLiquid(canvas, {
12
+ * colors: [0x00c6ff, 0x0072ff],
13
+ * progress: 0.6,
14
+ * speed: 0.5,
15
+ * thickness: 30,
16
+ * volume: 0.5,
17
+ * });
18
+ *
19
+ * @see https://webeach.github.io/gl-circular-progress/CircularProgressLiquid.html - Live Demo
20
+ * @see https://github.com/webeach/gl-circular-progress/blob/main/docs/en/CircularProgressLiquid.md - Documentation
21
+ */
22
+ class CircularProgressLiquid extends BaseCircularProgress {
23
+ extraState;
24
+ animationFrameId = null;
25
+ flowTime = 0.0;
26
+ gl = null;
27
+ gradientTexture = null;
28
+ lastFrameTime = 0;
29
+ locations = null;
30
+ program = null;
31
+ targetProgress = 0.0;
32
+ velocity = 0.0;
33
+ visualProgress = 0.0;
34
+ /**
35
+ * Creates an instance of CircularProgressLiquid.
36
+ *
37
+ * @param canvas - The HTMLCanvasElement to render on.
38
+ * @param options - Configuration options.
39
+ */
40
+ constructor(canvas, options) {
41
+ super(canvas, options);
42
+ this.extraState = {
43
+ volume: options.volume ?? 0,
44
+ };
45
+ this.targetProgress = this.state.progress;
46
+ this.visualProgress = this.state.progress;
47
+ this.animate = this.animate.bind(this);
48
+ this.initWebGL();
49
+ this.createGradientTexture();
50
+ this.resize();
51
+ this.startAnimation();
52
+ }
53
+ /**
54
+ * Cleans up WebGL resources and stops animation.
55
+ */
56
+ destroy() {
57
+ super.destroy();
58
+ if (this.animationFrameId !== null) {
59
+ cancelAnimationFrame(this.animationFrameId);
60
+ }
61
+ if (this.gl) {
62
+ if (this.program) {
63
+ this.gl.deleteProgram(this.program);
64
+ }
65
+ if (this.gradientTexture) {
66
+ this.gl.deleteTexture(this.gradientTexture);
67
+ }
68
+ // Note: We don't lose the context, but we clean up our resources
69
+ }
70
+ }
71
+ /**
72
+ * Gets the current volume.
73
+ */
74
+ get volume() {
75
+ return this.extraState.volume;
76
+ }
77
+ /**
78
+ * Sets the volume and updates rendering.
79
+ */
80
+ set volume(value) {
81
+ this.extraState.volume = Math.max(0, Math.min(1, value));
82
+ this.updateVolume();
83
+ }
84
+ /**
85
+ * Handles resizing of the canvas and updates WebGL viewport.
86
+ */
87
+ resize() {
88
+ if (!this.gl || !this.locations) {
89
+ return;
90
+ }
91
+ this.gl.viewport(0, 0, this.canvasWidth, this.canvasHeight);
92
+ this.gl.uniform2f(this.locations.uResolution, this.canvasWidth, this.canvasHeight);
93
+ }
94
+ /**
95
+ * Updates WebGL uniforms when colors change.
96
+ */
97
+ updateColors() {
98
+ this.createGradientTexture();
99
+ }
100
+ /**
101
+ * Updates the progress value.
102
+ *
103
+ * @param value - The new progress value (0.0 to 1.0).
104
+ */
105
+ updateProgress(value) {
106
+ this.targetProgress = value;
107
+ }
108
+ /**
109
+ * Updates WebGL uniforms when direction changes.
110
+ */
111
+ updateReversed() {
112
+ if (!this.gl || !this.locations) {
113
+ return;
114
+ }
115
+ this.gl.uniform1f(this.locations.uReversed, this.state.reversed ? 1.0 : 0.0);
116
+ }
117
+ /**
118
+ * Updates WebGL uniforms when speed changes.
119
+ */
120
+ updateSpeed() {
121
+ // Speed is used in animate loop, no uniform to update
122
+ }
123
+ /**
124
+ * Updates WebGL uniforms when start angle changes.
125
+ */
126
+ updateStartAngle() {
127
+ if (!this.gl || !this.locations) {
128
+ return;
129
+ }
130
+ const startAngleRad = this.state.startAngle * (Math.PI / 180.0);
131
+ this.gl.uniform1f(this.locations.uStartAngle, startAngleRad);
132
+ }
133
+ /**
134
+ * Updates WebGL uniforms when thickness changes.
135
+ */
136
+ updateThickness() {
137
+ // Thickness is calculated in animate loop based on canvas size, no direct uniform update needed
138
+ }
139
+ updateVolume() {
140
+ if (!this.gl || !this.locations) {
141
+ return;
142
+ }
143
+ this.gl.uniform1f(this.locations.uVolume, this.extraState.volume);
144
+ }
145
+ /**
146
+ * The main animation loop.
147
+ */
148
+ animate(time) {
149
+ if (this.isDestroyed || !this.gl || !this.locations) {
150
+ return;
151
+ }
152
+ if (!this.lastFrameTime) {
153
+ this.lastFrameTime = time;
154
+ }
155
+ const deltaTime = (time - this.lastFrameTime) * 0.001;
156
+ this.lastFrameTime = time;
157
+ // Update State
158
+ this.flowTime += deltaTime * this.state.speed;
159
+ const diff = this.targetProgress - this.visualProgress;
160
+ this.visualProgress += diff * 0.1;
161
+ this.velocity += (diff * 8.0 - this.velocity) * 0.2;
162
+ // Render
163
+ const startAngleRad = this.state.startAngle * (Math.PI / 180.0);
164
+ this.gl.uniform1f(this.locations.uFlowTime, this.flowTime);
165
+ this.gl.uniform1f(this.locations.uProgress, this.visualProgress);
166
+ this.gl.uniform1f(this.locations.uVelocity, this.velocity);
167
+ this.gl.uniform1f(this.locations.uReversed, this.state.reversed ? 1.0 : 0.0);
168
+ this.gl.uniform1f(this.locations.uStartAngle, startAngleRad);
169
+ this.gl.uniform1f(this.locations.uVolume, this.extraState.volume);
170
+ this.gl.uniform1i(this.locations.uGradientTexture, 0);
171
+ // Calculate Radius Uniforms
172
+ // The shader expects uOuterRadius and uInnerRadius in normalized coordinates (0.0 to 1.0 of half-width?)
173
+ // In the user code:
174
+ // radiusPx = canvas.width / 2
175
+ // outerR = 0.98
176
+ // thicknessRatio = scaledPxThickness / radiusPx
177
+ // innerR = outerR - thicknessRatio
178
+ // We need to be careful about aspect ratio. The shader corrects for aspect ratio:
179
+ // st.x *= u_resolution.x / u_resolution.y;
180
+ // So 'radius' is in UV space (0-1 approx).
181
+ const radiusPx = Math.min(this.canvasWidth, this.canvasHeight) / 2;
182
+ const outerR = 0.98; // Leave a small margin
183
+ const thicknessRatio = (this.state.thickness * this.dpr) / radiusPx;
184
+ const innerR = outerR - thicknessRatio;
185
+ this.gl.uniform1f(this.locations.uOuterRadius, outerR);
186
+ this.gl.uniform1f(this.locations.uInnerRadius, Math.max(0.0, innerR));
187
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
188
+ this.animationFrameId = requestAnimationFrame(this.animate);
189
+ }
190
+ /**
191
+ * Creates the gradient texture from the colors option.
192
+ */
193
+ createGradientTexture() {
194
+ if (!this.gl) {
195
+ return;
196
+ }
197
+ this.gradientTexture = this.gl.createTexture();
198
+ this.gl.activeTexture(this.gl.TEXTURE0);
199
+ this.gl.bindTexture(this.gl.TEXTURE_2D, this.gradientTexture);
200
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
201
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
202
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
203
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
204
+ // Create gradient on a temporary 2D canvas
205
+ const gradientCanvas = document.createElement('canvas');
206
+ gradientCanvas.width = 256;
207
+ gradientCanvas.height = 1;
208
+ const ctx = gradientCanvas.getContext('2d');
209
+ if (ctx) {
210
+ const grad = ctx.createLinearGradient(0, 0, 256, 0);
211
+ const { colors } = this.state;
212
+ if (colors.length === 1) {
213
+ const color = hexToCss(colors[0]);
214
+ grad.addColorStop(0, color);
215
+ grad.addColorStop(1, color);
216
+ }
217
+ else {
218
+ colors.forEach((hex, index) => {
219
+ const stop = index / (colors.length - 1);
220
+ grad.addColorStop(stop, hexToCss(hex));
221
+ });
222
+ }
223
+ ctx.fillStyle = grad;
224
+ ctx.fillRect(0, 0, 256, 1);
225
+ this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, gradientCanvas);
226
+ }
227
+ }
228
+ /**
229
+ * Creates and compiles a shader.
230
+ */
231
+ createShader(type, source) {
232
+ if (!this.gl) {
233
+ return null;
234
+ }
235
+ const shader = this.gl.createShader(type);
236
+ if (!shader) {
237
+ return null;
238
+ }
239
+ this.gl.shaderSource(shader, source);
240
+ this.gl.compileShader(shader);
241
+ if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
242
+ console.error(this.gl.getShaderInfoLog(shader));
243
+ this.gl.deleteShader(shader);
244
+ return null;
245
+ }
246
+ return shader;
247
+ }
248
+ /**
249
+ * Initializes WebGL context and shaders.
250
+ */
251
+ initWebGL() {
252
+ this.gl = this.canvas.getContext('webgl2', {
253
+ alpha: true,
254
+ antialias: true,
255
+ });
256
+ if (!this.gl) {
257
+ console.error('WebGL 2.0 is not supported');
258
+ return;
259
+ }
260
+ const vertexShader = this.createShader(this.gl.VERTEX_SHADER, vertexShaderSource);
261
+ const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, fragmentShaderSource);
262
+ if (!vertexShader || !fragmentShader) {
263
+ return;
264
+ }
265
+ this.program = this.gl.createProgram();
266
+ if (!this.program) {
267
+ return;
268
+ }
269
+ this.gl.attachShader(this.program, vertexShader);
270
+ this.gl.attachShader(this.program, fragmentShader);
271
+ this.gl.linkProgram(this.program);
272
+ if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) {
273
+ console.error(this.gl.getProgramInfoLog(this.program));
274
+ return;
275
+ }
276
+ this.gl.useProgram(this.program);
277
+ // Setup Geometry (Full Quad)
278
+ const positionBuffer = this.gl.createBuffer();
279
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer);
280
+ this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]), this.gl.STATIC_DRAW);
281
+ const posLoc = this.gl.getAttribLocation(this.program, 'a_position');
282
+ this.gl.enableVertexAttribArray(posLoc);
283
+ this.gl.vertexAttribPointer(posLoc, 2, this.gl.FLOAT, false, 0, 0);
284
+ // Get Uniform Locations
285
+ this.locations = {
286
+ uFlowTime: this.gl.getUniformLocation(this.program, 'u_flowTime'),
287
+ uGradientTexture: this.gl.getUniformLocation(this.program, 'u_gradientTexture'),
288
+ uInnerRadius: this.gl.getUniformLocation(this.program, 'u_innerRadius'),
289
+ uOuterRadius: this.gl.getUniformLocation(this.program, 'u_outerRadius'),
290
+ uProgress: this.gl.getUniformLocation(this.program, 'u_progress'),
291
+ uResolution: this.gl.getUniformLocation(this.program, 'u_resolution'),
292
+ uReversed: this.gl.getUniformLocation(this.program, 'u_reversed'),
293
+ uStartAngle: this.gl.getUniformLocation(this.program, 'u_startAngle'),
294
+ uVelocity: this.gl.getUniformLocation(this.program, 'u_velocity'),
295
+ uVolume: this.gl.getUniformLocation(this.program, 'u_volume'),
296
+ };
297
+ }
298
+ startAnimation() {
299
+ this.animationFrameId = requestAnimationFrame(this.animate);
300
+ }
301
+ }
302
+
303
+ export { CircularProgressLiquid };
304
+ //# sourceMappingURL=CircularProgressLiquid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CircularProgressLiquid.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,2 @@
1
+ export { CircularProgressLiquid } from './CircularProgressLiquid.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ var fragmentShaderSource = "#version 300 es\nprecision mediump float;in vec2 v_uv;out vec4 fragColor;uniform float u_flowTime;uniform float u_progress;uniform float u_velocity;uniform vec2 u_resolution;uniform sampler2D u_gradientTexture;uniform float u_innerRadius;uniform float u_outerRadius;uniform float u_reversed;uniform float u_startAngle;uniform float u_volume;\n#define PI 3.14159265359\n#define TWO_PI 6.28318530718\nfloat random(in vec2 st){return fract(sin(dot(st.xy,vec2(12.9898,78.233)))*43758.5453123);}void main(){vec2 st=v_uv*2.0-1.0;st.x*=u_resolution.x/u_resolution.y;float radius=length(st);float absAngle=atan(st.x,st.y);if(absAngle<0.0)absAngle+=TWO_PI;float relAngle=absAngle-u_startAngle;relAngle=mod(relAngle,TWO_PI);if(relAngle<0.0)relAngle+=TWO_PI;if(u_reversed>0.5){relAngle=TWO_PI-relAngle;}float angle=mod(relAngle,TWO_PI);float centerRadius=(u_innerRadius+u_outerRadius)*0.5;float trackWidth=u_outerRadius-u_innerRadius;float safeTrackWidth=max(trackWidth,0.001);float progressAngle=u_progress*TWO_PI;float flowProfile=1.0-pow(abs(radius-centerRadius)/(safeTrackWidth*0.5),2.0);flowProfile=clamp(flowProfile,0.0,1.0);float pressureBulge=u_velocity*flowProfile*1.0;float ripples=sin(radius*40.0-u_flowTime*15.0)*0.03*abs(u_velocity);float activeBoundary=progressAngle+pressureBulge+ripples;float halfWidth=safeTrackWidth*0.5;float trackCenter=centerRadius;float capRadius=halfWidth;float roundingFactor=1.0;if(u_progress>0.9){roundingFactor=(1.0-u_progress)*10.0;}float effectiveCapRadius=capRadius*roundingFactor;float dEnd=(activeBoundary-angle)*trackCenter;float dStart=angle*trackCenter;float dSide=halfWidth-abs(radius-trackCenter);float headRounding=1.0;if(dEnd<effectiveCapRadius&&dSide<effectiveCapRadius){float cx=effectiveCapRadius-dEnd;float cy=effectiveCapRadius-dSide;float cornerDist=sqrt(cx*cx+cy*cy);headRounding=1.0-smoothstep(effectiveCapRadius-0.005,effectiveCapRadius+0.005,cornerDist);}float tailRounding=1.0;if(dStart<effectiveCapRadius&&dSide<effectiveCapRadius){float cx=effectiveCapRadius-dStart;float cy=effectiveCapRadius-dSide;float cornerDist=sqrt(cx*cx+cy*cy);tailRounding=1.0-smoothstep(effectiveCapRadius-0.005,effectiveCapRadius+0.005,cornerDist);}float roundingAlpha=headRounding*tailRounding;float fill=smoothstep(0.0,0.02,activeBoundary-angle);if(u_progress>0.99)fill=1.0;if(u_progress<0.02&&angle>6.0)fill=0.0;float ringAlpha=smoothstep(u_innerRadius-0.01,u_innerRadius,radius)-smoothstep(u_outerRadius,u_outerRadius+0.01,radius);float t=angle/TWO_PI;vec3 waterColor=texture(u_gradientTexture,vec2(t,0.5)).rgb;float flowLines=sin(angle*20.0-u_flowTime*5.0)*sin(radius*100.0)*0.1;waterColor+=vec3(flowLines);float distToHead=abs(angle-activeBoundary);float foamThickness=0.02+abs(u_velocity)*0.15;float foam=smoothstep(foamThickness,0.0,distToHead)*fill;float foamSuppress=1.0-smoothstep(0.95,1.0,u_progress);foam*=foamSuppress;float sparkle=random(v_uv*vec2(100.0,100.0)+u_flowTime);foam*=(0.8+0.5*sparkle);vec3 finalColor=mix(waterColor,vec3(1.0),foam);float trackCenterDist=abs(radius-centerRadius)/(safeTrackWidth*0.5);float cylinderNormalZ=sqrt(1.0-trackCenterDist*trackCenterDist);float specular=pow(cylinderNormalZ,3.0)*0.4;float edgeShadow=smoothstep(0.8,1.0,trackCenterDist);vec3 lightedColor=finalColor+vec3(specular);lightedColor*=(1.0-edgeShadow*0.5);finalColor=mix(finalColor,lightedColor,u_volume);float finalAlpha=ringAlpha*fill*roundingAlpha;fragColor=vec4(finalColor,finalAlpha);}";
2
+
3
+ export { fragmentShaderSource as default };
4
+ //# sourceMappingURL=fragment.frag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragment.frag.js","sources":["../../../../../src/classes/CircularProgressLiquid/shaders/fragment.frag"],"sourcesContent":["#version 300 es\nprecision mediump float;\n\nin vec2 v_uv;\nout vec4 fragColor;\n\nuniform float u_flowTime;\nuniform float u_progress;\nuniform float u_velocity;\nuniform vec2 u_resolution;\nuniform sampler2D u_gradientTexture;\nuniform float u_innerRadius;\nuniform float u_outerRadius;\n\nuniform float u_reversed;\nuniform float u_startAngle;\nuniform float u_volume; // New uniform for 3D/Lens effect\n\n#define PI 3.14159265359\n#define TWO_PI 6.28318530718\n\nfloat random(in vec2 st) {\n return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);\n}\n\nvoid main() {\n vec2 st = v_uv * 2.0 - 1.0;\n st.x *= u_resolution.x / u_resolution.y;\n\n float radius = length(st);\n\n // --- ANGLE CALCULATION ---\n float absAngle = atan(st.x, st.y);\n if (absAngle < 0.0) absAngle += TWO_PI;\n\n float relAngle = absAngle - u_startAngle;\n relAngle = mod(relAngle, TWO_PI);\n if (relAngle < 0.0) relAngle += TWO_PI;\n\n if (u_reversed > 0.5) {\n relAngle = TWO_PI - relAngle;\n }\n\n float angle = mod(relAngle, TWO_PI);\n\n float centerRadius = (u_innerRadius + u_outerRadius) * 0.5;\n float trackWidth = u_outerRadius - u_innerRadius;\n float safeTrackWidth = max(trackWidth, 0.001);\n float progressAngle = u_progress * TWO_PI;\n\n // --- PHYSICS ---\n // Calculate physics first so activeBoundary can be used in rounded caps for the head\n float flowProfile = 1.0 - pow(abs(radius - centerRadius) / (safeTrackWidth * 0.5), 2.0);\n flowProfile = clamp(flowProfile, 0.0, 1.0);\n\n float pressureBulge = u_velocity * flowProfile * 1.0;\n float ripples = sin(radius * 40.0 - u_flowTime * 15.0) * 0.03 * abs(u_velocity);\n\n float activeBoundary = progressAngle + pressureBulge + ripples;\n\n // --- ROUNDED CAPS (BORDER RADIUS) ---\n // Logic adapted from Fire shader\n float halfWidth = safeTrackWidth * 0.5;\n float trackCenter = centerRadius;\n \n // Arc length at center radius\n // We want full rounding when progress is small, and diminishing rounding as it approaches 100%\n float capRadius = halfWidth; // Full rounding radius\n \n float roundingFactor = 1.0;\n if (u_progress > 0.9) {\n roundingFactor = (1.0 - u_progress) * 10.0; // 1.0 at 0.9, 0.0 at 1.0\n }\n \n float effectiveCapRadius = capRadius * roundingFactor;\n \n // Use activeBoundary for the Head to allow bulge/stretch\n float dEnd = (activeBoundary - angle) * trackCenter;\n float dStart = angle * trackCenter;\n float dSide = halfWidth - abs(radius - trackCenter);\n \n // Head Rounding\n float headRounding = 1.0;\n if (dEnd < effectiveCapRadius && dSide < effectiveCapRadius) {\n float cx = effectiveCapRadius - dEnd;\n float cy = effectiveCapRadius - dSide;\n float cornerDist = sqrt(cx*cx + cy*cy);\n headRounding = 1.0 - smoothstep(effectiveCapRadius - 0.005, effectiveCapRadius + 0.005, cornerDist);\n }\n \n // Tail Rounding\n float tailRounding = 1.0;\n if (dStart < effectiveCapRadius && dSide < effectiveCapRadius) {\n float cx = effectiveCapRadius - dStart;\n float cy = effectiveCapRadius - dSide;\n float cornerDist = sqrt(cx*cx + cy*cy);\n tailRounding = 1.0 - smoothstep(effectiveCapRadius - 0.005, effectiveCapRadius + 0.005, cornerDist);\n }\n \n float roundingAlpha = headRounding * tailRounding;\n\n // --- FILL ---\n float fill = smoothstep(0.0, 0.02, activeBoundary - angle);\n if (u_progress > 0.99) fill = 1.0;\n if (u_progress < 0.02 && angle > 6.0) fill = 0.0;\n\n // --- COLOR ---\n float ringAlpha = smoothstep(u_innerRadius - 0.01, u_innerRadius, radius) -\n smoothstep(u_outerRadius, u_outerRadius + 0.01, radius);\n\n float t = angle / TWO_PI;\n vec3 waterColor = texture(u_gradientTexture, vec2(t, 0.5)).rgb;\n\n float flowLines = sin(angle * 20.0 - u_flowTime * 5.0) * sin(radius * 100.0) * 0.1;\n waterColor += vec3(flowLines);\n\n // --- FOAM ---\n float distToHead = abs(angle - activeBoundary);\n float foamThickness = 0.02 + abs(u_velocity) * 0.15;\n float foam = smoothstep(foamThickness, 0.0, distToHead) * fill;\n float foamSuppress = 1.0 - smoothstep(0.95, 1.0, u_progress);\n foam *= foamSuppress;\n\n float sparkle = random(v_uv * vec2(100.0, 100.0) + u_flowTime);\n foam *= (0.8 + 0.5 * sparkle);\n\n vec3 finalColor = mix(waterColor, vec3(1.0), foam);\n\n // --- VOLUME / 3D LENS EFFECT ---\n // Calculate a \"bulge\" factor based on distance from center of the track\n float trackCenterDist = abs(radius - centerRadius) / (safeTrackWidth * 0.5); // 0 at center, 1 at edges\n \n // Simple lighting approximation\n // Light comes from top-left (let's say). \n // We can compute a normal-like vector.\n // Ideally, we want the center of the track to be brighter (specular highlight) \n // and edges to be slightly darker or refractive.\n \n // 1. Fake refraction/distortion at edges: already somewhat covered by flowProfile usage?\n // Let's just add a specular highlight for \"volume\".\n \n float cylinderNormalZ = sqrt(1.0 - trackCenterDist * trackCenterDist); // 1.0 at center, 0.0 at edges (semi-circle profile)\n \n // Specular highlight running along the center of the tube\n // Light direction assumption: perpendicular to screen? Or fixed directional?\n // Let's do a simple \"glossy tube\" look.\n float specular = pow(cylinderNormalZ, 3.0) * 0.4;\n \n // Shadowing at edges\n float edgeShadow = smoothstep(0.8, 1.0, trackCenterDist);\n \n // Apply volume effect based on u_volume intensity\n // If u_volume is 0, we just use finalColor.\n // If u_volume is 1, we apply full lighting.\n \n vec3 lightedColor = finalColor + vec3(specular); // Add highlight\n lightedColor *= (1.0 - edgeShadow * 0.5); // Darken edges\n \n finalColor = mix(finalColor, lightedColor, u_volume);\n\n // --- TRANSPARENT BACKGROUND ---\n float finalAlpha = ringAlpha * fill * roundingAlpha;\n\n fragColor = vec4(finalColor, finalAlpha);\n}\n"],"names":[],"mappings":"AAAA,2BAAe,62GAA62G;;;;"}
@@ -0,0 +1,4 @@
1
+ var vertexShaderSource = "#version 300 es\nin vec2 a_position;out vec2 v_uv;void main(){v_uv=a_position*0.5+0.5;gl_Position=vec4(a_position,0.0,1.0);}";
2
+
3
+ export { vertexShaderSource as default };
4
+ //# sourceMappingURL=vertex.vert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vertex.vert.js","sources":["../../../../../src/classes/CircularProgressLiquid/shaders/vertex.vert"],"sourcesContent":["#version 300 es\nin vec2 a_position;\nout vec2 v_uv;\n\nvoid main() {\n v_uv = a_position * 0.5 + 0.5;\n gl_Position = vec4(a_position, 0.0, 1.0);\n}\n"],"names":[],"mappings":"AAAA,yBAAe,8HAA8H;;;;"}
@@ -0,0 +1,3 @@
1
+ export { CircularProgressLiquid } from './classes/CircularProgressLiquid/CircularProgressLiquid.js';
2
+ export { CircularProgressFire } from './classes/CircularProgressFire/CircularProgressFire.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Converts a hexadecimal color number to a CSS color string.
3
+ *
4
+ * @param hex - The hexadecimal color number (e.g., 0xFF5733).
5
+ * @returns The CSS color string in #RRGGBB format.
6
+ *
7
+ * @example
8
+ * const cssColor = hexToCss(0xFF5733);
9
+ * // Returns '#ff5733'
10
+ *
11
+ * @example
12
+ * const cssColor = hexToCss(0x0000FF);
13
+ * // Returns '#0000ff'
14
+ */
15
+ function hexToCss(hex) {
16
+ return '#' + hex.toString(16).padStart(6, '0');
17
+ }
18
+
19
+ export { hexToCss };
20
+ //# sourceMappingURL=hexToCss.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hexToCss.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,113 @@
1
+ import { Mutable } from '../../types/core';
2
+ import type { BaseCircularProgressOptions } from './types';
3
+ /**
4
+ * Abstract base class for circular progress indicators.
5
+ * Handles common state and lifecycle management.
6
+ */
7
+ export declare abstract class BaseCircularProgress {
8
+ protected readonly state: Required<Mutable<BaseCircularProgressOptions>>;
9
+ protected canvas: HTMLCanvasElement;
10
+ protected canvasHeight: number;
11
+ protected canvasWidth: number;
12
+ protected dpr: number;
13
+ protected isDestroyed: boolean;
14
+ protected resizeObserver: ResizeObserver | null;
15
+ /**
16
+ * Creates an instance of BaseCircularProgress.
17
+ *
18
+ * @param canvas - The HTMLCanvasElement to render on.
19
+ * @param options - Configuration options.
20
+ */
21
+ protected constructor(canvas: HTMLCanvasElement, options: BaseCircularProgressOptions);
22
+ /**
23
+ * Cleans up resources, event listeners, and stops animations.
24
+ * Must be called when the instance is no longer needed.
25
+ */
26
+ destroy(): void;
27
+ /**
28
+ * Gets the current colors array.
29
+ */
30
+ get colors(): [number, ...number[]];
31
+ /**
32
+ * Sets the colors array and updates rendering.
33
+ */
34
+ set colors(value: readonly [number, ...number[]]);
35
+ /**
36
+ * Gets the current progress value.
37
+ */
38
+ get progress(): number;
39
+ /**
40
+ * Sets the progress value.
41
+ */
42
+ set progress(value: number);
43
+ /**
44
+ * Gets whether the progress fills in reverse direction.
45
+ */
46
+ get reversed(): boolean;
47
+ /**
48
+ * Sets the reverse direction flag and updates rendering.
49
+ */
50
+ set reversed(value: boolean);
51
+ /**
52
+ * Gets the current animation speed.
53
+ */
54
+ get speed(): number;
55
+ /**
56
+ * Sets the animation speed and updates rendering.
57
+ */
58
+ set speed(value: number);
59
+ /**
60
+ * Gets the current start angle in degrees.
61
+ */
62
+ get startAngle(): number;
63
+ /**
64
+ * Sets the start angle and updates rendering.
65
+ */
66
+ set startAngle(value: number);
67
+ /**
68
+ * Gets the current thickness in pixels.
69
+ */
70
+ get thickness(): number;
71
+ /**
72
+ * Sets the thickness and updates rendering.
73
+ */
74
+ set thickness(value: number);
75
+ /**
76
+ * Handles canvas resize events from ResizeObserver.
77
+ * Updates canvas dimensions with devicePixelRatio and calls resize().
78
+ */
79
+ protected handleResize(): void;
80
+ /**
81
+ * Handles rendering updates when canvas is resized.
82
+ * Should be implemented by subclasses to adjust WebGL viewport and uniforms.
83
+ */
84
+ protected abstract resize(): void;
85
+ /**
86
+ * Updates WebGL uniforms when colors change.
87
+ * Should regenerate gradient textures or update color uniforms.
88
+ */
89
+ protected abstract updateColors(): void;
90
+ /**
91
+ * Updates the progress value.
92
+ *
93
+ * @param value - The new progress value (0.0 to 1.0).
94
+ */
95
+ protected abstract updateProgress(value: number): void;
96
+ /**
97
+ * Updates WebGL uniforms when direction changes.
98
+ */
99
+ protected abstract updateReversed(): void;
100
+ /**
101
+ * Updates WebGL uniforms when speed changes.
102
+ */
103
+ protected abstract updateSpeed(): void;
104
+ /**
105
+ * Updates WebGL uniforms when start angle changes.
106
+ */
107
+ protected abstract updateStartAngle(): void;
108
+ /**
109
+ * Updates WebGL uniforms when thickness changes.
110
+ */
111
+ protected abstract updateThickness(): void;
112
+ }
113
+ //# sourceMappingURL=BaseCircularProgress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseCircularProgress.d.ts","sourceRoot":"","sources":["../../../../src/classes/BaseCircularProgress/BaseCircularProgress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAS3C,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,SAAS,CAAC;AAE3D;;;GAGG;AACH,8BAAsB,oBAAoB;IACxC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC,CAAC;IAEzE,SAAS,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACpC,SAAS,CAAC,YAAY,EAAE,MAAM,CAAK;IACnC,SAAS,CAAC,WAAW,EAAE,MAAM,CAAK;IAClC,SAAS,CAAC,GAAG,EAAE,MAAM,CAAK;IAC1B,SAAS,CAAC,WAAW,EAAE,OAAO,CAAS;IACvC,SAAS,CAAC,cAAc,EAAE,cAAc,GAAG,IAAI,CAAQ;IAEvD;;;;;OAKG;IACH,SAAS,aACP,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,2BAA2B;IAsBtC;;;OAGG;IACI,OAAO,IAAI,IAAI;IAWtB;;OAEG;IACH,IAAW,MAAM,IAAI,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAEzC;IAED;;OAEG;IACH,IAAW,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,EAGtD;IAED;;OAEG;IACH,IAAW,QAAQ,IAAI,MAAM,CAE5B;IAED;;OAEG;IACH,IAAW,QAAQ,CAAC,KAAK,EAAE,MAAM,EAIhC;IAED;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED;;OAEG;IACH,IAAW,QAAQ,CAAC,KAAK,EAAE,OAAO,EAGjC;IAED;;OAEG;IACH,IAAW,KAAK,IAAI,MAAM,CAEzB;IAED;;OAEG;IACH,IAAW,KAAK,CAAC,KAAK,EAAE,MAAM,EAG7B;IAED;;OAEG;IACH,IAAW,UAAU,IAAI,MAAM,CAE9B;IAED;;OAEG;IACH,IAAW,UAAU,CAAC,KAAK,EAAE,MAAM,EAGlC;IAED;;OAEG;IACH,IAAW,SAAS,IAAI,MAAM,CAE7B;IAED;;OAEG;IACH,IAAW,SAAS,CAAC,KAAK,EAAE,MAAM,EAGjC;IACD;;;OAGG;IACH,SAAS,CAAC,YAAY,IAAI,IAAI;IAoB9B;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,MAAM,IAAI,IAAI;IAEjC;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,YAAY,IAAI,IAAI;IAEvC;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAEtD;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,IAAI;IAEzC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,WAAW,IAAI,IAAI;IAEtC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,gBAAgB,IAAI,IAAI;IAE3C;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,eAAe,IAAI,IAAI;CAC3C"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Default colors (Black).
3
+ */
4
+ export declare const DEFAULT_COLORS: number[];
5
+ /**
6
+ * Default reversed state (false).
7
+ */
8
+ export declare const DEFAULT_REVERSED = false;
9
+ /**
10
+ * Default animation speed (1.0).
11
+ */
12
+ export declare const DEFAULT_SPEED = 1;
13
+ /**
14
+ * Default start angle (0 degrees).
15
+ */
16
+ export declare const DEFAULT_START_ANGLE = 0;
17
+ /**
18
+ * Default thickness (10px).
19
+ */
20
+ export declare const DEFAULT_THICKNESS = 10;
21
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/classes/BaseCircularProgress/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,cAAc,UAAa,CAAC;AAEzC;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAAQ,CAAC;AAEtC;;GAEG;AACH,eAAO,MAAM,aAAa,IAAM,CAAC;AAEjC;;GAEG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;GAEG;AACH,eAAO,MAAM,iBAAiB,KAAK,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { BaseCircularProgress } from './BaseCircularProgress';
2
+ export type { BaseCircularProgressOptions } from './types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/classes/BaseCircularProgress/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EAAE,2BAA2B,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Options for the BaseCircularProgress class.
3
+ */
4
+ export type BaseCircularProgressOptions = {
5
+ /**
6
+ * Array of colors in hex format (e.g., 0x000000).
7
+ * Used for gradients or solid colors.
8
+ * @default [0x000000]
9
+ */
10
+ readonly colors: readonly [number, ...number[]];
11
+ /**
12
+ * The initial progress value (0.0 to 1.0).
13
+ * @default 0
14
+ */
15
+ readonly progress?: number;
16
+ /**
17
+ * Whether the progress should fill in the reverse direction.
18
+ * @default false
19
+ */
20
+ readonly reversed?: boolean;
21
+ /**
22
+ * The animation speed or flow speed.
23
+ * Arbitrary unit depending on implementation.
24
+ * @default 1.0
25
+ */
26
+ readonly speed?: number;
27
+ /**
28
+ * The starting angle of the progress in degrees.
29
+ * 0 is usually the top or right depending on implementation.
30
+ * @default 0
31
+ */
32
+ readonly startAngle?: number;
33
+ /**
34
+ * The thickness of the progress ring in pixels.
35
+ * Relative to the canvas size.
36
+ * @default 10
37
+ */
38
+ readonly thickness?: number;
39
+ };
40
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/classes/BaseCircularProgress/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IAEhD;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAE5B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAE7B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC"}