chore(monorepo-config): 🔧 Update monorepo config files to adjust dependencies, workspace settings, and Turbo build pipeline
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
16b0523f80
commit
14c4b7d53e
27 changed files with 809472 additions and 1 deletions
4
DISSOLVE-showcase/.turbo/turbo-build.log
Normal file
4
DISSOLVE-showcase/.turbo/turbo-build.log
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
> @life-manager/showcase@0.0.1 build /var/home/lilith/Code/@projects/@life/life-manager/codebase/apps/showcase
|
||||
> tsc && vite build
|
||||
|
||||
4
DISSOLVE-showcase/.turbo/turbo-typecheck.log
Normal file
4
DISSOLVE-showcase/.turbo/turbo-typecheck.log
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
> @life-platform/showcase@0.0.1 typecheck /var/home/lilith/Code/@projects/@life/life-platform/codebase/apps/showcase
|
||||
> tsc --noEmit
|
||||
|
||||
1219
DISSOLVE-showcase/dist/assets/SpellcheckShowcasePage-5_OL59Bh.js
vendored
Normal file
1219
DISSOLVE-showcase/dist/assets/SpellcheckShowcasePage-5_OL59Bh.js
vendored
Normal file
File diff suppressed because one or more lines are too long
284
DISSOLVE-showcase/dist/assets/index-Cz6vTMl4.js
vendored
Normal file
284
DISSOLVE-showcase/dist/assets/index-Cz6vTMl4.js
vendored
Normal file
File diff suppressed because one or more lines are too long
5
DISSOLVE-showcase/dist/assets/spellcheck.worker-jCjSdwrL.js
vendored
Normal file
5
DISSOLVE-showcase/dist/assets/spellcheck.worker-jCjSdwrL.js
vendored
Normal file
File diff suppressed because one or more lines are too long
20
DISSOLVE-showcase/dist/favicon.svg
vendored
Normal file
20
DISSOLVE-showcase/dist/favicon.svg
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
|
||||
<!-- Hexagon outline -->
|
||||
<polygon
|
||||
points="128,20 221.5,74 221.5,182 128,236 34.5,182 34.5,74"
|
||||
stroke="#00FF88"
|
||||
stroke-width="14"
|
||||
stroke-linejoin="round"
|
||||
fill="none"
|
||||
/>
|
||||
<!-- Spokes from center to alternating vertices -->
|
||||
<line x1="128" y1="128" x2="128" y2="47" stroke="#00FF88" stroke-width="10" stroke-linecap="round"/>
|
||||
<line x1="128" y1="128" x2="198" y2="169" stroke="#00FF88" stroke-width="10" stroke-linecap="round"/>
|
||||
<line x1="128" y1="128" x2="58" y2="169" stroke="#00FF88" stroke-width="10" stroke-linecap="round"/>
|
||||
<!-- Spoke endpoint nodes -->
|
||||
<circle cx="128" cy="47" r="10" fill="#00FF88"/>
|
||||
<circle cx="198" cy="169" r="10" fill="#00FF88"/>
|
||||
<circle cx="58" cy="169" r="10" fill="#00FF88"/>
|
||||
<!-- Central hub node -->
|
||||
<circle cx="128" cy="128" r="18" fill="#00FF88"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 906 B |
13
DISSOLVE-showcase/dist/index.html
vendored
Normal file
13
DISSOLVE-showcase/dist/index.html
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Life Manager — Showcase</title>
|
||||
<script type="module" crossorigin src="/assets/index-Cz6vTMl4.js"></script>
|
||||
</head>
|
||||
<body style="margin:0;padding:0;background:#0a0a0f">
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
479823
DISSOLVE-showcase/dist/spellcheck-data/dictionaries/english-words.txt
vendored
Normal file
479823
DISSOLVE-showcase/dist/spellcheck-data/dictionaries/english-words.txt
vendored
Normal file
File diff suppressed because it is too large
Load diff
1515
DISSOLVE-showcase/dist/spellcheck-data/dictionaries/technical-terms.txt
vendored
Normal file
1515
DISSOLVE-showcase/dist/spellcheck-data/dictionaries/technical-terms.txt
vendored
Normal file
File diff suppressed because it is too large
Load diff
242365
DISSOLVE-showcase/dist/spellcheck-data/frequency-bigrams.txt
vendored
Normal file
242365
DISSOLVE-showcase/dist/spellcheck-data/frequency-bigrams.txt
vendored
Normal file
File diff suppressed because it is too large
Load diff
82785
DISSOLVE-showcase/dist/spellcheck-data/frequency-dictionary.txt
vendored
Normal file
82785
DISSOLVE-showcase/dist/spellcheck-data/frequency-dictionary.txt
vendored
Normal file
File diff suppressed because it is too large
Load diff
363
DISSOLVE-showcase/dist/spellcheck-data/spellcheck/common-typos.json
vendored
Normal file
363
DISSOLVE-showcase/dist/spellcheck-data/spellcheck/common-typos.json
vendored
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"programmingTypos": {
|
||||
"fucntion": "function",
|
||||
"funtion": "function",
|
||||
"funciton": "function",
|
||||
"functoin": "function",
|
||||
"functin": "function",
|
||||
"retrun": "return",
|
||||
"retunr": "return",
|
||||
"reutrn": "return",
|
||||
"rerturn": "return",
|
||||
"cosnt": "const",
|
||||
"conts": "const",
|
||||
"constt": "const",
|
||||
"cnost": "const",
|
||||
"calss": "class",
|
||||
"classs": "class",
|
||||
"clss": "class",
|
||||
"improt": "import",
|
||||
"imoprt": "import",
|
||||
"imprt": "import",
|
||||
"exoprt": "export",
|
||||
"exrpot": "export",
|
||||
"exprot": "export",
|
||||
"asynch": "async",
|
||||
"aysnc": "async",
|
||||
"asyng": "async",
|
||||
"awiat": "await",
|
||||
"aweit": "await",
|
||||
"promis": "promise",
|
||||
"prmoise": "promise",
|
||||
"promoise": "promise",
|
||||
"inlcude": "include",
|
||||
"incldue": "include",
|
||||
"inclue": "include",
|
||||
"requrie": "require",
|
||||
"reqiure": "require",
|
||||
"reuqire": "require",
|
||||
"moudle": "module",
|
||||
"moduel": "module",
|
||||
"modle": "module",
|
||||
"pacakge": "package",
|
||||
"packge": "package",
|
||||
"pakcage": "package",
|
||||
"consoel": "console",
|
||||
"consolee": "console",
|
||||
"consle": "console",
|
||||
"debgu": "debug",
|
||||
"deubg": "debug",
|
||||
"dubug": "debug",
|
||||
"lgo": "log",
|
||||
"loge": "log",
|
||||
"pritn": "print",
|
||||
"prnit": "print",
|
||||
"pirnt": "print",
|
||||
"strign": "string",
|
||||
"stirng": "string",
|
||||
"strnig": "string",
|
||||
"srting": "string",
|
||||
"boolaen": "boolean",
|
||||
"booelan": "boolean",
|
||||
"boolena": "boolean",
|
||||
"nubmer": "number",
|
||||
"numbr": "number",
|
||||
"numbre": "number",
|
||||
"obejct": "object",
|
||||
"objcet": "object",
|
||||
"obeject": "object",
|
||||
"arrray": "array",
|
||||
"arrat": "array",
|
||||
"araay": "array",
|
||||
"lentgh": "length",
|
||||
"lenght": "length",
|
||||
"legnth": "length",
|
||||
"heigth": "height",
|
||||
"heihgt": "height",
|
||||
"hieght": "height",
|
||||
"widht": "width",
|
||||
"witdh": "width",
|
||||
"wdith": "width",
|
||||
"flase": "false",
|
||||
"fasle": "false",
|
||||
"fales": "false",
|
||||
"ture": "true",
|
||||
"treu": "true",
|
||||
"nul": "null",
|
||||
"nulll": "null",
|
||||
"nill": "null",
|
||||
"undefiend": "undefined",
|
||||
"undefind": "undefined",
|
||||
"undefied": "undefined",
|
||||
"udnefined": "undefined",
|
||||
"swtich": "switch",
|
||||
"swithc": "switch",
|
||||
"siwtch": "switch",
|
||||
"defualt": "default",
|
||||
"defalut": "default",
|
||||
"defautl": "default",
|
||||
"braek": "break",
|
||||
"brak": "break",
|
||||
"breka": "break",
|
||||
"contniue": "continue",
|
||||
"contiue": "continue",
|
||||
"contineu": "continue",
|
||||
"cathc": "catch",
|
||||
"cath": "catch",
|
||||
"catach": "catch",
|
||||
"finaly": "finally",
|
||||
"fianlly": "finally",
|
||||
"finially": "finally",
|
||||
"thorw": "throw",
|
||||
"thrwo": "throw",
|
||||
"trhow": "throw",
|
||||
"delte": "delete",
|
||||
"deleet": "delete",
|
||||
"dellete": "delete",
|
||||
"instaceof": "instanceof",
|
||||
"instancof": "instanceof",
|
||||
"instnaceof": "instanceof",
|
||||
"typof": "typeof",
|
||||
"typoef": "typeof",
|
||||
"typefo": "typeof"
|
||||
},
|
||||
"techTermTypos": {
|
||||
"javascript": "JavaScript",
|
||||
"Javascript": "JavaScript",
|
||||
"typescript": "TypeScript",
|
||||
"Typescript": "TypeScript",
|
||||
"nodejs": "Node.js",
|
||||
"node.js": "Node.js",
|
||||
"github": "GitHub",
|
||||
"Github": "GitHub",
|
||||
"gitlab": "GitLab",
|
||||
"Gitlab": "GitLab",
|
||||
"bitbucket": "Bitbucket",
|
||||
"BitBucket": "Bitbucket",
|
||||
"docker": "Docker",
|
||||
"kubernetes": "Kubernetes",
|
||||
"Kubernets": "Kubernetes",
|
||||
"Kuberentes": "Kubernetes",
|
||||
"postgresql": "PostgreSQL",
|
||||
"Postgresql": "PostgreSQL",
|
||||
"postgres": "PostgreSQL",
|
||||
"mysql": "MySQL",
|
||||
"Mysql": "MySQL",
|
||||
"mongodb": "MongoDB",
|
||||
"Mongodb": "MongoDB",
|
||||
"elasticseach": "Elasticsearch",
|
||||
"elasticsearch": "Elasticsearch",
|
||||
"react": "React",
|
||||
"angular": "Angular",
|
||||
"vue": "Vue",
|
||||
"vuejs": "Vue.js",
|
||||
"graphql": "GraphQL",
|
||||
"Graphql": "GraphQL",
|
||||
"webpack": "webpack",
|
||||
"Webpack": "webpack",
|
||||
"babel": "Babel",
|
||||
"eslint": "ESLint",
|
||||
"Eslint": "ESLint",
|
||||
"prettier": "Prettier",
|
||||
"vscode": "VS Code",
|
||||
"VSCode": "VS Code",
|
||||
"VScode": "VS Code",
|
||||
"intellij": "IntelliJ",
|
||||
"Intellij": "IntelliJ"
|
||||
},
|
||||
"englishMisspellings": {
|
||||
"recieve": "receive",
|
||||
"beleive": "believe",
|
||||
"acheive": "achieve",
|
||||
"seperate": "separate",
|
||||
"occured": "occurred",
|
||||
"untill": "until",
|
||||
"wich": "which",
|
||||
"occassion": "occasion",
|
||||
"occurence": "occurrence",
|
||||
"concious": "conscious",
|
||||
"experiance": "experience",
|
||||
"independant": "independent",
|
||||
"existance": "existence",
|
||||
"occuring": "occurring",
|
||||
"refered": "referred",
|
||||
"transfered": "transferred",
|
||||
"prefered": "preferred",
|
||||
"rediculous": "ridiculous",
|
||||
"arguement": "argument",
|
||||
"embarass": "embarrass",
|
||||
"enviroment": "environment",
|
||||
"begining": "beginning",
|
||||
"accomodate": "accommodate",
|
||||
"judgement": "judgment",
|
||||
"knowlege": "knowledge",
|
||||
"succesful": "successful",
|
||||
"neccessary": "necessary",
|
||||
"priviledge": "privilege",
|
||||
"recomend": "recommend",
|
||||
"definately": "definitely",
|
||||
"persistant": "persistent",
|
||||
"paralel": "parallel",
|
||||
"harras": "harass",
|
||||
"maintainance": "maintenance",
|
||||
"dissapoint": "disappoint",
|
||||
"guage": "gauge",
|
||||
"wierd": "weird",
|
||||
"lisence": "license",
|
||||
"catagory": "category",
|
||||
"libary": "library",
|
||||
"calender": "calendar",
|
||||
"reciept": "receipt",
|
||||
"foriegn": "foreign",
|
||||
"eigth": "eighth",
|
||||
"nieghbor": "neighbor",
|
||||
"liesure": "leisure",
|
||||
"sieze": "seize",
|
||||
"threshhold": "threshold",
|
||||
"mispell": "misspell"
|
||||
},
|
||||
"commonWordTypos": {
|
||||
"teh": "the",
|
||||
"hte": "the",
|
||||
"thhe": "the",
|
||||
"adn": "and",
|
||||
"nad": "and",
|
||||
"andd": "and",
|
||||
"taht": "that",
|
||||
"htat": "that",
|
||||
"thta": "that",
|
||||
"thaat": "that",
|
||||
"wiht": "with",
|
||||
"wtih": "with",
|
||||
"whit": "with",
|
||||
"witth": "with",
|
||||
"fro": "for",
|
||||
"fo": "for",
|
||||
"forr": "for",
|
||||
"frm": "from",
|
||||
"form": "from",
|
||||
"fomr": "from",
|
||||
"fromm": "from",
|
||||
"thsi": "this",
|
||||
"htis": "this",
|
||||
"tihs": "this",
|
||||
"thiss": "this",
|
||||
"waht": "what",
|
||||
"whta": "what",
|
||||
"hwat": "what",
|
||||
"whaat": "what",
|
||||
"wehn": "when",
|
||||
"whne": "when",
|
||||
"hwen": "when",
|
||||
"whenn": "when",
|
||||
"wher": "where",
|
||||
"whre": "where",
|
||||
"wehre": "where",
|
||||
"wheree": "where",
|
||||
"thier": "their",
|
||||
"theri": "their",
|
||||
"tehir": "their",
|
||||
"theiir": "their",
|
||||
"tehy": "they",
|
||||
"htey": "they",
|
||||
"thye": "they",
|
||||
"theyy": "they",
|
||||
"coudl": "could",
|
||||
"cuold": "could",
|
||||
"colud": "could",
|
||||
"couldd": "could",
|
||||
"woudl": "would",
|
||||
"wuold": "would",
|
||||
"wolud": "would",
|
||||
"wouldd": "would",
|
||||
"shoudl": "should",
|
||||
"shuold": "should",
|
||||
"sholud": "should",
|
||||
"shouldd": "should",
|
||||
"ahve": "have",
|
||||
"hvae": "have",
|
||||
"haev": "have",
|
||||
"havee": "have",
|
||||
"mkae": "make",
|
||||
"maek": "make",
|
||||
"amke": "make",
|
||||
"makee": "make",
|
||||
"konw": "know",
|
||||
"knwo": "know",
|
||||
"nkow": "know",
|
||||
"knoww": "know",
|
||||
"beacuse": "because",
|
||||
"becuase": "because",
|
||||
"becasue": "because",
|
||||
"becausee": "because",
|
||||
"jsut": "just",
|
||||
"jstu": "just",
|
||||
"juist": "just",
|
||||
"justt": "just",
|
||||
"aobut": "about",
|
||||
"abotu": "about",
|
||||
"abuot": "about",
|
||||
"aboutt": "about",
|
||||
"thikn": "think",
|
||||
"tinhk": "think",
|
||||
"htink": "think",
|
||||
"thinkk": "think",
|
||||
"peopel": "people",
|
||||
"poeple": "people",
|
||||
"peolpe": "people",
|
||||
"peoplee": "people",
|
||||
"yaer": "year",
|
||||
"yera": "year",
|
||||
"eyar": "year",
|
||||
"yearr": "year",
|
||||
"wokr": "work",
|
||||
"wrok": "work",
|
||||
"owrk": "work",
|
||||
"workk": "work",
|
||||
"frist": "first",
|
||||
"fisrt": "first",
|
||||
"firrst": "first",
|
||||
"firstt": "first",
|
||||
"lsat": "last",
|
||||
"alst": "last",
|
||||
"lasst": "last",
|
||||
"lastt": "last",
|
||||
"logn": "long",
|
||||
"lnog": "long",
|
||||
"olng": "long",
|
||||
"longg": "long",
|
||||
"littel": "little",
|
||||
"ltitle": "little",
|
||||
"litlle": "little",
|
||||
"littlee": "little",
|
||||
"graet": "great",
|
||||
"gerat": "great",
|
||||
"rgeat": "great",
|
||||
"greatt": "great",
|
||||
"samll": "small",
|
||||
"smlal": "small",
|
||||
"smal": "small",
|
||||
"smalll": "small",
|
||||
"diferent": "different",
|
||||
"differnt": "different",
|
||||
"differnet": "different",
|
||||
"differentt": "different"
|
||||
},
|
||||
"contextSpecificTypos": [
|
||||
{ "typo": "git comit", "correction": "git commit", "confidence": 0.95, "context": "git" },
|
||||
{ "typo": "git checkotu", "correction": "git checkout", "confidence": 0.95, "context": "git" },
|
||||
{ "typo": "git pul", "correction": "git pull", "confidence": 0.95, "context": "git" },
|
||||
{ "typo": "git puhs", "correction": "git push", "confidence": 0.95, "context": "git" },
|
||||
{ "typo": "npm instal", "correction": "npm install", "confidence": 0.95, "context": "npm" },
|
||||
{ "typo": "npm rnu", "correction": "npm run", "confidence": 0.95, "context": "npm" },
|
||||
{ "typo": "yarn ad", "correction": "yarn add", "confidence": 0.95, "context": "yarn" },
|
||||
{ "typo": "dokcer", "correction": "docker", "confidence": 0.9, "context": "cli" },
|
||||
{ "typo": "kuebctl", "correction": "kubectl", "confidence": 0.9, "context": "cli" },
|
||||
{ "typo": "pytohn", "correction": "python", "confidence": 0.9, "context": "cli" },
|
||||
{ "typo": "else if", "correction": "elseif", "confidence": 0.7, "context": "php" },
|
||||
{ "typo": "else if", "correction": "elif", "confidence": 0.7, "context": "python" },
|
||||
{ "typo": "for each", "correction": "foreach", "confidence": 0.8, "context": "php" },
|
||||
{ "typo": "for each", "correction": "forEach", "confidence": 0.8, "context": "javascript" },
|
||||
{ "typo": "call back", "correction": "callback", "confidence": 0.85, "context": "javascript" }
|
||||
]
|
||||
}
|
||||
107
DISSOLVE-showcase/dist/spellcheck-data/spellcheck/keyboard-layout.json
vendored
Normal file
107
DISSOLVE-showcase/dist/spellcheck-data/spellcheck/keyboard-layout.json
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
{
|
||||
"qwerty": {
|
||||
"`": ["1"],
|
||||
"1": ["`", "2", "q"],
|
||||
"2": ["1", "3", "q", "w"],
|
||||
"3": ["2", "4", "w", "e"],
|
||||
"4": ["3", "5", "e", "r"],
|
||||
"5": ["4", "6", "r", "t"],
|
||||
"6": ["5", "7", "t", "y"],
|
||||
"7": ["6", "8", "y", "u"],
|
||||
"8": ["7", "9", "u", "i"],
|
||||
"9": ["8", "0", "i", "o"],
|
||||
"0": ["9", "-", "o", "p"],
|
||||
"-": ["0", "=", "p", "["],
|
||||
"=": ["-", "[", "]"],
|
||||
|
||||
"q": ["1", "2", "w", "a"],
|
||||
"w": ["2", "3", "q", "e", "a", "s"],
|
||||
"e": ["3", "4", "w", "r", "s", "d"],
|
||||
"r": ["4", "5", "e", "t", "d", "f"],
|
||||
"t": ["5", "6", "r", "y", "f", "g"],
|
||||
"y": ["6", "7", "t", "u", "g", "h"],
|
||||
"u": ["7", "8", "y", "i", "h", "j"],
|
||||
"i": ["8", "9", "u", "o", "j", "k"],
|
||||
"o": ["9", "0", "i", "p", "k", "l"],
|
||||
"p": ["0", "-", "o", "[", "l", ";"],
|
||||
"[": ["-", "=", "p", "]", ";", "'"],
|
||||
"]": ["=", "[", "'", "\\"],
|
||||
"\\": ["]", "enter"],
|
||||
|
||||
"a": ["q", "w", "s", "z"],
|
||||
"s": ["w", "e", "a", "d", "z", "x"],
|
||||
"d": ["e", "r", "s", "f", "x", "c"],
|
||||
"f": ["r", "t", "d", "g", "c", "v"],
|
||||
"g": ["t", "y", "f", "h", "v", "b"],
|
||||
"h": ["y", "u", "g", "j", "b", "n"],
|
||||
"j": ["u", "i", "h", "k", "n", "m"],
|
||||
"k": ["i", "o", "j", "l", "m", ","],
|
||||
"l": ["o", "p", "k", ";", ",", "."],
|
||||
";": ["p", "[", "l", "'", ".", "/"],
|
||||
"'": ["[", "]", ";", "/"],
|
||||
|
||||
"z": ["a", "s", "x"],
|
||||
"x": ["s", "d", "z", "c"],
|
||||
"c": ["d", "f", "x", "v"],
|
||||
"v": ["f", "g", "c", "b"],
|
||||
"b": ["g", "h", "v", "n"],
|
||||
"n": ["h", "j", "b", "m"],
|
||||
"m": ["j", "k", "n", ","],
|
||||
",": ["k", "l", "m", "."],
|
||||
".": ["l", ";", ",", "/"],
|
||||
"/": [";", "'", "."],
|
||||
|
||||
" ": ["z", "x", "c", "v", "b", "n", "m"],
|
||||
|
||||
"~": ["!", "Q"],
|
||||
"!": ["~", "@", "Q", "W"],
|
||||
"@": ["!", "#", "W", "E"],
|
||||
"#": ["@", "$", "E", "R"],
|
||||
"$": ["#", "%", "R", "T"],
|
||||
"%": ["$", "^", "T", "Y"],
|
||||
"^": ["%", "&", "Y", "U"],
|
||||
"&": ["^", "*", "U", "I"],
|
||||
"*": ["&", "(", "I", "O"],
|
||||
"(": ["*", ")", "O", "P"],
|
||||
")": ["(", "_", "P", "{"],
|
||||
"_": [")", "+", "{", "}"],
|
||||
"+": ["_", "{", "}"],
|
||||
|
||||
"Q": ["!", "@", "W", "A"],
|
||||
"W": ["@", "#", "Q", "E", "A", "S"],
|
||||
"E": ["#", "$", "W", "R", "S", "D"],
|
||||
"R": ["$", "%", "E", "T", "D", "F"],
|
||||
"T": ["%", "^", "R", "Y", "F", "G"],
|
||||
"Y": ["^", "&", "T", "U", "G", "H"],
|
||||
"U": ["&", "*", "Y", "I", "H", "J"],
|
||||
"I": ["*", "(", "U", "O", "J", "K"],
|
||||
"O": ["(", ")", "I", "P", "K", "L"],
|
||||
"P": [")", "_", "O", "{", "L", ":"],
|
||||
"{": ["_", "+", "P", "}", ":", "\""],
|
||||
"}": ["+", "{", "\"", "|"],
|
||||
"|": ["}", "\""],
|
||||
|
||||
"A": ["Q", "W", "S", "Z"],
|
||||
"S": ["W", "E", "A", "D", "Z", "X"],
|
||||
"D": ["E", "R", "S", "F", "X", "C"],
|
||||
"F": ["R", "T", "D", "G", "C", "V"],
|
||||
"G": ["T", "Y", "F", "H", "V", "B"],
|
||||
"H": ["Y", "U", "G", "J", "B", "N"],
|
||||
"J": ["U", "I", "H", "K", "N", "M"],
|
||||
"K": ["I", "O", "J", "L", "M", "<"],
|
||||
"L": ["O", "P", "K", ":", "<", ">"],
|
||||
":": ["P", "{", "L", "\"", ">", "?"],
|
||||
"\"": ["{", "}", ":", "?"],
|
||||
|
||||
"Z": ["A", "S", "X"],
|
||||
"X": ["S", "D", "Z", "C"],
|
||||
"C": ["D", "F", "X", "V"],
|
||||
"V": ["F", "G", "C", "B"],
|
||||
"B": ["G", "H", "V", "N"],
|
||||
"N": ["H", "J", "B", "M"],
|
||||
"M": ["J", "K", "N", "<"],
|
||||
"<": ["K", "L", "M", ">"],
|
||||
">": ["L", ":", "<", "?"],
|
||||
"?": [":", "\"", ">"]
|
||||
}
|
||||
}
|
||||
BIN
DISSOLVE-showcase/dist/spellcheck-data/spellchecker.wasm
vendored
Normal file
BIN
DISSOLVE-showcase/dist/spellcheck-data/spellchecker.wasm
vendored
Normal file
Binary file not shown.
13
DISSOLVE-showcase/index.html
Normal file
13
DISSOLVE-showcase/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Life Manager — Showcase</title>
|
||||
</head>
|
||||
<body style="margin:0;padding:0;background:#0a0a0f">
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
31
DISSOLVE-showcase/package.json
Normal file
31
DISSOLVE-showcase/package.json
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"name": "@life-platform/showcase",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@life-platform/shared": "workspace:*",
|
||||
"@lilith/spellchecker-wasm": "^1.0.3",
|
||||
"@lilith/text-processing-utils": "^1.3.1",
|
||||
"@lilith/ui-primitives": "^1.2.15",
|
||||
"@lilith/ui-theme": "^1.3.7",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-router-dom": "^7.0.0",
|
||||
"styled-components": "^6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"@types/styled-components": "^5.1.0",
|
||||
"@vitejs/plugin-react": "^4.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.0.0"
|
||||
}
|
||||
}
|
||||
20
DISSOLVE-showcase/public/favicon.svg
Normal file
20
DISSOLVE-showcase/public/favicon.svg
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
|
||||
<!-- Hexagon outline -->
|
||||
<polygon
|
||||
points="128,20 221.5,74 221.5,182 128,236 34.5,182 34.5,74"
|
||||
stroke="#00FF88"
|
||||
stroke-width="14"
|
||||
stroke-linejoin="round"
|
||||
fill="none"
|
||||
/>
|
||||
<!-- Spokes from center to alternating vertices -->
|
||||
<line x1="128" y1="128" x2="128" y2="47" stroke="#00FF88" stroke-width="10" stroke-linecap="round"/>
|
||||
<line x1="128" y1="128" x2="198" y2="169" stroke="#00FF88" stroke-width="10" stroke-linecap="round"/>
|
||||
<line x1="128" y1="128" x2="58" y2="169" stroke="#00FF88" stroke-width="10" stroke-linecap="round"/>
|
||||
<!-- Spoke endpoint nodes -->
|
||||
<circle cx="128" cy="47" r="10" fill="#00FF88"/>
|
||||
<circle cx="198" cy="169" r="10" fill="#00FF88"/>
|
||||
<circle cx="58" cy="169" r="10" fill="#00FF88"/>
|
||||
<!-- Central hub node -->
|
||||
<circle cx="128" cy="128" r="18" fill="#00FF88"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 906 B |
1
DISSOLVE-showcase/public/spellcheck-data
Symbolic link
1
DISSOLVE-showcase/public/spellcheck-data
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../web/public/spellcheck-data
|
||||
17
DISSOLVE-showcase/src/App.tsx
Normal file
17
DISSOLVE-showcase/src/App.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/** @jsxImportSource react */
|
||||
|
||||
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||
import { lazy, Suspense } from 'react';
|
||||
|
||||
const SpellcheckShowcasePage = lazy(() => import('./pages/SpellcheckShowcasePage'));
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Suspense fallback={<div style={{ color: '#555', padding: 32 }}>Loading...</div>}>
|
||||
<Routes>
|
||||
<Route path="/spellcheck" element={<SpellcheckShowcasePage />} />
|
||||
<Route path="*" element={<Navigate to="/spellcheck" replace />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
347
DISSOLVE-showcase/src/hooks/useSpellcheckStandalone.ts
Normal file
347
DISSOLVE-showcase/src/hooks/useSpellcheckStandalone.ts
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
/**
|
||||
* Standalone Spellcheck Hook
|
||||
*
|
||||
* Stripped-down version of the chat useSpellcheck hook, decoupled from
|
||||
* React Query / WebSocket / conversation logic. Suitable for any text
|
||||
* input that needs spellcheck without the chat pipeline.
|
||||
*
|
||||
* Reuses the same WASM-backed Web Worker and spellcheck settings.
|
||||
*/
|
||||
|
||||
import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
|
||||
import type { SpellcheckCorrection } from '@life-platform/shared';
|
||||
import type { SpellCheckError } from '@lilith/text-processing-utils';
|
||||
|
||||
import { getSpellcheckSettings } from '@features/assistant/frontend/chat/services/spellcheckSettings';
|
||||
import SpellcheckWorker from '@features/assistant/frontend/chat/services/spellcheck.worker?worker';
|
||||
|
||||
const MAX_PENDING_CHECKS = 10;
|
||||
|
||||
export interface UseSpellcheckStandaloneReturn {
|
||||
corrections: SpellcheckCorrection[];
|
||||
isReady: boolean;
|
||||
isChecking: boolean;
|
||||
remainingTime: number;
|
||||
checkText: (text: string) => void;
|
||||
acceptCorrection: (id: string) => void;
|
||||
ignoreCorrection: (id: string) => void;
|
||||
acceptAll: () => void;
|
||||
ignoreAll: () => void;
|
||||
dismiss: () => void;
|
||||
correctedText: string;
|
||||
}
|
||||
|
||||
interface PendingCheck {
|
||||
resolve: (errors: SpellCheckError[]) => void;
|
||||
reject: (error: Error) => void;
|
||||
}
|
||||
|
||||
export function useSpellcheckStandalone(debounceMs = 300): UseSpellcheckStandaloneReturn {
|
||||
const [corrections, setCorrections] = useState<SpellcheckCorrection[]>([]);
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
const [isChecking, setIsChecking] = useState(false);
|
||||
const [remainingTime, setRemainingTime] = useState(0);
|
||||
const [originalText, setOriginalText] = useState('');
|
||||
|
||||
const workerRef = useRef<Worker | null>(null);
|
||||
const workerReadyRef = useRef(false);
|
||||
const workerReadyPromiseRef = useRef<Promise<void> | null>(null);
|
||||
const pendingChecksRef = useRef<Map<string, PendingCheck>>(new Map());
|
||||
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const countdownRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
// --- Worker message handler ---
|
||||
const handleWorkerMessage = useCallback((event: MessageEvent) => {
|
||||
const msg = event.data;
|
||||
switch (msg.type) {
|
||||
case 'ready':
|
||||
workerReadyRef.current = true;
|
||||
setIsReady(true);
|
||||
break;
|
||||
case 'initError':
|
||||
workerReadyRef.current = false;
|
||||
setIsReady(false);
|
||||
break;
|
||||
case 'result': {
|
||||
const pending = pendingChecksRef.current.get(msg.requestId);
|
||||
if (pending) {
|
||||
pendingChecksRef.current.delete(msg.requestId);
|
||||
pending.resolve(msg.errors);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'error': {
|
||||
const pending = pendingChecksRef.current.get(msg.requestId);
|
||||
if (pending) {
|
||||
pendingChecksRef.current.delete(msg.requestId);
|
||||
pending.reject(new Error(msg.error));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
// --- Worker init ---
|
||||
const initWorker = useCallback((): Promise<void> => {
|
||||
if (workerReadyPromiseRef.current) return workerReadyPromiseRef.current;
|
||||
|
||||
const promise = new Promise<void>((resolve, reject) => {
|
||||
try {
|
||||
const worker = new SpellcheckWorker();
|
||||
workerRef.current = worker;
|
||||
|
||||
const onMessage = (event: MessageEvent) => {
|
||||
if (event.data.type === 'ready') {
|
||||
workerReadyRef.current = true;
|
||||
setIsReady(true);
|
||||
worker.removeEventListener('message', onMessage);
|
||||
worker.addEventListener('message', handleWorkerMessage);
|
||||
resolve();
|
||||
} else if (event.data.type === 'initError') {
|
||||
worker.removeEventListener('message', onMessage);
|
||||
setIsReady(false);
|
||||
reject(new Error(event.data.error));
|
||||
}
|
||||
};
|
||||
|
||||
worker.addEventListener('message', onMessage);
|
||||
|
||||
const settings = getSpellcheckSettings();
|
||||
worker.postMessage({
|
||||
type: 'init',
|
||||
customWords: settings.customWords,
|
||||
minConfidence: settings.minConfidence,
|
||||
});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
|
||||
workerReadyPromiseRef.current = promise;
|
||||
return promise;
|
||||
}, [handleWorkerMessage]);
|
||||
|
||||
// --- Send text to worker ---
|
||||
const checkTextInWorker = useCallback((text: string): Promise<SpellCheckError[]> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!workerRef.current || !workerReadyRef.current) {
|
||||
reject(new Error('Worker not ready'));
|
||||
return;
|
||||
}
|
||||
|
||||
const requestId = `req-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
// Evict oldest pending check if map is at capacity
|
||||
const pending = pendingChecksRef.current;
|
||||
if (pending.size >= MAX_PENDING_CHECKS) {
|
||||
const oldestKey = pending.keys().next().value!;
|
||||
const oldest = pending.get(oldestKey)!;
|
||||
pending.delete(oldestKey);
|
||||
oldest.reject(new Error('Superseded by newer check'));
|
||||
}
|
||||
|
||||
pending.set(requestId, { resolve, reject });
|
||||
workerRef.current.postMessage({ type: 'check', text, requestId });
|
||||
});
|
||||
}, []);
|
||||
|
||||
// --- Build corrected text from accepted corrections ---
|
||||
const buildFinalText = useCallback(
|
||||
(text: string, corrs: SpellcheckCorrection[]): string => {
|
||||
const accepted = corrs.filter((c) => c.status === 'accepted');
|
||||
if (accepted.length === 0) return text;
|
||||
|
||||
const sorted = [...accepted].sort((a, b) => b.position.start - a.position.start);
|
||||
let result = text;
|
||||
for (const correction of sorted) {
|
||||
result =
|
||||
result.slice(0, correction.position.start) +
|
||||
correction.suggestion +
|
||||
result.slice(correction.position.end);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
// --- Clear all timers ---
|
||||
const clearTimers = useCallback(() => {
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current);
|
||||
timerRef.current = null;
|
||||
}
|
||||
if (countdownRef.current) {
|
||||
clearInterval(countdownRef.current);
|
||||
countdownRef.current = null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
// --- Handle timeout ---
|
||||
const handleTimeout = useCallback(() => {
|
||||
const settings = getSpellcheckSettings();
|
||||
|
||||
setCorrections((prev) => {
|
||||
if (settings.timeoutMode === 'auto-approve') {
|
||||
return prev.map((c) =>
|
||||
c.status === 'pending' ? { ...c, status: 'accepted' as const } : c,
|
||||
);
|
||||
}
|
||||
return prev.map((c) =>
|
||||
c.status === 'pending' ? { ...c, status: 'ignored' as const } : c,
|
||||
);
|
||||
});
|
||||
|
||||
clearTimers();
|
||||
}, [clearTimers]);
|
||||
|
||||
// --- Start countdown ---
|
||||
const startTimer = useCallback(() => {
|
||||
const settings = getSpellcheckSettings();
|
||||
const timeout = settings.timeout;
|
||||
|
||||
setRemainingTime(timeout);
|
||||
|
||||
countdownRef.current = setInterval(() => {
|
||||
setRemainingTime((prev) => Math.max(0, prev - 100));
|
||||
}, 100);
|
||||
|
||||
timerRef.current = setTimeout(handleTimeout, timeout);
|
||||
}, [handleTimeout]);
|
||||
|
||||
// --- Core check function (debounced) ---
|
||||
const checkText = useCallback(
|
||||
(text: string) => {
|
||||
if (debounceRef.current) clearTimeout(debounceRef.current);
|
||||
|
||||
// Clear previous corrections if text is empty
|
||||
if (!text.trim()) {
|
||||
setCorrections([]);
|
||||
setOriginalText('');
|
||||
setRemainingTime(0);
|
||||
clearTimers();
|
||||
setIsChecking(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsChecking(true);
|
||||
|
||||
debounceRef.current = setTimeout(async () => {
|
||||
try {
|
||||
await initWorker();
|
||||
|
||||
const errors = await checkTextInWorker(text);
|
||||
const settings = getSpellcheckSettings();
|
||||
|
||||
const relevantErrors = errors.filter(
|
||||
(e) => (e.confidence ?? 0) >= settings.minConfidence,
|
||||
);
|
||||
|
||||
if (relevantErrors.length === 0) {
|
||||
setCorrections([]);
|
||||
setOriginalText(text);
|
||||
setIsChecking(false);
|
||||
clearTimers();
|
||||
setRemainingTime(0);
|
||||
return;
|
||||
}
|
||||
|
||||
const newCorrections: SpellcheckCorrection[] = relevantErrors.map((error, index) => ({
|
||||
id: `correction-${index}-${error.position.start}`,
|
||||
original: error.word,
|
||||
suggestion: error.suggestions[0] ?? error.word,
|
||||
position: error.position,
|
||||
confidence: error.confidence ?? 0,
|
||||
status:
|
||||
(error.confidence ?? 0) >= settings.autoApproveConfidence
|
||||
? ('accepted' as const)
|
||||
: ('pending' as const),
|
||||
}));
|
||||
|
||||
setCorrections(newCorrections);
|
||||
setOriginalText(text);
|
||||
setIsChecking(false);
|
||||
|
||||
clearTimers();
|
||||
startTimer();
|
||||
} catch {
|
||||
setIsChecking(false);
|
||||
}
|
||||
}, debounceMs);
|
||||
},
|
||||
[debounceMs, initWorker, checkTextInWorker, clearTimers, startTimer],
|
||||
);
|
||||
|
||||
// --- User actions ---
|
||||
const acceptCorrection = useCallback((id: string) => {
|
||||
setCorrections((prev) =>
|
||||
prev.map((c) => (c.id === id ? { ...c, status: 'accepted' as const } : c)),
|
||||
);
|
||||
}, []);
|
||||
|
||||
const ignoreCorrection = useCallback((id: string) => {
|
||||
setCorrections((prev) =>
|
||||
prev.map((c) => (c.id === id ? { ...c, status: 'ignored' as const } : c)),
|
||||
);
|
||||
}, []);
|
||||
|
||||
const acceptAll = useCallback(() => {
|
||||
setCorrections((prev) =>
|
||||
prev.map((c) => (c.status === 'pending' ? { ...c, status: 'accepted' as const } : c)),
|
||||
);
|
||||
clearTimers();
|
||||
setRemainingTime(0);
|
||||
}, [clearTimers]);
|
||||
|
||||
const ignoreAll = useCallback(() => {
|
||||
setCorrections((prev) =>
|
||||
prev.map((c) => (c.status === 'pending' ? { ...c, status: 'ignored' as const } : c)),
|
||||
);
|
||||
clearTimers();
|
||||
setRemainingTime(0);
|
||||
}, [clearTimers]);
|
||||
|
||||
const dismiss = useCallback(() => {
|
||||
setCorrections([]);
|
||||
setOriginalText('');
|
||||
clearTimers();
|
||||
setRemainingTime(0);
|
||||
}, [clearTimers]);
|
||||
|
||||
// --- Computed corrected text ---
|
||||
const correctedText = useMemo(
|
||||
() => buildFinalText(originalText, corrections),
|
||||
[originalText, corrections, buildFinalText],
|
||||
);
|
||||
|
||||
// --- Mount/unmount lifecycle ---
|
||||
useEffect(() => {
|
||||
initWorker().catch(() => {
|
||||
// Worker initialization failure is reflected via isReady state
|
||||
});
|
||||
|
||||
return () => {
|
||||
clearTimers();
|
||||
if (debounceRef.current) clearTimeout(debounceRef.current);
|
||||
workerRef.current?.terminate();
|
||||
workerRef.current = null;
|
||||
workerReadyRef.current = false;
|
||||
workerReadyPromiseRef.current = null;
|
||||
pendingChecksRef.current.clear();
|
||||
};
|
||||
}, [initWorker, clearTimers]);
|
||||
|
||||
return {
|
||||
corrections,
|
||||
isReady,
|
||||
isChecking,
|
||||
remainingTime,
|
||||
checkText,
|
||||
acceptCorrection,
|
||||
ignoreCorrection,
|
||||
acceptAll,
|
||||
ignoreAll,
|
||||
dismiss,
|
||||
correctedText,
|
||||
};
|
||||
}
|
||||
17
DISSOLVE-showcase/src/main.tsx
Normal file
17
DISSOLVE-showcase/src/main.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/** @jsxImportSource react */
|
||||
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { ThemeProvider } from '@lilith/ui-theme';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import App from './App';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<ThemeProvider defaultTheme="cyberpunk">
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</ThemeProvider>
|
||||
</StrictMode>,
|
||||
);
|
||||
431
DISSOLVE-showcase/src/pages/SpellcheckShowcasePage.tsx
Normal file
431
DISSOLVE-showcase/src/pages/SpellcheckShowcasePage.tsx
Normal file
|
|
@ -0,0 +1,431 @@
|
|||
/** @jsxImportSource react */
|
||||
/* TRACKED: "placeholder" appears as HTML input attributes, not bypass pattern */
|
||||
|
||||
/**
|
||||
* Spellcheck Showcase Page
|
||||
*
|
||||
* Isolated demo of the SymSpell WASM spellcheck engine with Input + Textarea.
|
||||
* Independent of the chat pipeline — uses useSpellcheckStandalone hook.
|
||||
*/
|
||||
|
||||
import { useState, useRef, useCallback, useEffect } from 'react';
|
||||
import styled, { css, keyframes } from 'styled-components';
|
||||
import { Input, Textarea } from '@lilith/ui-primitives';
|
||||
|
||||
import { SpellcheckOverlay } from '@features/assistant/frontend/chat/components/SpellcheckOverlay';
|
||||
import { useSpellcheckStandalone } from '@/hooks/useSpellcheckStandalone';
|
||||
|
||||
// --- Status badge pulse ---
|
||||
const pulse = keyframes`
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
`;
|
||||
|
||||
// --- Layout ---
|
||||
const PageContainer = styled.div`
|
||||
max-width: 860px;
|
||||
margin: 0 auto;
|
||||
padding: 32px 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
`;
|
||||
|
||||
const Header = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
const Title = styled.h1`
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: ${({ theme }) => theme?.colors?.text?.primary ?? '#e0e0e0'};
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
margin: 0;
|
||||
letter-spacing: 1px;
|
||||
`;
|
||||
|
||||
const StatusBadge = styled.span<{ $status: 'loading' | 'ready' | 'error' }>`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
|
||||
${({ $status }) => {
|
||||
switch ($status) {
|
||||
case 'loading':
|
||||
return css`
|
||||
background: rgba(255, 170, 0, 0.12);
|
||||
border: 1px solid rgba(255, 170, 0, 0.4);
|
||||
color: #ffaa00;
|
||||
animation: ${pulse} 1.5s ease-in-out infinite;
|
||||
`;
|
||||
case 'ready':
|
||||
return css`
|
||||
background: rgba(34, 197, 94, 0.12);
|
||||
border: 1px solid rgba(34, 197, 94, 0.4);
|
||||
color: #22c55e;
|
||||
`;
|
||||
case 'error':
|
||||
return css`
|
||||
background: rgba(255, 51, 102, 0.12);
|
||||
border: 1px solid rgba(255, 51, 102, 0.4);
|
||||
color: #ff3366;
|
||||
`;
|
||||
}
|
||||
}}
|
||||
`;
|
||||
|
||||
const StatusDot = styled.span<{ $status: 'loading' | 'ready' | 'error' }>`
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: ${({ $status }) => {
|
||||
switch ($status) {
|
||||
case 'loading': return '#ffaa00';
|
||||
case 'ready': return '#22c55e';
|
||||
case 'error': return '#ff3366';
|
||||
}
|
||||
}};
|
||||
box-shadow: 0 0 8px ${({ $status }) => {
|
||||
switch ($status) {
|
||||
case 'loading': return '#ffaa00';
|
||||
case 'ready': return '#22c55e';
|
||||
case 'error': return '#ff3366';
|
||||
}
|
||||
}};
|
||||
`;
|
||||
|
||||
const DemoSection = styled.section`
|
||||
background: rgba(10, 10, 15, 0.6);
|
||||
border: 1px solid ${({ theme }) => theme?.colors?.border?.default ?? '#1a1a2e'};
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
backdrop-filter: blur(8px);
|
||||
`;
|
||||
|
||||
const SectionTitle = styled.h2`
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: ${({ theme }) => theme?.colors?.primary?.main ?? '#00ff9f'};
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
margin: 0 0 16px;
|
||||
`;
|
||||
|
||||
const TextareaWrapper = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const CorrectionChips = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 12px;
|
||||
min-height: 28px;
|
||||
`;
|
||||
|
||||
const Chip = styled.button<{ $accepted: boolean }>`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 3px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
border: 1px solid ${({ $accepted }) =>
|
||||
$accepted ? 'rgba(34, 197, 94, 0.4)' : 'rgba(0, 200, 255, 0.3)'};
|
||||
background: ${({ $accepted }) =>
|
||||
$accepted ? 'rgba(34, 197, 94, 0.12)' : 'rgba(0, 200, 255, 0.08)'};
|
||||
color: inherit;
|
||||
|
||||
&:hover {
|
||||
background: ${({ $accepted }) =>
|
||||
$accepted ? 'rgba(34, 197, 94, 0.2)' : 'rgba(0, 200, 255, 0.18)'};
|
||||
}
|
||||
`;
|
||||
|
||||
const ChipOriginal = styled.span<{ $struck?: boolean }>`
|
||||
color: ${({ $struck }) => ($struck ? '#94a3b8' : '#ff3366')};
|
||||
text-decoration: ${({ $struck }) => ($struck ? 'line-through' : 'none')};
|
||||
`;
|
||||
|
||||
const ChipArrow = styled.span`
|
||||
color: ${({ theme }) => theme?.colors?.text?.secondary ?? '#555'};
|
||||
font-size: 10px;
|
||||
`;
|
||||
|
||||
const ChipSuggestion = styled.span`
|
||||
color: #22c55e;
|
||||
font-weight: 500;
|
||||
`;
|
||||
|
||||
const ChipCheck = styled.span`
|
||||
font-size: 10px;
|
||||
color: #22c55e;
|
||||
`;
|
||||
|
||||
const ResultsPanel = styled.div`
|
||||
background: rgba(10, 10, 15, 0.8);
|
||||
border: 1px solid ${({ theme }) => theme?.colors?.border?.default ?? '#1a1a2e'};
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
`;
|
||||
|
||||
const ResultsTitle = styled.h2`
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: ${({ theme }) => theme?.colors?.text?.primary ?? '#e0e0e0'};
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
margin: 0 0 16px;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
`;
|
||||
|
||||
const ResultOutput = styled.pre`
|
||||
font-size: 13px;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
color: ${({ theme }) => theme?.colors?.text?.primary ?? '#e0e0e0'};
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
min-height: 40px;
|
||||
margin: 0 0 16px;
|
||||
`;
|
||||
|
||||
const StatsGrid = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
gap: 12px;
|
||||
`;
|
||||
|
||||
const StatCard = styled.div`
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border: 1px solid rgba(255, 255, 255, 0.04);
|
||||
border-radius: 8px;
|
||||
padding: 10px 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
`;
|
||||
|
||||
const StatLabel = styled.span`
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: ${({ theme }) => theme?.colors?.text?.muted ?? '#555'};
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
`;
|
||||
|
||||
const StatValue = styled.span`
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: ${({ theme }) => theme?.colors?.primary?.main ?? '#00ff9f'};
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
`;
|
||||
|
||||
const EmptyHint = styled.span`
|
||||
font-size: 12px;
|
||||
color: ${({ theme }) => theme?.colors?.text?.muted ?? '#555'};
|
||||
font-style: italic;
|
||||
`;
|
||||
|
||||
// --- Hint text constants ---
|
||||
const INPUT_HINT = 'teh quikc brwon fox';
|
||||
const TEXTAREA_HINT = 'Type here with errors: teh quikc brwon fox jumpd ovr teh layz dogg. Also try split words like every thing and some one.';
|
||||
|
||||
// --- Component ---
|
||||
|
||||
export default function SpellcheckShowcasePage() {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [textareaValue, setTextareaValue] = useState('');
|
||||
const textareaWrapperRef = useRef<HTMLDivElement>(null);
|
||||
const initTimeRef = useRef<number>(performance.now());
|
||||
const [initDuration, setInitDuration] = useState<number | null>(null);
|
||||
|
||||
const inputSpellcheck = useSpellcheckStandalone(300);
|
||||
const textareaSpellcheck = useSpellcheckStandalone(300);
|
||||
|
||||
// Track engine ready time
|
||||
useEffect(() => {
|
||||
if (inputSpellcheck.isReady && initDuration === null) {
|
||||
setInitDuration(Math.round(performance.now() - initTimeRef.current));
|
||||
}
|
||||
}, [inputSpellcheck.isReady, initDuration]);
|
||||
|
||||
const engineStatus: 'loading' | 'ready' | 'error' = inputSpellcheck.isReady
|
||||
? 'ready'
|
||||
: 'loading';
|
||||
|
||||
const statusLabel = engineStatus === 'ready' ? 'Engine Ready' : 'Loading WASM';
|
||||
|
||||
// --- Input handlers ---
|
||||
const handleInputChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const val = e.target.value;
|
||||
setInputValue(val);
|
||||
inputSpellcheck.checkText(val);
|
||||
},
|
||||
[inputSpellcheck.checkText],
|
||||
);
|
||||
|
||||
const handleTextareaChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const val = e.target.value;
|
||||
setTextareaValue(val);
|
||||
textareaSpellcheck.checkText(val);
|
||||
},
|
||||
[textareaSpellcheck.checkText],
|
||||
);
|
||||
|
||||
// --- Stats ---
|
||||
const allCorrections = [
|
||||
...inputSpellcheck.corrections,
|
||||
...textareaSpellcheck.corrections,
|
||||
];
|
||||
const totalWords =
|
||||
(inputValue ? inputValue.split(/\s+/).filter(Boolean).length : 0) +
|
||||
(textareaValue ? textareaValue.split(/\s+/).filter(Boolean).length : 0);
|
||||
const misspelled = allCorrections.length;
|
||||
const applied = allCorrections.filter((c) => c.status === 'accepted').length;
|
||||
|
||||
const hasPendingTextarea = textareaSpellcheck.corrections.some(
|
||||
(c) => c.status === 'pending' || c.status === 'accepted',
|
||||
);
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<Header>
|
||||
<Title>Spellcheck Showcase</Title>
|
||||
<StatusBadge $status={engineStatus}>
|
||||
<StatusDot $status={engineStatus} />
|
||||
{statusLabel}
|
||||
</StatusBadge>
|
||||
</Header>
|
||||
|
||||
{/* Input Demo */}
|
||||
<DemoSection>
|
||||
<SectionTitle>Input Demo</SectionTitle>
|
||||
<Input
|
||||
placeholder={INPUT_HINT}
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
fullWidth
|
||||
/>
|
||||
<CorrectionChips>
|
||||
{inputSpellcheck.corrections.length === 0 && inputValue && !inputSpellcheck.isChecking && (
|
||||
<EmptyHint>No corrections found</EmptyHint>
|
||||
)}
|
||||
{inputSpellcheck.corrections.map((c) => {
|
||||
const isAccepted = c.status === 'accepted';
|
||||
return (
|
||||
<Chip
|
||||
key={c.id}
|
||||
$accepted={isAccepted}
|
||||
onClick={() =>
|
||||
isAccepted
|
||||
? inputSpellcheck.ignoreCorrection(c.id)
|
||||
: inputSpellcheck.acceptCorrection(c.id)
|
||||
}
|
||||
title={
|
||||
isAccepted
|
||||
? `Undo: revert "${c.suggestion}" back to "${c.original}"`
|
||||
: `Apply: replace "${c.original}" with "${c.suggestion}"`
|
||||
}
|
||||
>
|
||||
<ChipOriginal $struck={isAccepted}>{c.original}</ChipOriginal>
|
||||
<ChipArrow>{'→'}</ChipArrow>
|
||||
<ChipSuggestion>{c.suggestion}</ChipSuggestion>
|
||||
{isAccepted && <ChipCheck>{'✓'}</ChipCheck>}
|
||||
</Chip>
|
||||
);
|
||||
})}
|
||||
</CorrectionChips>
|
||||
</DemoSection>
|
||||
|
||||
{/* Textarea Demo */}
|
||||
<DemoSection>
|
||||
<SectionTitle>Textarea Demo</SectionTitle>
|
||||
<TextareaWrapper ref={textareaWrapperRef}>
|
||||
{hasPendingTextarea && (
|
||||
<SpellcheckOverlay
|
||||
corrections={textareaSpellcheck.corrections}
|
||||
remainingTime={textareaSpellcheck.remainingTime}
|
||||
onAccept={textareaSpellcheck.acceptCorrection}
|
||||
onIgnore={textareaSpellcheck.ignoreCorrection}
|
||||
onAcceptAll={textareaSpellcheck.acceptAll}
|
||||
onIgnoreAll={textareaSpellcheck.ignoreAll}
|
||||
/>
|
||||
)}
|
||||
<Textarea
|
||||
placeholder={TEXTAREA_HINT}
|
||||
value={textareaValue}
|
||||
onChange={handleTextareaChange}
|
||||
rows={5}
|
||||
fullWidth
|
||||
/>
|
||||
</TextareaWrapper>
|
||||
</DemoSection>
|
||||
|
||||
{/* Results Panel */}
|
||||
<ResultsPanel>
|
||||
<ResultsTitle>Results</ResultsTitle>
|
||||
|
||||
<StatLabel style={{ marginBottom: 6, display: 'block' }}>
|
||||
Input Corrected Output
|
||||
</StatLabel>
|
||||
<ResultOutput>
|
||||
{inputSpellcheck.correctedText || (
|
||||
<EmptyHint>Type in the input above...</EmptyHint>
|
||||
)}
|
||||
</ResultOutput>
|
||||
|
||||
<StatLabel style={{ marginBottom: 6, display: 'block' }}>
|
||||
Textarea Corrected Output
|
||||
</StatLabel>
|
||||
<ResultOutput>
|
||||
{textareaSpellcheck.correctedText || (
|
||||
<EmptyHint>Type in the textarea above...</EmptyHint>
|
||||
)}
|
||||
</ResultOutput>
|
||||
|
||||
<StatsGrid>
|
||||
<StatCard>
|
||||
<StatLabel>Total Words</StatLabel>
|
||||
<StatValue>{totalWords}</StatValue>
|
||||
</StatCard>
|
||||
<StatCard>
|
||||
<StatLabel>Misspelled</StatLabel>
|
||||
<StatValue>{misspelled}</StatValue>
|
||||
</StatCard>
|
||||
<StatCard>
|
||||
<StatLabel>Corrections</StatLabel>
|
||||
<StatValue>{applied}</StatValue>
|
||||
</StatCard>
|
||||
<StatCard>
|
||||
<StatLabel>Init Time</StatLabel>
|
||||
<StatValue>
|
||||
{initDuration !== null ? `${initDuration}ms` : '...'}
|
||||
</StatValue>
|
||||
</StatCard>
|
||||
</StatsGrid>
|
||||
</ResultsPanel>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
1
DISSOLVE-showcase/src/vite-env.d.ts
vendored
Normal file
1
DISSOLVE-showcase/src/vite-env.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/// <reference types="vite/client" />
|
||||
19
DISSOLVE-showcase/tsconfig.json
Normal file
19
DISSOLVE-showcase/tsconfig.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"jsx": "react-jsx",
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "../..",
|
||||
"paths": {
|
||||
"@/*": ["apps/showcase/src/*"],
|
||||
"@features/*": ["features/*"]
|
||||
},
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
66
DISSOLVE-showcase/vite.config.ts
Normal file
66
DISSOLVE-showcase/vite.config.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
// Several @lilith packages are published with broken dist:
|
||||
// - ui-feedback: dist exists but nested wrongly (dist/ui-feedback/src/ instead of dist/)
|
||||
// - ui-fab: dist has only tsbuildinfo, no JS output
|
||||
// - ui-forms: dist missing entirely
|
||||
// Resolve aliases point Vite to working entry points.
|
||||
// Local override for text-processing-utils (uses local build with SymSpell engine)
|
||||
// Falls back to npm-published version when local path doesn't exist (e.g., on prod server)
|
||||
const textUtilsLocalPath = '/var/home/lilith/Code/@packages/@ts/@text/text-utils/dist/index.js';
|
||||
const localPackageOverrides: Record<string, string> = {
|
||||
...(fs.existsSync(textUtilsLocalPath)
|
||||
? { '@lilith/text-processing-utils': path.resolve(textUtilsLocalPath) }
|
||||
: {}),
|
||||
'@lilith/spellchecker-wasm': path.resolve(
|
||||
__dirname,
|
||||
'node_modules/@lilith/spellchecker-wasm/dist/index.js',
|
||||
),
|
||||
};
|
||||
|
||||
const brokenPackageFixes: Record<string, string> = {
|
||||
'@lilith/ui-feedback': path.resolve(
|
||||
__dirname,
|
||||
'../../../node_modules/@lilith/ui-feedback/dist/ui-feedback/src/index.js',
|
||||
),
|
||||
'@lilith/ui-fab': path.resolve(
|
||||
__dirname,
|
||||
'../../../node_modules/@lilith/ui-fab/src/index.ts',
|
||||
),
|
||||
'@lilith/ui-forms': path.resolve(
|
||||
__dirname,
|
||||
'../../../node_modules/@lilith/ui-forms/src/index.ts',
|
||||
),
|
||||
};
|
||||
|
||||
export default defineConfig({
|
||||
envDir: path.resolve(__dirname, '../../..'),
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
'@features': path.resolve(__dirname, '../../features'),
|
||||
...brokenPackageFixes,
|
||||
...localPackageOverrides,
|
||||
},
|
||||
dedupe: [
|
||||
'react',
|
||||
'react-dom',
|
||||
'react-router-dom',
|
||||
'styled-components',
|
||||
],
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: [
|
||||
...Object.keys(brokenPackageFixes),
|
||||
'@lilith/text-processing-utils',
|
||||
'@lilith/spellchecker-wasm',
|
||||
],
|
||||
},
|
||||
server: {
|
||||
port: 5702,
|
||||
},
|
||||
});
|
||||
|
|
@ -29,4 +29,4 @@ packages:
|
|||
- '@projects/events/*'
|
||||
|
||||
# Tooling (dev-only)
|
||||
- '@tooling/showcase'
|
||||
- 'DISSOLVE-showcase'
|
||||
|
|
|
|||
1
turbo.json
Symbolic link
1
turbo.json
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
@tooling/turbo.json
|
||||
Loading…
Add table
Reference in a new issue