{ "name": "type-writer", "type": "registry:component", "dependencies": [ "motion" ], "files": [ { "type": "registry:component", "content": "\"use client\";\n\n/**\n * @author: @dorianbaffier\n * @description: Typewriter\n * @version: 1.0.0\n * @date: 2025-06-26\n * @license: MIT\n * @website: https://kokonutui.com\n * @github: https://github.com/kokonut-labs/kokonutui\n */\n\nimport { motion } from \"motion/react\";\nimport { useEffect, useRef, useState } from \"react\";\n\ntype TypewriterSequence = {\n text: string;\n deleteAfter?: boolean;\n pauseAfter?: number;\n};\n\ntype TypewriterTitleProps = {\n sequences?: TypewriterSequence[];\n typingSpeed?: number;\n startDelay?: number;\n autoLoop?: boolean;\n loopDelay?: number;\n deleteSpeed?: number;\n pauseBeforeDelete?: number;\n naturalVariance?: boolean;\n};\n\nconst DEFAULT_SEQUENCES: TypewriterSequence[] = [\n { text: \"Typewriter\", deleteAfter: true },\n { text: \"Multiple Words\", deleteAfter: true },\n { text: \"Auto Loop\", deleteAfter: false },\n];\n\nexport default function TypewriterTitle({\n sequences = DEFAULT_SEQUENCES,\n typingSpeed = 50,\n startDelay = 200,\n autoLoop = true,\n loopDelay = 1000,\n deleteSpeed = 30,\n pauseBeforeDelete = 1000,\n naturalVariance = true,\n}: TypewriterTitleProps) {\n const [displayText, setDisplayText] = useState(\"\");\n const sequenceIndexRef = useRef(0);\n const charIndexRef = useRef(0);\n const isDeletingRef = useRef(false);\n const timeoutRef = useRef(null);\n\n // Initialize with the sequences provided\n const sequencesRef = useRef(sequences);\n useEffect(() => {\n sequencesRef.current = sequences;\n }, [sequences]);\n\n useEffect(() => {\n const getTypingDelay = () => {\n if (!naturalVariance) {\n return typingSpeed;\n }\n\n // More natural human typing pattern\n const random = Math.random();\n\n // 10% chance of a longer pause (thinking/hesitation)\n if (random < 0.1) {\n return typingSpeed * 2;\n }\n\n // 10% chance of a burst (fast typing)\n if (random > 0.9) {\n return typingSpeed * 0.5;\n }\n\n // Standard variance (+/- 40%)\n const variance = 0.4;\n const min = typingSpeed * (1 - variance);\n const max = typingSpeed * (1 + variance);\n return Math.random() * (max - min) + min;\n };\n\n const runTypewriter = () => {\n const currentSequence = sequencesRef.current[sequenceIndexRef.current];\n if (!currentSequence) {\n return;\n }\n\n if (isDeletingRef.current) {\n if (charIndexRef.current > 0) {\n charIndexRef.current -= 1;\n setDisplayText(currentSequence.text.slice(0, charIndexRef.current));\n timeoutRef.current = setTimeout(runTypewriter, deleteSpeed);\n } else {\n isDeletingRef.current = false;\n const isLastSequence =\n sequenceIndexRef.current === sequencesRef.current.length - 1;\n\n if (isLastSequence && autoLoop) {\n timeoutRef.current = setTimeout(() => {\n sequenceIndexRef.current = 0;\n runTypewriter();\n }, loopDelay);\n } else if (!isLastSequence) {\n timeoutRef.current = setTimeout(() => {\n sequenceIndexRef.current += 1;\n runTypewriter();\n }, 100); // Quick transition to next word\n }\n }\n } else if (charIndexRef.current < currentSequence.text.length) {\n charIndexRef.current += 1;\n setDisplayText(currentSequence.text.slice(0, charIndexRef.current));\n timeoutRef.current = setTimeout(runTypewriter, getTypingDelay());\n } else {\n const pauseDuration = currentSequence.pauseAfter ?? pauseBeforeDelete;\n\n if (currentSequence.deleteAfter) {\n timeoutRef.current = setTimeout(() => {\n isDeletingRef.current = true;\n runTypewriter();\n }, pauseDuration);\n } else {\n const isLastSequence =\n sequenceIndexRef.current === sequencesRef.current.length - 1;\n\n if (isLastSequence && autoLoop) {\n timeoutRef.current = setTimeout(() => {\n sequenceIndexRef.current = 0;\n charIndexRef.current = 0;\n setDisplayText(\"\");\n runTypewriter();\n }, loopDelay);\n } else if (!isLastSequence) {\n timeoutRef.current = setTimeout(() => {\n sequenceIndexRef.current += 1;\n charIndexRef.current = 0;\n setDisplayText(\"\");\n runTypewriter();\n }, pauseDuration);\n }\n }\n }\n };\n\n // Start the loop\n timeoutRef.current = setTimeout(runTypewriter, startDelay);\n\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, [\n // Only restart effect if timing configs change.\n // We use sequencesRef for content to avoid restarting on array reference change.\n typingSpeed,\n deleteSpeed,\n pauseBeforeDelete,\n autoLoop,\n loopDelay,\n startDelay,\n naturalVariance,\n ]);\n\n return (\n
\n
\n \n \n {displayText}\n \n \n \n
\n
\n );\n}\n", "path": "/components/kokonutui/type-writer.tsx", "target": "components/kokonutui/type-writer.tsx" } ] }