ink-motion 0.1.0 → 1.0.1
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.
- package/README.md +2 -2
- package/dist/index.cjs +128 -93
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +109 -62
- package/dist/index.d.ts +109 -62
- package/dist/index.js +113 -78
- package/dist/index.js.map +1 -1
- package/package.json +38 -33
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Text } from 'ink';
|
|
2
|
-
import
|
|
2
|
+
import { useRef, useEffect, useState, useMemo } from 'react';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
5
|
|
|
5
|
-
// src/components/
|
|
6
|
+
// src/components/Fade.tsx
|
|
6
7
|
var TARGET_FPS = 60;
|
|
7
8
|
var FRAME_INTERVAL_MS = 1e3 / TARGET_FPS;
|
|
8
9
|
function useAnimationFrame(callback, enabled = true) {
|
|
@@ -35,6 +36,15 @@ function useAnimationFrame(callback, enabled = true) {
|
|
|
35
36
|
};
|
|
36
37
|
}, [enabled]);
|
|
37
38
|
}
|
|
39
|
+
|
|
40
|
+
// src/hooks/useElapsedTime.ts
|
|
41
|
+
function useElapsedTime(enabled = true, speed = 1) {
|
|
42
|
+
const [elapsedTime, setElapsedTime] = useState(0);
|
|
43
|
+
useAnimationFrame((deltaTime) => {
|
|
44
|
+
setElapsedTime((prev) => prev + deltaTime * speed);
|
|
45
|
+
}, enabled);
|
|
46
|
+
return elapsedTime;
|
|
47
|
+
}
|
|
38
48
|
var HEX_BASE = 16;
|
|
39
49
|
var DECIMAL_BASE = 10;
|
|
40
50
|
var HEX_BYTE_LENGTH = 2;
|
|
@@ -132,6 +142,87 @@ function getEasingFunction(name = "linear") {
|
|
|
132
142
|
function clamp(value, min, max) {
|
|
133
143
|
return Math.min(Math.max(value, min), max);
|
|
134
144
|
}
|
|
145
|
+
var DEFAULT_FROM = 0;
|
|
146
|
+
var DEFAULT_TO = 1;
|
|
147
|
+
var DEFAULT_DURATION_MS = 1e3;
|
|
148
|
+
var DEFAULT_EASING = "ease-out";
|
|
149
|
+
var DEFAULT_LOOP = false;
|
|
150
|
+
var DEFAULT_SPEED = 1;
|
|
151
|
+
function calculateOpacity(elapsedTime, duration, from, to, easing) {
|
|
152
|
+
const progress = Math.min(elapsedTime / duration, 1);
|
|
153
|
+
const easedProgress = getEasingFunction(easing)(progress);
|
|
154
|
+
return from + (to - from) * easedProgress;
|
|
155
|
+
}
|
|
156
|
+
function Fade({
|
|
157
|
+
children,
|
|
158
|
+
color,
|
|
159
|
+
from = DEFAULT_FROM,
|
|
160
|
+
to = DEFAULT_TO,
|
|
161
|
+
duration = DEFAULT_DURATION_MS,
|
|
162
|
+
easing = DEFAULT_EASING,
|
|
163
|
+
loop = DEFAULT_LOOP,
|
|
164
|
+
speed = DEFAULT_SPEED,
|
|
165
|
+
enabled = true,
|
|
166
|
+
onComplete
|
|
167
|
+
}) {
|
|
168
|
+
const [hasCompleted, setHasCompleted] = useState(false);
|
|
169
|
+
const elapsedTime = useElapsedTime(enabled && !hasCompleted, speed);
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
if (!enabled)
|
|
172
|
+
return;
|
|
173
|
+
const isComplete = elapsedTime >= duration;
|
|
174
|
+
if (isComplete && !loop && !hasCompleted) {
|
|
175
|
+
setHasCompleted(true);
|
|
176
|
+
onComplete?.();
|
|
177
|
+
}
|
|
178
|
+
}, [elapsedTime, duration, loop, hasCompleted, enabled, onComplete]);
|
|
179
|
+
const fadedText = useMemo(() => {
|
|
180
|
+
const effectiveTime = loop ? elapsedTime % duration : Math.min(elapsedTime, duration);
|
|
181
|
+
const opacity = calculateOpacity(effectiveTime, duration, from, to, easing);
|
|
182
|
+
const baseColor = color ?? "#ffffff";
|
|
183
|
+
const fadedColor = applyOpacity(baseColor, opacity);
|
|
184
|
+
return colorize(children, fadedColor);
|
|
185
|
+
}, [children, elapsedTime, duration, from, to, easing, color, loop]);
|
|
186
|
+
return /* @__PURE__ */ jsx(Text, { children: fadedText });
|
|
187
|
+
}
|
|
188
|
+
var DEFAULT_COLOR = "#ffffff";
|
|
189
|
+
var DEFAULT_MIN_INTENSITY = 0.3;
|
|
190
|
+
var DEFAULT_MAX_INTENSITY = 1;
|
|
191
|
+
var DEFAULT_DURATION_MS2 = 1e3;
|
|
192
|
+
var DEFAULT_SPEED2 = 1;
|
|
193
|
+
var FULL_CIRCLE_RADIANS = Math.PI * 2;
|
|
194
|
+
var SINE_WAVE_OFFSET = 1;
|
|
195
|
+
var SINE_WAVE_NORMALIZE = 0.5;
|
|
196
|
+
var MIN_INTENSITY = 0;
|
|
197
|
+
var MAX_INTENSITY = 1;
|
|
198
|
+
function calculateFlashIntensity(time, duration, minIntensity, maxIntensity) {
|
|
199
|
+
const clampedMin = clamp(minIntensity, MIN_INTENSITY, MAX_INTENSITY);
|
|
200
|
+
const clampedMax = clamp(maxIntensity, MIN_INTENSITY, MAX_INTENSITY);
|
|
201
|
+
const normalizedTime = time / duration;
|
|
202
|
+
const phase = normalizedTime * FULL_CIRCLE_RADIANS;
|
|
203
|
+
const sineWave = Math.sin(phase);
|
|
204
|
+
const normalizedSine = (sineWave + SINE_WAVE_OFFSET) * SINE_WAVE_NORMALIZE;
|
|
205
|
+
const intensityRange = clampedMax - clampedMin;
|
|
206
|
+
return clampedMin + normalizedSine * intensityRange;
|
|
207
|
+
}
|
|
208
|
+
function Flash({
|
|
209
|
+
children,
|
|
210
|
+
color = DEFAULT_COLOR,
|
|
211
|
+
minIntensity = DEFAULT_MIN_INTENSITY,
|
|
212
|
+
maxIntensity = DEFAULT_MAX_INTENSITY,
|
|
213
|
+
duration = DEFAULT_DURATION_MS2,
|
|
214
|
+
speed = DEFAULT_SPEED2,
|
|
215
|
+
enabled = true
|
|
216
|
+
}) {
|
|
217
|
+
const elapsedTime = useElapsedTime(enabled, speed);
|
|
218
|
+
const cycleTime = elapsedTime % duration;
|
|
219
|
+
const flashText = useMemo(() => {
|
|
220
|
+
const intensity = calculateFlashIntensity(cycleTime, duration, minIntensity, maxIntensity);
|
|
221
|
+
const adjustedColor = applyOpacity(color, intensity);
|
|
222
|
+
return colorize(children, adjustedColor);
|
|
223
|
+
}, [children, cycleTime, duration, minIntensity, maxIntensity, color]);
|
|
224
|
+
return /* @__PURE__ */ jsx(Text, { children: flashText });
|
|
225
|
+
}
|
|
135
226
|
|
|
136
227
|
// src/utils/text.ts
|
|
137
228
|
function splitChars(text) {
|
|
@@ -143,25 +234,23 @@ function getTextLength(text) {
|
|
|
143
234
|
function mapChars(text, fn) {
|
|
144
235
|
return splitChars(text).map(fn).join("");
|
|
145
236
|
}
|
|
146
|
-
|
|
147
|
-
// src/components/Shimmer.tsx
|
|
148
237
|
var DEFAULT_COLORS = ["#666666", "#ffffff", "#666666"];
|
|
149
238
|
var DEFAULT_WIDTH = 4;
|
|
150
239
|
var DEFAULT_INTENSITY = 1;
|
|
151
240
|
var DEFAULT_DIRECTION = "right";
|
|
152
|
-
var
|
|
241
|
+
var DEFAULT_SPEED3 = 1;
|
|
153
242
|
var ANIMATION_CYCLE_MS = 2e3;
|
|
154
243
|
var COLOUR_TRANSITION_MIDPOINT = 0.5;
|
|
155
244
|
var COLOUR_PROGRESS_MULTIPLIER = 2;
|
|
156
|
-
var
|
|
157
|
-
var
|
|
245
|
+
var MIN_INTENSITY2 = 0;
|
|
246
|
+
var MAX_INTENSITY2 = 1;
|
|
158
247
|
function Shimmer({
|
|
159
248
|
children,
|
|
160
249
|
colors = DEFAULT_COLORS,
|
|
161
250
|
width = DEFAULT_WIDTH,
|
|
162
251
|
intensity = DEFAULT_INTENSITY,
|
|
163
252
|
direction = DEFAULT_DIRECTION,
|
|
164
|
-
speed =
|
|
253
|
+
speed = DEFAULT_SPEED3,
|
|
165
254
|
enabled = true,
|
|
166
255
|
onComplete
|
|
167
256
|
}) {
|
|
@@ -187,7 +276,7 @@ function Shimmer({
|
|
|
187
276
|
const isWithinShimmerWidth = distance < width;
|
|
188
277
|
const colorProgress = isWithinShimmerWidth ? distance / width : 1;
|
|
189
278
|
const [startColor, peakColor, endColor] = colors;
|
|
190
|
-
const adjustedIntensity = clamp(intensity,
|
|
279
|
+
const adjustedIntensity = clamp(intensity, MIN_INTENSITY2, MAX_INTENSITY2);
|
|
191
280
|
const isFirstHalf = colorProgress < COLOUR_TRANSITION_MIDPOINT;
|
|
192
281
|
const currentColor = isFirstHalf ? interpolateColor(
|
|
193
282
|
startColor,
|
|
@@ -201,7 +290,7 @@ function Shimmer({
|
|
|
201
290
|
return colorize(char, currentColor);
|
|
202
291
|
});
|
|
203
292
|
}, [children, offset, colors, width, intensity, direction, totalWidth]);
|
|
204
|
-
return /* @__PURE__ */
|
|
293
|
+
return /* @__PURE__ */ jsx(Text, { children: shimmerText });
|
|
205
294
|
}
|
|
206
295
|
var CURSOR_BLINK_INTERVAL_MS = 500;
|
|
207
296
|
function useCursorBlink({
|
|
@@ -265,12 +354,10 @@ function useTypewriterProgress({
|
|
|
265
354
|
}, [visibleCharacters, enabled, speed, variance, initialDelay, hasCompletedTyping, onComplete]);
|
|
266
355
|
return visibleCharacters;
|
|
267
356
|
}
|
|
268
|
-
|
|
269
|
-
// src/components/Typewriter.tsx
|
|
270
357
|
var DEFAULT_CURSOR = "\u258B";
|
|
271
358
|
var DEFAULT_VARIANCE = 0.3;
|
|
272
359
|
var DEFAULT_DELAY = 0;
|
|
273
|
-
var
|
|
360
|
+
var DEFAULT_SPEED4 = 1;
|
|
274
361
|
var FIRST_CHARACTER_INDEX2 = 0;
|
|
275
362
|
function getCursorCharacter(cursor) {
|
|
276
363
|
return typeof cursor === "string" ? cursor : DEFAULT_CURSOR;
|
|
@@ -289,7 +376,7 @@ function Typewriter({
|
|
|
289
376
|
cursorColor,
|
|
290
377
|
variance = DEFAULT_VARIANCE,
|
|
291
378
|
delay = DEFAULT_DELAY,
|
|
292
|
-
speed =
|
|
379
|
+
speed = DEFAULT_SPEED4,
|
|
293
380
|
enabled = true,
|
|
294
381
|
onComplete
|
|
295
382
|
}) {
|
|
@@ -319,81 +406,29 @@ function Typewriter({
|
|
|
319
406
|
const cursorText = formatCursor(cursorCharacter, cursorColor, color);
|
|
320
407
|
return coloredText + cursorText;
|
|
321
408
|
}, [characters, visibleCharacters, color, cursor, cursorColor, showCursor, hasCompletedTyping, isCursorEnabled]);
|
|
322
|
-
return /* @__PURE__ */
|
|
323
|
-
}
|
|
324
|
-
function useElapsedTime(enabled = true, speed = 1) {
|
|
325
|
-
const [elapsedTime, setElapsedTime] = useState(0);
|
|
326
|
-
useAnimationFrame((deltaTime) => {
|
|
327
|
-
setElapsedTime((prev) => prev + deltaTime * speed);
|
|
328
|
-
}, enabled);
|
|
329
|
-
return elapsedTime;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// src/components/Fade.tsx
|
|
333
|
-
var DEFAULT_FROM = 0;
|
|
334
|
-
var DEFAULT_TO = 1;
|
|
335
|
-
var DEFAULT_DURATION_MS = 1e3;
|
|
336
|
-
var DEFAULT_EASING = "ease-out";
|
|
337
|
-
var DEFAULT_LOOP = false;
|
|
338
|
-
var DEFAULT_SPEED3 = 1;
|
|
339
|
-
function calculateOpacity(elapsedTime, duration, from, to, easing) {
|
|
340
|
-
const progress = Math.min(elapsedTime / duration, 1);
|
|
341
|
-
const easedProgress = getEasingFunction(easing)(progress);
|
|
342
|
-
return from + (to - from) * easedProgress;
|
|
343
|
-
}
|
|
344
|
-
function Fade({
|
|
345
|
-
children,
|
|
346
|
-
color,
|
|
347
|
-
from = DEFAULT_FROM,
|
|
348
|
-
to = DEFAULT_TO,
|
|
349
|
-
duration = DEFAULT_DURATION_MS,
|
|
350
|
-
easing = DEFAULT_EASING,
|
|
351
|
-
loop = DEFAULT_LOOP,
|
|
352
|
-
speed = DEFAULT_SPEED3,
|
|
353
|
-
enabled = true,
|
|
354
|
-
onComplete
|
|
355
|
-
}) {
|
|
356
|
-
const [hasCompleted, setHasCompleted] = useState(false);
|
|
357
|
-
const elapsedTime = useElapsedTime(enabled && !hasCompleted, speed);
|
|
358
|
-
useEffect(() => {
|
|
359
|
-
if (!enabled)
|
|
360
|
-
return;
|
|
361
|
-
const isComplete = elapsedTime >= duration;
|
|
362
|
-
if (isComplete && !loop && !hasCompleted) {
|
|
363
|
-
setHasCompleted(true);
|
|
364
|
-
onComplete?.();
|
|
365
|
-
}
|
|
366
|
-
}, [elapsedTime, duration, loop, hasCompleted, enabled, onComplete]);
|
|
367
|
-
const fadedText = useMemo(() => {
|
|
368
|
-
const effectiveTime = loop ? elapsedTime % duration : Math.min(elapsedTime, duration);
|
|
369
|
-
const opacity = calculateOpacity(effectiveTime, duration, from, to, easing);
|
|
370
|
-
const baseColor = color ?? "#ffffff";
|
|
371
|
-
const fadedColor = applyOpacity(baseColor, opacity);
|
|
372
|
-
return colorize(children, fadedColor);
|
|
373
|
-
}, [children, elapsedTime, duration, from, to, easing, color, loop]);
|
|
374
|
-
return /* @__PURE__ */ React.createElement(Text, null, fadedText);
|
|
409
|
+
return /* @__PURE__ */ jsx(Text, { children: displayText });
|
|
375
410
|
}
|
|
376
411
|
var DEFAULT_COLORS2 = ["#888888", "#ffffff"];
|
|
377
412
|
var DEFAULT_AMPLITUDE = 0.5;
|
|
378
413
|
var DEFAULT_FREQUENCY = 2;
|
|
379
414
|
var DEFAULT_TYPE = "brightness";
|
|
380
|
-
var
|
|
381
|
-
var
|
|
415
|
+
var DEFAULT_SPEED5 = 1;
|
|
416
|
+
var FULL_CIRCLE_RADIANS2 = Math.PI * 2;
|
|
382
417
|
var WAVE_PERIOD_MS = 2e3;
|
|
383
418
|
var MIN_AMPLITUDE = 0;
|
|
384
419
|
var MAX_AMPLITUDE = 1;
|
|
385
|
-
var
|
|
386
|
-
var
|
|
420
|
+
var SINE_WAVE_OFFSET2 = 1;
|
|
421
|
+
var SINE_WAVE_NORMALIZE2 = 0.5;
|
|
387
422
|
var VERTICAL_SHIFT_THRESHOLD = 0.5;
|
|
388
423
|
function calculateWaveValue(characterIndex, time, frequency, amplitude) {
|
|
389
424
|
const clampedAmplitude = clamp(amplitude, MIN_AMPLITUDE, MAX_AMPLITUDE);
|
|
390
425
|
const safeFrequency = frequency || DEFAULT_FREQUENCY;
|
|
391
|
-
const waveLength =
|
|
392
|
-
const spatialComponent = characterIndex / waveLength *
|
|
393
|
-
const temporalComponent = time *
|
|
426
|
+
const waveLength = FULL_CIRCLE_RADIANS2 / safeFrequency;
|
|
427
|
+
const spatialComponent = characterIndex / waveLength * FULL_CIRCLE_RADIANS2;
|
|
428
|
+
const temporalComponent = time * FULL_CIRCLE_RADIANS2;
|
|
394
429
|
const phase = spatialComponent - temporalComponent;
|
|
395
430
|
const sineWave = Math.sin(phase);
|
|
396
|
-
const normalizedSine = (sineWave +
|
|
431
|
+
const normalizedSine = (sineWave + SINE_WAVE_OFFSET2) * SINE_WAVE_NORMALIZE2;
|
|
397
432
|
const amplitudeComplement = MAX_AMPLITUDE - clampedAmplitude;
|
|
398
433
|
return normalizedSine * clampedAmplitude + amplitudeComplement;
|
|
399
434
|
}
|
|
@@ -403,7 +438,7 @@ function Wave({
|
|
|
403
438
|
amplitude = DEFAULT_AMPLITUDE,
|
|
404
439
|
frequency = DEFAULT_FREQUENCY,
|
|
405
440
|
type = DEFAULT_TYPE,
|
|
406
|
-
speed =
|
|
441
|
+
speed = DEFAULT_SPEED5,
|
|
407
442
|
enabled = true
|
|
408
443
|
}) {
|
|
409
444
|
const elapsedTime = useElapsedTime(enabled, speed);
|
|
@@ -423,9 +458,9 @@ function Wave({
|
|
|
423
458
|
return shouldShift ? ` ${character}` : character;
|
|
424
459
|
});
|
|
425
460
|
}, [children, normalizedTime, colors, amplitude, frequency, type]);
|
|
426
|
-
return /* @__PURE__ */
|
|
461
|
+
return /* @__PURE__ */ jsx(Text, { children: waveText });
|
|
427
462
|
}
|
|
428
463
|
|
|
429
|
-
export { Fade, Shimmer, Typewriter, Wave, useAnimationFrame, useCursorBlink, useElapsedTime, useTypewriterProgress };
|
|
464
|
+
export { Fade, Flash, Shimmer, Typewriter, Wave, useAnimationFrame, useCursorBlink, useElapsedTime, useTypewriterProgress };
|
|
430
465
|
//# sourceMappingURL=index.js.map
|
|
431
466
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useAnimationFrame.ts","../src/utils/colors.ts","../src/utils/easing.ts","../src/utils/text.ts","../src/components/Shimmer.tsx","../src/hooks/useCursorBlink.ts","../src/hooks/useTypewriterProgress.ts","../src/components/Typewriter.tsx","../src/hooks/useElapsedTime.ts","../src/components/Fade.tsx","../src/components/Wave.tsx"],"names":["useState","useRef","useEffect","DEFAULT_SPEED","FIRST_CHARACTER_INDEX","useMemo","React","Text","DEFAULT_COLORS"],"mappings":"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","file":"index.js","sourcesContent":["import { useEffect, useRef } from 'react'\n\nconst TARGET_FPS = 60\nconst FRAME_INTERVAL_MS = 1000 / TARGET_FPS\n\n/**\n * Runs a callback on every animation frame using setInterval\n *\n * @param callback - Function called each frame with delta time in ms\n * @param enabled - Whether animation is active\n */\nexport function useAnimationFrame(\n callback: (deltaTime: number) => void,\n enabled: boolean = true,\n): void {\n const intervalRef = useRef<Timer>()\n const previousTimeRef = useRef<number>()\n const callbackRef = useRef(callback)\n\n useEffect(() => {\n callbackRef.current = callback\n }, [callback])\n\n useEffect(() => {\n if (!enabled) {\n if (intervalRef.current) {\n clearInterval(intervalRef.current)\n }\n previousTimeRef.current = undefined\n return\n }\n\n intervalRef.current = setInterval(() => {\n const currentTime = Date.now()\n if (previousTimeRef.current !== undefined) {\n const deltaTime = currentTime - previousTimeRef.current\n callbackRef.current(deltaTime)\n }\n previousTimeRef.current = currentTime\n }, FRAME_INTERVAL_MS)\n\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current)\n }\n }\n }, [enabled])\n}\n","import chalk from 'chalk'\nimport type { Colour } from '../types/index.js'\n\nconst HEX_BASE = 16\nconst DECIMAL_BASE = 10\nconst HEX_BYTE_LENGTH = 2\nconst HEX_PAD_CHARACTER = '0'\nconst MIN_RGB_COMPONENTS = 3\nconst INTERPOLATION_MIDPOINT = 0.5\n\nconst HEX_COLOR_REGEX = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i\nconst RGB_NUMBERS_REGEX = /\\d+/g\n\nfunction rgbToHex(red: number, green: number, blue: number): string {\n const redHex = red.toString(HEX_BASE).padStart(HEX_BYTE_LENGTH, HEX_PAD_CHARACTER)\n const greenHex = green.toString(HEX_BASE).padStart(HEX_BYTE_LENGTH, HEX_PAD_CHARACTER)\n const blueHex = blue.toString(HEX_BASE).padStart(HEX_BYTE_LENGTH, HEX_PAD_CHARACTER)\n return `#${redHex}${greenHex}${blueHex}`\n}\n\n/**\n * Converts a hex color string to RGB values\n *\n * @param hex - Hex color string (with or without #)\n * @returns Tuple of [red, green, blue] values (0-255) or null if invalid\n */\nexport function hexToRgb(hex: string): [number, number, number] | null {\n const result = HEX_COLOR_REGEX.exec(hex)\n if (!result)\n return null\n\n const [, redHex, greenHex, blueHex] = result\n return [\n Number.parseInt(redHex, HEX_BASE),\n Number.parseInt(greenHex, HEX_BASE),\n Number.parseInt(blueHex, HEX_BASE),\n ]\n}\n\n/**\n * Applies color to text using chalk\n *\n * @param text - Text to colorize\n * @param color - Color as hex (#ff0000), rgb(255,0,0), or named color (red, blue, etc)\n * @returns Colorized text with ANSI escape codes\n */\nexport function colorize(text: string, color: Colour): string {\n try {\n if (color.startsWith('#'))\n return chalk.hex(color)(text)\n\n if (color.startsWith('rgb')) {\n const match = color.match(RGB_NUMBERS_REGEX)\n if (!match || match.length < MIN_RGB_COMPONENTS)\n return text\n\n const [redStr, greenStr, blueStr] = match\n const red = Number.parseInt(redStr, DECIMAL_BASE)\n const green = Number.parseInt(greenStr, DECIMAL_BASE)\n const blue = Number.parseInt(blueStr, DECIMAL_BASE)\n\n if (Number.isNaN(red) || Number.isNaN(green) || Number.isNaN(blue))\n return text\n\n return chalk.rgb(red, green, blue)(text)\n }\n\n const chalkColor = chalk[color as keyof typeof chalk]\n if (typeof chalkColor === 'function')\n return chalkColor(text)\n\n return text\n }\n catch {\n return text\n }\n}\n\n/**\n * Applies opacity to a hex color by darkening it\n * Note: Terminal approximation - true opacity not possible in most terminals\n *\n * @param color - Hex color string\n * @param opacity - Opacity value from 0 (transparent/black) to 1 (fully opaque)\n * @returns Adjusted hex color or original color if not hex format\n */\nexport function applyOpacity(color: Colour, opacity: number): Colour {\n if (!color.startsWith('#'))\n return color\n\n const rgb = hexToRgb(color)\n if (!rgb)\n return color\n\n const [red, green, blue] = rgb\n const adjustedRed = Math.round(red * opacity)\n const adjustedGreen = Math.round(green * opacity)\n const adjustedBlue = Math.round(blue * opacity)\n\n return rgbToHex(adjustedRed, adjustedGreen, adjustedBlue)\n}\n\n/**\n * Interpolates between two colors\n *\n * @param color1 - Starting color\n * @param color2 - Ending color\n * @param progress - Interpolation progress from 0 to 1\n * @returns Interpolated color (hex if both inputs are hex, otherwise snaps to nearest)\n */\nexport function interpolateColor(\n color1: Colour,\n color2: Colour,\n progress: number,\n): Colour {\n const isColor1Hex = color1.startsWith('#')\n const isColor2Hex = color2.startsWith('#')\n\n if (!isColor1Hex || !isColor2Hex)\n return progress < INTERPOLATION_MIDPOINT ? color1 : color2\n\n const rgb1 = hexToRgb(color1)\n const rgb2 = hexToRgb(color2)\n\n if (!rgb1 || !rgb2)\n return progress < INTERPOLATION_MIDPOINT ? color1 : color2\n\n const [red1, green1, blue1] = rgb1\n const [red2, green2, blue2] = rgb2\n\n const red = Math.round(red1 + (red2 - red1) * progress)\n const green = Math.round(green1 + (green2 - green1) * progress)\n const blue = Math.round(blue1 + (blue2 - blue1) * progress)\n\n return rgbToHex(red, green, blue)\n}\n","import type { EasingFunction, EasingName } from '../types/index.js'\n\nexport const linear: EasingFunction = time => time\n\nexport const easeIn: EasingFunction = time => time * time\n\nexport const easeOut: EasingFunction = time => time * (2 - time)\n\nexport const easeInOut: EasingFunction = (time) => {\n return time < 0.5\n ? 2 * time * time\n : -1 + (4 - 2 * time) * time\n}\n\nexport const easingFunctions: Record<EasingName, EasingFunction> = {\n 'linear': linear,\n 'ease-in': easeIn,\n 'ease-out': easeOut,\n 'ease-in-out': easeInOut,\n}\n\nexport function getEasingFunction(name: EasingName = 'linear'): EasingFunction {\n return easingFunctions[name]\n}\n\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max)\n}\n\nexport function normalize(value: number, min: number, max: number): number {\n return clamp((value - min) / (max - min), 0, 1)\n}\n\nexport function lerp(start: number, end: number, progress: number): number {\n return start + (end - start) * progress\n}\n","export function splitChars(text: string): string[] {\n return [...text]\n}\n\nexport function getTextLength(text: string): number {\n return [...text].length\n}\n\nexport function mapChars(\n text: string,\n fn: (char: string, index: number) => string,\n): string {\n return splitChars(text).map(fn).join('')\n}\n","import { Text } from 'ink'\nimport React, { useMemo, useState } from 'react'\nimport type { BaseEffectProps, Colour } from '../types/index.js'\nimport { useAnimationFrame } from '../hooks/useAnimationFrame.js'\nimport { colorize, interpolateColor } from '../utils/colors.js'\nimport { clamp } from '../utils/easing.js'\nimport { getTextLength, mapChars } from '../utils/text.js'\n\ntype ShimmerDirection = 'left' | 'right'\n\ninterface ShimmerProps extends BaseEffectProps {\n /**\n * Gradient colours for the shimmer effect [start, peak, end]\n * @default ['#666666', '#ffffff', '#666666']\n * @example ['#60a5fa', '#3b82f6', '#60a5fa']\n */\n colors?: [Colour, Colour, Colour]\n\n /**\n * Width of the shimmer band in characters\n * @default 4\n */\n width?: number\n\n /**\n * Brightness multiplier (0-1)\n * @default 1\n */\n intensity?: number\n\n /**\n * Direction of shimmer movement\n * @default 'right'\n */\n direction?: ShimmerDirection\n}\n\nconst DEFAULT_COLORS: [Colour, Colour, Colour] = ['#666666', '#ffffff', '#666666']\nconst DEFAULT_WIDTH = 4\nconst DEFAULT_INTENSITY = 1\nconst DEFAULT_DIRECTION: ShimmerDirection = 'right'\nconst DEFAULT_SPEED = 1\nconst ANIMATION_CYCLE_MS = 2000\nconst COLOUR_TRANSITION_MIDPOINT = 0.5\nconst COLOUR_PROGRESS_MULTIPLIER = 2\nconst MIN_INTENSITY = 0\nconst MAX_INTENSITY = 1\n\n/**\n * Shimmer effect component that creates a moving highlight across text\n *\n * Creates a shimmering animation by interpolating through a gradient of colours\n * that sweeps across the text from left to right or right to left.\n *\n * @example\n * ```tsx\n * <Shimmer colors={['#60a5fa', '#3b82f6', '#60a5fa']} intensity={0.8}>\n * Loading...\n * </Shimmer>\n * ```\n */\nexport function Shimmer({\n children,\n colors = DEFAULT_COLORS,\n width = DEFAULT_WIDTH,\n intensity = DEFAULT_INTENSITY,\n direction = DEFAULT_DIRECTION,\n speed = DEFAULT_SPEED,\n enabled = true,\n onComplete,\n}: ShimmerProps) {\n const [offset, setOffset] = useState(0)\n const textLength = getTextLength(children)\n const totalWidth = textLength + width\n\n useAnimationFrame((deltaTime) => {\n setOffset((previousOffset) => {\n const movement = (deltaTime / ANIMATION_CYCLE_MS) * speed * totalWidth\n const newOffset = direction === 'right'\n ? previousOffset + movement\n : previousOffset - movement\n\n const hasCompletedCycle = Math.abs(newOffset) >= totalWidth\n if (hasCompletedCycle) {\n onComplete?.()\n return 0\n }\n\n return newOffset\n })\n }, enabled)\n\n const shimmerText = useMemo(() => {\n return mapChars(children, (char, index) => {\n const normalizedOffset = direction === 'right' ? offset : totalWidth - offset\n const distance = Math.abs(index - normalizedOffset)\n\n const isWithinShimmerWidth = distance < width\n const colorProgress = isWithinShimmerWidth\n ? distance / width\n : 1\n\n const [startColor, peakColor, endColor] = colors\n const adjustedIntensity = clamp(intensity, MIN_INTENSITY, MAX_INTENSITY)\n\n const isFirstHalf = colorProgress < COLOUR_TRANSITION_MIDPOINT\n const currentColor = isFirstHalf\n ? interpolateColor(\n startColor,\n peakColor,\n colorProgress * COLOUR_PROGRESS_MULTIPLIER * adjustedIntensity,\n )\n : interpolateColor(\n peakColor,\n endColor,\n (colorProgress - COLOUR_TRANSITION_MIDPOINT) * COLOUR_PROGRESS_MULTIPLIER * adjustedIntensity,\n )\n\n return colorize(char, currentColor)\n })\n }, [children, offset, colors, width, intensity, direction, totalWidth])\n\n return <Text>{shimmerText}</Text>\n}\n","import { useEffect, useRef, useState } from 'react'\n\nconst CURSOR_BLINK_INTERVAL_MS = 500\n\ninterface UseCursorBlinkOptions {\n enabled: boolean\n isComplete: boolean\n}\n\n/**\n * Hook that manages cursor blinking state\n *\n * @param options - Configuration for cursor blink\n * @returns Whether cursor should be visible\n */\nexport function useCursorBlink({\n enabled,\n isComplete,\n}: UseCursorBlinkOptions): boolean {\n const [showCursor, setShowCursor] = useState(true)\n const intervalRef = useRef<Timer>()\n\n useEffect(() => {\n if (!enabled || isComplete) {\n setShowCursor(false)\n return\n }\n\n intervalRef.current = setInterval(() => {\n setShowCursor(previous => !previous)\n }, CURSOR_BLINK_INTERVAL_MS)\n\n return () => {\n if (intervalRef.current)\n clearInterval(intervalRef.current)\n }\n }, [enabled, isComplete])\n\n return showCursor\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { clamp } from '../utils/easing.js'\n\nconst BASE_CHARACTER_DELAY_MS = 80\nconst MIN_VARIANCE = 0\nconst MAX_VARIANCE = 1\nconst FIRST_CHARACTER_INDEX = 0\n\nfunction calculateCharacterDelay(\n speed: number,\n variance: number,\n): number {\n const clampedVariance = clamp(variance, MIN_VARIANCE, MAX_VARIANCE)\n const randomVariance = (Math.random() - 0.5) * clampedVariance\n return (BASE_CHARACTER_DELAY_MS / speed) * (1 + randomVariance)\n}\n\ninterface UseTypewriterProgressOptions {\n totalCharacters: number\n speed: number\n variance: number\n initialDelay: number\n enabled: boolean\n onComplete?: () => void\n}\n\n/**\n * Hook that manages typewriter character reveal timing\n *\n * @param options - Configuration for typewriter progress\n * @returns Number of characters to display\n */\nexport function useTypewriterProgress({\n totalCharacters,\n speed,\n variance,\n initialDelay,\n enabled,\n onComplete,\n}: UseTypewriterProgressOptions): number {\n const [visibleCharacters, setVisibleCharacters] = useState(0)\n const timeoutRef = useRef<Timer>()\n const hasCompletedTyping = visibleCharacters >= totalCharacters\n\n useEffect(() => {\n if (!enabled)\n return\n\n if (hasCompletedTyping) {\n onComplete?.()\n return\n }\n\n const characterDelay = calculateCharacterDelay(speed, variance)\n const isFirstCharacter = visibleCharacters === FIRST_CHARACTER_INDEX\n const totalDelay = isFirstCharacter ? initialDelay + characterDelay : characterDelay\n\n timeoutRef.current = setTimeout(() => {\n setVisibleCharacters(previous => previous + 1)\n }, totalDelay)\n\n return () => {\n if (timeoutRef.current)\n clearTimeout(timeoutRef.current)\n }\n }, [visibleCharacters, enabled, speed, variance, initialDelay, hasCompletedTyping, onComplete])\n\n return visibleCharacters\n}\n","import { Text } from 'ink'\nimport React, { useMemo } from 'react'\nimport type { BaseEffectProps, Colour } from '../types/index.js'\nimport { useCursorBlink } from '../hooks/useCursorBlink.js'\nimport { useTypewriterProgress } from '../hooks/useTypewriterProgress.js'\nimport { colorize } from '../utils/colors.js'\nimport { getTextLength, splitChars } from '../utils/text.js'\n\ninterface TypewriterProps extends BaseEffectProps {\n /**\n * Text colour\n * @default undefined (inherits)\n * @example 'green'\n */\n color?: Colour\n\n /**\n * Cursor character or false to disable\n * @default '▋'\n * @example '█'\n */\n cursor?: string | boolean\n\n /**\n * Cursor colour (defaults to text colour)\n * @default undefined\n * @example 'cyan'\n */\n cursorColor?: Colour\n\n /**\n * Typing speed randomness (0-1) for more human-like typing\n * @default 0.3\n */\n variance?: number\n\n /**\n * Initial delay before typing starts (ms)\n * @default 0\n */\n delay?: number\n}\n\nconst DEFAULT_CURSOR = '▋'\nconst DEFAULT_VARIANCE = 0.3\nconst DEFAULT_DELAY = 0\nconst DEFAULT_SPEED = 1\nconst FIRST_CHARACTER_INDEX = 0\n\nfunction getCursorCharacter(cursor: string | boolean): string {\n return typeof cursor === 'string' ? cursor : DEFAULT_CURSOR\n}\n\nfunction formatCursor(\n cursorCharacter: string,\n cursorColor: Colour | undefined,\n textColor: Colour | undefined,\n): string {\n if (cursorColor)\n return colorize(cursorCharacter, cursorColor)\n\n if (textColor)\n return colorize(cursorCharacter, textColor)\n\n return cursorCharacter\n}\n\n/**\n * Typewriter effect component that reveals text character by character\n *\n * Simulates typing text with configurable speed, variance for realistic timing,\n * and an optional cursor. The cursor blinks while typing is in progress.\n *\n * @example\n * ```tsx\n * <Typewriter color=\"green\" cursor=\"█\" variance={0.5} speed={2}>\n * npm install ink-motion\n * </Typewriter>\n * ```\n */\nexport function Typewriter({\n children,\n color,\n cursor = DEFAULT_CURSOR,\n cursorColor,\n variance = DEFAULT_VARIANCE,\n delay = DEFAULT_DELAY,\n speed = DEFAULT_SPEED,\n enabled = true,\n onComplete,\n}: TypewriterProps) {\n const characters = splitChars(children)\n const totalCharacters = getTextLength(children)\n\n const visibleCharacters = useTypewriterProgress({\n totalCharacters,\n speed,\n variance,\n initialDelay: delay,\n enabled,\n onComplete,\n })\n\n const hasCompletedTyping = visibleCharacters >= totalCharacters\n const isCursorEnabled = cursor !== false\n\n const showCursor = useCursorBlink({\n enabled: enabled && isCursorEnabled,\n isComplete: hasCompletedTyping,\n })\n\n const displayText = useMemo(() => {\n const visibleText = characters.slice(FIRST_CHARACTER_INDEX, visibleCharacters).join('')\n const coloredText = color ? colorize(visibleText, color) : visibleText\n\n const shouldShowCursor = isCursorEnabled && showCursor && !hasCompletedTyping\n if (!shouldShowCursor)\n return coloredText\n\n const cursorCharacter = getCursorCharacter(cursor)\n const cursorText = formatCursor(cursorCharacter, cursorColor, color)\n\n return coloredText + cursorText\n }, [characters, visibleCharacters, color, cursor, cursorColor, showCursor, hasCompletedTyping, isCursorEnabled])\n\n return <Text>{displayText}</Text>\n}\n","import { useState } from 'react'\nimport { useAnimationFrame } from './useAnimationFrame.js'\n\n/**\n * Hook that tracks elapsed time since mount or last reset\n *\n * @param enabled - Whether to track time\n * @param speed - Speed multiplier (default: 1)\n * @returns Elapsed time in milliseconds\n */\nexport function useElapsedTime(\n enabled: boolean = true,\n speed: number = 1,\n): number {\n const [elapsedTime, setElapsedTime] = useState(0)\n\n useAnimationFrame((deltaTime) => {\n setElapsedTime(prev => prev + deltaTime * speed)\n }, enabled)\n\n return elapsedTime\n}\n","import { Text } from 'ink'\nimport React, { useEffect, useMemo, useState } from 'react'\nimport type { BaseEffectProps, Colour, EasingName } from '../types/index.js'\nimport { useElapsedTime } from '../hooks/useElapsedTime.js'\nimport { applyOpacity, colorize } from '../utils/colors.js'\nimport { getEasingFunction } from '../utils/easing.js'\n\ninterface FadeProps extends BaseEffectProps {\n /**\n * Text colour\n * @default '#ffffff'\n * @example 'yellow'\n */\n color?: Colour\n\n /**\n * Starting opacity (0-1)\n * @default 0\n */\n from?: number\n\n /**\n * Ending opacity (0-1)\n * @default 1\n */\n to?: number\n\n /**\n * Duration in milliseconds\n * @default 1000\n */\n duration?: number\n\n /**\n * Easing function\n * @default 'ease-out'\n */\n easing?: EasingName\n\n /**\n * Loop animation continuously\n * @default false\n */\n loop?: boolean\n}\n\nconst DEFAULT_FROM = 0\nconst DEFAULT_TO = 1\nconst DEFAULT_DURATION_MS = 1000\nconst DEFAULT_EASING: EasingName = 'ease-out'\nconst DEFAULT_LOOP = false\nconst DEFAULT_SPEED = 1\n\nfunction calculateOpacity(\n elapsedTime: number,\n duration: number,\n from: number,\n to: number,\n easing: EasingName,\n): number {\n const progress = Math.min(elapsedTime / duration, 1)\n const easedProgress = getEasingFunction(easing)(progress)\n return from + (to - from) * easedProgress\n}\n\n/**\n * Fade effect component that smoothly transitions text opacity\n *\n * Animates text from one opacity to another using configurable easing functions.\n * Can loop continuously or run once.\n *\n * @example\n * ```tsx\n * <Fade color=\"yellow\" from={0} to={1} duration={500} easing=\"ease-in\">\n * Success!\n * </Fade>\n * ```\n */\nexport function Fade({\n children,\n color,\n from = DEFAULT_FROM,\n to = DEFAULT_TO,\n duration = DEFAULT_DURATION_MS,\n easing = DEFAULT_EASING,\n loop = DEFAULT_LOOP,\n speed = DEFAULT_SPEED,\n enabled = true,\n onComplete,\n}: FadeProps) {\n const [hasCompleted, setHasCompleted] = useState(false)\n const elapsedTime = useElapsedTime(enabled && !hasCompleted, speed)\n\n useEffect(() => {\n if (!enabled)\n return\n\n const isComplete = elapsedTime >= duration\n\n if (isComplete && !loop && !hasCompleted) {\n setHasCompleted(true)\n onComplete?.()\n }\n }, [elapsedTime, duration, loop, hasCompleted, enabled, onComplete])\n\n const fadedText = useMemo(() => {\n const effectiveTime = loop ? elapsedTime % duration : Math.min(elapsedTime, duration)\n const opacity = calculateOpacity(effectiveTime, duration, from, to, easing)\n\n const baseColor = color ?? '#ffffff'\n const fadedColor = applyOpacity(baseColor, opacity)\n\n return colorize(children, fadedColor)\n }, [children, elapsedTime, duration, from, to, easing, color, loop])\n\n return <Text>{fadedText}</Text>\n}\n","import { Text } from 'ink'\nimport React, { useMemo } from 'react'\nimport type { BaseEffectProps, Colour } from '../types/index.js'\nimport { useElapsedTime } from '../hooks/useElapsedTime.js'\nimport { colorize, interpolateColor } from '../utils/colors.js'\nimport { clamp } from '../utils/easing.js'\nimport { mapChars } from '../utils/text.js'\n\ntype WaveType = 'brightness' | 'vertical'\n\ninterface WaveProps extends BaseEffectProps {\n /**\n * Gradient colours for the wave [dark, bright]\n * @default ['#888888', '#ffffff']\n * @example ['#ec4899', '#8b5cf6']\n */\n colors?: [Colour, Colour]\n\n /**\n * Wave height (0-1)\n * @default 0.5\n */\n amplitude?: number\n\n /**\n * Number of wave cycles across text\n * @default 2\n */\n frequency?: number\n\n /**\n * Wave effect type\n * @default 'brightness'\n */\n type?: WaveType\n}\n\nconst DEFAULT_COLORS: [Colour, Colour] = ['#888888', '#ffffff']\nconst DEFAULT_AMPLITUDE = 0.5\nconst DEFAULT_FREQUENCY = 2\nconst DEFAULT_TYPE: WaveType = 'brightness'\nconst DEFAULT_SPEED = 1\nconst FULL_CIRCLE_RADIANS = Math.PI * 2\nconst WAVE_PERIOD_MS = 2000\nconst MIN_AMPLITUDE = 0\nconst MAX_AMPLITUDE = 1\nconst SINE_WAVE_OFFSET = 1\nconst SINE_WAVE_NORMALIZE = 0.5\nconst VERTICAL_SHIFT_THRESHOLD = 0.5\n\nfunction calculateWaveValue(\n characterIndex: number,\n time: number,\n frequency: number,\n amplitude: number,\n): number {\n const clampedAmplitude = clamp(amplitude, MIN_AMPLITUDE, MAX_AMPLITUDE)\n const safeFrequency = frequency || DEFAULT_FREQUENCY\n const waveLength = FULL_CIRCLE_RADIANS / safeFrequency\n const spatialComponent = (characterIndex / waveLength) * FULL_CIRCLE_RADIANS\n const temporalComponent = time * FULL_CIRCLE_RADIANS\n const phase = spatialComponent - temporalComponent\n const sineWave = Math.sin(phase)\n const normalizedSine = (sineWave + SINE_WAVE_OFFSET) * SINE_WAVE_NORMALIZE\n const amplitudeComplement = MAX_AMPLITUDE - clampedAmplitude\n return normalizedSine * clampedAmplitude + amplitudeComplement\n}\n\n/**\n * Wave effect component that creates a wave motion through text\n *\n * Animates text with a sine wave pattern that can affect brightness or vertical position.\n * The wave continuously flows through the text.\n *\n * @example\n * ```tsx\n * <Wave colors={['#ec4899', '#8b5cf6']} amplitude={0.7} frequency={3}>\n * ~~ wavy text ~~\n * </Wave>\n * ```\n */\nexport function Wave({\n children,\n colors = DEFAULT_COLORS,\n amplitude = DEFAULT_AMPLITUDE,\n frequency = DEFAULT_FREQUENCY,\n type = DEFAULT_TYPE,\n speed = DEFAULT_SPEED,\n enabled = true,\n}: WaveProps) {\n const elapsedTime = useElapsedTime(enabled, speed)\n const normalizedTime = elapsedTime / WAVE_PERIOD_MS\n\n const waveText = useMemo(() => {\n if (type === 'brightness') {\n return mapChars(children, (character, index) => {\n const waveValue = calculateWaveValue(index, normalizedTime, frequency, amplitude)\n const [darkColor, brightColor] = colors\n const characterColor = interpolateColor(darkColor, brightColor, waveValue)\n return colorize(character, characterColor)\n })\n }\n\n return mapChars(children, (character, index) => {\n const waveValue = calculateWaveValue(index, normalizedTime, frequency, amplitude)\n const shouldShift = waveValue > VERTICAL_SHIFT_THRESHOLD\n return shouldShift ? ` ${character}` : character\n })\n }, [children, normalizedTime, colors, amplitude, frequency, type])\n\n return <Text>{waveText}</Text>\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useAnimationFrame.ts","../src/hooks/useElapsedTime.ts","../src/utils/colors.ts","../src/utils/easing.ts","../src/components/Fade.tsx","../src/components/Flash.tsx","../src/utils/text.ts","../src/components/Shimmer.tsx","../src/hooks/useCursorBlink.ts","../src/hooks/useTypewriterProgress.ts","../src/components/Typewriter.tsx","../src/components/Wave.tsx"],"names":["useState","useEffect","DEFAULT_DURATION_MS","DEFAULT_SPEED","useMemo","jsx","Text","MIN_INTENSITY","MAX_INTENSITY","useRef","FIRST_CHARACTER_INDEX","DEFAULT_COLORS","FULL_CIRCLE_RADIANS","SINE_WAVE_OFFSET","SINE_WAVE_NORMALIZE"],"mappings":"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","file":"index.js","sourcesContent":["import { useEffect, useRef } from 'react'\n\nconst TARGET_FPS = 60\nconst FRAME_INTERVAL_MS = 1000 / TARGET_FPS\n\n/**\n * Runs a callback on every animation frame using setInterval\n *\n * @param callback - Function called each frame with delta time in ms\n * @param enabled - Whether animation is active\n */\nexport function useAnimationFrame(\n callback: (deltaTime: number) => void,\n enabled: boolean = true,\n): void {\n const intervalRef = useRef<Timer>()\n const previousTimeRef = useRef<number>()\n const callbackRef = useRef(callback)\n\n useEffect(() => {\n callbackRef.current = callback\n }, [callback])\n\n useEffect(() => {\n if (!enabled) {\n if (intervalRef.current) {\n clearInterval(intervalRef.current)\n }\n previousTimeRef.current = undefined\n return\n }\n\n intervalRef.current = setInterval(() => {\n const currentTime = Date.now()\n if (previousTimeRef.current !== undefined) {\n const deltaTime = currentTime - previousTimeRef.current\n callbackRef.current(deltaTime)\n }\n previousTimeRef.current = currentTime\n }, FRAME_INTERVAL_MS)\n\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current)\n }\n }\n }, [enabled])\n}\n","import { useState } from 'react'\nimport { useAnimationFrame } from './useAnimationFrame.js'\n\n/**\n * Hook that tracks elapsed time since mount or last reset\n *\n * @param enabled - Whether to track time\n * @param speed - Speed multiplier (default: 1)\n * @returns Elapsed time in milliseconds\n */\nexport function useElapsedTime(\n enabled: boolean = true,\n speed: number = 1,\n): number {\n const [elapsedTime, setElapsedTime] = useState(0)\n\n useAnimationFrame((deltaTime) => {\n setElapsedTime(prev => prev + deltaTime * speed)\n }, enabled)\n\n return elapsedTime\n}\n","import type { Colour } from '../types/index.js'\nimport chalk from 'chalk'\n\nconst HEX_BASE = 16\nconst DECIMAL_BASE = 10\nconst HEX_BYTE_LENGTH = 2\nconst HEX_PAD_CHARACTER = '0'\nconst MIN_RGB_COMPONENTS = 3\nconst INTERPOLATION_MIDPOINT = 0.5\n\nconst HEX_COLOR_REGEX = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i\nconst RGB_NUMBERS_REGEX = /\\d+/g\n\nfunction rgbToHex(red: number, green: number, blue: number): string {\n const redHex = red.toString(HEX_BASE).padStart(HEX_BYTE_LENGTH, HEX_PAD_CHARACTER)\n const greenHex = green.toString(HEX_BASE).padStart(HEX_BYTE_LENGTH, HEX_PAD_CHARACTER)\n const blueHex = blue.toString(HEX_BASE).padStart(HEX_BYTE_LENGTH, HEX_PAD_CHARACTER)\n return `#${redHex}${greenHex}${blueHex}`\n}\n\n/**\n * Converts a hex color string to RGB values\n *\n * @param hex - Hex color string (with or without #)\n * @returns Tuple of [red, green, blue] values (0-255) or null if invalid\n */\nexport function hexToRgb(hex: string): [number, number, number] | null {\n const result = HEX_COLOR_REGEX.exec(hex)\n if (!result)\n return null\n\n const [, redHex, greenHex, blueHex] = result\n return [\n Number.parseInt(redHex, HEX_BASE),\n Number.parseInt(greenHex, HEX_BASE),\n Number.parseInt(blueHex, HEX_BASE),\n ]\n}\n\n/**\n * Applies color to text using chalk\n *\n * @param text - Text to colorize\n * @param color - Color as hex (#ff0000), rgb(255,0,0), or named color (red, blue, etc)\n * @returns Colorized text with ANSI escape codes\n */\nexport function colorize(text: string, color: Colour): string {\n try {\n if (color.startsWith('#'))\n return chalk.hex(color)(text)\n\n if (color.startsWith('rgb')) {\n const match = color.match(RGB_NUMBERS_REGEX)\n if (!match || match.length < MIN_RGB_COMPONENTS)\n return text\n\n const [redStr, greenStr, blueStr] = match\n const red = Number.parseInt(redStr, DECIMAL_BASE)\n const green = Number.parseInt(greenStr, DECIMAL_BASE)\n const blue = Number.parseInt(blueStr, DECIMAL_BASE)\n\n if (Number.isNaN(red) || Number.isNaN(green) || Number.isNaN(blue))\n return text\n\n return chalk.rgb(red, green, blue)(text)\n }\n\n const chalkColor = chalk[color as keyof typeof chalk]\n if (typeof chalkColor === 'function')\n return chalkColor(text)\n\n return text\n }\n catch {\n return text\n }\n}\n\n/**\n * Applies opacity to a hex color by darkening it\n * Note: Terminal approximation - true opacity not possible in most terminals\n *\n * @param color - Hex color string\n * @param opacity - Opacity value from 0 (transparent/black) to 1 (fully opaque)\n * @returns Adjusted hex color or original color if not hex format\n */\nexport function applyOpacity(color: Colour, opacity: number): Colour {\n if (!color.startsWith('#'))\n return color\n\n const rgb = hexToRgb(color)\n if (!rgb)\n return color\n\n const [red, green, blue] = rgb\n const adjustedRed = Math.round(red * opacity)\n const adjustedGreen = Math.round(green * opacity)\n const adjustedBlue = Math.round(blue * opacity)\n\n return rgbToHex(adjustedRed, adjustedGreen, adjustedBlue)\n}\n\n/**\n * Interpolates between two colors\n *\n * @param color1 - Starting color\n * @param color2 - Ending color\n * @param progress - Interpolation progress from 0 to 1\n * @returns Interpolated color (hex if both inputs are hex, otherwise snaps to nearest)\n */\nexport function interpolateColor(\n color1: Colour,\n color2: Colour,\n progress: number,\n): Colour {\n const isColor1Hex = color1.startsWith('#')\n const isColor2Hex = color2.startsWith('#')\n\n if (!isColor1Hex || !isColor2Hex)\n return progress < INTERPOLATION_MIDPOINT ? color1 : color2\n\n const rgb1 = hexToRgb(color1)\n const rgb2 = hexToRgb(color2)\n\n if (!rgb1 || !rgb2)\n return progress < INTERPOLATION_MIDPOINT ? color1 : color2\n\n const [red1, green1, blue1] = rgb1\n const [red2, green2, blue2] = rgb2\n\n const red = Math.round(red1 + (red2 - red1) * progress)\n const green = Math.round(green1 + (green2 - green1) * progress)\n const blue = Math.round(blue1 + (blue2 - blue1) * progress)\n\n return rgbToHex(red, green, blue)\n}\n","import type { EasingFunction, EasingName } from '../types/index.js'\n\nexport const linear: EasingFunction = time => time\n\nexport const easeIn: EasingFunction = time => time * time\n\nexport const easeOut: EasingFunction = time => time * (2 - time)\n\nexport const easeInOut: EasingFunction = (time) => {\n return time < 0.5\n ? 2 * time * time\n : -1 + (4 - 2 * time) * time\n}\n\nexport const easingFunctions: Record<EasingName, EasingFunction> = {\n 'linear': linear,\n 'ease-in': easeIn,\n 'ease-out': easeOut,\n 'ease-in-out': easeInOut,\n}\n\nexport function getEasingFunction(name: EasingName = 'linear'): EasingFunction {\n return easingFunctions[name]\n}\n\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max)\n}\n\nexport function normalize(value: number, min: number, max: number): number {\n return clamp((value - min) / (max - min), 0, 1)\n}\n\nexport function lerp(start: number, end: number, progress: number): number {\n return start + (end - start) * progress\n}\n","import type { BaseEffectProps, Colour, EasingName } from '../types/index.js'\nimport { Text } from 'ink'\nimport { useEffect, useMemo, useState } from 'react'\nimport { useElapsedTime } from '../hooks/useElapsedTime.js'\nimport { applyOpacity, colorize } from '../utils/colors.js'\nimport { getEasingFunction } from '../utils/easing.js'\n\ninterface FadeProps extends BaseEffectProps {\n /**\n * Text colour\n * @default '#ffffff'\n * @example 'yellow'\n */\n color?: Colour\n\n /**\n * Starting opacity (0-1)\n * @default 0\n */\n from?: number\n\n /**\n * Ending opacity (0-1)\n * @default 1\n */\n to?: number\n\n /**\n * Duration in milliseconds\n * @default 1000\n */\n duration?: number\n\n /**\n * Easing function\n * @default 'ease-out'\n */\n easing?: EasingName\n\n /**\n * Loop animation continuously\n * @default false\n */\n loop?: boolean\n}\n\nconst DEFAULT_FROM = 0\nconst DEFAULT_TO = 1\nconst DEFAULT_DURATION_MS = 1000\nconst DEFAULT_EASING: EasingName = 'ease-out'\nconst DEFAULT_LOOP = false\nconst DEFAULT_SPEED = 1\n\nfunction calculateOpacity(\n elapsedTime: number,\n duration: number,\n from: number,\n to: number,\n easing: EasingName,\n): number {\n const progress = Math.min(elapsedTime / duration, 1)\n const easedProgress = getEasingFunction(easing)(progress)\n return from + (to - from) * easedProgress\n}\n\n/**\n * Fade effect component that smoothly transitions text opacity\n *\n * Animates text from one opacity to another using configurable easing functions.\n * Can loop continuously or run once.\n *\n * @example\n * ```tsx\n * <Fade color=\"yellow\" from={0} to={1} duration={500} easing=\"ease-in\">\n * Success!\n * </Fade>\n * ```\n */\nexport function Fade({\n children,\n color,\n from = DEFAULT_FROM,\n to = DEFAULT_TO,\n duration = DEFAULT_DURATION_MS,\n easing = DEFAULT_EASING,\n loop = DEFAULT_LOOP,\n speed = DEFAULT_SPEED,\n enabled = true,\n onComplete,\n}: FadeProps) {\n const [hasCompleted, setHasCompleted] = useState(false)\n const elapsedTime = useElapsedTime(enabled && !hasCompleted, speed)\n\n useEffect(() => {\n if (!enabled)\n return\n\n const isComplete = elapsedTime >= duration\n\n if (isComplete && !loop && !hasCompleted) {\n // eslint-disable-next-line react-hooks-extra/no-direct-set-state-in-use-effect -- Intentional: prevents calling onComplete multiple times by setting completion flag once\n setHasCompleted(true)\n onComplete?.()\n }\n }, [elapsedTime, duration, loop, hasCompleted, enabled, onComplete])\n\n const fadedText = useMemo(() => {\n const effectiveTime = loop ? elapsedTime % duration : Math.min(elapsedTime, duration)\n const opacity = calculateOpacity(effectiveTime, duration, from, to, easing)\n\n const baseColor = color ?? '#ffffff'\n const fadedColor = applyOpacity(baseColor, opacity)\n\n return colorize(children, fadedColor)\n }, [children, elapsedTime, duration, from, to, easing, color, loop])\n\n return <Text>{fadedText}</Text>\n}\n","import type { BaseEffectProps, Colour } from '../types/index.js'\nimport { Text } from 'ink'\nimport { useMemo } from 'react'\nimport { useElapsedTime } from '../hooks/useElapsedTime.js'\nimport { applyOpacity, colorize } from '../utils/colors.js'\nimport { clamp } from '../utils/easing.js'\n\ninterface FlashProps extends BaseEffectProps {\n /**\n * Text colour\n * @default '#ffffff'\n * @example 'cyan'\n */\n color?: Colour\n\n /**\n * Minimum brightness (0-1)\n * @default 0.3\n */\n minIntensity?: number\n\n /**\n * Maximum brightness (0-1)\n * @default 1\n */\n maxIntensity?: number\n\n /**\n * Flash cycle duration in milliseconds\n * @default 1000\n */\n duration?: number\n}\n\nconst DEFAULT_COLOR = '#ffffff'\nconst DEFAULT_MIN_INTENSITY = 0.3\nconst DEFAULT_MAX_INTENSITY = 1\nconst DEFAULT_DURATION_MS = 1000\nconst DEFAULT_SPEED = 1\nconst FULL_CIRCLE_RADIANS = Math.PI * 2\nconst SINE_WAVE_OFFSET = 1\nconst SINE_WAVE_NORMALIZE = 0.5\nconst MIN_INTENSITY = 0\nconst MAX_INTENSITY = 1\n\nfunction calculateFlashIntensity(\n time: number,\n duration: number,\n minIntensity: number,\n maxIntensity: number,\n): number {\n const clampedMin = clamp(minIntensity, MIN_INTENSITY, MAX_INTENSITY)\n const clampedMax = clamp(maxIntensity, MIN_INTENSITY, MAX_INTENSITY)\n const normalizedTime = time / duration\n const phase = normalizedTime * FULL_CIRCLE_RADIANS\n const sineWave = Math.sin(phase)\n const normalizedSine = (sineWave + SINE_WAVE_OFFSET) * SINE_WAVE_NORMALIZE\n const intensityRange = clampedMax - clampedMin\n return clampedMin + normalizedSine * intensityRange\n}\n\n/**\n * Flash effect component that creates a pulsing neon-like glow\n *\n * Animates text with a continuous brightness pulse that oscillates\n * between minimum and maximum intensity levels, creating a flashing\n * or glowing neon effect.\n *\n * @example\n * ```tsx\n * <Flash color=\"cyan\" minIntensity={0.3} maxIntensity={1} duration={800}>\n * ⚡ NEON GLOW ⚡\n * </Flash>\n * ```\n */\nexport function Flash({\n children,\n color = DEFAULT_COLOR,\n minIntensity = DEFAULT_MIN_INTENSITY,\n maxIntensity = DEFAULT_MAX_INTENSITY,\n duration = DEFAULT_DURATION_MS,\n speed = DEFAULT_SPEED,\n enabled = true,\n}: FlashProps) {\n const elapsedTime = useElapsedTime(enabled, speed)\n const cycleTime = elapsedTime % duration\n\n const flashText = useMemo(() => {\n const intensity = calculateFlashIntensity(cycleTime, duration, minIntensity, maxIntensity)\n const adjustedColor = applyOpacity(color, intensity)\n return colorize(children, adjustedColor)\n }, [children, cycleTime, duration, minIntensity, maxIntensity, color])\n\n return <Text>{flashText}</Text>\n}\n","export function splitChars(text: string): string[] {\n return [...text]\n}\n\nexport function getTextLength(text: string): number {\n return [...text].length\n}\n\nexport function mapChars(\n text: string,\n fn: (char: string, index: number) => string,\n): string {\n return splitChars(text).map(fn).join('')\n}\n","import type { BaseEffectProps, Colour } from '../types/index.js'\nimport { Text } from 'ink'\nimport { useMemo, useState } from 'react'\nimport { useAnimationFrame } from '../hooks/useAnimationFrame.js'\nimport { colorize, interpolateColor } from '../utils/colors.js'\nimport { clamp } from '../utils/easing.js'\nimport { getTextLength, mapChars } from '../utils/text.js'\n\ntype ShimmerDirection = 'left' | 'right'\n\ninterface ShimmerProps extends BaseEffectProps {\n /**\n * Gradient colours for the shimmer effect [start, peak, end]\n * @default ['#666666', '#ffffff', '#666666']\n * @example ['#60a5fa', '#3b82f6', '#60a5fa']\n */\n colors?: [Colour, Colour, Colour]\n\n /**\n * Width of the shimmer band in characters\n * @default 4\n */\n width?: number\n\n /**\n * Brightness multiplier (0-1)\n * @default 1\n */\n intensity?: number\n\n /**\n * Direction of shimmer movement\n * @default 'right'\n */\n direction?: ShimmerDirection\n}\n\nconst DEFAULT_COLORS: [Colour, Colour, Colour] = ['#666666', '#ffffff', '#666666']\nconst DEFAULT_WIDTH = 4\nconst DEFAULT_INTENSITY = 1\nconst DEFAULT_DIRECTION: ShimmerDirection = 'right'\nconst DEFAULT_SPEED = 1\nconst ANIMATION_CYCLE_MS = 2000\nconst COLOUR_TRANSITION_MIDPOINT = 0.5\nconst COLOUR_PROGRESS_MULTIPLIER = 2\nconst MIN_INTENSITY = 0\nconst MAX_INTENSITY = 1\n\n/**\n * Shimmer effect component that creates a moving highlight across text\n *\n * Creates a shimmering animation by interpolating through a gradient of colours\n * that sweeps across the text from left to right or right to left.\n *\n * @example\n * ```tsx\n * <Shimmer colors={['#60a5fa', '#3b82f6', '#60a5fa']} intensity={0.8}>\n * Loading...\n * </Shimmer>\n * ```\n */\nexport function Shimmer({\n children,\n colors = DEFAULT_COLORS,\n width = DEFAULT_WIDTH,\n intensity = DEFAULT_INTENSITY,\n direction = DEFAULT_DIRECTION,\n speed = DEFAULT_SPEED,\n enabled = true,\n onComplete,\n}: ShimmerProps) {\n const [offset, setOffset] = useState(0)\n const textLength = getTextLength(children)\n const totalWidth = textLength + width\n\n useAnimationFrame((deltaTime) => {\n setOffset((previousOffset) => {\n const movement = (deltaTime / ANIMATION_CYCLE_MS) * speed * totalWidth\n const newOffset = direction === 'right'\n ? previousOffset + movement\n : previousOffset - movement\n\n const hasCompletedCycle = Math.abs(newOffset) >= totalWidth\n if (hasCompletedCycle) {\n onComplete?.()\n return 0\n }\n\n return newOffset\n })\n }, enabled)\n\n const shimmerText = useMemo(() => {\n return mapChars(children, (char, index) => {\n const normalizedOffset = direction === 'right' ? offset : totalWidth - offset\n const distance = Math.abs(index - normalizedOffset)\n\n const isWithinShimmerWidth = distance < width\n const colorProgress = isWithinShimmerWidth\n ? distance / width\n : 1\n\n const [startColor, peakColor, endColor] = colors\n const adjustedIntensity = clamp(intensity, MIN_INTENSITY, MAX_INTENSITY)\n\n const isFirstHalf = colorProgress < COLOUR_TRANSITION_MIDPOINT\n const currentColor = isFirstHalf\n ? interpolateColor(\n startColor,\n peakColor,\n colorProgress * COLOUR_PROGRESS_MULTIPLIER * adjustedIntensity,\n )\n : interpolateColor(\n peakColor,\n endColor,\n (colorProgress - COLOUR_TRANSITION_MIDPOINT) * COLOUR_PROGRESS_MULTIPLIER * adjustedIntensity,\n )\n\n return colorize(char, currentColor)\n })\n }, [children, offset, colors, width, intensity, direction, totalWidth])\n\n return <Text>{shimmerText}</Text>\n}\n","import { useEffect, useRef, useState } from 'react'\n\nconst CURSOR_BLINK_INTERVAL_MS = 500\n\ninterface UseCursorBlinkOptions {\n enabled: boolean\n isComplete: boolean\n}\n\n/**\n * Hook that manages cursor blinking state\n *\n * @param options - Configuration for cursor blink\n * @param options.enabled - Whether cursor blinking is enabled\n * @param options.isComplete - Whether typing is complete\n * @returns Whether cursor should be visible\n */\nexport function useCursorBlink({\n enabled,\n isComplete,\n}: UseCursorBlinkOptions): boolean {\n const [showCursor, setShowCursor] = useState(true)\n const intervalRef = useRef<Timer>()\n\n useEffect(() => {\n if (!enabled || isComplete) {\n // eslint-disable-next-line react-hooks-extra/no-direct-set-state-in-use-effect -- Intentional: synchronizing cursor visibility with prop changes\n setShowCursor(false)\n return\n }\n\n intervalRef.current = setInterval(() => {\n setShowCursor(previous => !previous)\n }, CURSOR_BLINK_INTERVAL_MS)\n\n return () => {\n if (intervalRef.current)\n clearInterval(intervalRef.current)\n }\n }, [enabled, isComplete])\n\n return showCursor\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { clamp } from '../utils/easing.js'\n\nconst BASE_CHARACTER_DELAY_MS = 80\nconst MIN_VARIANCE = 0\nconst MAX_VARIANCE = 1\nconst FIRST_CHARACTER_INDEX = 0\n\nfunction calculateCharacterDelay(\n speed: number,\n variance: number,\n): number {\n const clampedVariance = clamp(variance, MIN_VARIANCE, MAX_VARIANCE)\n const randomVariance = (Math.random() - 0.5) * clampedVariance\n return (BASE_CHARACTER_DELAY_MS / speed) * (1 + randomVariance)\n}\n\ninterface UseTypewriterProgressOptions {\n totalCharacters: number\n speed: number\n variance: number\n initialDelay: number\n enabled: boolean\n onComplete?: () => void\n}\n\n/**\n * Hook that manages typewriter character reveal timing\n *\n * @param options - Configuration for typewriter progress\n * @param options.totalCharacters - Total number of characters to reveal\n * @param options.speed - Animation speed multiplier\n * @param options.variance - Typing variance for human-like timing (0-1)\n * @param options.initialDelay - Delay before typing starts in milliseconds\n * @param options.enabled - Whether the animation is enabled\n * @param options.onComplete - Callback when typing completes\n * @returns Number of characters to display\n */\nexport function useTypewriterProgress({\n totalCharacters,\n speed,\n variance,\n initialDelay,\n enabled,\n onComplete,\n}: UseTypewriterProgressOptions): number {\n const [visibleCharacters, setVisibleCharacters] = useState(0)\n const timeoutRef = useRef<Timer>()\n const hasCompletedTyping = visibleCharacters >= totalCharacters\n\n useEffect(() => {\n if (!enabled)\n return\n\n if (hasCompletedTyping) {\n onComplete?.()\n return\n }\n\n const characterDelay = calculateCharacterDelay(speed, variance)\n const isFirstCharacter = visibleCharacters === FIRST_CHARACTER_INDEX\n const totalDelay = isFirstCharacter ? initialDelay + characterDelay : characterDelay\n\n timeoutRef.current = setTimeout(() => {\n setVisibleCharacters(previous => previous + 1)\n }, totalDelay)\n\n return () => {\n if (timeoutRef.current)\n clearTimeout(timeoutRef.current)\n }\n }, [visibleCharacters, enabled, speed, variance, initialDelay, hasCompletedTyping, onComplete])\n\n return visibleCharacters\n}\n","import type { BaseEffectProps, Colour } from '../types/index.js'\nimport { Text } from 'ink'\nimport { useMemo } from 'react'\nimport { useCursorBlink } from '../hooks/useCursorBlink.js'\nimport { useTypewriterProgress } from '../hooks/useTypewriterProgress.js'\nimport { colorize } from '../utils/colors.js'\nimport { getTextLength, splitChars } from '../utils/text.js'\n\ninterface TypewriterProps extends BaseEffectProps {\n /**\n * Text colour\n * @default undefined (inherits)\n * @example 'green'\n */\n color?: Colour\n\n /**\n * Cursor character or false to disable\n * @default '▋'\n * @example '█'\n */\n cursor?: string | boolean\n\n /**\n * Cursor colour (defaults to text colour)\n * @default undefined\n * @example 'cyan'\n */\n cursorColor?: Colour\n\n /**\n * Typing speed randomness (0-1) for more human-like typing\n * @default 0.3\n */\n variance?: number\n\n /**\n * Initial delay before typing starts (ms)\n * @default 0\n */\n delay?: number\n}\n\nconst DEFAULT_CURSOR = '▋'\nconst DEFAULT_VARIANCE = 0.3\nconst DEFAULT_DELAY = 0\nconst DEFAULT_SPEED = 1\nconst FIRST_CHARACTER_INDEX = 0\n\nfunction getCursorCharacter(cursor: string | boolean): string {\n return typeof cursor === 'string' ? cursor : DEFAULT_CURSOR\n}\n\nfunction formatCursor(\n cursorCharacter: string,\n cursorColor: Colour | undefined,\n textColor: Colour | undefined,\n): string {\n if (cursorColor)\n return colorize(cursorCharacter, cursorColor)\n\n if (textColor)\n return colorize(cursorCharacter, textColor)\n\n return cursorCharacter\n}\n\n/**\n * Typewriter effect component that reveals text character by character\n *\n * Simulates typing text with configurable speed, variance for realistic timing,\n * and an optional cursor. The cursor blinks while typing is in progress.\n *\n * @example\n * ```tsx\n * <Typewriter color=\"green\" cursor=\"█\" variance={0.5} speed={2}>\n * npm install ink-motion\n * </Typewriter>\n * ```\n */\nexport function Typewriter({\n children,\n color,\n cursor = DEFAULT_CURSOR,\n cursorColor,\n variance = DEFAULT_VARIANCE,\n delay = DEFAULT_DELAY,\n speed = DEFAULT_SPEED,\n enabled = true,\n onComplete,\n}: TypewriterProps) {\n const characters = splitChars(children)\n const totalCharacters = getTextLength(children)\n\n const visibleCharacters = useTypewriterProgress({\n totalCharacters,\n speed,\n variance,\n initialDelay: delay,\n enabled,\n onComplete,\n })\n\n const hasCompletedTyping = visibleCharacters >= totalCharacters\n const isCursorEnabled = cursor !== false\n\n const showCursor = useCursorBlink({\n enabled: enabled && isCursorEnabled,\n isComplete: hasCompletedTyping,\n })\n\n const displayText = useMemo(() => {\n const visibleText = characters.slice(FIRST_CHARACTER_INDEX, visibleCharacters).join('')\n const coloredText = color ? colorize(visibleText, color) : visibleText\n\n const shouldShowCursor = isCursorEnabled && showCursor && !hasCompletedTyping\n if (!shouldShowCursor)\n return coloredText\n\n const cursorCharacter = getCursorCharacter(cursor)\n const cursorText = formatCursor(cursorCharacter, cursorColor, color)\n\n return coloredText + cursorText\n }, [characters, visibleCharacters, color, cursor, cursorColor, showCursor, hasCompletedTyping, isCursorEnabled])\n\n return <Text>{displayText}</Text>\n}\n","import type { BaseEffectProps, Colour } from '../types/index.js'\nimport { Text } from 'ink'\nimport { useMemo } from 'react'\nimport { useElapsedTime } from '../hooks/useElapsedTime.js'\nimport { colorize, interpolateColor } from '../utils/colors.js'\nimport { clamp } from '../utils/easing.js'\nimport { mapChars } from '../utils/text.js'\n\ntype WaveType = 'brightness' | 'vertical'\n\ninterface WaveProps extends BaseEffectProps {\n /**\n * Gradient colours for the wave [dark, bright]\n * @default ['#888888', '#ffffff']\n * @example ['#ec4899', '#8b5cf6']\n */\n colors?: [Colour, Colour]\n\n /**\n * Wave height (0-1)\n * @default 0.5\n */\n amplitude?: number\n\n /**\n * Number of wave cycles across text\n * @default 2\n */\n frequency?: number\n\n /**\n * Wave effect type\n * @default 'brightness'\n */\n type?: WaveType\n}\n\nconst DEFAULT_COLORS: [Colour, Colour] = ['#888888', '#ffffff']\nconst DEFAULT_AMPLITUDE = 0.5\nconst DEFAULT_FREQUENCY = 2\nconst DEFAULT_TYPE: WaveType = 'brightness'\nconst DEFAULT_SPEED = 1\nconst FULL_CIRCLE_RADIANS = Math.PI * 2\nconst WAVE_PERIOD_MS = 2000\nconst MIN_AMPLITUDE = 0\nconst MAX_AMPLITUDE = 1\nconst SINE_WAVE_OFFSET = 1\nconst SINE_WAVE_NORMALIZE = 0.5\nconst VERTICAL_SHIFT_THRESHOLD = 0.5\n\nfunction calculateWaveValue(\n characterIndex: number,\n time: number,\n frequency: number,\n amplitude: number,\n): number {\n const clampedAmplitude = clamp(amplitude, MIN_AMPLITUDE, MAX_AMPLITUDE)\n const safeFrequency = frequency || DEFAULT_FREQUENCY\n const waveLength = FULL_CIRCLE_RADIANS / safeFrequency\n const spatialComponent = (characterIndex / waveLength) * FULL_CIRCLE_RADIANS\n const temporalComponent = time * FULL_CIRCLE_RADIANS\n const phase = spatialComponent - temporalComponent\n const sineWave = Math.sin(phase)\n const normalizedSine = (sineWave + SINE_WAVE_OFFSET) * SINE_WAVE_NORMALIZE\n const amplitudeComplement = MAX_AMPLITUDE - clampedAmplitude\n return normalizedSine * clampedAmplitude + amplitudeComplement\n}\n\n/**\n * Wave effect component that creates a wave motion through text\n *\n * Animates text with a sine wave pattern that can affect brightness or vertical position.\n * The wave continuously flows through the text.\n *\n * @example\n * ```tsx\n * <Wave colors={['#ec4899', '#8b5cf6']} amplitude={0.7} frequency={3}>\n * ~~ wavy text ~~\n * </Wave>\n * ```\n */\nexport function Wave({\n children,\n colors = DEFAULT_COLORS,\n amplitude = DEFAULT_AMPLITUDE,\n frequency = DEFAULT_FREQUENCY,\n type = DEFAULT_TYPE,\n speed = DEFAULT_SPEED,\n enabled = true,\n}: WaveProps) {\n const elapsedTime = useElapsedTime(enabled, speed)\n const normalizedTime = elapsedTime / WAVE_PERIOD_MS\n\n const waveText = useMemo(() => {\n if (type === 'brightness') {\n return mapChars(children, (character, index) => {\n const waveValue = calculateWaveValue(index, normalizedTime, frequency, amplitude)\n const [darkColor, brightColor] = colors\n const characterColor = interpolateColor(darkColor, brightColor, waveValue)\n return colorize(character, characterColor)\n })\n }\n\n return mapChars(children, (character, index) => {\n const waveValue = calculateWaveValue(index, normalizedTime, frequency, amplitude)\n const shouldShift = waveValue > VERTICAL_SHIFT_THRESHOLD\n return shouldShift ? ` ${character}` : character\n })\n }, [children, normalizedTime, colors, amplitude, frequency, type])\n\n return <Text>{waveText}</Text>\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,11 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ink-motion",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"description": "Motion and animation components for Ink - beautiful text effects for your CLI",
|
|
5
3
|
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
4
|
+
"version": "1.0.1",
|
|
5
|
+
"description": "Motion and animation components for Ink - beautiful text effects for your CLI",
|
|
6
|
+
"author": "James Glenn",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/JR-G/ink-motion#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/JR-G/ink-motion.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/JR-G/ink-motion/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"ink",
|
|
18
|
+
"cli",
|
|
19
|
+
"terminal",
|
|
20
|
+
"animation",
|
|
21
|
+
"motion",
|
|
22
|
+
"text-effects",
|
|
23
|
+
"react",
|
|
24
|
+
"shimmer",
|
|
25
|
+
"typewriter",
|
|
26
|
+
"fade",
|
|
27
|
+
"wave",
|
|
28
|
+
"flash",
|
|
29
|
+
"neon"
|
|
30
|
+
],
|
|
9
31
|
"exports": {
|
|
10
32
|
".": {
|
|
11
33
|
"import": {
|
|
@@ -18,6 +40,9 @@
|
|
|
18
40
|
}
|
|
19
41
|
}
|
|
20
42
|
},
|
|
43
|
+
"main": "./dist/index.cjs",
|
|
44
|
+
"module": "./dist/index.js",
|
|
45
|
+
"types": "./dist/index.d.ts",
|
|
21
46
|
"files": [
|
|
22
47
|
"dist"
|
|
23
48
|
],
|
|
@@ -30,46 +55,26 @@
|
|
|
30
55
|
"lint": "eslint .",
|
|
31
56
|
"prepublishOnly": "bun run build && publint"
|
|
32
57
|
},
|
|
33
|
-
"keywords": [
|
|
34
|
-
"ink",
|
|
35
|
-
"cli",
|
|
36
|
-
"terminal",
|
|
37
|
-
"animation",
|
|
38
|
-
"motion",
|
|
39
|
-
"text-effects",
|
|
40
|
-
"react",
|
|
41
|
-
"shimmer",
|
|
42
|
-
"typewriter",
|
|
43
|
-
"fade",
|
|
44
|
-
"wave"
|
|
45
|
-
],
|
|
46
|
-
"author": "James Glenn",
|
|
47
|
-
"license": "MIT",
|
|
48
|
-
"repository": {
|
|
49
|
-
"type": "git",
|
|
50
|
-
"url": "git+https://github.com/JR-G/ink-motion.git"
|
|
51
|
-
},
|
|
52
|
-
"bugs": {
|
|
53
|
-
"url": "https://github.com/JR-G/ink-motion/issues"
|
|
54
|
-
},
|
|
55
|
-
"homepage": "https://github.com/JR-G/ink-motion#readme",
|
|
56
58
|
"peerDependencies": {
|
|
57
59
|
"ink": "^5.0.0",
|
|
58
60
|
"react": "^18.0.0"
|
|
59
61
|
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"chalk": "^5.3.0"
|
|
64
|
+
},
|
|
60
65
|
"devDependencies": {
|
|
61
|
-
"@antfu/eslint-config": "^
|
|
66
|
+
"@antfu/eslint-config": "^6.7.3",
|
|
67
|
+
"@eslint-react/eslint-plugin": "^2.3.13",
|
|
62
68
|
"@types/bun": "latest",
|
|
63
69
|
"@types/react": "^18.3.12",
|
|
64
|
-
"eslint": "^9.
|
|
70
|
+
"eslint": "^9.39.2",
|
|
71
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
72
|
+
"eslint-plugin-react-refresh": "^0.4.26",
|
|
65
73
|
"ink": "^5.0.1",
|
|
66
74
|
"ink-testing-library": "^4.0.0",
|
|
67
75
|
"publint": "^0.2.12",
|
|
68
76
|
"react": "^18.3.1",
|
|
69
77
|
"tsup": "^8.3.5",
|
|
70
78
|
"typescript": "^5.6.3"
|
|
71
|
-
},
|
|
72
|
-
"dependencies": {
|
|
73
|
-
"chalk": "^5.3.0"
|
|
74
79
|
}
|
|
75
80
|
}
|