import * as react from 'react';
import { RefObject } from 'react';
import { Variants, HTMLMotionProps } from 'motion/react';
import { Options, HooksOptions } from 'react-markdown';
/**
* Per-character animation overrides for a single special character.
* Used in the `specialCharacters` prop.
*
* ### `delay` vs `delayAfter`
*
* - **`delay`** overrides how long the typewriter waits *before* this character appears
* (i.e. it replaces the global stagger delay for this specific character).
* No consecutive rule: every occurrence gets its own override, even in `"..."`.
*
* - **`delayAfter`** inserts a pause *after* this character before the next one appears.
* Consecutive rule applies: for `"..."` only the **last** `.` triggers the pause;
* for `". . ."` each `.` triggers its own pause because a space separates them.
*
* @example
* // Pause 400 ms after each sentence-ending "." but not within "..."
* // (the pause fires only after the third dot in "...").
* const specialChars = {
* ".": { delayAfter: 400 },
* ",": { delayAfter: 150 },
* "!": { delayAfter: 500 },
* "?": { delayAfter: 500 },
* };
*/
interface SpecialCharacterOptions {
/**
* Overrides the global stagger delay for THIS character — i.e. how long the typewriter
* waits before this character appears.
*
* **No consecutive rule**: every occurrence gets its own override, even in `"..."`.
*/
delay?: number;
/**
* Inserts a pause *after* this character before the next one appears.
*
* **Consecutive rule applies**: in `"..."` only the LAST `.` triggers; in `"?!?"` only the last `?`
* triggers; in `". . ."` each `.` triggers (a space breaks the sequence).
*/
delayAfter?: number;
/**
* Custom motion variants for this character.
* Overrides `motionProps.characterVariants` for this specific character.
*
* @default The value of `motionProps.characterVariants`
*/
characterVariants?: Variants;
}
interface TypewriterProps {
/**
* The delay in milliseconds between the appearance of one letter and the next.
* @default 10
*/
delay?: number;
/**
* Per-character animation overrides for special characters (e.g. punctuation).
*
* Each key must be a **single character**. The value is a {@link SpecialCharacterOptions}
* object with three optional fields:
*
* - **`delay`** — overrides how long the typewriter waits *before* this character appears.
* No consecutive rule: every occurrence gets its own override, including in `"..."`.
* - **`delayAfter`** — inserts a pause *after* this character before the next one appears.
* Consecutive rule: in `"..."` only the **last** `.` triggers; in `"?!?"` only the last `?` triggers;
* in `". . ."` each `.` triggers (a space breaks the sequence).
* - **`characterVariants`** — custom framer-motion variants for this character.
*
* @example
* ```tsx
* // Pause after punctuation; "..." pauses once (after the third dot);
* // "?!?" pauses once (after the last "?").
*
* Hello, world. How are you?! Really?!? Wow...
*
* ```
*/
specialCharacters?: {
[char: string]: SpecialCharacterOptions;
};
/**
* When `true`, consecutive special characters (any combination) accumulate their
* `delayAfter` values: the **last** character in the run fires with the sum of all
* delays in that run; all preceding ones are silenced.
*
* A "run" is any unbroken sequence of characters that each have a `delayAfter`
* configured. A non-special character (or a React element boundary) breaks the run.
*
* | Text | `false` (default) | `true` |
* |------|-------------------|--------|
* | `"..."` (`.delayAfter=400`) | last `.` → 400 ms | last `.` → 1 200 ms |
* | `".!."` (`.`=400, `!`=500) | last `.` → 400 ms | last `.` → 1 300 ms |
* | `". . ."` | each `.` fires (space breaks) | each `.` fires (space breaks) |
*
* @default false
*/
accumulateConsecutiveDelays?: boolean;
/**
* The props to pass to the [motion span](https://motion.dev/docs/react-motion-component).
*
* The `characterVariants` parameter has been added to be able to modify the animation of each individual letter
*/
motionProps?: Omit, "variants"> & {
/**
* The motion variants for each individual letter.
*
* @default { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { opacity: { duration: 0 } } } }
*/
characterVariants?: Variants;
/**
* A callback that is called when the animation of a letter is complete.
* The callback is called with the reference to the letter.
*
* @example
* ```tsx
* import { useRef } from "react";
*
* export default function NarrationScreen() {
* const paragraphRef = useRef(null);
* const scrollToEnd = useCallback((ref: { current: HTMLSpanElement | null }) => {
* if (paragraphRef.current && ref.current) {
* let scrollTop = ref.current.offsetTop - paragraphRef.current.clientHeight / 2;
* paragraphRef.current.scrollTo({
* top: scrollTop,
* behavior: "auto",
* });
* }
* }, []);
* return (
*
*
* Hello World
*
*
* );
* }
* ```
*/
onCharacterAnimationComplete?: (letterRef: RefObject) => void;
};
}
interface MarkdownTypewriterProps extends Options, TypewriterProps {
}
interface MarkdownTypewriterHooksProps extends HooksOptions, TypewriterProps {
}
declare function MarkdownTypewriter(props: MarkdownTypewriterProps): react.JSX.Element;
declare function MarkdownTypewriterAsync(props: MarkdownTypewriterProps): Promise;
declare function MarkdownTypewriterHooks(props: MarkdownTypewriterHooksProps): react.JSX.Element;
export { MarkdownTypewriter, MarkdownTypewriterAsync, MarkdownTypewriterHooks, type MarkdownTypewriterHooksProps, type MarkdownTypewriterProps, type SpecialCharacterOptions };