@ertekinno/human-like
Version:
A sophisticated React typewriter effect library with realistic human typing behavior, mobile/desktop keyboards, and comprehensive theming support
3 lines (2 loc) • 38.7 kB
JavaScript
"use strict";var e=Object.defineProperty,t=(t,s,i)=>((t,s,i)=>s in t?e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[s]=i)(t,"symbol"!=typeof s?s+"":s,i);const s=require("react"),i={BASE_SPEED:80,SPEED_VARIATION:40,MIN_CHAR_DELAY:25,SENTENCE_PAUSE:500,COMMA_PAUSE:200,WORD_SPACE:150,LINE_BREAK:800,REALIZATION_DELAY:300,CORRECTION_PAUSE:250,BACKSPACE_SPEED:60,THINKING_PAUSE:400,FATIGUE_INCREMENT:.5,BURST_SPEED_MULTIPLIER:.6,CONCENTRATION_PAUSE:800,SHIFT_HESITATION:100,CAPS_LOCK_ON_DELAY:150,CAPS_LOCK_OFF_DELAY:100,CAPS_SEQUENCE_THRESHOLD:3,NUMBER_ROW_PENALTY:35,SYMBOL_BASE_PENALTY:25,LOOK_AHEAD_CHANCE:.08},r={MISTAKE_FREQUENCY:.03,CONCENTRATION_LAPSE:.03,BURST_TYPING:.15,FATIGUE_FACTOR:1e-4,OVERCORRECTION_RATE:.2},a={q:["w","a","s"],w:["q","e","a","s","d"],e:["w","r","s","d","f"],r:["e","t","d","f","g"],t:["r","y","f","g","h"],y:["t","u","g","h","j"],u:["y","i","h","j","k"],i:["u","o","j","k","l"],o:["i","p","k","l",";"],p:["o","[","l",";","'"],"[":["p","]",";","'"],"]":["[","\\","'"],a:["q","w","s","z"],s:["q","w","e","a","d","z","x"],d:["w","e","r","s","f","x","c"],f:["e","r","t","d","g","c","v"],g:["r","t","y","f","h","v","b"],h:["t","y","u","g","j","b","n"],j:["y","u","i","h","k","n","m"],k:["u","i","o","j","l","m",","],l:["i","o","p","k",";",",","."],";":["o","p","[","l","'",".","/"],"'":["p","[","]",";","/","."],z:["a","s","x"],x:["z","s","d","c"],c:["x","d","f","v"," "],v:["c","f","g","b"," "],b:["v","g","h","n"," "],n:["b","h","j","m"," "],m:["n","j","k",","," "],",":["m","k","l","."],".":[",","l",";","/","'"],"/":[".",";","'"]," ":["c","v","b","n","m"]},o={q:["w","a","s"],w:["q","e","s","a"],e:["w","r","d","s"],r:["e","t","f","d"],t:["r","y","g","f"],y:["t","u","h","g"],u:["y","i","j","h"],i:["u","o","k","j"],o:["i","p","l","k"],p:["o","l"],a:["q","w","s","z"],s:["a","d","w","e","z","x"],d:["s","f","e","r","x","c"],f:["d","g","r","t","c","v"],g:["f","h","t","y","v","b"],h:["g","j","y","u","b","n"],j:["h","k","u","i","n","m"],k:["j","l","i","o","m"],l:["k","o","p"],z:["a","s","x"],x:["z","c","s","d"],c:["x","v","d","f"," "],v:["c","b","f","g"," "],b:["v","n","g","h"," "],n:["b","m","h","j"," "],m:["n","j","k"," "]," ":["c","v","b","n","m","x","z"],1:["2","q","w"],2:["1","3","q","w","e"],3:["2","4","w","e","r"],4:["3","5","e","r","t"],5:["4","6","r","t","y"],6:["5","7","t","y","u"],7:["6","8","y","u","i"],8:["7","9","u","i","o"],9:["8","0","i","o","p"],0:["9","o","p"]},n=a;function c(e){return"mobile"===e?o:a}const h=new Set(["the","and","for","are","but","not","you","all","can","had","her","was","one","our","out","day","get","has","him","his","how","man","new","now","old","see","two","way","who","boy","did","its","let","put","say","she","too","use","that","with","have","this","will","your","from","they","know","want","been","good","much","some","time","very","when","come","here","just","like","long","make","many","over","such","take","than","them","well","were"]),u={the:"teh",and:"adn",for:"fro",you:"yuo",that:"taht",this:"tihs",with:"wiht",have:"ahve",from:"form",they:"thye",been:"bene",than:"htan",what:"waht",your:"yuor",when:"wehn",there:"tehre",their:"thier",would:"woudl",could:"coudl",should:"shoudl",through:"trhough",because:"becasue",before:"beofre",after:"aftre",where:"whree",which:"whihc",between:"betwene",different:"differnet",important:"importnat",example:"exmaple",without:"withuot",another:"antoher",development:"developement",environment:"enviroment",government:"goverment",management:"managment",information:"infromation",available:"availabe",business:"buisness",complete:"compelte",language:"langauge",experience:"experiance",position:"postion",question:"quesiton",remember:"remeber",separate:"seperate",something:"somehting",together:"togehter",understand:"udnerstand"},l=new Set(["!","@","#","$","%","^","&","*","(",")","-","_","+","=","[","]","{","}","\\","|",";",":","'",'"',",",".","<",">","/","?","`","~"]),d=new Set(["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","!","@","#","$","%","^","&","*","(",")","_","+","{","}","|",":",'"',"<",">","?","~"]),p=new Set(["0","1","2","3","4","5","6","7","8","9"]),y={".":1,",":1,"?":1,"!":1,";":1,":":1,"'":1,'"':1,"-":2,_:2,"(":2,")":2,"[":2,"]":2,"/":2,"@":3,"#":3,$:3,"%":3,"^":3,"&":3,"*":3,"+":3,"=":3,"{":3,"}":3,"\\":3,"|":3,"`":3,"~":3,"<":3,">":3},m=new Set([".","!","?"]),g=new Set([",",";",":"]),k=new Set(["\n","\r\n","\r"]),b={speed:i.BASE_SPEED,speedVariation:i.SPEED_VARIATION,mistakeFrequency:r.MISTAKE_FREQUENCY,mistakeTypes:{adjacent:!0,random:!1,doubleChar:!0,commonTypos:!0},fatigueEffect:!0,concentrationLapses:!0,overcorrection:!0,debug:!1,sentencePause:i.SENTENCE_PAUSE,wordPause:i.WORD_SPACE,thinkingPause:i.THINKING_PAUSE,minCharDelay:i.MIN_CHAR_DELAY,backspaceSpeed:i.BACKSPACE_SPEED,realizationDelay:i.REALIZATION_DELAY,correctionPause:i.CORRECTION_PAUSE,keyboardMode:"mobile"},f={views:{letters:["q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k","l","z","x","c","v","b","n","m"],numbers:["1","2","3","4","5","6","7","8","9","0","-","/",":",";","(",")","$","&","@",'"',".",",","?","!","'","[","]","{","}","#","%","^","*","+","=","_","\\","|","~","<",">","€","£","¥","•","..."],symbols:["[","]","{","}","#","%","^","*","+","=","_","\\","|","~","<",">","€","£","¥","•",".",",","?","!","'",'"',"/",":",";","(",")","$","&","@","`","§","¿","¡","«","»","°","†","‡","…","‰","′","″","‹","›"],emoji:["😀","😃","😄","😁","😆","😅","🤣","😂","🙂","🙃","😉","😊","😇","🥰","😍","🤩","😘","😗","☺","😚","😙","🥲","😋","😛","😜","🤪","😝","🤑","🤗","🤭"]},viewSwitchers:{toNumbers:"123",toSymbols:"#+=",toLetters:"ABC",toEmoji:"😀"},modifiers:{shift:"⇧",caps:"CAPS",space:"space",enter:"return",backspace:"⌫"},keyDurations:{letter:80,number:90,symbol:100,modifier:120,viewSwitch:110,space:75,enter:90,backspace:110}},C={...Object.fromEntries("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".split("").map(e=>[e,"letters"])),1:"numbers",2:"numbers",3:"numbers",4:"numbers",5:"numbers",6:"numbers",7:"numbers",8:"numbers",9:"numbers",0:"numbers","-":"numbers","/":"numbers",":":"numbers",";":"numbers","(":"numbers",")":"numbers",$:"numbers","&":"numbers","@":"numbers",'"':"numbers",".":"numbers",",":"numbers","?":"numbers","!":"numbers","'":"numbers","[":"symbols","]":"symbols","{":"symbols","}":"symbols","#":"symbols","%":"symbols","^":"symbols","*":"symbols","+":"symbols","=":"symbols",_:"symbols","\\":"symbols","|":"symbols","~":"symbols","<":"symbols",">":"symbols","€":"symbols","£":"symbols","¥":"symbols","•":"symbols","`":"symbols","§":"symbols","¿":"symbols","¡":"symbols","«":"symbols","»":"symbols","°":"symbols","†":"symbols","‡":"symbols","…":"symbols","‰":"symbols","′":"symbols","″":"symbols","‹":"symbols","›":"symbols"},w={views:{letters:["q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k","l","z","x","c","v","b","n","m"],numbers:["1","2","3","4","5","6","7","8","9","0"],symbols:["`","~","!","@","#","$","%","^","&","*","(",")","-","_","+","=","[","]","{","}","\\","|",";",":","'",'"',",",".","<",">","/","?"]},viewSwitchers:{toNumbers:"numbers",toSymbols:"symbols",toLetters:"letters"},modifiers:{shift:"shift",caps:"caps lock",space:"space",enter:"enter",backspace:"backspace"},keyDurations:{letter:70,number:85,symbol:95,modifier:90,viewSwitch:0,space:60,enter:80,backspace:100}},T={a:["a",!1],b:["b",!1],c:["c",!1],d:["d",!1],e:["e",!1],f:["f",!1],g:["g",!1],h:["h",!1],i:["i",!1],j:["j",!1],k:["k",!1],l:["l",!1],m:["m",!1],n:["n",!1],o:["o",!1],p:["p",!1],q:["q",!1],r:["r",!1],s:["s",!1],t:["t",!1],u:["u",!1],v:["v",!1],w:["w",!1],x:["x",!1],y:["y",!1],z:["z",!1],A:["a",!0],B:["b",!0],C:["c",!0],D:["d",!0],E:["e",!0],F:["f",!0],G:["g",!0],H:["h",!0],I:["i",!0],J:["j",!0],K:["k",!0],L:["l",!0],M:["m",!0],N:["n",!0],O:["o",!0],P:["p",!0],Q:["q",!0],R:["r",!0],S:["s",!0],T:["t",!0],U:["u",!0],V:["v",!0],W:["w",!0],X:["x",!0],Y:["y",!0],Z:["z",!0],1:["1",!1],2:["2",!1],3:["3",!1],4:["4",!1],5:["5",!1],6:["6",!1],7:["7",!1],8:["8",!1],9:["9",!1],0:["0",!1],"!":["1",!0],"@":["2",!0],"#":["3",!0],$:["4",!0],"%":["5",!0],"^":["6",!0],"&":["7",!0],"*":["8",!0],"(":["9",!0],")":["0",!0],"`":["`",!1],"-":["-",!1],"=":["=",!1],"[":["[",!1],"]":["]",!1],"\\":["\\",!1],";":[";",!1],"'":["'",!1],",":[",",!1],".":[".",!1],"/":["/",!1]," ":["space",!1],"\n":["enter",!1],"\t":["tab",!1],"~":["`",!0],_:["-",!0],"+":["=",!0],"{":["[",!0],"}":["]",!0],"|":["\\",!0],":":[";",!0],'"':["'",!0],"<":[",",!0],">":[".",!0],"?":["/",!0]},S={MOBILE_CASUAL:{name:"Mobile Casual",description:"Average smartphone user, thumb typing",baseMultiplier:1,keyDurations:{letter:120,number:140,symbol:160,modifier:180,viewSwitch:150,space:100,enter:130,backspace:150},viewSwitchDelay:50,consecutiveKeyBonus:.9,complexSymbolPenalty:40,capsLockTransitionDelay:60},MOBILE_FAST:{name:"Mobile Fast",description:"Experienced mobile user, swipe/predictive text habits",baseMultiplier:.7,keyDurations:{letter:80,number:95,symbol:110,modifier:120,viewSwitch:100,space:70,enter:90,backspace:110},viewSwitchDelay:30,consecutiveKeyBonus:.85,complexSymbolPenalty:25,capsLockTransitionDelay:40},MOBILE_CAREFUL:{name:"Mobile Careful",description:"Deliberate mobile typing, hunt-and-peck style",baseMultiplier:1.5,keyDurations:{letter:180,number:220,symbol:280,modifier:250,viewSwitch:200,space:150,enter:180,backspace:220},viewSwitchDelay:80,consecutiveKeyBonus:.95,complexSymbolPenalty:60,capsLockTransitionDelay:100},TABLET:{name:"Tablet",description:"Tablet device, often landscape mode with more fingers",baseMultiplier:.8,keyDurations:{letter:90,number:110,symbol:130,modifier:140,viewSwitch:120,space:80,enter:100,backspace:130},viewSwitchDelay:40,consecutiveKeyBonus:.8,complexSymbolPenalty:30,capsLockTransitionDelay:50}},x={DESKTOP_AVERAGE:{name:"Desktop Average",description:"Average desktop user, ~40 WPM",baseMultiplier:1,keyDurations:{letter:80,number:100,symbol:120,modifier:90,viewSwitch:0,space:70,enter:90,backspace:100},viewSwitchDelay:0,consecutiveKeyBonus:.85,complexSymbolPenalty:30,capsLockTransitionDelay:50},DESKTOP_FAST:{name:"Desktop Fast",description:"Experienced typist, ~70+ WPM, touch typing",baseMultiplier:.6,keyDurations:{letter:50,number:65,symbol:80,modifier:60,viewSwitch:0,space:45,enter:60,backspace:70},viewSwitchDelay:0,consecutiveKeyBonus:.8,complexSymbolPenalty:15,capsLockTransitionDelay:25},DESKTOP_PROGRAMMER:{name:"Desktop Programmer",description:"Developer typing, frequent symbols and modifiers",baseMultiplier:.7,keyDurations:{letter:60,number:70,symbol:75,modifier:65,viewSwitch:0,space:50,enter:70,backspace:80},viewSwitchDelay:0,consecutiveKeyBonus:.8,complexSymbolPenalty:10,capsLockTransitionDelay:30},DESKTOP_SLOW:{name:"Desktop Slow",description:"Hunt-and-peck typing, looking at keyboard",baseMultiplier:2,keyDurations:{letter:160,number:200,symbol:250,modifier:180,viewSwitch:0,space:140,enter:160,backspace:180},viewSwitchDelay:0,consecutiveKeyBonus:.95,complexSymbolPenalty:80,capsLockTransitionDelay:120},DESKTOP_GAMING:{name:"Desktop Gaming",description:"Mechanical keyboard, gaming-optimized typing",baseMultiplier:.5,keyDurations:{letter:40,number:50,symbol:60,modifier:45,viewSwitch:0,space:35,enter:50,backspace:60},viewSwitchDelay:0,consecutiveKeyBonus:.75,complexSymbolPenalty:10,capsLockTransitionDelay:20}};function L(e,t){return"mobile"===e?"fast"===t?S.MOBILE_FAST:"slow"===t||"careful"===t?S.MOBILE_CAREFUL:"tablet"===t?S.TABLET:S.MOBILE_CASUAL:"fast"===t?x.DESKTOP_FAST:"slow"===t?x.DESKTOP_SLOW:"programmer"===t||"developer"===t?x.DESKTOP_PROGRAMMER:"gaming"===t?x.DESKTOP_GAMING:x.DESKTOP_AVERAGE}function E(e,t,s){let i=e*t.baseMultiplier;return s.isConsecutiveSameHand&&(i*=t.consecutiveKeyBonus),s.isComplexSymbol&&(i+=t.complexSymbolPenalty),s.isViewSwitch&&(i+=t.viewSwitchDelay),s.isCapsLockTransition&&(i+=t.capsLockTransitionDelay),i}class M{constructor(e={}){t(this,"config"),t(this,"layout"),t(this,"state"),t(this,"timingProfile"),this.config={keyboardMode:"mobile",capsLockThreshold:3,useNaturalTiming:!0,debug:!1,...e},this.timingProfile=L(this.config.keyboardMode,this.config.typingSpeed);const s=e.customLayout||("mobile"===this.config.keyboardMode?f:w);var i,r;this.layout=(i=s,r=this.timingProfile,{...i,keyDurations:{...r.keyDurations}}),this.state={currentView:"letters",capsLockActive:!1,shiftActive:!1,recentCharacters:[],mode:this.config.keyboardMode},this.debug("KeyboardAnalyzer initialized",{config:this.config,timingProfile:this.timingProfile.name,layout:this.layout.viewSwitchers})}analyzeCharacter(e,t,s){this.debug(`Analyzing character: "${e}" at index ${t}`),this.updateRecentCharacters(e);const i=this.analyzeCapsLockSequence(e,t,s);let r=[];r="mobile"===this.config.keyboardMode?this.analyzeMobileCharacter(e,i):this.analyzeDesktopCharacter(e,i);const a=r.reduce((e,t)=>e+t.duration,0),o={character:e,keys:r,totalDuration:a,usesCapsLock:i.isCapsLockSequence};return this.debug(`Generated sequence for "${e}":`,o),o}analyzeMobileCharacter(e,t){const s=[];let i=0;if(" "===e)return[{key:this.layout.modifiers.space,character:e,type:"space",keyboardView:this.state.currentView,isCapsLock:!1,duration:this.layout.keyDurations.space,sequenceIndex:0,sequenceLength:1}];if("\n"===e)return[{key:this.layout.modifiers.enter,character:e,type:"enter",keyboardView:this.state.currentView,isCapsLock:!1,duration:this.layout.keyDurations.enter,sequenceIndex:0,sequenceLength:1}];const r=this.getCharacterView(e);if(this.state.currentView!==r){this.getViewSwitchSequence(this.state.currentView,r).forEach(e=>{s.push({...e,sequenceIndex:i++,sequenceLength:0})}),this.state.currentView=r}if(e.match(/[A-Z]/))if(t.isCapsLockSequence){if(t.isFirst)s.push(this.createCapsKey(!0,i++)),this.state.capsLockActive=!0;else if(t.isLast)return s.push(this.createLetterKey(e.toLowerCase(),i++,t.isCapsLockSequence)),s.push(this.createCapsKey(!1,i++)),this.state.capsLockActive=!1,s.forEach(e=>e.sequenceLength=s.length),s}else s.push(this.createShiftKey(i++));const a=this.createCharacterKey(e,i++,t.isCapsLockSequence);return s.push(a),s.forEach(e=>e.sequenceLength=s.length),s}analyzeDesktopCharacter(e,t){const s=[];let i=0;if(" "===e)return[{key:"space",character:e,type:"space",keyboardView:"letters",isCapsLock:!1,duration:this.layout.keyDurations.space,sequenceIndex:0,sequenceLength:1}];if("\n"===e)return[{key:"enter",character:e,type:"enter",keyboardView:"letters",isCapsLock:!1,duration:this.layout.keyDurations.enter,sequenceIndex:0,sequenceLength:1}];const r=T[e];if(!r)return this.debug(`No desktop mapping found for character: "${e}"`),[{key:e.toLowerCase(),character:e,type:"letter",keyboardView:"letters",isCapsLock:!1,duration:this.layout.keyDurations.letter,sequenceIndex:0,sequenceLength:1}];const[a,o]=r;if(e.match(/[A-Z]/))if(t.isCapsLockSequence){if(t.isFirst)s.push({key:"caps lock",character:e,type:"modifier",keyboardView:"letters",isCapsLock:!0,duration:this.layout.keyDurations.modifier,sequenceIndex:i++,sequenceLength:0});else if(t.isLast)return s.push({key:a,character:e,type:"letter",keyboardView:"letters",isCapsLock:!0,duration:this.layout.keyDurations.letter,sequenceIndex:i++,sequenceLength:0}),s.push({key:"caps lock",character:e,type:"modifier",keyboardView:"letters",isCapsLock:!0,duration:this.layout.keyDurations.modifier,sequenceIndex:i++,sequenceLength:0}),s.forEach(e=>e.sequenceLength=s.length),s}else o&&s.push({key:"shift",character:e,type:"modifier",keyboardView:"letters",isCapsLock:!1,duration:this.layout.keyDurations.modifier,sequenceIndex:i++,sequenceLength:0});else o&&s.push({key:"shift",character:e,type:"modifier",keyboardView:"symbols",isCapsLock:!1,duration:this.layout.keyDurations.modifier,sequenceIndex:i++,sequenceLength:0});const n=e.match(/[a-zA-Z]/)?"letter":e.match(/[0-9]/)?"number":"symbol";return s.push({key:a,character:e,type:n,keyboardView:"letter"===n?"letters":"number"===n?"numbers":"symbols",isCapsLock:t.isCapsLockSequence,duration:this.layout.keyDurations[n],sequenceIndex:i++,sequenceLength:0}),s.forEach(e=>e.sequenceLength=s.length),s}getCharacterView(e){return"desktop"===this.config.keyboardMode?"letters":C[e]||"letters"}getViewSwitchSequence(e,t){if(e===t)return[];const s=[];switch(t){case"numbers":s.push({key:this.layout.viewSwitchers.toNumbers,character:"",type:"view-switch",keyboardView:e,isCapsLock:!1,duration:this.layout.keyDurations.viewSwitch,sequenceIndex:0,sequenceLength:0});break;case"symbols":"letters"===e&&s.push({key:this.layout.viewSwitchers.toNumbers,character:"",type:"view-switch",keyboardView:e,isCapsLock:!1,duration:this.layout.keyDurations.viewSwitch,sequenceIndex:0,sequenceLength:0}),s.push({key:this.layout.viewSwitchers.toSymbols,character:"",type:"view-switch",keyboardView:"numbers",isCapsLock:!1,duration:this.layout.keyDurations.viewSwitch,sequenceIndex:0,sequenceLength:0});break;case"letters":s.push({key:this.layout.viewSwitchers.toLetters,character:"",type:"view-switch",keyboardView:e,isCapsLock:!1,duration:this.layout.keyDurations.viewSwitch,sequenceIndex:0,sequenceLength:0})}return s}createCapsKey(e,t){const s=E(this.layout.keyDurations.modifier,this.timingProfile,{isCapsLockTransition:!0});return{key:this.layout.modifiers.caps,character:"",type:"modifier",keyboardView:this.state.currentView,isCapsLock:!0,duration:e?s+20:s,sequenceIndex:t,sequenceLength:0}}createShiftKey(e){return{key:this.layout.modifiers.shift,character:"",type:"modifier",keyboardView:this.state.currentView,isCapsLock:!1,duration:this.layout.keyDurations.modifier,sequenceIndex:e,sequenceLength:0}}createLetterKey(e,t,s){return{key:e,character:e,type:"letter",keyboardView:"letters",isCapsLock:s,duration:this.layout.keyDurations.letter,sequenceIndex:t,sequenceLength:0}}createCharacterKey(e,t,s){const i=e.toLowerCase(),r=e.match(/[a-zA-Z]/)?"letter":e.match(/[0-9]/)?"number":"symbol",a=this.layout.keyDurations[r],o="symbol"===r&&this.isComplexSymbol(e),n=E(a,this.timingProfile,{isComplexSymbol:o});return{key:i,character:e,type:r,keyboardView:this.state.currentView,isCapsLock:s,duration:n,sequenceIndex:t,sequenceLength:0}}updateRecentCharacters(e){this.state.recentCharacters.push(e),this.state.recentCharacters.length>10&&this.state.recentCharacters.shift()}analyzeCapsLockSequence(e,t,s){if(!e.match(/[A-Z]/))return{isCapsLockSequence:!1,isFirst:!1,isLast:!1};let i=t,r=t;for(;i>0;){const e=s[i-1];if(!e.match(/[A-Z]/)&&" "!==e)break;i--}for(;r<s.length-1;){const e=s[r+1];if(!e.match(/[A-Z]/)&&" "!==e)break;r++}let a=0;for(let h=i;h<=r;h++)s[h].match(/[A-Z]/)&&a++;const o=a>=this.config.capsLockThreshold;let n=i;for(;n<=r&&!s[n].match(/[A-Z]/);)n++;let c=r;for(;c>=i&&!s[c].match(/[A-Z]/);)c--;return{isCapsLockSequence:o,isFirst:o&&t===n,isLast:o&&t===c}}isComplexSymbol(e){return new Set(["@","#","$","%","^","&","*","+","=","{","}","\\","|","`","~","<",">"]).has(e)}debug(e,t){this.config.debug&&console.log(`[KeyboardAnalyzer] ${e}`,t||"")}resetState(){this.state={currentView:"letters",capsLockActive:!1,shiftActive:!1,recentCharacters:[],mode:this.config.keyboardMode}}analyzeBackspace(){this.debug("Analyzing backspace key");const e={key:"mobile"===this.config.keyboardMode?this.layout.modifiers.backspace:"backspace",character:"\b",type:"backspace",keyboardView:this.state.currentView,isCapsLock:!1,duration:this.layout.keyDurations.backspace||this.layout.keyDurations.modifier||120,sequenceIndex:0,sequenceLength:1},t={character:"\b",keys:[e],totalDuration:e.duration,usesCapsLock:!1};return this.debug("Generated backspace sequence:",t),t}getState(){return{...this.state}}}class D{constructor(e,s={}){t(this,"config"),t(this,"text"),t(this,"currentIndex",0),t(this,"displayText",""),t(this,"state","idle"),t(this,"timeoutId",null),t(this,"keyTimeouts",new Set),t(this,"stats"),t(this,"events",[]),t(this,"mistakes",[]),t(this,"correctionQueue",[]),t(this,"isCorrectingMistake",!1),t(this,"charactersTyped",0),t(this,"fatigueLevel",0),t(this,"pauseStartTime",0),t(this,"totalPausedTime",0),t(this,"keyboardAnalyzer"),t(this,"onStateChange"),t(this,"onCharacter"),t(this,"onMistake"),t(this,"onBackspace"),t(this,"onComplete"),t(this,"onProgress"),t(this,"onKey"),this.text=e??"",this.config={...b,...s},this.stats=this.initializeStats(),this.config.onKey&&(this.onKey=this.config.onKey),this.keyboardAnalyzer=new M({keyboardMode:this.config.keyboardMode||"mobile",capsLockThreshold:i.CAPS_SEQUENCE_THRESHOLD,useNaturalTiming:!0,debug:this.config.debug})}debug(...e){this.config.debug&&console.log(...e)}safeCallback(e,...t){if(e)try{e(...t)}catch(s){this.config.debug&&console.warn("Callback error:",s)}}initializeStats(){return{totalCharacters:this.text.length,charactersTyped:0,mistakesMade:0,mistakesCorrected:0,startTime:0,currentWPM:0,averageCharDelay:0,totalDuration:0}}start(){if("typing"===this.state||"correcting"===this.state)return;this.state="typing";const e=Date.now();0===this.stats.startTime&&(this.stats.startTime=e),this.safeCallback(this.onStateChange,"typing"),this.scheduleNextCharacter()}stop(){this.timeoutId&&(clearTimeout(this.timeoutId),this.timeoutId=null),this.keyTimeouts.forEach(e=>clearTimeout(e)),this.keyTimeouts.clear();for(const e of this.mistakes)e.corrected||(e.corrected=!0);this.correctionQueue=[],this.isCorrectingMistake=!1,this.state="idle",this.safeCallback(this.onStateChange,"idle")}pause(){"typing"!==this.state&&"correcting"!==this.state||(this.timeoutId&&(clearTimeout(this.timeoutId),this.timeoutId=null),this.pauseStartTime=Date.now(),this.state="paused",this.safeCallback(this.onStateChange,"paused"))}resume(){"paused"===this.state&&(this.pauseStartTime>0&&(this.totalPausedTime+=Date.now()-this.pauseStartTime,this.pauseStartTime=0),this.state="typing",this.safeCallback(this.onStateChange,"typing"),this.scheduleNextCharacter())}skip(){this.currentIndex=this.text.length,this.displayText=this.text,this.state="completed",this.stats.endTime=Date.now(),this.safeCallback(this.onStateChange,"completed"),this.safeCallback(this.onComplete)}reset(){this.stop(),this.currentIndex=0,this.displayText="",this.charactersTyped=0,this.fatigueLevel=0,this.mistakes=[],this.correctionQueue=[],this.isCorrectingMistake=!1,this.events=[],this.pauseStartTime=0,this.totalPausedTime=0,this.stats=this.initializeStats(),this.state="idle",this.safeCallback(this.onStateChange,"idle")}scheduleNextCharacter(){if(0===this.text.length)return void this.completeTyping();if(this.currentIndex>=this.text.length){const e=this.mistakes.some(e=>!e.corrected),t=this.correctionQueue.length>0;return e||t?(this.debug(`📝 Reached end but have ${this.mistakes.filter(e=>!e.corrected).length} uncorrected mistakes and ${this.correctionQueue.length} queued corrections`),void this.processNextCorrection()):this.keyTimeouts.size>0?(this.debug(`⏳ Reached end but waiting for ${this.keyTimeouts.size} key timeouts to complete`),void(this.timeoutId=window.setTimeout(()=>{this.scheduleNextCharacter()},50))):void this.completeTyping()}const e=this.text[this.currentIndex],t=this.calculateCharacterDelayWithKeyboard(e);if(this.currentIndex>0&&this.shouldHaveConcentrationLapse())return this.state="thinking",this.safeCallback(this.onStateChange,"thinking"),void(this.timeoutId=window.setTimeout(()=>{this.state="typing",this.safeCallback(this.onStateChange,"typing"),this.scheduleNextCharacter()},i.CONCENTRATION_PAUSE));const s=this.shouldMakeMistake(e);t<30?requestAnimationFrame(()=>{s?this.makeMistake(e):this.typeCharacter(e)}):this.timeoutId=window.setTimeout(()=>{s?this.makeMistake(e):this.typeCharacter(e)},t)}calculateCharacterDelayWithKeyboard(e){const t=this.keyboardAnalyzer.analyzeCharacter(e,this.currentIndex,this.text),s=this.config.speed/80,a=t.keys.map(e=>({...e,duration:Math.round(e.duration*s)})),o=a.reduce((e,t)=>e+t.duration,0);this.debug(`🎹 Natural timing for "${e}": ${a.length} keys, ${o}ms total (speed: ${this.config.speed}ms, multiplier: ${s.toFixed(2)})`);let n=0;a.forEach(e=>{const t=window.setTimeout(()=>{this.safeCallback(this.onKey,e),this.debug(`🔑 Key press: "${e.key}" (${e.type}) - ${e.duration}ms`),this.keyTimeouts.delete(t)},n);this.keyTimeouts.add(t),n+=e.duration});let c=o;if(c+=2*(Math.random()-.5)*this.config.speedVariation,this.config.fatigueEffect&&(c+=this.fatigueLevel,this.fatigueLevel+=i.FATIGUE_INCREMENT),Math.random()<r.BURST_TYPING&&(c*=i.BURST_SPEED_MULTIPLIER),m.has(e))c+=this.config.sentencePause;else if(g.has(e))c+=i.COMMA_PAUSE;else if(k.has(e))c+=i.LINE_BREAK;else if(" "===e){c+=this.config.wordPause;const e=this.getNextWord();e&&this.isComplexWord(e)&&(c+=this.config.thinkingPause)}return Math.max(this.config.minCharDelay,c)}shouldMakeMistake(e){if(" "===e||k.has(e)||0===this.currentIndex)return!1;let t=this.config.mistakeFrequency;return p.has(e)&&(t*=1.5),d.has(e)&&(t*=1.2),y[e]&&y[e]>=3&&(t*=1.3),!l.has(e)||y[e]&&1!==y[e]||(t*=.5),this.shouldMakeLookAheadMistake()&&(t*=2),Math.random()<t}makeMistake(e){const t=this.selectMistakeType(e),s=this.generateMistakeChar(e,t);if(!s)return void this.typeCharacter(e);const i={type:t,originalChar:e,mistakeChar:s,position:this.currentIndex,corrected:!1,realizationTime:this.config.realizationDelay+150*Math.random()};this.mistakes.push(i),this.stats.mistakesMade++,this.debug(`🔴 MISTAKE: "${e}" → "${s}" (${t}) at pos ${this.currentIndex}`),this.displayText+=s,this.currentIndex++,this.charactersTyped++,this.stats.charactersTyped=this.charactersTyped,this.recordEvent({type:"mistake",position:i.position,timestamp:Date.now(),char:s,mistake:i}),this.safeCallback(this.onCharacter,s,this.currentIndex-s.length),this.safeCallback(this.onMistake,i),this.updateProgress();const r=Math.max(i.realizationTime,200);this.debug(`📝 Adding "${s}" to correction queue (will correct in ${r}ms)`),this.timeoutId=window.setTimeout(()=>{i.corrected||(this.correctionQueue.push(i),this.processNextCorrection())},r)}processNextCorrection(){if(this.isCorrectingMistake||0===this.correctionQueue.length)return;const e=this.correctionQueue.pop();e.corrected?this.processNextCorrection():(this.debug(`🔧 Processing correction for "${e.mistakeChar}" → "${e.originalChar}"`),this.correctMistake(e))}correctMistake(e){this.isCorrectingMistake=!0,this.state="correcting",this.safeCallback(this.onStateChange,"correcting"),this.debug(`🔧 Correcting mistake: "${e.mistakeChar}" → should be "${e.originalChar}"`),this.debug(`📍 Current text: "${this.displayText}", current position: ${this.currentIndex}, mistake position: ${e.position}`);const t=e.position,s=this.displayText.length-t;if(this.debug(`🔄 Need to delete ${s} chars to get back to position ${t}`),s<=0)return this.debug("⚠️ Already at correct position, marking as corrected"),void this.finishCorrection(e);this.performBackspaceSequence(e,s,t)}performBackspaceSequence(e,t,s){let i=0;const r=this.config.backspaceSpeed,a=()=>{if(i<t&&this.displayText.length>s){if(this.displayText=this.displayText.slice(0,-1),i++,this.recordEvent({type:"backspace",position:this.displayText.length,timestamp:Date.now()}),this.keyboardAnalyzer&&this.onKey){const e=this.keyboardAnalyzer.analyzeBackspace(),t=this.config.speed/80;e.keys.map(e=>({...e,duration:Math.round(e.duration*t)})).forEach(e=>{this.safeCallback(this.onKey,e)})}this.safeCallback(this.onBackspace),this.debug(`⬅️ Backspace ${i}/${t}: "${this.displayText}" (length: ${this.displayText.length})`),i<t&&this.displayText.length>s?this.timeoutId=window.setTimeout(a,r):this.finishCorrection(e)}else this.finishCorrection(e)};this.timeoutId=window.setTimeout(a,r)}finishCorrection(e){this.currentIndex=e.position,this.displayText=this.text.slice(0,e.position),e.corrected=!0,this.stats.mistakesCorrected++,this.isCorrectingMistake=!1,this.debug(`✅ Correction complete. Position: ${this.currentIndex}, Text: "${this.displayText}"`),this.debug(`📝 Ready to type correct character: "${e.originalChar}"`),this.timeoutId=window.setTimeout(()=>{this.state="typing",this.safeCallback(this.onStateChange,"typing"),this.typeCharacter(e.originalChar),this.correctionQueue.length>0?this.timeoutId=window.setTimeout(()=>{this.processNextCorrection()},this.config.realizationDelay):this.currentIndex>=this.text.length&&(this.timeoutId=window.setTimeout(()=>{this.completeTyping()},100))},this.config.correctionPause)}typeCharacter(e){this.displayText+=e,this.currentIndex++,this.charactersTyped++,this.stats.charactersTyped=this.charactersTyped;const t=Date.now();this.recordEvent({type:"char",char:e,position:this.currentIndex-1,timestamp:t}),this.safeCallback(this.onCharacter,e,this.currentIndex-1),this.updateProgress(),this.updateWPM(),this.updateTotalDuration(),this.scheduleNextCharacter()}selectMistakeType(e){const t=[],s=c(this.config.keyboardMode||"desktop");if(this.config.mistakeTypes.adjacent&&s[e.toLowerCase()]&&t.push("adjacent"),this.config.mistakeTypes.random&&t.push("random"),this.config.mistakeTypes.doubleChar&&t.push("doubleChar"),this.config.mistakeTypes.commonTypos&&t.push("commonTypo"),this.shouldMakeLookAheadMistake()){const e=t.filter(e=>"commonTypo"===e||"adjacent"===e);if(e.length>0)return e[Math.floor(Math.random()*e.length)]}return t[Math.floor(Math.random()*t.length)]||"adjacent"}generateMistakeChar(e,t){const s=e===e.toUpperCase()&&e!==e.toLowerCase();switch(t){case"adjacent":const t=c(this.config.keyboardMode||"desktop")[e.toLowerCase()];if(t&&t.length>0){let e=t[Math.floor(Math.random()*t.length)];return s?e.toUpperCase():e.toLowerCase()}return null;case"doubleChar":return e+e;case"random":const i="abcdefghijklmnopqrstuvwxyz";let r=i[Math.floor(Math.random()*i.length)];return s?r.toUpperCase():r.toLowerCase();case"commonTypo":const a=this.getCurrentWord();if(a&&u[a.toLowerCase()]){const e=u[a.toLowerCase()],t=this.currentIndex-this.getWordStartIndex(a);if(t<e.length){let i=e[t];return s?i.toUpperCase():i.toLowerCase()}}const o=c(this.config.keyboardMode||"desktop")[e.toLowerCase()];if(o&&o.length>0){let e=o[Math.floor(Math.random()*o.length)];return s?e.toUpperCase():e.toLowerCase()}return null;default:return null}}shouldHaveConcentrationLapse(){return this.config.concentrationLapses&&Math.random()<r.CONCENTRATION_LAPSE}getNextWord(){const e=this.text.slice(this.currentIndex+1).match(/^\s*(\w+)/);return e?e[1]:null}getCurrentWord(){const e=this.text.slice(0,this.currentIndex),t=this.text.slice(this.currentIndex),s=e.match(/(\w+)$/),i=t.match(/^(\w*)/);return(s?s[1]:"")+(i?i[1]:"")||null}getWordStartIndex(e){const t=this.text.slice(0,this.currentIndex).lastIndexOf(e);return-1!==t?t:this.currentIndex}isComplexWord(e){return e.length>7||!h.has(e.toLowerCase())}shouldMakeLookAheadMistake(){const e=this.text.slice(this.currentIndex).slice(0,4);return["ing","tion","ly","ed","er","est","ness"].some(t=>e.includes(t.slice(0,Math.min(t.length,e.length))))&&Math.random()<i.LOOK_AHEAD_CHANCE}completeTyping(){const e=this.mistakes.filter(e=>!e.corrected),t=this.correctionQueue.length>0;if(e.length>0||t||this.isCorrectingMistake){this.debug(`🔍 Found ${e.length} uncorrected mistakes, ${this.correctionQueue.length} queued corrections, correcting: ${this.isCorrectingMistake}`);for(const t of e)this.correctionQueue.includes(t)||this.correctionQueue.push(t);return!this.isCorrectingMistake&&this.correctionQueue.length>0&&this.processNextCorrection(),void("completed"!==this.state&&(this.timeoutId=window.setTimeout(()=>{this.completeTyping()},500)))}if(this.keyTimeouts.size>0)return this.debug(`⚠️ Warning: completeTyping called with ${this.keyTimeouts.size} pending key timeouts`),void(this.timeoutId=window.setTimeout(()=>{this.completeTyping()},100));if(this.displayText=this.text,this.currentIndex=this.text.length,"completed"!==this.state){this.state="completed";const e=Date.now();this.stats.endTime=e,this.updateTotalDuration(),this.safeCallback(this.onStateChange,"completed"),this.safeCallback(this.onComplete),this.debug(`🎉 Typing completed! Final text: "${this.displayText}"`),this.debug(`⏱️ Total duration: ${this.stats.totalDuration}ms`),this.safeCallback(this.onProgress,100)}}updateProgress(){let e=this.getProgress();const t=this.mistakes.some(e=>!e.corrected),s=this.correctionQueue.length>0;e>=100&&(t||s||this.isCorrectingMistake)&&(e=Math.min(99,e)),this.safeCallback(this.onProgress,e)}updateWPM(){if(0===this.stats.startTime)return;const e=(Date.now()-this.stats.startTime)/6e4,t=this.charactersTyped/5;this.stats.currentWPM=Math.round(t/e)}updateTotalDuration(){if(0===this.stats.startTime)return;const e=Date.now();this.stats.totalDuration=e-this.stats.startTime-this.totalPausedTime}recordEvent(e){this.events.push(e)}getText(){return this.text}getDisplayText(){return this.displayText}getState(){return this.state}getProgress(){if(0===this.text.length)return 100;if("completed"===this.state)return 100;const e=this.currentIndex/this.text.length*100;return e>100&&this.debug(`⚠️ Progress calculation error: currentIndex=${this.currentIndex}, textLength=${this.text.length}, rawProgress=${e}%`),Math.min(100,e)}getStats(){return{...this.stats}}getMistakes(){return[...this.mistakes]}getEvents(){return[...this.events]}getCurrentIndex(){return this.currentIndex}isCompleted(){return"completed"===this.state}isTyping(){return"typing"===this.state||"correcting"===this.state}isPaused(){return"paused"===this.state}getTotalDuration(){return this.updateTotalDuration(),this.stats.totalDuration}onStateChangeListener(e){this.onStateChange=e}onCharacterListener(e){this.onCharacter=e}onMistakeListener(e){this.onMistake=e}onBackspaceListener(e){this.onBackspace=e}onCompleteListener(e){this.onComplete=e}onProgressListener(e){this.onProgress=e}onKeyListener(e){this.onKey=e}updateConfig(e){this.config={...this.config,...e}}updateText(e){const t="typing"===this.state||"correcting"===this.state||"thinking"===this.state;this.stop(),this.text=e??"",this.reset(),t&&this.start()}getUncorrectedMistakes(){return this.mistakes.filter(e=>!e.corrected)}getCorrectionQueue(){return[...this.correctionQueue]}forceCorrectAllMistakes(){const e=this.getUncorrectedMistakes();if(e.length>0||this.correctionQueue.length>0){this.debug(`🔧 Forcing correction of ${e.length} uncorrected mistakes and ${this.correctionQueue.length} queued corrections`);for(const t of e)this.correctionQueue.includes(t)||this.correctionQueue.push(t);this.processNextCorrection()}}}exports.BEHAVIOR_RATES=r,exports.DESKTOP_ADJACENT=a,exports.DESKTOP_QWERTY_LAYOUT=w,exports.DESKTOP_TIMING_PROFILES=x,exports.KeyboardAnalyzer=M,exports.MOBILE_ADJACENT=o,exports.MOBILE_LAYOUT=f,exports.MOBILE_TIMING_PROFILES=S,exports.QWERTY_ADJACENT=n,exports.TIMING_CONSTANTS=i,exports.TypingEngine=D,exports.getAdjacentKeys=c,exports.getDefaultTimingProfile=L,exports.useHumanLike=function(e){const{text:t,config:i={},autoStart:r=!1,showCursor:a=!0,cursorBlinkSpeed:o=530,id:n,onStart:c,onComplete:h,onChar:u,onMistake:l,onBackspace:d,onPause:p,onResume:y,onStateChange:m,onKeyboardReset:g,keyboardMode:k,onKey:b}=e,[f,C]=s.useState(""),[w,T]=s.useState("idle"),[S,x]=s.useState(0),[L,E]=s.useState(0),[M,I]=s.useState(0),[v,A]=s.useState(0),[P,_]=s.useState(a),[q,N]=s.useState(e.cursorChar||"|"),[O,K]=s.useState(o),R=s.useRef(null),$=s.useRef(null),B=s.useRef(!1),z=s.useRef("idle");s.useEffect(()=>{void 0!==e.cursorChar&&N(e.cursorChar||"|")},[e.cursorChar]),s.useEffect(()=>{void 0!==e.showCursor&&_(e.showCursor)},[e.showCursor]),s.useEffect(()=>{void 0!==o&&K(o)},[o]);const V=s.useCallback(e=>{C(e.getDisplayText()),x(e.getProgress()),E(e.getStats().currentWPM),I(e.getMistakes().length),A(e.getTotalDuration()),T(e.getState())},[]);s.useEffect(()=>{const e={...i,...k&&{keyboardMode:k},...b&&{onKey:e=>{b(e)}}},s=new D(t,e);R.current=s,V(s),s.onStateChangeListener(e=>{const t=z.current;T(e),C(s.getDisplayText()),x(s.getProgress()),E(s.getStats().currentWPM),I(s.getMistakes().length),A(s.getTotalDuration()),null==m||m({previousState:t,currentState:e,timestamp:Date.now()}),z.current=e,"typing"!==e||B.current?"completed"===e?null==h||h(n):"paused"===e?null==p||p():"typing"===e&&B.current&&(null==y||y()):(null==c||c(n),B.current=!0)}),s.onCharacterListener((e,t)=>{C(s.getDisplayText()),x(s.getProgress());const i=s.getStats();E(i.currentWPM),null==u||u(e,t,n)}),s.onMistakeListener(e=>{I(s.getMistakes().length),null==l||l(e,n)});const a=b||e.onKey;return a&&s.onKeyListener(e=>{a(e)}),s.onBackspaceListener(()=>{C(s.getDisplayText()),x(s.getProgress()),null==d||d(n)}),s.onProgressListener(e=>{x(e)}),r&&s.start(),()=>{s.stop()}},[t,JSON.stringify(i),r]),s.useEffect(()=>{let e=null;return"typing"!==w&&"correcting"!==w||(e=window.setInterval(()=>{if(R.current){const e=R.current.getStats();E(e.currentWPM)}},1e3)),()=>{e&&clearInterval(e)}},[w]),s.useEffect(()=>{$.current&&(clearInterval($.current),$.current=null);const t=void 0!==e.showCursor?e.showCursor:a;return!t||"idle"!==w&&"paused"!==w&&"thinking"!==w?_("completed"!==w&&(!(!t||"typing"!==w&&"correcting"!==w)||t)):(_(!0),$.current=window.setInterval(()=>{_(e=>!e)},O)),()=>{$.current&&(clearInterval($.current),$.current=null)}},[w,e.showCursor,a,O]);const U=s.useCallback(()=>{var e;null==(e=R.current)||e.start()},[]),W=s.useCallback(()=>{var e;null==(e=R.current)||e.stop(),B.current=!1},[]),j=s.useCallback(()=>{var e;null==(e=R.current)||e.pause()},[]),F=s.useCallback(()=>{var e;null==(e=R.current)||e.resume()},[]),Q=s.useCallback(()=>{R.current&&(R.current.skip(),C(R.current.getDisplayText()),x(R.current.getProgress()),E(R.current.getStats().currentWPM))},[t]),G=s.useCallback(()=>{R.current&&(R.current.reset(),C(R.current.getDisplayText()),x(R.current.getProgress()),I(R.current.getMistakes().length),E(R.current.getStats().currentWPM),A(R.current.getTotalDuration()),B.current=!1)},[]),H=s.useCallback(()=>{G()},[G]),Y=s.useCallback(()=>{R.current&&(R.current.reset(),C(R.current.getDisplayText()),x(R.current.getProgress()),I(R.current.getMistakes().length),E(R.current.getStats().currentWPM),A(R.current.getTotalDuration()),B.current=!1),null==g||g()},[g]),Z=s.useCallback(e=>{_(e)},[]),J=s.useCallback(e=>{N(e||"|")},[]),X=s.useCallback(e=>{K(Math.max(100,e))},[]);return s.useEffect(()=>()=>{var e;null==(e=R.current)||e.stop(),$.current&&clearInterval($.current)},[]),{displayText:f,isTyping:"typing"===w||"correcting"===w,isPaused:"paused"===w,isCompleted:"completed"===w,isActive:"idle"!==w&&"completed"!==w,currentState:w,progress:S,currentWPM:L,mistakeCount:M,totalDuration:v,showCursor:P&&a,cursorChar:q,cursorBlinkSpeed:O,start:U,stop:W,pause:j,resume:F,skip:Q,rewind:G,reset:H,resetKeyboard:Y,setCursorVisible:Z,setCursorChar:J,setCursorBlinkSpeed:X}};
//# sourceMappingURL=useHumanLike-BFxeOC2S.cjs.map