tweak and edit more stuff
This commit is contained in:
parent
47d5c2fee2
commit
1ae824fa4b
9 changed files with 409 additions and 149 deletions
|
@ -15,12 +15,16 @@
|
||||||
"@tanstack/virtual-core": "^3.0.1",
|
"@tanstack/virtual-core": "^3.0.1",
|
||||||
"@tiptap/core": "^2.1.13",
|
"@tiptap/core": "^2.1.13",
|
||||||
"@tiptap/extension-document": "^2.1.13",
|
"@tiptap/extension-document": "^2.1.13",
|
||||||
|
"@tiptap/extension-dropcursor": "^2.1.13",
|
||||||
|
"@tiptap/extension-gapcursor": "^2.1.13",
|
||||||
|
"@tiptap/extension-hard-break": "^2.1.13",
|
||||||
"@tiptap/extension-history": "^2.1.13",
|
"@tiptap/extension-history": "^2.1.13",
|
||||||
"@tiptap/extension-mention": "^2.1.13",
|
"@tiptap/extension-mention": "^2.1.13",
|
||||||
"@tiptap/extension-paragraph": "^2.1.13",
|
"@tiptap/extension-paragraph": "^2.1.13",
|
||||||
"@tiptap/extension-placeholder": "^2.1.13",
|
"@tiptap/extension-placeholder": "^2.1.13",
|
||||||
"@tiptap/extension-text": "^2.1.13",
|
"@tiptap/extension-text": "^2.1.13",
|
||||||
"@tiptap/pm": "^2.1.13",
|
"@tiptap/pm": "^2.1.13",
|
||||||
|
"@tiptap/starter-kit": "^2.1.13",
|
||||||
"@tiptap/suggestion": "^2.1.13",
|
"@tiptap/suggestion": "^2.1.13",
|
||||||
"i18next": "^23.7.8",
|
"i18next": "^23.7.8",
|
||||||
"marked": "^11.0.1",
|
"marked": "^11.0.1",
|
||||||
|
|
158
pnpm-lock.yaml
158
pnpm-lock.yaml
|
@ -26,6 +26,15 @@ dependencies:
|
||||||
'@tiptap/extension-document':
|
'@tiptap/extension-document':
|
||||||
specifier: ^2.1.13
|
specifier: ^2.1.13
|
||||||
version: 2.1.13(@tiptap/core@2.1.13)
|
version: 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
'@tiptap/extension-dropcursor':
|
||||||
|
specifier: ^2.1.13
|
||||||
|
version: 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||||
|
'@tiptap/extension-gapcursor':
|
||||||
|
specifier: ^2.1.13
|
||||||
|
version: 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||||
|
'@tiptap/extension-hard-break':
|
||||||
|
specifier: ^2.1.13
|
||||||
|
version: 2.1.13(@tiptap/core@2.1.13)
|
||||||
'@tiptap/extension-history':
|
'@tiptap/extension-history':
|
||||||
specifier: ^2.1.13
|
specifier: ^2.1.13
|
||||||
version: 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
version: 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||||
|
@ -44,6 +53,9 @@ dependencies:
|
||||||
'@tiptap/pm':
|
'@tiptap/pm':
|
||||||
specifier: ^2.1.13
|
specifier: ^2.1.13
|
||||||
version: 2.1.13
|
version: 2.1.13
|
||||||
|
'@tiptap/starter-kit':
|
||||||
|
specifier: ^2.1.13
|
||||||
|
version: 2.1.13(@tiptap/pm@2.1.13)
|
||||||
'@tiptap/suggestion':
|
'@tiptap/suggestion':
|
||||||
specifier: ^2.1.13
|
specifier: ^2.1.13
|
||||||
version: 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
version: 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||||
|
@ -831,6 +843,22 @@ packages:
|
||||||
'@tiptap/pm': 2.1.13
|
'@tiptap/pm': 2.1.13
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-blockquote@2.1.13(@tiptap/core@2.1.13):
|
||||||
|
resolution: {integrity: sha512-oe6wSQACmODugoP9XH3Ouffjy4BsOBWfTC+dETHNCG6ZED6ShHN3CB9Vr7EwwRgmm2WLaKAjMO1sVumwH+Z1rg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-bold@2.1.13(@tiptap/core@2.1.13):
|
||||||
|
resolution: {integrity: sha512-6cHsQTh/rUiG4jkbJer3vk7g60I5tBwEBSGpdxmEHh83RsvevD8+n92PjA24hYYte5RNlATB011E1wu8PVhSvw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@tiptap/extension-bubble-menu@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
/@tiptap/extension-bubble-menu@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
||||||
resolution: {integrity: sha512-Hm7e1GX3AI6lfaUmr6WqsS9MMyXIzCkhh+VQi6K8jj4Q4s8kY4KPoAyD/c3v9pZ/dieUtm2TfqrOCkbHzsJQBg==}
|
resolution: {integrity: sha512-Hm7e1GX3AI6lfaUmr6WqsS9MMyXIzCkhh+VQi6K8jj4Q4s8kY4KPoAyD/c3v9pZ/dieUtm2TfqrOCkbHzsJQBg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -842,6 +870,32 @@ packages:
|
||||||
tippy.js: 6.3.7
|
tippy.js: 6.3.7
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-bullet-list@2.1.13(@tiptap/core@2.1.13):
|
||||||
|
resolution: {integrity: sha512-NkWlQ5bLPUlcROj6G/d4oqAxMf3j3wfndGOPp0z8OoXJtVbVoXl/aMSlLbVgE6n8r6CS8MYxKhXNxrb7Ll2foA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-code-block@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
||||||
|
resolution: {integrity: sha512-E3tweNExPOV+t1ODKX0MDVsS0aeHGWc1ECt+uyp6XwzsN0bdF2A5+pttQqM7sTcMnQkVACGFbn9wDeLRRcfyQg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
'@tiptap/pm': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
'@tiptap/pm': 2.1.13
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-code@2.1.13(@tiptap/core@2.1.13):
|
||||||
|
resolution: {integrity: sha512-f5fLYlSgliVVa44vd7lQGvo49+peC+Z2H0Fn84TKNCH7tkNZzouoJsHYn0/enLaQ9Sq+24YPfqulfiwlxyiT8w==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@tiptap/extension-document@2.1.13(@tiptap/core@2.1.13):
|
/@tiptap/extension-document@2.1.13(@tiptap/core@2.1.13):
|
||||||
resolution: {integrity: sha512-wLwiTWsVmZTGIE5duTcHRmW4ulVxNW4nmgfpk95+mPn1iKyNGtrVhGWleLhBlTj+DWXDtcfNWZgqZkZNzhkqYQ==}
|
resolution: {integrity: sha512-wLwiTWsVmZTGIE5duTcHRmW4ulVxNW4nmgfpk95+mPn1iKyNGtrVhGWleLhBlTj+DWXDtcfNWZgqZkZNzhkqYQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -850,6 +904,16 @@ packages:
|
||||||
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-dropcursor@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
||||||
|
resolution: {integrity: sha512-NAyJi4BJxH7vl/2LNS1X0ndwFKjEtX+cRgshXCnMyh7qNpIRW6Plczapc/W1OiMncOEhZJfpZfkRSfwG01FWFg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
'@tiptap/pm': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
'@tiptap/pm': 2.1.13
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@tiptap/extension-floating-menu@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
/@tiptap/extension-floating-menu@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
||||||
resolution: {integrity: sha512-9Oz7pk1Nts2+EyY+rYfnREGbLzQ5UFazAvRhF6zAJdvyuDmAYm0Jp6s0GoTrpV0/dJEISoFaNpPdMJOb9EBNRw==}
|
resolution: {integrity: sha512-9Oz7pk1Nts2+EyY+rYfnREGbLzQ5UFazAvRhF6zAJdvyuDmAYm0Jp6s0GoTrpV0/dJEISoFaNpPdMJOb9EBNRw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -861,6 +925,32 @@ packages:
|
||||||
tippy.js: 6.3.7
|
tippy.js: 6.3.7
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-gapcursor@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
||||||
|
resolution: {integrity: sha512-Cl5apsoTcyPPCgE3ThufxQxZ1wyqqh+9uxUN9VF9AbeTkid6oPZvKXwaILf6AFnkSy+SuKrb9kZD2iaezxpzXw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
'@tiptap/pm': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
'@tiptap/pm': 2.1.13
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-hard-break@2.1.13(@tiptap/core@2.1.13):
|
||||||
|
resolution: {integrity: sha512-TGkMzMQayuKg+vN4du0x1ahEItBLcCT1jdWeRsjdM8gHfzbPLdo4PQhVsvm1I0xaZmbJZelhnVsUwRZcIu1WNA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-heading@2.1.13(@tiptap/core@2.1.13):
|
||||||
|
resolution: {integrity: sha512-PEmc19QLmlVUTiHWoF0hpgNTNPNU0nlaFmMKskzO+cx5Df4xvHmv/UqoIwp7/UFbPMkfVJT1ozQU7oD1IWn9Hg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@tiptap/extension-history@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
/@tiptap/extension-history@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
||||||
resolution: {integrity: sha512-1ouitThGTBUObqw250aDwGLMNESBH5PRXIGybsCFO1bktdmWtEw7m72WY41EuX2BH8iKJpcYPerl3HfY1vmCNw==}
|
resolution: {integrity: sha512-1ouitThGTBUObqw250aDwGLMNESBH5PRXIGybsCFO1bktdmWtEw7m72WY41EuX2BH8iKJpcYPerl3HfY1vmCNw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -871,6 +961,32 @@ packages:
|
||||||
'@tiptap/pm': 2.1.13
|
'@tiptap/pm': 2.1.13
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-horizontal-rule@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
||||||
|
resolution: {integrity: sha512-7OgjgNqZXvBejgULNdMSma2M1nzv4bbZG+FT5XMFZmEOxR9IB1x/RzChjPdeicff2ZK2sfhMBc4Y9femF5XkUg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
'@tiptap/pm': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
'@tiptap/pm': 2.1.13
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-italic@2.1.13(@tiptap/core@2.1.13):
|
||||||
|
resolution: {integrity: sha512-HyDJfuDn5hzwGKZiANcvgz6wcum6bEgb4wmJnfej8XanTMJatNVv63TVxCJ10dSc9KGpPVcIkg6W8/joNXIEbw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-list-item@2.1.13(@tiptap/core@2.1.13):
|
||||||
|
resolution: {integrity: sha512-6e8iiCWXOiJTl1XOwVW2tc0YG18h70HUtEHFCx2m5HspOGFKsFEaSS3qYxOheM9HxlmQeDt8mTtqftRjEFRxPQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@tiptap/extension-mention@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)(@tiptap/suggestion@2.1.13):
|
/@tiptap/extension-mention@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)(@tiptap/suggestion@2.1.13):
|
||||||
resolution: {integrity: sha512-OYqaucyBiCN/CmDYjpOVX74RJcIEKmAqiZxUi8Gfaq7ryEO5a8Gk93nK+8uZ0onaqHE+mHpoLFFbcAFbOPgkUQ==}
|
resolution: {integrity: sha512-OYqaucyBiCN/CmDYjpOVX74RJcIEKmAqiZxUi8Gfaq7ryEO5a8Gk93nK+8uZ0onaqHE+mHpoLFFbcAFbOPgkUQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -883,6 +999,14 @@ packages:
|
||||||
'@tiptap/suggestion': 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
'@tiptap/suggestion': 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-ordered-list@2.1.13(@tiptap/core@2.1.13):
|
||||||
|
resolution: {integrity: sha512-UO4ZAL5Vrr1WwER5VjgmeNIWHpqy9cnIRo1En07gZ0OWTjs1eITPcu+4TCn1ZG6DhoFvAQzE5DTxxdhIotg+qw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@tiptap/extension-paragraph@2.1.13(@tiptap/core@2.1.13):
|
/@tiptap/extension-paragraph@2.1.13(@tiptap/core@2.1.13):
|
||||||
resolution: {integrity: sha512-cEoZBJrsQn69FPpUMePXG/ltGXtqKISgypj70PEHXt5meKDjpmMVSY4/8cXvFYEYsI9GvIwyAK0OrfAHiSoROA==}
|
resolution: {integrity: sha512-cEoZBJrsQn69FPpUMePXG/ltGXtqKISgypj70PEHXt5meKDjpmMVSY4/8cXvFYEYsI9GvIwyAK0OrfAHiSoROA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -901,6 +1025,14 @@ packages:
|
||||||
'@tiptap/pm': 2.1.13
|
'@tiptap/pm': 2.1.13
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/extension-strike@2.1.13(@tiptap/core@2.1.13):
|
||||||
|
resolution: {integrity: sha512-VN6zlaCNCbyJUCDyBFxavw19XmQ4LkCh8n20M8huNqW77lDGXA2A7UcWLHaNBpqAijBRu9mWI8l4Bftyf2fcAw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.0.0
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@tiptap/extension-text@2.1.13(@tiptap/core@2.1.13):
|
/@tiptap/extension-text@2.1.13(@tiptap/core@2.1.13):
|
||||||
resolution: {integrity: sha512-zzsTTvu5U67a8WjImi6DrmpX2Q/onLSaj+LRWPh36A1Pz2WaxW5asZgaS+xWCnR+UrozlCALWa01r7uv69jq0w==}
|
resolution: {integrity: sha512-zzsTTvu5U67a8WjImi6DrmpX2Q/onLSaj+LRWPh36A1Pz2WaxW5asZgaS+xWCnR+UrozlCALWa01r7uv69jq0w==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -932,6 +1064,32 @@ packages:
|
||||||
prosemirror-view: 1.32.6
|
prosemirror-view: 1.32.6
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@tiptap/starter-kit@2.1.13(@tiptap/pm@2.1.13):
|
||||||
|
resolution: {integrity: sha512-ph/mUR/OwPtPkZ5rNHINxubpABn8fHnvJSdhXFrY/q6SKoaO11NZXgegRaiG4aL7O6Sz4LsZVw6Sm0Ae+GJmrg==}
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||||
|
'@tiptap/extension-blockquote': 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
'@tiptap/extension-bold': 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
'@tiptap/extension-bullet-list': 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
'@tiptap/extension-code': 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
'@tiptap/extension-code-block': 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||||
|
'@tiptap/extension-document': 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
'@tiptap/extension-dropcursor': 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||||
|
'@tiptap/extension-gapcursor': 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||||
|
'@tiptap/extension-hard-break': 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
'@tiptap/extension-heading': 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
'@tiptap/extension-history': 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||||
|
'@tiptap/extension-horizontal-rule': 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||||
|
'@tiptap/extension-italic': 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
'@tiptap/extension-list-item': 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
'@tiptap/extension-ordered-list': 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
'@tiptap/extension-paragraph': 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
'@tiptap/extension-strike': 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
'@tiptap/extension-text': 2.1.13(@tiptap/core@2.1.13)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@tiptap/pm'
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@tiptap/suggestion@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
/@tiptap/suggestion@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
||||||
resolution: {integrity: sha512-Y05TsiXTFAJ5SrfoV+21MAxig5UNbY0AVa03lQlh/yicTRPpIc6hgZzblB0uxDSYoj6+kaHE4MIZvPvhUD8BJQ==}
|
resolution: {integrity: sha512-Y05TsiXTFAJ5SrfoV+21MAxig5UNbY0AVa03lQlh/yicTRPpIc6hgZzblB0uxDSYoj6+kaHE4MIZvPvhUD8BJQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
53
src/App.scss
53
src/App.scss
|
@ -7,7 +7,7 @@
|
||||||
"nav-spaces nav-rooms main sidebar"
|
"nav-spaces nav-rooms main sidebar"
|
||||||
"status status main sidebar";
|
"status status main sidebar";
|
||||||
grid-template-columns: 64px 256px 1fr auto;
|
grid-template-columns: 64px 256px 1fr auto;
|
||||||
grid-template-rows: 64px 1fr 72px;
|
grid-template-rows: 48px 1fr 72px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#header {
|
#header {
|
||||||
|
@ -95,7 +95,11 @@
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #ffffff33;
|
background: #ffffff11;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background: #ffffff22;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,11 +343,21 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
scrollbar-color: var(--background-1) var(--background-3);
|
scrollbar-color: var(--background-1) var(--background-3);
|
||||||
overflow-anchor: none;
|
// overflow-anchor: none;
|
||||||
|
|
||||||
& > .spacer {
|
& > .spacer {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
margin-bottom: 32px;
|
min-height: 32px;
|
||||||
|
padding: 0 4px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: start;
|
||||||
|
justify-content: end;
|
||||||
|
|
||||||
|
& > button {
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& > .spacer-bottom {
|
& > .spacer-bottom {
|
||||||
|
@ -354,6 +368,7 @@
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: -1px;
|
top: -1px;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
|
visibility: hidden;
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
padding: 1px 8px 0;
|
padding: 1px 8px 0;
|
||||||
|
@ -366,8 +381,25 @@
|
||||||
|
|
||||||
& > .real {
|
& > .real {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transition: all .2s;
|
|
||||||
border-bottom: solid var(--background-3) 0;
|
border-bottom: solid var(--background-3) 0;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > button {
|
||||||
|
display: none;
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 4px;
|
||||||
|
font-weight: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.setup > .real {
|
||||||
|
transition: all .2s;
|
||||||
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.stuck > .real {
|
&.stuck > .real {
|
||||||
|
@ -375,6 +407,17 @@
|
||||||
border-bottom: solid var(--background-3) 1px;
|
border-bottom: solid var(--background-3) 1px;
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > button {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
src/App.tsx
13
src/App.tsx
|
@ -1,4 +1,4 @@
|
||||||
import { Match, Show, Switch, VoidProps, createSignal, onCleanup } from "solid-js";
|
import { Match, Show, Switch, VoidProps, createSelector, createSignal, onCleanup } from "solid-js";
|
||||||
import "./App.scss";
|
import "./App.scss";
|
||||||
import { Client, Room } from "sdk";
|
import { Client, Room } from "sdk";
|
||||||
import { Home, RoomView } from "./Main";
|
import { Home, RoomView } from "./Main";
|
||||||
|
@ -31,7 +31,7 @@ function App(props: VoidProps<{ client: Client }>) {
|
||||||
Object.assign(globalThis, { client: props.client });
|
Object.assign(globalThis, { client: props.client });
|
||||||
|
|
||||||
props.client.lists.subscribe("rooms", {
|
props.client.lists.subscribe("rooms", {
|
||||||
ranges: [[0, 120]],
|
ranges: [[0, 99999]],
|
||||||
required_state: [["m.room.name", ""], ["m.room.topic", ""]],
|
required_state: [["m.room.name", ""], ["m.room.topic", ""]],
|
||||||
// timeline_limit: 3,
|
// timeline_limit: 3,
|
||||||
} as any);
|
} as any);
|
||||||
|
@ -41,11 +41,12 @@ function App(props: VoidProps<{ client: Client }>) {
|
||||||
});
|
});
|
||||||
|
|
||||||
const thread = () => globals.roomState.focusedThread();
|
const thread = () => globals.roomState.focusedThread();
|
||||||
|
|
||||||
|
const isSelected = createSelector(() => globals.globalState.focusedRoom());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header id="header">
|
<header id="header"></header>
|
||||||
</header>
|
|
||||||
<main id="main">
|
<main id="main">
|
||||||
<Show when={globals.globalState.focusedRoom()} fallback={<Home client={props.client} />}>
|
<Show when={globals.globalState.focusedRoom()} fallback={<Home client={props.client} />}>
|
||||||
<RoomView room={globals.globalState.focusedRoom()!} />
|
<RoomView room={globals.globalState.focusedRoom()!} />
|
||||||
|
@ -53,8 +54,8 @@ function App(props: VoidProps<{ client: Client }>) {
|
||||||
</main>
|
</main>
|
||||||
<nav id="nav-rooms">
|
<nav id="nav-rooms">
|
||||||
<ul>
|
<ul>
|
||||||
<li onClick={() => change({ type: "focusRoom", room: null })}>home</li>
|
<li classList={{ selected: isSelected(null) }} onClick={() => change({ type: "focusRoom", room: null })}>home</li>
|
||||||
{rooms().rooms.map(i => <li onClick={() => change({ type: "focusRoom", room: i })}>{i.getState("m.room.name")?.content.name || "unnamed"}</li>)}
|
{rooms().rooms.map(i => <li classList={{ selected: isSelected(i) }} onClick={() => change({ type: "focusRoom", room: i })}>{i.getState("m.room.name")?.content.name || "unnamed"}</li>)}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<nav id="nav-spaces"></nav>
|
<nav id="nav-spaces"></nav>
|
||||||
|
|
113
src/Editor.tsx
113
src/Editor.tsx
|
@ -1,114 +1,3 @@
|
||||||
import { createEditor, EditorContent } from "tiptap-solid";
|
|
||||||
import Document from "@tiptap/extension-document";
|
|
||||||
import Paragraph from "@tiptap/extension-paragraph";
|
|
||||||
import Text from "@tiptap/extension-text";
|
|
||||||
import History from "@tiptap/extension-history";
|
|
||||||
// import Mention from "@tiptap/extension-mention";
|
|
||||||
import Placeholder from "@tiptap/extension-placeholder";
|
|
||||||
import "./Editor.scss";
|
|
||||||
// import { Accessor, createEffect, createSignal, VoidProps } from "solid-js";
|
|
||||||
// import { Node } from "@tiptap/core";
|
|
||||||
import { Extension, Mark, markInputRule, wrappingInputRule } from "@tiptap/core";
|
|
||||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
||||||
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
|
||||||
import { marked } from "marked";
|
|
||||||
|
|
||||||
// function Suggestions(props: VoidProps<{ suggestions: Accessor<Array<string>> }>) {
|
|
||||||
// createEffect(() => console.log(props.suggestions()));
|
|
||||||
// return (
|
|
||||||
// <ul>
|
|
||||||
// {props.suggestions().map(i => <li>{i}</li>)}
|
|
||||||
// </ul>
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
const Markdown = Extension.create({
|
|
||||||
addKeyboardShortcuts() {
|
|
||||||
return {
|
|
||||||
"Mod-b": () => {
|
|
||||||
const { state } = this.editor.view;
|
|
||||||
const { selection } = state;
|
|
||||||
const { from, to } = selection;
|
|
||||||
this.editor.chain()
|
|
||||||
.insertContentAt({ from: to, to }, "**", { updateSelection: true })
|
|
||||||
.insertContentAt({ from, to: from }, "**", { updateSelection: true })
|
|
||||||
.setTextSelection({ from: from + 2, to: to + 2 })
|
|
||||||
.focus()
|
|
||||||
.run();
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
addProseMirrorPlugins() {
|
|
||||||
return [new Plugin({
|
|
||||||
key: new PluginKey("markdown"),
|
|
||||||
props: {
|
|
||||||
decorations(state) {
|
|
||||||
const decorations = [];
|
|
||||||
let pos = 0;
|
|
||||||
|
|
||||||
function walk(token) {
|
|
||||||
if (token.type === "em") {
|
|
||||||
decorations.push(Decoration.inline(pos, pos + 2, { class: "syn" }));
|
|
||||||
decorations.push(Decoration.inline(pos + 2, pos + token.raw.length, { style: "font-style:italic" }));
|
|
||||||
decorations.push(Decoration.inline(pos + token.raw.length, pos + token.raw.length + 1, { class: "syn" }));
|
|
||||||
pos += token.raw.length;
|
|
||||||
} else if (token.type === "strong") {
|
|
||||||
decorations.push(Decoration.inline(pos + 1, pos + 3, { class: "syn" }));
|
|
||||||
decorations.push(Decoration.inline(pos + 3, pos + token.raw.length - 1, { style: "font-weight:bold" }));
|
|
||||||
decorations.push(Decoration.inline(pos + token.raw.length - 1, pos + token.raw.length + 1, { class: "syn" }));
|
|
||||||
pos += token.raw.length;
|
|
||||||
} else if (token.type === "codespan") {
|
|
||||||
decorations.push(Decoration.inline(pos, pos + 2, { class: "syn" }));
|
|
||||||
decorations.push(Decoration.inline(pos + 2, pos + token.raw.length, { nodeName: "code" }));
|
|
||||||
decorations.push(Decoration.inline(pos + token.raw.length, pos + token.raw.length + 1, { class: "syn" }));
|
|
||||||
pos += token.raw.length;
|
|
||||||
} else if (token.type === "text") {
|
|
||||||
pos += token.raw.length;
|
|
||||||
} else {
|
|
||||||
for (const t of token.tokens || []) {
|
|
||||||
walk(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log(state.doc.textContent)
|
|
||||||
// console.log(marked.lexer(state.doc.textContent));
|
|
||||||
for (const token of marked.lexer(state.doc.textContent)) walk(token);
|
|
||||||
return DecorationSet.create(state.doc, decorations);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export function Editor() {
|
export function Editor() {
|
||||||
let editorEl;
|
return <textarea />;
|
||||||
const editor = createEditor({
|
|
||||||
content: "hello world *asdf*",
|
|
||||||
autofocus: true,
|
|
||||||
extensions: [
|
|
||||||
Document,
|
|
||||||
Paragraph,
|
|
||||||
Text,
|
|
||||||
History,
|
|
||||||
Placeholder.configure({
|
|
||||||
placeholder: "write something nice...",
|
|
||||||
}),
|
|
||||||
Markdown,
|
|
||||||
]
|
|
||||||
});
|
|
||||||
function doThing() {
|
|
||||||
const ed = editor()!;
|
|
||||||
// ed.chain().focus().insertContent("hello world!").run();
|
|
||||||
ed.state.selection.$from
|
|
||||||
ed.state.selection.$to
|
|
||||||
ed.chain().focus().insertContentAt(0, "content").run();
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div class="editor" ref={editorEl}>
|
|
||||||
<button onClick={doThing}>do thing</button>
|
|
||||||
<EditorContent editor={editor()} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
10
src/Main.tsx
10
src/Main.tsx
|
@ -34,11 +34,19 @@ function Threads(props: VoidProps<{ room: Room }>) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ROOM_TYPE_CHAT = "jw.chat";
|
||||||
|
const ROOM_TYPE_SPACE = "m.space";
|
||||||
|
|
||||||
export function Home(props: VoidProps<{ client: Client }>) {
|
export function Home(props: VoidProps<{ client: Client }>) {
|
||||||
function handleSubmit(e: SubmitEvent) {
|
function handleSubmit(e: SubmitEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const name = (e.target as HTMLFormElement).elements.namedItem("name")! as HTMLInputElement;
|
const name = (e.target as HTMLFormElement).elements.namedItem("name")! as HTMLInputElement;
|
||||||
props.client.rooms.create({ initialState: [{ type: "m.room.name", content: { name: name.value }, stateKey: ""}] });
|
props.client.rooms.create({
|
||||||
|
creationContent: {
|
||||||
|
type: ROOM_TYPE_CHAT,
|
||||||
|
},
|
||||||
|
initialState: [{ type: "m.room.name", content: { name: name.value }, stateKey: ""}]
|
||||||
|
});
|
||||||
name.value = "";
|
name.value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,9 @@ import { Text, Time } from "./Atoms";
|
||||||
export function ThreadsItem(props: VoidProps<{ thread: Thread }>) {
|
export function ThreadsItem(props: VoidProps<{ thread: Thread }>) {
|
||||||
const [globals, change] = useGlobals();
|
const [globals, change] = useGlobals();
|
||||||
|
|
||||||
|
const THREAD_PREVIEW_COUNT = 10;
|
||||||
const [preview, { mutate }] = createResource(props.thread, async (thread) => {
|
const [preview, { mutate }] = createResource(props.thread, async (thread) => {
|
||||||
return thread.room.timelines.forThread(thread, "start");
|
return thread.timelines.fetch("start");
|
||||||
}, {
|
}, {
|
||||||
storage: (init) => createSignal(init, { equals: false }),
|
storage: (init) => createSignal(init, { equals: false }),
|
||||||
});
|
});
|
||||||
|
@ -21,7 +22,7 @@ export function ThreadsItem(props: VoidProps<{ thread: Thread }>) {
|
||||||
onCleanup(() => props.thread.room.off("timeline", refresh));
|
onCleanup(() => props.thread.room.off("timeline", refresh));
|
||||||
|
|
||||||
const [thread, setThread] = createSignal(props.thread, { equals: false });
|
const [thread, setThread] = createSignal(props.thread, { equals: false });
|
||||||
const remaining = () => preview() && (thread().messageCount - Math.min(preview()!.getEvents().length, 10));
|
const remaining = () => preview() && (thread().messageCount - Math.min(preview()!.getEvents().length, THREAD_PREVIEW_COUNT ));
|
||||||
|
|
||||||
const willOpenThread = (target: "default" | "latest") => (e: MouseEvent) => {
|
const willOpenThread = (target: "default" | "latest") => (e: MouseEvent) => {
|
||||||
// TODO: target
|
// TODO: target
|
||||||
|
@ -48,7 +49,7 @@ export function ThreadsItem(props: VoidProps<{ thread: Thread }>) {
|
||||||
</header>
|
</header>
|
||||||
<div class="preview">
|
<div class="preview">
|
||||||
<Show when={!preview.loading}>
|
<Show when={!preview.loading}>
|
||||||
{preview()?.getEvents().filter(ev => ev.type === "m.message").slice(0, 10).map((ev, idx) => <Message event={ev} title={idx === 0} />)}
|
{preview()?.getEvents().filter(ev => ev.type === "m.message").slice(0, THREAD_PREVIEW_COUNT).map((ev, idx) => <Message event={ev} title={idx === 0} />)}
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<Show when={remaining() && false}>
|
<Show when={remaining() && false}>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { For, ParentProps, Show, VoidProps, createEffect, createResource, createSignal, onCleanup, onMount } from "solid-js";
|
import { For, ParentProps, Show, VoidProps, createEffect, createResource, createSignal, onCleanup, onMount } from "solid-js";
|
||||||
import "./App.scss";
|
import "./App.scss";
|
||||||
import { Client, Room, Thread, Timeline } from "sdk";
|
import { Client, Room, Thread } from "sdk";
|
||||||
import { Message, TextBlock, ThreadsItem } from "./Room";
|
import { Message, TextBlock, ThreadsItem } from "./Room";
|
||||||
// import { computePosition, shift, offset, autoUpdate } from "@floating-ui/dom";
|
// import { computePosition, shift, offset, autoUpdate } from "@floating-ui/dom";
|
||||||
// import { Portal } from "solid-js/web";
|
// import { Portal } from "solid-js/web";
|
||||||
|
@ -8,6 +8,7 @@ import { Time } from "./Atoms";
|
||||||
import { ThreadTimeline } from "sdk/dist/src/timeline";
|
import { ThreadTimeline } from "sdk/dist/src/timeline";
|
||||||
import { debounce } from "@solid-primitives/scheduled";
|
import { debounce } from "@solid-primitives/scheduled";
|
||||||
import { Editor } from "./Editor";
|
import { Editor } from "./Editor";
|
||||||
|
import { useGlobals } from "./Context";
|
||||||
// import { createVirtualizer } from "@tanstack/solid-virtual";
|
// import { createVirtualizer } from "@tanstack/solid-virtual";
|
||||||
|
|
||||||
export function ThreadView(props: VoidProps<{ thread: Thread }>) {
|
export function ThreadView(props: VoidProps<{ thread: Thread }>) {
|
||||||
|
@ -25,9 +26,13 @@ export function ThreadView(props: VoidProps<{ thread: Thread }>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [timeline, { mutate }] = createResource(() => props.thread, async (thread) => {
|
// updates when the thread changes
|
||||||
return thread.room.timelines.forThread(thread, "end");
|
const [timelineReal] = createResource(() => props.thread, async (thread) => {
|
||||||
}, {
|
return thread.timelines.fetch("end", 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
// updates every time there's new events
|
||||||
|
const [timeline, { mutate }] = createResource(() => timelineReal(), (t) => t, {
|
||||||
storage: (init) => createSignal(init, { equals: false }),
|
storage: (init) => createSignal(init, { equals: false }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -43,15 +48,14 @@ export function ThreadView(props: VoidProps<{ thread: Thread }>) {
|
||||||
|
|
||||||
let oldTimeline: ThreadTimeline | undefined;
|
let oldTimeline: ThreadTimeline | undefined;
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
oldTimeline?.off("timelineUpdate", refresh);
|
|
||||||
oldTimeline?.off("timelineAppend", refresh);
|
oldTimeline?.off("timelineAppend", refresh);
|
||||||
timeline()?.on("timelineUpdate", refresh);
|
timelineReal()?.on("timelineAppend", refresh);
|
||||||
timeline()?.on("timelineAppend", refresh);
|
oldTimeline = timelineReal();
|
||||||
oldTimeline = timeline();
|
setIsAtBeginning(timelineReal()?.isAtBeginning || false);
|
||||||
setIsAtBeginning(timeline()?.isAtBeginning || false);
|
scrollEl.scrollBy(0, 999999);
|
||||||
}, timeline);
|
// paginate();
|
||||||
|
}, timelineReal);
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
oldTimeline?.off("timelineUpdate", refresh);
|
|
||||||
oldTimeline?.off("timelineAppend", refresh);
|
oldTimeline?.off("timelineAppend", refresh);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -63,19 +67,21 @@ export function ThreadView(props: VoidProps<{ thread: Thread }>) {
|
||||||
|
|
||||||
if (scrollEl.scrollTop < PAGINATE_MARGIN && !isPaginating() && !isAtBeginning()) {
|
if (scrollEl.scrollTop < PAGINATE_MARGIN && !isPaginating() && !isAtBeginning()) {
|
||||||
const scrollRefEl = scrollEl.querySelector(".message") as HTMLElement | undefined;
|
const scrollRefEl = scrollEl.querySelector(".message") as HTMLElement | undefined;
|
||||||
const oldOffsetTop = (scrollRefEl?.offsetTop || 0);
|
|
||||||
setIsPaginating(true);
|
setIsPaginating(true);
|
||||||
await timeline()?.paginate("b", 10);
|
await timeline()?.paginate("b");
|
||||||
const newOffsetTop = scrollRefEl?.offsetTop || 0;
|
const oldOffsetTop = scrollRefEl?.offsetTop || 0;
|
||||||
refresh();
|
refresh();
|
||||||
|
const newOffsetTop = scrollRefEl?.offsetTop || 0;
|
||||||
|
console.log("scroll diff ", scrollRefEl, newOffsetTop, oldOffsetTop);
|
||||||
|
console.log("element moved by px", newOffsetTop - oldOffsetTop);
|
||||||
|
console.log("isAutoscrolling?", isAutoscrolling);
|
||||||
scrollEl.scrollBy(0, newOffsetTop - oldOffsetTop);
|
scrollEl.scrollBy(0, newOffsetTop - oldOffsetTop);
|
||||||
// scrollEl.scrollTo(0, oldOffsetTop);
|
|
||||||
setIsPaginating(false);
|
setIsPaginating(false);
|
||||||
if (timeline()?.isAtBeginning) setIsAtBeginning(true);
|
if (timeline()?.isAtBeginning) setIsAtBeginning(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleScroll = debounce(paginate, 100);
|
const handleScroll = () => paginate();
|
||||||
const events = () => timeline()?.getEvents().filter(ev => ev.type === "m.message");
|
const events = () => timeline()?.getEvents().filter(ev => ev.type === "m.message");
|
||||||
// const eventsLength = () => events()?.length || 0;
|
// const eventsLength = () => events()?.length || 0;
|
||||||
|
|
||||||
|
@ -119,26 +125,32 @@ export function ThreadView(props: VoidProps<{ thread: Thread }>) {
|
||||||
function ThreadInfo(props: VoidProps<{ thread: Thread, showHeader: boolean }>) {
|
function ThreadInfo(props: VoidProps<{ thread: Thread, showHeader: boolean }>) {
|
||||||
const event = () => props.thread.baseEvent;
|
const event = () => props.thread.baseEvent;
|
||||||
let headerEl: HTMLHeadingElement;
|
let headerEl: HTMLHeadingElement;
|
||||||
|
const [_globals, change] = useGlobals();
|
||||||
|
const [setup, setSetup] = createSignal(false);
|
||||||
const [stuck, setStuck] = createSignal(false);
|
const [stuck, setStuck] = createSignal(false);
|
||||||
|
|
||||||
// FIXME: this feels hacky, and has a flash of "unstuck" style + 1px offsets occasionally
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const observer = new IntersectionObserver(([e]) => setStuck(!e.isIntersecting), {
|
const observer = new IntersectionObserver(([e]) => {
|
||||||
|
setStuck(!e.isIntersecting);
|
||||||
|
setTimeout(() => setSetup(true), 10);
|
||||||
|
}, {
|
||||||
threshold: 1,
|
threshold: 1,
|
||||||
root: headerEl.parentElement,
|
root: headerEl.parentElement,
|
||||||
});
|
});
|
||||||
|
|
||||||
observer.observe(headerEl);
|
observer.observe(headerEl);
|
||||||
onCleanup(() => observer.unobserve(headerEl));
|
onCleanup(() => observer.unobserve(headerEl));
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{props.showHeader && <div class="spacer"></div>}
|
{props.showHeader && <div class="spacer">
|
||||||
<div class="thread-title" ref={headerEl!} classList={{ stuck: stuck() || !props.showHeader }}>
|
<button onClick={() => change({ type: "focusThread", thread: null })}>close thread</button>
|
||||||
|
</div>}
|
||||||
|
<div class="thread-title" ref={headerEl!} classList={{ stuck: stuck() || !props.showHeader, setup: setup() }}>
|
||||||
<h1 class="real">
|
<h1 class="real">
|
||||||
<TextBlock text={event().content.title} fallback="Untitled thread" />
|
<TextBlock text={event().content.title} fallback="Untitled thread" />
|
||||||
|
<button onClick={() => change({ type: "focusThread", thread: null })}>close thread</button>
|
||||||
</h1>
|
</h1>
|
||||||
<h1 class="fake" aria-hidden="true">
|
<h1 class="fake" aria-hidden="true">
|
||||||
<TextBlock text={event().content.title} fallback="Untitled thread" />
|
<TextBlock text={event().content.title} fallback="Untitled thread" />
|
||||||
|
|
144
src/_Editor.tsx
Normal file
144
src/_Editor.tsx
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
import { createEditor, EditorContent } from "tiptap-solid";
|
||||||
|
import Document from "@tiptap/extension-document";
|
||||||
|
import Paragraph from "@tiptap/extension-paragraph";
|
||||||
|
import Text from "@tiptap/extension-text";
|
||||||
|
import History from "@tiptap/extension-history";
|
||||||
|
// import Mention from "@tiptap/extension-mention";
|
||||||
|
import Placeholder from "@tiptap/extension-placeholder";
|
||||||
|
import "./Editor.scss";
|
||||||
|
// import { Accessor, createEffect, createSignal, VoidProps } from "solid-js";
|
||||||
|
import { Extension, Mark, markInputRule, wrappingInputRule } from "@tiptap/core";
|
||||||
|
import * as tiptap from "@tiptap/core";
|
||||||
|
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||||
|
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
||||||
|
import { Node } from "@tiptap/pm/model";
|
||||||
|
import { Marked, marked, Token } from "marked";
|
||||||
|
import { Dropcursor } from "@tiptap/extension-dropcursor";
|
||||||
|
import { Gapcursor } from "@tiptap/extension-gapcursor";
|
||||||
|
import { HardBreak } from "@tiptap/extension-hard-break";
|
||||||
|
|
||||||
|
// function Suggestions(props: VoidProps<{ suggestions: Accessor<Array<string>> }>) {
|
||||||
|
// createEffect(() => console.log(props.suggestions()));
|
||||||
|
// return (
|
||||||
|
// <ul>
|
||||||
|
// {props.suggestions().map(i => <li>{i}</li>)}
|
||||||
|
// </ul>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
const Markdown = Extension.create({
|
||||||
|
addKeyboardShortcuts() {
|
||||||
|
return {
|
||||||
|
"Mod-b": () => {
|
||||||
|
const { state } = this.editor.view;
|
||||||
|
const { selection } = state;
|
||||||
|
const { from, to } = selection;
|
||||||
|
this.editor.chain()
|
||||||
|
.insertContentAt({ from: to, to }, "**", { updateSelection: true })
|
||||||
|
.insertContentAt({ from, to: from }, "**", { updateSelection: true })
|
||||||
|
.setTextSelection({ from: from + 2, to: to + 2 })
|
||||||
|
.focus()
|
||||||
|
.run();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
"Shift-Enter": () => {
|
||||||
|
const { state } = this.editor.view;
|
||||||
|
const { selection } = state;
|
||||||
|
const { from, to } = selection;
|
||||||
|
const { paragraph } = state.schema.nodes;
|
||||||
|
console.log(paragraph)
|
||||||
|
// this.editor.chain().deleteSelection().unsetCode(from, paragraph.create())
|
||||||
|
this.editor.chain().setNode("paragraph").run();
|
||||||
|
console.log("s+enter");
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"Enter": () => {
|
||||||
|
console.log("enter");
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addProseMirrorPlugins() {
|
||||||
|
return [new Plugin({
|
||||||
|
key: new PluginKey("markdown"),
|
||||||
|
props: {
|
||||||
|
decorations(state) {
|
||||||
|
const decorations: Array<Decoration> = [];
|
||||||
|
console.log(state.doc, state.doc.textContent)
|
||||||
|
|
||||||
|
state.doc.descendants((node, nodePos) => {
|
||||||
|
if (!node.isText) return;
|
||||||
|
|
||||||
|
let pos = nodePos - 1;
|
||||||
|
const walk = (ast: Token) => {
|
||||||
|
switch (ast.type) {
|
||||||
|
case "em": {
|
||||||
|
const end = pos + ast.raw.length + 1;
|
||||||
|
decorations.push(Decoration.inline(pos, pos + 2, { class: "syn" }));
|
||||||
|
decorations.push(Decoration.inline(pos + 2, end - 1, { style: "font-style: italic" }));
|
||||||
|
decorations.push(Decoration.inline(end - 1, end, { class: "syn" }));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "strong": {
|
||||||
|
const end = pos + ast.raw.length + 1;
|
||||||
|
decorations.push(Decoration.inline(pos, pos + 3, { class: "syn" }));
|
||||||
|
decorations.push(Decoration.inline(pos + 3, end - 2, { style: "font-weight: bold" }));
|
||||||
|
decorations.push(Decoration.inline(end - 2, end, { class: "syn" }));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "codespan": {
|
||||||
|
const end = pos + ast.raw.length + 1;
|
||||||
|
decorations.push(Decoration.inline(pos, pos + 2, { class: "syn" }));
|
||||||
|
decorations.push(Decoration.inline(pos + 2, end - 1, { style: "font-style: italic" }));
|
||||||
|
decorations.push(Decoration.inline(end - 1, end, { class: "syn" }));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "code": {
|
||||||
|
// console.log(ast)
|
||||||
|
// const end = pos + ast.raw.length + 1;
|
||||||
|
// decorations.push(Decoration.inline(pos, pos + 2, { class: "syn" }));
|
||||||
|
// decorations.push(Decoration.inline(pos + 2, end - 1, { style: "font-style: italic" }));
|
||||||
|
// decorations.push(Decoration.inline(end - 1, end, { class: "syn" }));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "paragraph": ast.tokens?.forEach(walk); return;
|
||||||
|
// default: console.log(ast);
|
||||||
|
}
|
||||||
|
pos += ast.raw.length;
|
||||||
|
};
|
||||||
|
// console.log(node, marked.lexer(node.text!))
|
||||||
|
marked.lexer(node.text!).forEach(walk);
|
||||||
|
});
|
||||||
|
|
||||||
|
return DecorationSet.create(state.doc, decorations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export function Editor() {
|
||||||
|
let editorEl;
|
||||||
|
const editor = createEditor({
|
||||||
|
content: "hello world *asdf*",
|
||||||
|
autofocus: true,
|
||||||
|
extensions: [
|
||||||
|
Document,
|
||||||
|
Paragraph,
|
||||||
|
Text,
|
||||||
|
History,
|
||||||
|
Gapcursor,
|
||||||
|
Dropcursor,
|
||||||
|
HardBreak,
|
||||||
|
Placeholder.configure({
|
||||||
|
placeholder: "write something nice...",
|
||||||
|
}),
|
||||||
|
Markdown,
|
||||||
|
]
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div class="editor" ref={editorEl}>
|
||||||
|
<EditorContent editor={editor()} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in a new issue