mirror of
				https://github.com/Blah-IM/Weblah.git
				synced 2025-11-04 03:41:37 +00:00 
			
		
		
		
	feat: basic rich text input & format
This commit is contained in:
		
							parent
							
								
									e386fe2583
								
							
						
					
					
						commit
						ca380e9ce6
					
				
					 8 changed files with 219 additions and 24 deletions
				
			
		
							
								
								
									
										88
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										88
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -9,7 +9,9 @@
 | 
			
		|||
			"version": "0.0.1",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"svelte-boring-avatars": "^1.2.6",
 | 
			
		||||
				"tailwind-merge": "^2.5.2"
 | 
			
		||||
				"tailwind-merge": "^2.5.2",
 | 
			
		||||
				"typewriter-editor": "^0.12.6",
 | 
			
		||||
				"zod": "^3.23.8"
 | 
			
		||||
			},
 | 
			
		||||
			"devDependencies": {
 | 
			
		||||
				"@sveltejs/adapter-auto": "^3.0.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +53,6 @@
 | 
			
		|||
			"version": "2.3.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
 | 
			
		||||
			"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "Apache-2.0",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@jridgewell/gen-mapping": "^0.3.5",
 | 
			
		||||
| 
						 | 
				
			
			@ -642,7 +643,6 @@
 | 
			
		|||
			"version": "0.3.5",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
 | 
			
		||||
			"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@jridgewell/set-array": "^1.2.1",
 | 
			
		||||
| 
						 | 
				
			
			@ -657,7 +657,6 @@
 | 
			
		|||
			"version": "3.1.2",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
 | 
			
		||||
			"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"engines": {
 | 
			
		||||
				"node": ">=6.0.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -667,7 +666,6 @@
 | 
			
		|||
			"version": "1.2.1",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
 | 
			
		||||
			"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"engines": {
 | 
			
		||||
				"node": ">=6.0.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -677,14 +675,12 @@
 | 
			
		|||
			"version": "1.5.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
 | 
			
		||||
			"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/@jridgewell/trace-mapping": {
 | 
			
		||||
			"version": "0.3.25",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
 | 
			
		||||
			"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@jridgewell/resolve-uri": "^3.1.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -747,6 +743,16 @@
 | 
			
		|||
			"dev": true,
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/@popperjs/core": {
 | 
			
		||||
			"version": "2.11.8",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
 | 
			
		||||
			"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"funding": {
 | 
			
		||||
				"type": "opencollective",
 | 
			
		||||
				"url": "https://opencollective.com/popperjs"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/@rollup/rollup-android-arm-eabi": {
 | 
			
		||||
			"version": "4.21.1",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.1.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -1096,7 +1102,6 @@
 | 
			
		|||
			"version": "1.0.5",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
 | 
			
		||||
			"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/@types/json-schema": {
 | 
			
		||||
| 
						 | 
				
			
			@ -1342,6 +1347,24 @@
 | 
			
		|||
				"url": "https://opencollective.com/eslint"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/@typewriter/delta": {
 | 
			
		||||
			"version": "1.2.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@typewriter/delta/-/delta-1.2.0.tgz",
 | 
			
		||||
			"integrity": "sha512-AGFvfeXkef7qUBuBbbAw7werb/BlHuhIY8xmDdm+WECoqX8YxhWtcikaI42UhBlwbpCI2R0jEkaJml5Ngb4uzw==",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"fast-diff": "1.3.0"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/@typewriter/document": {
 | 
			
		||||
			"version": "0.9.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@typewriter/document/-/document-0.9.0.tgz",
 | 
			
		||||
			"integrity": "sha512-PAoqXCHmNtx3qM1vxiRFGJC2tWiQdbXHD19xf48jCbvwZIn5Lkc2FS9GibscPhjSqqK3GYpo1aCkWbUTbaDblg==",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@typewriter/delta": "^1.2.0"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/@vitest/expect": {
 | 
			
		||||
			"version": "2.0.5",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -1433,7 +1456,6 @@
 | 
			
		|||
			"version": "8.12.1",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
 | 
			
		||||
			"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"bin": {
 | 
			
		||||
				"acorn": "bin/acorn"
 | 
			
		||||
| 
						 | 
				
			
			@ -1534,7 +1556,6 @@
 | 
			
		|||
			"version": "5.3.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
 | 
			
		||||
			"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "Apache-2.0",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"dequal": "^2.0.3"
 | 
			
		||||
| 
						 | 
				
			
			@ -1592,7 +1613,6 @@
 | 
			
		|||
			"version": "4.1.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
 | 
			
		||||
			"integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "Apache-2.0",
 | 
			
		||||
			"engines": {
 | 
			
		||||
				"node": ">= 0.4"
 | 
			
		||||
| 
						 | 
				
			
			@ -1822,7 +1842,6 @@
 | 
			
		|||
			"version": "1.0.4",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
 | 
			
		||||
			"integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@jridgewell/sourcemap-codec": "^1.4.15",
 | 
			
		||||
| 
						 | 
				
			
			@ -1898,7 +1917,6 @@
 | 
			
		|||
			"version": "2.3.1",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
 | 
			
		||||
			"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"mdn-data": "2.0.30",
 | 
			
		||||
| 
						 | 
				
			
			@ -1970,7 +1988,6 @@
 | 
			
		|||
			"version": "2.0.3",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
 | 
			
		||||
			"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"engines": {
 | 
			
		||||
				"node": ">=6"
 | 
			
		||||
| 
						 | 
				
			
			@ -2014,6 +2031,12 @@
 | 
			
		|||
			"dev": true,
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/easy-signal": {
 | 
			
		||||
			"version": "4.1.5",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/easy-signal/-/easy-signal-4.1.5.tgz",
 | 
			
		||||
			"integrity": "sha512-OXMHK788HYND/KHYAlBwYULThsDHaToDSXsiuTfMi4QLp2UfCsssr+Qhb5CIzdNTjDuCKiS9j331QHaa4Lelhw==",
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/electron-to-chromium": {
 | 
			
		||||
			"version": "1.5.13",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -2330,7 +2353,6 @@
 | 
			
		|||
			"version": "3.0.3",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
 | 
			
		||||
			"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@types/estree": "^1.0.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -2377,6 +2399,12 @@
 | 
			
		|||
			"dev": true,
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/fast-diff": {
 | 
			
		||||
			"version": "1.3.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
 | 
			
		||||
			"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
 | 
			
		||||
			"license": "Apache-2.0"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/fast-glob": {
 | 
			
		||||
			"version": "3.3.2",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -2843,7 +2871,6 @@
 | 
			
		|||
			"version": "3.0.2",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
 | 
			
		||||
			"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@types/estree": "*"
 | 
			
		||||
| 
						 | 
				
			
			@ -2991,7 +3018,6 @@
 | 
			
		|||
			"version": "3.0.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
 | 
			
		||||
			"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/locate-path": {
 | 
			
		||||
| 
						 | 
				
			
			@ -3052,7 +3078,6 @@
 | 
			
		|||
			"version": "0.30.11",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
 | 
			
		||||
			"integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@jridgewell/sourcemap-codec": "^1.5.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -3062,7 +3087,6 @@
 | 
			
		|||
			"version": "2.0.30",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
 | 
			
		||||
			"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "CC0-1.0"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/merge-stream": {
 | 
			
		||||
| 
						 | 
				
			
			@ -3477,7 +3501,6 @@
 | 
			
		|||
			"version": "3.1.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
 | 
			
		||||
			"integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@types/estree": "^1.0.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -4134,7 +4157,6 @@
 | 
			
		|||
			"version": "1.2.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
 | 
			
		||||
			"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "BSD-3-Clause",
 | 
			
		||||
			"engines": {
 | 
			
		||||
				"node": ">=0.10.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -4390,7 +4412,6 @@
 | 
			
		|||
			"version": "4.2.19",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.19.tgz",
 | 
			
		||||
			"integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@ampproject/remapping": "^2.2.1",
 | 
			
		||||
| 
						 | 
				
			
			@ -4887,6 +4908,20 @@
 | 
			
		|||
				}
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/typewriter-editor": {
 | 
			
		||||
			"version": "0.12.6",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/typewriter-editor/-/typewriter-editor-0.12.6.tgz",
 | 
			
		||||
			"integrity": "sha512-ZFaFLLjPHL8QzGHPu5tAPbxZxPYB+21dopURqmbDdFEOoNurhFTFrl61Q6hfF7aJL6G9M8TKwAZS5nqV6H618A==",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@popperjs/core": "^2.11.8",
 | 
			
		||||
				"@typewriter/document": "^0.9.0",
 | 
			
		||||
				"easy-signal": "^4.1.3"
 | 
			
		||||
			},
 | 
			
		||||
			"peerDependencies": {
 | 
			
		||||
				"svelte": ">=3.43.0 <5"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/update-browserslist-db": {
 | 
			
		||||
			"version": "1.1.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -5271,6 +5306,15 @@
 | 
			
		|||
			"funding": {
 | 
			
		||||
				"url": "https://github.com/sponsors/sindresorhus"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/zod": {
 | 
			
		||||
			"version": "3.23.8",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
 | 
			
		||||
			"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"funding": {
 | 
			
		||||
				"url": "https://github.com/sponsors/colinhacks"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,8 @@
 | 
			
		|||
	"type": "module",
 | 
			
		||||
	"dependencies": {
 | 
			
		||||
		"svelte-boring-avatars": "^1.2.6",
 | 
			
		||||
		"tailwind-merge": "^2.5.2"
 | 
			
		||||
		"tailwind-merge": "^2.5.2",
 | 
			
		||||
		"typewriter-editor": "^0.12.6",
 | 
			
		||||
		"zod": "^3.23.8"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,3 +43,9 @@
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@layer utilities {
 | 
			
		||||
	.rich-text {
 | 
			
		||||
		@apply prose prose-slate dark:prose-invert prose-p:my-0 prose-p:leading-tight prose-code:before:content-[''] prose-code:after:content-[''];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@
 | 
			
		|||
	</head>
 | 
			
		||||
	<body
 | 
			
		||||
		data-sveltekit-preload-data="hover"
 | 
			
		||||
		class="relative h-[100dvh] max-w-[100vw] touch-pan-x touch-pan-y select-none overflow-hidden bg-sb-secondary text-sf-primary"
 | 
			
		||||
		class="relative flex h-[100dvh] max-w-[100vw] touch-pan-x touch-pan-y select-none flex-col overflow-hidden bg-sb-secondary text-sf-primary"
 | 
			
		||||
	>
 | 
			
		||||
		<div style="display: contents">%sveltekit.body%</div>
 | 
			
		||||
	</body>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										19
									
								
								src/lib/components/RichTextInput.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/lib/components/RichTextInput.svelte
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
<script lang="ts">
 | 
			
		||||
	import { browser } from '$app/environment';
 | 
			
		||||
	import type { Delta } from 'typewriter-editor';
 | 
			
		||||
 | 
			
		||||
	export let delta: Delta;
 | 
			
		||||
 | 
			
		||||
	let className = '';
 | 
			
		||||
	export { className as class };
 | 
			
		||||
 | 
			
		||||
	const loadClientComponent = async () => {
 | 
			
		||||
		if (!browser) return;
 | 
			
		||||
		const { default: ClientInput } = await import('./RichTextInput/ClientInput.svelte');
 | 
			
		||||
		return ClientInput;
 | 
			
		||||
	};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
{#await loadClientComponent() then Input}
 | 
			
		||||
	<svelte:component this={Input} bind:delta class={className}><slot /></svelte:component>
 | 
			
		||||
{/await}
 | 
			
		||||
							
								
								
									
										24
									
								
								src/lib/components/RichTextInput/ClientInput.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/lib/components/RichTextInput/ClientInput.svelte
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
<script lang="ts">
 | 
			
		||||
	import InputFrame from '$lib/components/InputFrame.svelte';
 | 
			
		||||
	import { tw } from '$lib/tw';
 | 
			
		||||
	import { Delta, Editor, asRoot } from 'typewriter-editor';
 | 
			
		||||
 | 
			
		||||
	let className = '';
 | 
			
		||||
	export { className as class };
 | 
			
		||||
 | 
			
		||||
	export let delta: Delta;
 | 
			
		||||
 | 
			
		||||
	const editor = new Editor();
 | 
			
		||||
	delta = editor.getDelta();
 | 
			
		||||
	editor.on('change', () => {
 | 
			
		||||
		delta = editor.getDelta();
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	$: editor.setDelta(delta);
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<InputFrame class={tw('overflow-y-auto', className)}>
 | 
			
		||||
	<div class="rich-text w-full outline-none" use:asRoot={editor}>
 | 
			
		||||
		<slot />
 | 
			
		||||
	</div>
 | 
			
		||||
</InputFrame>
 | 
			
		||||
							
								
								
									
										70
									
								
								src/lib/richText.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/lib/richText.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
import type { AttributeMap, Delta } from 'typewriter-editor';
 | 
			
		||||
import { z } from 'zod';
 | 
			
		||||
 | 
			
		||||
export const blahRichTextSpanAttributesSchema = z.object({
 | 
			
		||||
	b: z.boolean().default(false),
 | 
			
		||||
	i: z.boolean().default(false),
 | 
			
		||||
	u: z.boolean().default(false),
 | 
			
		||||
	s: z.boolean().default(false),
 | 
			
		||||
	m: z.boolean().default(false),
 | 
			
		||||
	hashtag: z.boolean().default(false),
 | 
			
		||||
	link: z.string().url().optional()
 | 
			
		||||
});
 | 
			
		||||
export type BlahRichTextSpanAttributes = z.input<typeof blahRichTextSpanAttributesSchema>;
 | 
			
		||||
 | 
			
		||||
export const blahRichTextSpanSchema = z.union([
 | 
			
		||||
	z.tuple([z.string()]),
 | 
			
		||||
	z.tuple([z.string(), blahRichTextSpanAttributesSchema])
 | 
			
		||||
]);
 | 
			
		||||
export type BlahRichTextSpan = z.input<typeof blahRichTextSpanSchema>;
 | 
			
		||||
 | 
			
		||||
export const blahRichTextBlockSchema = z.array(blahRichTextSpanSchema);
 | 
			
		||||
export type BlahRichTextBlock = z.input<typeof blahRichTextBlockSchema>;
 | 
			
		||||
 | 
			
		||||
export const blahRichTextSchema = z.array(blahRichTextBlockSchema);
 | 
			
		||||
export type BlahRichText = z.input<typeof blahRichTextSchema>;
 | 
			
		||||
 | 
			
		||||
function isObjectEmpty(obj: object) {
 | 
			
		||||
	for (const _ in obj) return false;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function deltaAttributesToBlahRichTextSpanAttributes(
 | 
			
		||||
	attributes?: AttributeMap
 | 
			
		||||
): BlahRichTextSpanAttributes | null {
 | 
			
		||||
	if (!attributes) return null;
 | 
			
		||||
 | 
			
		||||
	const blahRichTextSpanAttributes: BlahRichTextSpanAttributes = {};
 | 
			
		||||
 | 
			
		||||
	if (attributes.bold) blahRichTextSpanAttributes.b = true;
 | 
			
		||||
	if (attributes.italic) blahRichTextSpanAttributes.i = true;
 | 
			
		||||
	if (attributes.code) blahRichTextSpanAttributes.m = true;
 | 
			
		||||
	if (attributes.link) blahRichTextSpanAttributes.link = attributes.link;
 | 
			
		||||
 | 
			
		||||
	if (attributes.underline) blahRichTextSpanAttributes.u = true;
 | 
			
		||||
	if (attributes.strike) blahRichTextSpanAttributes.s = true;
 | 
			
		||||
 | 
			
		||||
	return isObjectEmpty(blahRichTextSpanAttributes) ? null : blahRichTextSpanAttributes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function deltaToBlahRichText(delta: Delta): BlahRichText {
 | 
			
		||||
	const blocks: BlahRichText = [];
 | 
			
		||||
 | 
			
		||||
	let block: BlahRichTextBlock = [];
 | 
			
		||||
	for (const op of delta.ops) {
 | 
			
		||||
		const lines = op.insert?.split('\n');
 | 
			
		||||
		if (!lines) continue;
 | 
			
		||||
		const attributes = deltaAttributesToBlahRichTextSpanAttributes(op.attributes);
 | 
			
		||||
 | 
			
		||||
		const line = lines.shift();
 | 
			
		||||
		block.push(attributes ? [line, attributes] : [line]);
 | 
			
		||||
 | 
			
		||||
		for (const line of lines) {
 | 
			
		||||
			blocks.push(block);
 | 
			
		||||
			block = [];
 | 
			
		||||
			block.push(attributes ? [line, attributes] : [line]);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return blocks;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								src/routes/(internal)/_rich-text/+page.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/routes/(internal)/_rich-text/+page.svelte
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
<script lang="ts">
 | 
			
		||||
	import RichTextInput from '$lib/components/RichTextInput.svelte';
 | 
			
		||||
	import { deltaToBlahRichText } from '$lib/richText';
 | 
			
		||||
	import type { Delta } from 'typewriter-editor';
 | 
			
		||||
 | 
			
		||||
	let delta: Delta;
 | 
			
		||||
 | 
			
		||||
	$: brt = delta ? deltaToBlahRichText(delta) : null;
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<RichTextInput bind:delta class="m-4 max-h-32">
 | 
			
		||||
	<p>A <strong>quick</strong> brown <em>fox</em> jumps over the lazy dog.</p>
 | 
			
		||||
	<p>A test engineer <a href="https://example.com">tests</a> the <code>RichTextInput</code>.</p>
 | 
			
		||||
</RichTextInput>
 | 
			
		||||
 | 
			
		||||
<div class="flex min-h-0 flex-1 gap-4 p-4">
 | 
			
		||||
	<div class="flex min-h-0 flex-1 flex-col">
 | 
			
		||||
		<h2 class="text-lg">Delta (Editor's internal representation)</h2>
 | 
			
		||||
		<div class="min-h-0 flex-1 select-text overflow-auto">
 | 
			
		||||
			<pre><code>{JSON.stringify(delta, null, 2)}</code></pre>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<div class="flex min-h-0 flex-1 flex-col">
 | 
			
		||||
		<h2 class="text-lg">Blah Rich Text</h2>
 | 
			
		||||
		<div class="min-h-0 flex-1 select-text overflow-auto">
 | 
			
		||||
			<pre><code>{JSON.stringify(brt, null, 2)}</code></pre>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue