@principal-ai/logo-component 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.TelemetryReveal = void 0;
37
+ const react_1 = __importStar(require("react"));
38
+ const presets_1 = require("./presets");
39
+ let globalIdCounter = 0;
40
+ /**
41
+ * Estimates path length from simple SVG path data (M/L commands).
42
+ */
43
+ function estimatePathLength(d) {
44
+ const coords = d.match(/-?\d+\.?\d*/g);
45
+ if (!coords || coords.length < 4)
46
+ return 100;
47
+ let totalLength = 0;
48
+ for (let i = 2; i < coords.length; i += 2) {
49
+ const x1 = parseFloat(coords[i - 2]);
50
+ const y1 = parseFloat(coords[i - 1]);
51
+ const x2 = parseFloat(coords[i]);
52
+ const y2 = parseFloat(coords[i + 1]);
53
+ totalLength += Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
54
+ }
55
+ return totalLength || 100;
56
+ }
57
+ /**
58
+ * Parse start and end points from path data (M x1,y1 L x2,y2)
59
+ */
60
+ function getPathEndpoints(d) {
61
+ const coords = d.match(/-?\d+\.?\d*/g);
62
+ if (!coords || coords.length < 4) {
63
+ return { start: { x: 0, y: 0 }, end: { x: 0, y: 0 } };
64
+ }
65
+ return {
66
+ start: { x: parseFloat(coords[0]), y: parseFloat(coords[1]) },
67
+ end: { x: parseFloat(coords[coords.length - 2]), y: parseFloat(coords[coords.length - 1]) },
68
+ };
69
+ }
70
+ /**
71
+ * Seeded random for consistent chaos patterns
72
+ */
73
+ function seededRandom(seed) {
74
+ return () => {
75
+ seed = (seed * 1103515245 + 12345) & 0x7fffffff;
76
+ return seed / 0x7fffffff;
77
+ };
78
+ }
79
+ const TelemetryReveal = ({ width = 200, height = 200, paths: customPaths, preset = "network", viewBox, chaosMode = "none", chaosDuration = 3, dotsDuration = 2, flowDuration = 2, flowDelay = 0.3, particlesPerPath = 2, particleRadius = 3, color = "currentColor", particleColor, strokeWidth = 2, opacity = 0.9, showGlow = true, fadeAfterAssembly = true, fadeOpacity = 0.5, loop = true, loopDelay = 1, }) => {
80
+ const idRef = (0, react_1.useRef)(null);
81
+ if (idRef.current === null) {
82
+ idRef.current = `tr${globalIdCounter++}`;
83
+ }
84
+ const uniqueId = idRef.current;
85
+ const finalParticleColor = particleColor || color;
86
+ const isFragmented = chaosMode === "fragmented";
87
+ // Loop via remount
88
+ const [cycle, setCycle] = (0, react_1.useState)(0);
89
+ // Resolve paths from custom or preset
90
+ const resolvedPaths = (0, react_1.useMemo)(() => {
91
+ if (customPaths)
92
+ return customPaths;
93
+ const presetConfig = presets_1.TELEMETRY_PRESETS[preset];
94
+ return (presetConfig === null || presetConfig === void 0 ? void 0 : presetConfig.paths) || [];
95
+ }, [customPaths, preset]);
96
+ // Resolve viewBox
97
+ const resolvedViewBox = (0, react_1.useMemo)(() => {
98
+ var _a;
99
+ if (viewBox)
100
+ return viewBox;
101
+ if (!customPaths && ((_a = presets_1.TELEMETRY_PRESETS[preset]) === null || _a === void 0 ? void 0 : _a.viewBox)) {
102
+ return presets_1.TELEMETRY_PRESETS[preset].viewBox;
103
+ }
104
+ return "0 0 200 200";
105
+ }, [viewBox, customPaths, preset]);
106
+ // Calculate path lengths and endpoints
107
+ const pathData = (0, react_1.useMemo)(() => {
108
+ return resolvedPaths.map((p) => ({
109
+ length: estimatePathLength(p.d),
110
+ endpoints: getPathEndpoints(p.d),
111
+ }));
112
+ }, [resolvedPaths]);
113
+ // Generate offsets for fragmented mode
114
+ const fragmentOffsets = (0, react_1.useMemo)(() => {
115
+ const random = seededRandom(42);
116
+ return resolvedPaths.map(() => ({
117
+ x: (random() - 0.5) * 80,
118
+ y: (random() - 0.5) * 80,
119
+ }));
120
+ }, [resolvedPaths]);
121
+ // Timing calculations
122
+ // In fragmented mode: dots appear, extend to lines, then assemble
123
+ // Phase 1: Dots appear one at a time (dotsDuration / 2)
124
+ // Phase 2: Lines draw one at a time (dotsDuration / 2)
125
+ // Phase 3: Lines move to final position one at a time (chaosDuration)
126
+ // Phase 4: Particles flow
127
+ const numPaths = resolvedPaths.length || 1;
128
+ const perItemDotsDuration = (dotsDuration * 0.5) / numPaths;
129
+ const perItemLinesDuration = (dotsDuration * 0.5) / numPaths;
130
+ const perItemAssemblyDuration = chaosDuration / numPaths;
131
+ const dotsPhaseEnd = dotsDuration * 0.5;
132
+ const linesPhaseEnd = dotsDuration;
133
+ const assemblyEndTime = isFragmented ? dotsDuration + chaosDuration : 0;
134
+ const flowBeginTime = assemblyEndTime + flowDelay;
135
+ // Total animation duration (last particle finishes at flowBeginTime + flowDuration)
136
+ const totalDuration = flowBeginTime + flowDuration;
137
+ // Remount-based loop
138
+ (0, react_1.useEffect)(() => {
139
+ if (!loop)
140
+ return;
141
+ const timeout = setTimeout(() => {
142
+ setCycle((c) => c + 1);
143
+ }, (totalDuration + loopDelay) * 1000);
144
+ return () => clearTimeout(timeout);
145
+ }, [loop, totalDuration, loopDelay, cycle]);
146
+ return (react_1.default.createElement("svg", { key: cycle, width: width, height: height, viewBox: resolvedViewBox, xmlns: "http://www.w3.org/2000/svg", style: { opacity } },
147
+ react_1.default.createElement("defs", null,
148
+ showGlow && (react_1.default.createElement("filter", { id: `glow-${uniqueId}`, x: "-50%", y: "-50%", width: "200%", height: "200%" },
149
+ react_1.default.createElement("feGaussianBlur", { stdDeviation: "2", result: "blur" }),
150
+ react_1.default.createElement("feMerge", null,
151
+ react_1.default.createElement("feMergeNode", { in: "blur" }),
152
+ react_1.default.createElement("feMergeNode", { in: "SourceGraphic" })))),
153
+ react_1.default.createElement("radialGradient", { id: `particleGradient-${uniqueId}`, cx: "50%", cy: "50%", r: "50%" },
154
+ react_1.default.createElement("stop", { offset: "0%", style: { stopColor: finalParticleColor, stopOpacity: 1 } }),
155
+ react_1.default.createElement("stop", { offset: "100%", style: { stopColor: finalParticleColor, stopOpacity: 0.3 } }))),
156
+ react_1.default.createElement("g", { className: "telemetry-paths" }, resolvedPaths.map((path, index) => {
157
+ const offset = fragmentOffsets[index];
158
+ const { length: pathLength, endpoints } = pathData[index] || { length: 100, endpoints: { start: { x: 0, y: 0 }, end: { x: 0, y: 0 } } };
159
+ const dotRadius = strokeWidth / 2;
160
+ return (react_1.default.createElement("g", { key: path.id || `path-${index}`, transform: isFragmented ? `translate(${offset.x}, ${offset.y})` : undefined },
161
+ isFragmented && (react_1.default.createElement(react_1.default.Fragment, null,
162
+ react_1.default.createElement("circle", { cx: endpoints.start.x, cy: endpoints.start.y, r: dotRadius, fill: color, opacity: "0" },
163
+ react_1.default.createElement("animate", { attributeName: "opacity", from: "0", to: "1", dur: "0.15s", begin: `${index * perItemDotsDuration}s`, fill: "freeze" }),
164
+ react_1.default.createElement("animate", { attributeName: "opacity", from: "1", to: "0", dur: "0.2s", begin: `${dotsPhaseEnd + (index + 1) * perItemLinesDuration}s`, fill: "freeze" })),
165
+ react_1.default.createElement("circle", { cx: endpoints.end.x, cy: endpoints.end.y, r: dotRadius, fill: color, opacity: "0" },
166
+ react_1.default.createElement("animate", { attributeName: "opacity", from: "0", to: "1", dur: "0.15s", begin: `${index * perItemDotsDuration}s`, fill: "freeze" }),
167
+ react_1.default.createElement("animate", { attributeName: "opacity", from: "1", to: "0", dur: "0.2s", begin: `${dotsPhaseEnd + (index + 1) * perItemLinesDuration}s`, fill: "freeze" })))),
168
+ react_1.default.createElement("path", { d: path.d, fill: "none", stroke: color, strokeWidth: strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", opacity: isFragmented ? 0 : 1, strokeDasharray: isFragmented ? pathLength : undefined, strokeDashoffset: isFragmented ? pathLength : undefined },
169
+ isFragmented && (react_1.default.createElement(react_1.default.Fragment, null,
170
+ react_1.default.createElement("animate", { attributeName: "opacity", values: `0;1;1;${fadeAfterAssembly ? fadeOpacity : 1}`, keyTimes: "0;0.1;0.9;1", dur: `${perItemLinesDuration + chaosDuration}s`, begin: `${dotsPhaseEnd + index * perItemLinesDuration}s`, fill: "freeze" }),
171
+ react_1.default.createElement("animate", { attributeName: "stroke-dashoffset", from: pathLength, to: "0", dur: `${perItemLinesDuration}s`, begin: `${dotsPhaseEnd + index * perItemLinesDuration}s`, fill: "freeze", calcMode: "spline", keySplines: "0.4 0 0.2 1", keyTimes: "0;1" }))),
172
+ !isFragmented && fadeAfterAssembly && (react_1.default.createElement("animate", { attributeName: "opacity", from: "1", to: fadeOpacity, dur: "0.5s", begin: `${flowBeginTime}s`, fill: "freeze" }))),
173
+ isFragmented && (react_1.default.createElement("animateTransform", { attributeName: "transform", type: "translate", from: `${offset.x} ${offset.y}`, to: "0 0", dur: `${perItemAssemblyDuration}s`, begin: `${linesPhaseEnd + index * perItemAssemblyDuration}s`, fill: "freeze", calcMode: "spline", keySplines: "0.33 0 0.2 1", keyTimes: "0;1" }))));
174
+ })),
175
+ react_1.default.createElement("g", { className: "telemetry-particles", filter: showGlow ? `url(#glow-${uniqueId})` : undefined }, resolvedPaths.flatMap((path, pathIndex) => {
176
+ return Array.from({ length: particlesPerPath }).map((_, particleIndex) => {
177
+ const particleDelay = (particleIndex / particlesPerPath) * flowDuration;
178
+ const beginTime = flowBeginTime + particleDelay;
179
+ return (react_1.default.createElement("circle", { key: `particle-${pathIndex}-${particleIndex}`, r: particleRadius, fill: `url(#particleGradient-${uniqueId})`, opacity: "0" },
180
+ react_1.default.createElement("animateMotion", { dur: `${flowDuration}s`, begin: `${beginTime}s`, fill: "freeze", path: path.d }),
181
+ react_1.default.createElement("animate", { attributeName: "opacity", values: "0;1;1;0", keyTimes: "0;0.1;0.9;1", dur: `${flowDuration}s`, begin: `${beginTime}s`, fill: "freeze" })));
182
+ });
183
+ }))));
184
+ };
185
+ exports.TelemetryReveal = TelemetryReveal;
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ type ChaosMode = "none" | "fragmented";
3
+ interface TextRevealProps {
4
+ /** The text to display */
5
+ text: string;
6
+ /** Width of the SVG container */
7
+ width?: number;
8
+ /** Height of the SVG container */
9
+ height?: number;
10
+ /** Scale factor for the text (default 1) */
11
+ scale?: number;
12
+ /** Letter spacing in units (default 4) */
13
+ letterSpacing?: number;
14
+ /** Center the text in the container */
15
+ centerText?: boolean;
16
+ chaosMode?: ChaosMode;
17
+ chaosDuration?: number;
18
+ dotsDuration?: number;
19
+ flowDuration?: number;
20
+ flowDelay?: number;
21
+ particlesPerPath?: number;
22
+ particleRadius?: number;
23
+ color?: string;
24
+ particleColor?: string;
25
+ strokeWidth?: number;
26
+ opacity?: number;
27
+ showGlow?: boolean;
28
+ fadeAfterAssembly?: boolean;
29
+ fadeOpacity?: number;
30
+ loop?: boolean;
31
+ loopDelay?: number;
32
+ }
33
+ export declare const TextReveal: React.FC<TextRevealProps>;
34
+ export {};
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.TextReveal = void 0;
37
+ const react_1 = __importStar(require("react"));
38
+ const strokeCharacters_1 = require("./strokeCharacters");
39
+ let globalIdCounter = 0;
40
+ /**
41
+ * Estimates path length from SVG path data.
42
+ */
43
+ function estimatePathLength(d) {
44
+ const coords = d.match(/-?\d+\.?\d*/g);
45
+ if (!coords || coords.length < 4)
46
+ return 100;
47
+ let totalLength = 0;
48
+ for (let i = 2; i < coords.length; i += 2) {
49
+ const x1 = parseFloat(coords[i - 2]);
50
+ const y1 = parseFloat(coords[i - 1]);
51
+ const x2 = parseFloat(coords[i]);
52
+ const y2 = parseFloat(coords[i + 1]);
53
+ totalLength += Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
54
+ }
55
+ return totalLength || 100;
56
+ }
57
+ /**
58
+ * Parse start and end points from path data
59
+ */
60
+ function getPathEndpoints(d) {
61
+ const coords = d.match(/-?\d+\.?\d*/g);
62
+ if (!coords || coords.length < 4) {
63
+ return { start: { x: 0, y: 0 }, end: { x: 0, y: 0 } };
64
+ }
65
+ return {
66
+ start: { x: parseFloat(coords[0]), y: parseFloat(coords[1]) },
67
+ end: { x: parseFloat(coords[coords.length - 2]), y: parseFloat(coords[coords.length - 1]) },
68
+ };
69
+ }
70
+ /**
71
+ * Seeded random for consistent chaos patterns
72
+ */
73
+ function seededRandom(seed) {
74
+ return () => {
75
+ seed = (seed * 1103515245 + 12345) & 0x7fffffff;
76
+ return seed / 0x7fffffff;
77
+ };
78
+ }
79
+ const TextReveal = ({ text, width = 400, height = 100, scale = 1, letterSpacing = 4, centerText = true, chaosMode = "none", chaosDuration = 2, dotsDuration = 1.5, flowDuration = 2, flowDelay = 0.3, particlesPerPath = 1, particleRadius = 2.5, color = "currentColor", particleColor, strokeWidth = 2, opacity = 0.9, showGlow = true, fadeAfterAssembly = true, fadeOpacity = 0.5, loop = true, loopDelay = 1, }) => {
80
+ const idRef = (0, react_1.useRef)(null);
81
+ if (idRef.current === null) {
82
+ idRef.current = `txr${globalIdCounter++}`;
83
+ }
84
+ const uniqueId = idRef.current;
85
+ const finalParticleColor = particleColor || color;
86
+ const isFragmented = chaosMode === "fragmented";
87
+ // Loop via remount
88
+ const [cycle, setCycle] = (0, react_1.useState)(0);
89
+ // Layout the text into paths
90
+ const { paths: layoutPaths, width: textWidth, height: textHeight } = (0, react_1.useMemo)(() => {
91
+ return (0, strokeCharacters_1.layoutText)(text, { letterSpacing, scale });
92
+ }, [text, letterSpacing, scale]);
93
+ // Calculate centering offset
94
+ const offsetX = centerText ? (width - textWidth) / 2 : 0;
95
+ const offsetY = centerText ? (height - textHeight) / 2 : 0;
96
+ // Apply offset to paths
97
+ const resolvedPaths = (0, react_1.useMemo)(() => {
98
+ return layoutPaths.map((path) => (Object.assign(Object.assign({}, path), { d: offsetPath(path.d, offsetX, offsetY) })));
99
+ }, [layoutPaths, offsetX, offsetY]);
100
+ // Calculate path data
101
+ const pathData = (0, react_1.useMemo)(() => {
102
+ return resolvedPaths.map((p) => ({
103
+ length: estimatePathLength(p.d),
104
+ endpoints: getPathEndpoints(p.d),
105
+ }));
106
+ }, [resolvedPaths]);
107
+ // Generate offsets for fragmented mode
108
+ const fragmentOffsets = (0, react_1.useMemo)(() => {
109
+ const random = seededRandom(42);
110
+ return resolvedPaths.map(() => ({
111
+ x: (random() - 0.5) * 120,
112
+ y: (random() - 0.5) * 80,
113
+ }));
114
+ }, [resolvedPaths]);
115
+ // Timing calculations
116
+ const numPaths = resolvedPaths.length || 1;
117
+ const perItemDotsDuration = (dotsDuration * 0.5) / numPaths;
118
+ const perItemLinesDuration = (dotsDuration * 0.5) / numPaths;
119
+ const perItemAssemblyDuration = chaosDuration / numPaths;
120
+ const dotsPhaseEnd = dotsDuration * 0.5;
121
+ const linesPhaseEnd = dotsDuration;
122
+ const assemblyEndTime = isFragmented ? dotsDuration + chaosDuration : 0;
123
+ const flowBeginTime = assemblyEndTime + flowDelay;
124
+ // Total animation duration
125
+ const totalDuration = flowBeginTime + flowDuration;
126
+ // Remount-based loop
127
+ (0, react_1.useEffect)(() => {
128
+ if (!loop)
129
+ return;
130
+ const timeout = setTimeout(() => {
131
+ setCycle((c) => c + 1);
132
+ }, (totalDuration + loopDelay) * 1000);
133
+ return () => clearTimeout(timeout);
134
+ }, [loop, totalDuration, loopDelay, cycle]);
135
+ // Calculate viewBox to fit content
136
+ const viewBox = `0 0 ${width} ${height}`;
137
+ return (react_1.default.createElement("svg", { key: cycle, width: width, height: height, viewBox: viewBox, xmlns: "http://www.w3.org/2000/svg", style: { opacity } },
138
+ react_1.default.createElement("defs", null,
139
+ showGlow && (react_1.default.createElement("filter", { id: `glow-${uniqueId}`, x: "-50%", y: "-50%", width: "200%", height: "200%" },
140
+ react_1.default.createElement("feGaussianBlur", { stdDeviation: "2", result: "blur" }),
141
+ react_1.default.createElement("feMerge", null,
142
+ react_1.default.createElement("feMergeNode", { in: "blur" }),
143
+ react_1.default.createElement("feMergeNode", { in: "SourceGraphic" })))),
144
+ react_1.default.createElement("radialGradient", { id: `particleGradient-${uniqueId}`, cx: "50%", cy: "50%", r: "50%" },
145
+ react_1.default.createElement("stop", { offset: "0%", style: { stopColor: finalParticleColor, stopOpacity: 1 } }),
146
+ react_1.default.createElement("stop", { offset: "100%", style: { stopColor: finalParticleColor, stopOpacity: 0.3 } }))),
147
+ react_1.default.createElement("g", { className: "text-paths" }, resolvedPaths.map((path, index) => {
148
+ const offset = fragmentOffsets[index];
149
+ const { length: pathLength, endpoints } = pathData[index] || {
150
+ length: 100,
151
+ endpoints: { start: { x: 0, y: 0 }, end: { x: 0, y: 0 } },
152
+ };
153
+ const dotRadius = strokeWidth / 2;
154
+ return (react_1.default.createElement("g", { key: path.id, transform: isFragmented ? `translate(${offset.x}, ${offset.y})` : undefined },
155
+ isFragmented && (react_1.default.createElement(react_1.default.Fragment, null,
156
+ react_1.default.createElement("circle", { cx: endpoints.start.x, cy: endpoints.start.y, r: dotRadius, fill: color, opacity: "0" },
157
+ react_1.default.createElement("animate", { attributeName: "opacity", from: "0", to: "1", dur: "0.15s", begin: `${index * perItemDotsDuration}s`, fill: "freeze" }),
158
+ react_1.default.createElement("animate", { attributeName: "opacity", from: "1", to: "0", dur: "0.2s", begin: `${dotsPhaseEnd + (index + 1) * perItemLinesDuration}s`, fill: "freeze" })),
159
+ react_1.default.createElement("circle", { cx: endpoints.end.x, cy: endpoints.end.y, r: dotRadius, fill: color, opacity: "0" },
160
+ react_1.default.createElement("animate", { attributeName: "opacity", from: "0", to: "1", dur: "0.15s", begin: `${index * perItemDotsDuration}s`, fill: "freeze" }),
161
+ react_1.default.createElement("animate", { attributeName: "opacity", from: "1", to: "0", dur: "0.2s", begin: `${dotsPhaseEnd + (index + 1) * perItemLinesDuration}s`, fill: "freeze" })))),
162
+ react_1.default.createElement("path", { d: path.d, fill: "none", stroke: color, strokeWidth: strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", opacity: isFragmented ? 0 : 1, strokeDasharray: isFragmented ? pathLength : undefined, strokeDashoffset: isFragmented ? pathLength : undefined },
163
+ isFragmented && (react_1.default.createElement(react_1.default.Fragment, null,
164
+ react_1.default.createElement("animate", { attributeName: "opacity", values: `0;1;1;${fadeAfterAssembly ? fadeOpacity : 1}`, keyTimes: "0;0.1;0.9;1", dur: `${perItemLinesDuration + chaosDuration}s`, begin: `${dotsPhaseEnd + index * perItemLinesDuration}s`, fill: "freeze" }),
165
+ react_1.default.createElement("animate", { attributeName: "stroke-dashoffset", from: pathLength, to: "0", dur: `${perItemLinesDuration}s`, begin: `${dotsPhaseEnd + index * perItemLinesDuration}s`, fill: "freeze", calcMode: "spline", keySplines: "0.4 0 0.2 1", keyTimes: "0;1" }))),
166
+ !isFragmented && fadeAfterAssembly && (react_1.default.createElement("animate", { attributeName: "opacity", from: "1", to: fadeOpacity, dur: "0.5s", begin: `${flowBeginTime}s`, fill: "freeze" }))),
167
+ isFragmented && (react_1.default.createElement("animateTransform", { attributeName: "transform", type: "translate", from: `${offset.x} ${offset.y}`, to: "0 0", dur: `${perItemAssemblyDuration}s`, begin: `${linesPhaseEnd + index * perItemAssemblyDuration}s`, fill: "freeze", calcMode: "spline", keySplines: "0.33 0 0.2 1", keyTimes: "0;1" }))));
168
+ })),
169
+ react_1.default.createElement("g", { className: "text-particles", filter: showGlow ? `url(#glow-${uniqueId})` : undefined }, resolvedPaths.flatMap((path, pathIndex) => {
170
+ return Array.from({ length: particlesPerPath }).map((_, particleIndex) => {
171
+ const particleDelay = (particleIndex / particlesPerPath) * flowDuration;
172
+ const beginTime = flowBeginTime + particleDelay;
173
+ return (react_1.default.createElement("circle", { key: `particle-${pathIndex}-${particleIndex}`, r: particleRadius, fill: `url(#particleGradient-${uniqueId})`, opacity: "0" },
174
+ react_1.default.createElement("animateMotion", { dur: `${flowDuration}s`, begin: `${beginTime}s`, fill: "freeze", path: path.d }),
175
+ react_1.default.createElement("animate", { attributeName: "opacity", values: "0;1;1;0", keyTimes: "0;0.1;0.9;1", dur: `${flowDuration}s`, begin: `${beginTime}s`, fill: "freeze" })));
176
+ });
177
+ }))));
178
+ };
179
+ exports.TextReveal = TextReveal;
180
+ /**
181
+ * Offset all coordinates in a path by the given amounts
182
+ */
183
+ function offsetPath(d, offsetX, offsetY) {
184
+ let coordIndex = 0;
185
+ return d.replace(/-?\d+\.?\d*/g, (match) => {
186
+ const num = parseFloat(match);
187
+ const isY = coordIndex % 2 === 1;
188
+ coordIndex++;
189
+ if (isY) {
190
+ return String(num + offsetY);
191
+ }
192
+ else {
193
+ return String(num + offsetX);
194
+ }
195
+ });
196
+ }