Improve timeline scrolling
This commit is contained in:
parent
6e3ede18bf
commit
47d5c2fee2
12 changed files with 904 additions and 138 deletions
17
package.json
17
package.json
|
@ -10,9 +10,24 @@
|
|||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.5.3",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@solid-primitives/scheduled": "^1.4.1",
|
||||
"@tanstack/solid-virtual": "^3.0.1",
|
||||
"@tanstack/virtual-core": "^3.0.1",
|
||||
"@tiptap/core": "^2.1.13",
|
||||
"@tiptap/extension-document": "^2.1.13",
|
||||
"@tiptap/extension-history": "^2.1.13",
|
||||
"@tiptap/extension-mention": "^2.1.13",
|
||||
"@tiptap/extension-paragraph": "^2.1.13",
|
||||
"@tiptap/extension-placeholder": "^2.1.13",
|
||||
"@tiptap/extension-text": "^2.1.13",
|
||||
"@tiptap/pm": "^2.1.13",
|
||||
"@tiptap/suggestion": "^2.1.13",
|
||||
"i18next": "^23.7.8",
|
||||
"marked": "^11.0.1",
|
||||
"nanoid": "^5.0.4",
|
||||
"sdk": "link:../sdk-ts",
|
||||
"solid-js": "^1.8.7"
|
||||
"solid-js": "^1.8.7",
|
||||
"tiptap-solid": "^0.1.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"sass": "^1.69.5",
|
||||
|
|
524
pnpm-lock.yaml
524
pnpm-lock.yaml
|
@ -11,15 +11,60 @@ dependencies:
|
|||
'@popperjs/core':
|
||||
specifier: ^2.11.8
|
||||
version: 2.11.8
|
||||
'@solid-primitives/scheduled':
|
||||
specifier: ^1.4.1
|
||||
version: 1.4.1(solid-js@1.8.7)
|
||||
'@tanstack/solid-virtual':
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1(solid-js@1.8.7)
|
||||
'@tanstack/virtual-core':
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
'@tiptap/core':
|
||||
specifier: ^2.1.13
|
||||
version: 2.1.13(@tiptap/pm@2.1.13)
|
||||
'@tiptap/extension-document':
|
||||
specifier: ^2.1.13
|
||||
version: 2.1.13(@tiptap/core@2.1.13)
|
||||
'@tiptap/extension-history':
|
||||
specifier: ^2.1.13
|
||||
version: 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||
'@tiptap/extension-mention':
|
||||
specifier: ^2.1.13
|
||||
version: 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)(@tiptap/suggestion@2.1.13)
|
||||
'@tiptap/extension-paragraph':
|
||||
specifier: ^2.1.13
|
||||
version: 2.1.13(@tiptap/core@2.1.13)
|
||||
'@tiptap/extension-placeholder':
|
||||
specifier: ^2.1.13
|
||||
version: 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||
'@tiptap/extension-text':
|
||||
specifier: ^2.1.13
|
||||
version: 2.1.13(@tiptap/core@2.1.13)
|
||||
'@tiptap/pm':
|
||||
specifier: ^2.1.13
|
||||
version: 2.1.13
|
||||
'@tiptap/suggestion':
|
||||
specifier: ^2.1.13
|
||||
version: 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||
i18next:
|
||||
specifier: ^23.7.8
|
||||
version: 23.7.8
|
||||
marked:
|
||||
specifier: ^11.0.1
|
||||
version: 11.0.1
|
||||
nanoid:
|
||||
specifier: ^5.0.4
|
||||
version: 5.0.4
|
||||
sdk:
|
||||
specifier: link:../sdk-ts
|
||||
version: link:../sdk-ts
|
||||
solid-js:
|
||||
specifier: ^1.8.7
|
||||
version: 1.8.7
|
||||
tiptap-solid:
|
||||
specifier: ^0.1.8
|
||||
version: 0.1.8(@tiptap/core@2.1.13)(@tiptap/extension-bubble-menu@2.1.13)(@tiptap/extension-floating-menu@2.1.13)(solid-js@1.8.7)
|
||||
|
||||
devDependencies:
|
||||
sass:
|
||||
|
@ -621,6 +666,34 @@ packages:
|
|||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||
dev: false
|
||||
|
||||
/@remirror/core-constants@2.0.2:
|
||||
resolution: {integrity: sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==}
|
||||
dev: false
|
||||
|
||||
/@remirror/core-helpers@3.0.0:
|
||||
resolution: {integrity: sha512-tusEgQJIqg4qKj6HSBUFcyRnWnziw3neh4T9wOmsPGHFC3w9kl5KSrDb9UAgE8uX6y32FnS7vJ955mWOl3n50A==}
|
||||
dependencies:
|
||||
'@remirror/core-constants': 2.0.2
|
||||
'@remirror/types': 1.0.1
|
||||
'@types/object.omit': 3.0.3
|
||||
'@types/object.pick': 1.3.4
|
||||
'@types/throttle-debounce': 2.1.0
|
||||
case-anything: 2.1.13
|
||||
dash-get: 1.0.2
|
||||
deepmerge: 4.3.1
|
||||
fast-deep-equal: 3.1.3
|
||||
make-error: 1.3.6
|
||||
object.omit: 3.0.0
|
||||
object.pick: 1.3.0
|
||||
throttle-debounce: 3.0.1
|
||||
dev: false
|
||||
|
||||
/@remirror/types@1.0.1:
|
||||
resolution: {integrity: sha512-VlZQxwGnt1jtQ18D6JqdIF+uFZo525WEqrfp9BOc3COPpK4+AWCgdnAWL+ho6imWcoINlGjR/+3b6y5C1vBVEA==}
|
||||
dependencies:
|
||||
type-fest: 2.19.0
|
||||
dev: false
|
||||
|
||||
/@rollup/rollup-android-arm-eabi@4.7.0:
|
||||
resolution: {integrity: sha512-rGku10pL1StFlFvXX5pEv88KdGW6DHUghsxyP/aRYb9eH+74jTGJ3U0S/rtlsQ4yYq1Hcc7AMkoJOb1xu29Fxw==}
|
||||
cpu: [arm]
|
||||
|
@ -725,6 +798,150 @@ packages:
|
|||
dev: true
|
||||
optional: true
|
||||
|
||||
/@solid-primitives/scheduled@1.4.1(solid-js@1.8.7):
|
||||
resolution: {integrity: sha512-OLcNXwYpX7HUOEqNPcmR31dkyI1E2imkMDBRlqsGT0ZhJV1L2g0TEREpo4nm/kUhh8LVQzkfnxS+GONx9kh90A==}
|
||||
peerDependencies:
|
||||
solid-js: ^1.6.12
|
||||
dependencies:
|
||||
solid-js: 1.8.7
|
||||
dev: false
|
||||
|
||||
/@tanstack/solid-virtual@3.0.1(solid-js@1.8.7):
|
||||
resolution: {integrity: sha512-DxP3GUBEDUNdCH50Q2RgRkaol3bAGpkMcJAdUIPWywEL37TkH/MC748nees0EXRylrC7RMP0zVNN3Z94WFBULA==}
|
||||
peerDependencies:
|
||||
solid-js: ^1.3.0
|
||||
dependencies:
|
||||
'@tanstack/virtual-core': 3.0.0
|
||||
solid-js: 1.8.7
|
||||
dev: false
|
||||
|
||||
/@tanstack/virtual-core@3.0.0:
|
||||
resolution: {integrity: sha512-SYXOBTjJb05rXa2vl55TTwO40A6wKu0R5i1qQwhJYNDIqaIGF7D0HsLw+pJAyi2OvntlEIVusx3xtbbgSUi6zg==}
|
||||
dev: false
|
||||
|
||||
/@tanstack/virtual-core@3.0.1:
|
||||
resolution: {integrity: sha512-By6TTR3u6rmAWRD7STXqI8WP9q1jYrqVCz88lNTgOf/cUm5cNF6Uj7dej/1+LUj42KMwFusyxGS908HlGBhE2Q==}
|
||||
dev: false
|
||||
|
||||
/@tiptap/core@2.1.13(@tiptap/pm@2.1.13):
|
||||
resolution: {integrity: sha512-cMC8bgTN63dj1Mv82iDeeLl6sa9kY0Pug8LSalxVEptRmyFVsVxGgu2/6Y3T+9aCYScxfS06EkA8SdzFMAwYTQ==}
|
||||
peerDependencies:
|
||||
'@tiptap/pm': ^2.0.0
|
||||
dependencies:
|
||||
'@tiptap/pm': 2.1.13
|
||||
dev: false
|
||||
|
||||
/@tiptap/extension-bubble-menu@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
||||
resolution: {integrity: sha512-Hm7e1GX3AI6lfaUmr6WqsS9MMyXIzCkhh+VQi6K8jj4Q4s8kY4KPoAyD/c3v9pZ/dieUtm2TfqrOCkbHzsJQBg==}
|
||||
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
|
||||
tippy.js: 6.3.7
|
||||
dev: false
|
||||
|
||||
/@tiptap/extension-document@2.1.13(@tiptap/core@2.1.13):
|
||||
resolution: {integrity: sha512-wLwiTWsVmZTGIE5duTcHRmW4ulVxNW4nmgfpk95+mPn1iKyNGtrVhGWleLhBlTj+DWXDtcfNWZgqZkZNzhkqYQ==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.0.0
|
||||
dependencies:
|
||||
'@tiptap/core': 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):
|
||||
resolution: {integrity: sha512-9Oz7pk1Nts2+EyY+rYfnREGbLzQ5UFazAvRhF6zAJdvyuDmAYm0Jp6s0GoTrpV0/dJEISoFaNpPdMJOb9EBNRw==}
|
||||
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
|
||||
tippy.js: 6.3.7
|
||||
dev: false
|
||||
|
||||
/@tiptap/extension-history@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
||||
resolution: {integrity: sha512-1ouitThGTBUObqw250aDwGLMNESBH5PRXIGybsCFO1bktdmWtEw7m72WY41EuX2BH8iKJpcYPerl3HfY1vmCNw==}
|
||||
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-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==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.0.0
|
||||
'@tiptap/pm': ^2.0.0
|
||||
'@tiptap/suggestion': ^2.0.0
|
||||
dependencies:
|
||||
'@tiptap/core': 2.1.13(@tiptap/pm@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
|
||||
|
||||
/@tiptap/extension-paragraph@2.1.13(@tiptap/core@2.1.13):
|
||||
resolution: {integrity: sha512-cEoZBJrsQn69FPpUMePXG/ltGXtqKISgypj70PEHXt5meKDjpmMVSY4/8cXvFYEYsI9GvIwyAK0OrfAHiSoROA==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.0.0
|
||||
dependencies:
|
||||
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||
dev: false
|
||||
|
||||
/@tiptap/extension-placeholder@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
||||
resolution: {integrity: sha512-vIY7y7UbqsrAW/y8bDE9eRenbQEU16kNHB5Wri8RU1YiUZpkPgdXP/pLqyjIIq95SwP/vdTIHjHoQ77VLRl1hA==}
|
||||
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-text@2.1.13(@tiptap/core@2.1.13):
|
||||
resolution: {integrity: sha512-zzsTTvu5U67a8WjImi6DrmpX2Q/onLSaj+LRWPh36A1Pz2WaxW5asZgaS+xWCnR+UrozlCALWa01r7uv69jq0w==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.0.0
|
||||
dependencies:
|
||||
'@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
|
||||
dev: false
|
||||
|
||||
/@tiptap/pm@2.1.13:
|
||||
resolution: {integrity: sha512-zNbA7muWsHuVg12GrTgN/j119rLePPq5M8dZgkKxUwdw8VmU3eUyBp1SihPEXJ2U0MGdZhNhFX7Y74g11u66sg==}
|
||||
dependencies:
|
||||
prosemirror-changeset: 2.2.1
|
||||
prosemirror-collab: 1.3.1
|
||||
prosemirror-commands: 1.5.2
|
||||
prosemirror-dropcursor: 1.8.1
|
||||
prosemirror-gapcursor: 1.3.2
|
||||
prosemirror-history: 1.3.2
|
||||
prosemirror-inputrules: 1.3.0
|
||||
prosemirror-keymap: 1.2.2
|
||||
prosemirror-markdown: 1.11.2
|
||||
prosemirror-menu: 1.2.4
|
||||
prosemirror-model: 1.19.3
|
||||
prosemirror-schema-basic: 1.2.2
|
||||
prosemirror-schema-list: 1.3.0
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-tables: 1.3.5
|
||||
prosemirror-trailing-node: 2.0.7(prosemirror-model@1.19.3)(prosemirror-state@1.4.3)(prosemirror-view@1.32.6)
|
||||
prosemirror-transform: 1.8.0
|
||||
prosemirror-view: 1.32.6
|
||||
dev: false
|
||||
|
||||
/@tiptap/suggestion@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13):
|
||||
resolution: {integrity: sha512-Y05TsiXTFAJ5SrfoV+21MAxig5UNbY0AVa03lQlh/yicTRPpIc6hgZzblB0uxDSYoj6+kaHE4MIZvPvhUD8BJQ==}
|
||||
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
|
||||
|
||||
/@types/babel__core@7.20.5:
|
||||
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
||||
dependencies:
|
||||
|
@ -754,6 +971,18 @@ packages:
|
|||
'@babel/types': 7.23.5
|
||||
dev: true
|
||||
|
||||
/@types/object.omit@3.0.3:
|
||||
resolution: {integrity: sha512-xrq4bQTBGYY2cw+gV4PzoG2Lv3L0pjZ1uXStRRDQoATOYW1lCsFQHhQ+OkPhIcQoqLjAq7gYif7D14Qaa6Zbew==}
|
||||
dev: false
|
||||
|
||||
/@types/object.pick@1.3.4:
|
||||
resolution: {integrity: sha512-5PjwB0uP2XDp3nt5u5NJAG2DORHIRClPzWT/TTZhJ2Ekwe8M5bA9tvPdi9NO/n2uvu2/ictat8kgqvLfcIE1SA==}
|
||||
dev: false
|
||||
|
||||
/@types/throttle-debounce@2.1.0:
|
||||
resolution: {integrity: sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==}
|
||||
dev: false
|
||||
|
||||
/ansi-styles@3.2.1:
|
||||
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
|
||||
engines: {node: '>=4'}
|
||||
|
@ -769,6 +998,10 @@ packages:
|
|||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
dev: false
|
||||
|
||||
/babel-plugin-jsx-dom-expressions@0.37.9(@babel/core@7.23.5):
|
||||
resolution: {integrity: sha512-6w+zs2i14fVanj4e1hXCU5cp+x0U0LJ5jScknpMZZUteHhwFRGJflHMVJ+xAcW7ku41FYjr7DgtK9mnc2SXlJg==}
|
||||
peerDependencies:
|
||||
|
@ -818,6 +1051,11 @@ packages:
|
|||
resolution: {integrity: sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==}
|
||||
dev: true
|
||||
|
||||
/case-anything@2.1.13:
|
||||
resolution: {integrity: sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==}
|
||||
engines: {node: '>=12.13'}
|
||||
dev: false
|
||||
|
||||
/chalk@2.4.2:
|
||||
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
|
||||
engines: {node: '>=4'}
|
||||
|
@ -856,9 +1094,17 @@ packages:
|
|||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||
dev: true
|
||||
|
||||
/crelt@1.0.6:
|
||||
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
|
||||
dev: false
|
||||
|
||||
/csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
/dash-get@1.0.2:
|
||||
resolution: {integrity: sha512-4FbVrHDwfOASx7uQVxeiCTo7ggSdYZbqs8lH+WU6ViypPlDbe9y6IP5VVUDQBv9DcnyaiPT5XT0UWHgJ64zLeQ==}
|
||||
dev: false
|
||||
|
||||
/debug@4.3.4:
|
||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
|
@ -871,10 +1117,20 @@ packages:
|
|||
ms: 2.1.2
|
||||
dev: true
|
||||
|
||||
/deepmerge@4.3.1:
|
||||
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/electron-to-chromium@1.4.609:
|
||||
resolution: {integrity: sha512-ihiCP7PJmjoGNuLpl7TjNA8pCQWu09vGyjlPYw1Rqww4gvNuCcmvl+44G+2QyJ6S2K4o+wbTS++Xz0YN8Q9ERw==}
|
||||
dev: true
|
||||
|
||||
/entities@3.0.1:
|
||||
resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==}
|
||||
engines: {node: '>=0.12'}
|
||||
dev: false
|
||||
|
||||
/esbuild@0.19.8:
|
||||
resolution: {integrity: sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -915,6 +1171,15 @@ packages:
|
|||
engines: {node: '>=0.8.0'}
|
||||
dev: true
|
||||
|
||||
/escape-string-regexp@4.0.0:
|
||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/fast-deep-equal@3.1.3:
|
||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||
dev: false
|
||||
|
||||
/fill-range@7.0.1:
|
||||
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -973,6 +1238,13 @@ packages:
|
|||
binary-extensions: 2.2.0
|
||||
dev: true
|
||||
|
||||
/is-extendable@1.0.1:
|
||||
resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
is-plain-object: 2.0.4
|
||||
dev: false
|
||||
|
||||
/is-extglob@2.1.1:
|
||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -990,11 +1262,23 @@ packages:
|
|||
engines: {node: '>=0.12.0'}
|
||||
dev: true
|
||||
|
||||
/is-plain-object@2.0.4:
|
||||
resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
isobject: 3.0.1
|
||||
dev: false
|
||||
|
||||
/is-what@4.1.16:
|
||||
resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
|
||||
engines: {node: '>=12.13'}
|
||||
dev: true
|
||||
|
||||
/isobject@3.0.1:
|
||||
resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
dev: true
|
||||
|
@ -1011,12 +1295,43 @@ packages:
|
|||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/linkify-it@4.0.1:
|
||||
resolution: {integrity: sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==}
|
||||
dependencies:
|
||||
uc.micro: 1.0.6
|
||||
dev: false
|
||||
|
||||
/lru-cache@5.1.1:
|
||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||
dependencies:
|
||||
yallist: 3.1.1
|
||||
dev: true
|
||||
|
||||
/make-error@1.3.6:
|
||||
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
|
||||
dev: false
|
||||
|
||||
/markdown-it@13.0.2:
|
||||
resolution: {integrity: sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
entities: 3.0.1
|
||||
linkify-it: 4.0.1
|
||||
mdurl: 1.0.1
|
||||
uc.micro: 1.0.6
|
||||
dev: false
|
||||
|
||||
/marked@11.0.1:
|
||||
resolution: {integrity: sha512-P4kDhFEMlvLePBPRwOcMOv6+lYUbhfbSxJFs3Jb4Qx7v6K7l+k8Dxh9CEGfRvK71tL+qIFz5y7Pe4uzt4+/A3A==}
|
||||
engines: {node: '>= 18'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/mdurl@1.0.1:
|
||||
resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==}
|
||||
dev: false
|
||||
|
||||
/merge-anything@5.1.7:
|
||||
resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==}
|
||||
engines: {node: '>=12.13'}
|
||||
|
@ -1034,6 +1349,12 @@ packages:
|
|||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/nanoid@5.0.4:
|
||||
resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==}
|
||||
engines: {node: ^18 || >=20}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/node-releases@2.0.14:
|
||||
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
|
||||
dev: true
|
||||
|
@ -1043,6 +1364,24 @@ packages:
|
|||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/object.omit@3.0.0:
|
||||
resolution: {integrity: sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
is-extendable: 1.0.1
|
||||
dev: false
|
||||
|
||||
/object.pick@1.3.0:
|
||||
resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
isobject: 3.0.1
|
||||
dev: false
|
||||
|
||||
/orderedmap@2.1.1:
|
||||
resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
|
||||
dev: false
|
||||
|
||||
/picocolors@1.0.0:
|
||||
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
||||
dev: true
|
||||
|
@ -1061,6 +1400,149 @@ packages:
|
|||
source-map-js: 1.0.2
|
||||
dev: true
|
||||
|
||||
/prosemirror-changeset@2.2.1:
|
||||
resolution: {integrity: sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==}
|
||||
dependencies:
|
||||
prosemirror-transform: 1.8.0
|
||||
dev: false
|
||||
|
||||
/prosemirror-collab@1.3.1:
|
||||
resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==}
|
||||
dependencies:
|
||||
prosemirror-state: 1.4.3
|
||||
dev: false
|
||||
|
||||
/prosemirror-commands@1.5.2:
|
||||
resolution: {integrity: sha512-hgLcPaakxH8tu6YvVAaILV2tXYsW3rAdDR8WNkeKGcgeMVQg3/TMhPdVoh7iAmfgVjZGtcOSjKiQaoeKjzd2mQ==}
|
||||
dependencies:
|
||||
prosemirror-model: 1.19.3
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.8.0
|
||||
dev: false
|
||||
|
||||
/prosemirror-dropcursor@1.8.1:
|
||||
resolution: {integrity: sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==}
|
||||
dependencies:
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.8.0
|
||||
prosemirror-view: 1.32.6
|
||||
dev: false
|
||||
|
||||
/prosemirror-gapcursor@1.3.2:
|
||||
resolution: {integrity: sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==}
|
||||
dependencies:
|
||||
prosemirror-keymap: 1.2.2
|
||||
prosemirror-model: 1.19.3
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-view: 1.32.6
|
||||
dev: false
|
||||
|
||||
/prosemirror-history@1.3.2:
|
||||
resolution: {integrity: sha512-/zm0XoU/N/+u7i5zepjmZAEnpvjDtzoPWW6VmKptcAnPadN/SStsBjMImdCEbb3seiNTpveziPTIrXQbHLtU1g==}
|
||||
dependencies:
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.8.0
|
||||
prosemirror-view: 1.32.6
|
||||
rope-sequence: 1.3.4
|
||||
dev: false
|
||||
|
||||
/prosemirror-inputrules@1.3.0:
|
||||
resolution: {integrity: sha512-z1GRP2vhh5CihYMQYsJSa1cOwXb3SYxALXOIfAkX8nZserARtl9LiL+CEl+T+OFIsXc3mJIHKhbsmRzC0HDAXA==}
|
||||
dependencies:
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.8.0
|
||||
dev: false
|
||||
|
||||
/prosemirror-keymap@1.2.2:
|
||||
resolution: {integrity: sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==}
|
||||
dependencies:
|
||||
prosemirror-state: 1.4.3
|
||||
w3c-keyname: 2.2.8
|
||||
dev: false
|
||||
|
||||
/prosemirror-markdown@1.11.2:
|
||||
resolution: {integrity: sha512-Eu5g4WPiCdqDTGhdSsG9N6ZjACQRYrsAkrF9KYfdMaCmjIApH75aVncsWYOJvEk2i1B3i8jZppv3J/tnuHGiUQ==}
|
||||
dependencies:
|
||||
markdown-it: 13.0.2
|
||||
prosemirror-model: 1.19.3
|
||||
dev: false
|
||||
|
||||
/prosemirror-menu@1.2.4:
|
||||
resolution: {integrity: sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==}
|
||||
dependencies:
|
||||
crelt: 1.0.6
|
||||
prosemirror-commands: 1.5.2
|
||||
prosemirror-history: 1.3.2
|
||||
prosemirror-state: 1.4.3
|
||||
dev: false
|
||||
|
||||
/prosemirror-model@1.19.3:
|
||||
resolution: {integrity: sha512-tgSnwN7BS7/UM0sSARcW+IQryx2vODKX4MI7xpqY2X+iaepJdKBPc7I4aACIsDV/LTaTjt12Z56MhDr9LsyuZQ==}
|
||||
dependencies:
|
||||
orderedmap: 2.1.1
|
||||
dev: false
|
||||
|
||||
/prosemirror-schema-basic@1.2.2:
|
||||
resolution: {integrity: sha512-/dT4JFEGyO7QnNTe9UaKUhjDXbTNkiWTq/N4VpKaF79bBjSExVV2NXmJpcM7z/gD7mbqNjxbmWW5nf1iNSSGnw==}
|
||||
dependencies:
|
||||
prosemirror-model: 1.19.3
|
||||
dev: false
|
||||
|
||||
/prosemirror-schema-list@1.3.0:
|
||||
resolution: {integrity: sha512-Hz/7gM4skaaYfRPNgr421CU4GSwotmEwBVvJh5ltGiffUJwm7C8GfN/Bc6DR1EKEp5pDKhODmdXXyi9uIsZl5A==}
|
||||
dependencies:
|
||||
prosemirror-model: 1.19.3
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.8.0
|
||||
dev: false
|
||||
|
||||
/prosemirror-state@1.4.3:
|
||||
resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==}
|
||||
dependencies:
|
||||
prosemirror-model: 1.19.3
|
||||
prosemirror-transform: 1.8.0
|
||||
prosemirror-view: 1.32.6
|
||||
dev: false
|
||||
|
||||
/prosemirror-tables@1.3.5:
|
||||
resolution: {integrity: sha512-JSZ2cCNlApu/ObAhdPyotrjBe2cimniniTpz60YXzbL0kZ+47nEYk2LWbfKU2lKpBkUNquta2PjteoNi4YCluQ==}
|
||||
dependencies:
|
||||
prosemirror-keymap: 1.2.2
|
||||
prosemirror-model: 1.19.3
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.8.0
|
||||
prosemirror-view: 1.32.6
|
||||
dev: false
|
||||
|
||||
/prosemirror-trailing-node@2.0.7(prosemirror-model@1.19.3)(prosemirror-state@1.4.3)(prosemirror-view@1.32.6):
|
||||
resolution: {integrity: sha512-8zcZORYj/8WEwsGo6yVCRXFMOfBo0Ub3hCUvmoWIZYfMP26WqENU0mpEP27w7mt8buZWuGrydBewr0tOArPb1Q==}
|
||||
peerDependencies:
|
||||
prosemirror-model: ^1.19.0
|
||||
prosemirror-state: ^1.4.2
|
||||
prosemirror-view: ^1.31.2
|
||||
dependencies:
|
||||
'@remirror/core-constants': 2.0.2
|
||||
'@remirror/core-helpers': 3.0.0
|
||||
escape-string-regexp: 4.0.0
|
||||
prosemirror-model: 1.19.3
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-view: 1.32.6
|
||||
dev: false
|
||||
|
||||
/prosemirror-transform@1.8.0:
|
||||
resolution: {integrity: sha512-BaSBsIMv52F1BVVMvOmp1yzD3u65uC3HTzCBQV1WDPqJRQ2LuHKcyfn0jwqodo8sR9vVzMzZyI+Dal5W9E6a9A==}
|
||||
dependencies:
|
||||
prosemirror-model: 1.19.3
|
||||
dev: false
|
||||
|
||||
/prosemirror-view@1.32.6:
|
||||
resolution: {integrity: sha512-26r5LvyDlPgUNVf7ZdNdGrMJnylwjJtUJTfDuYOANIVx9lqWD1WCBlGg283weYQGKUC64DXR25LeAmliB9CrFQ==}
|
||||
dependencies:
|
||||
prosemirror-model: 1.19.3
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.8.0
|
||||
dev: false
|
||||
|
||||
/readdirp@3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
|
@ -1093,6 +1575,10 @@ packages:
|
|||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/rope-sequence@1.3.4:
|
||||
resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
|
||||
dev: false
|
||||
|
||||
/sass@1.69.5:
|
||||
resolution: {integrity: sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
@ -1141,6 +1627,31 @@ packages:
|
|||
has-flag: 3.0.0
|
||||
dev: true
|
||||
|
||||
/throttle-debounce@3.0.1:
|
||||
resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/tippy.js@6.3.7:
|
||||
resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==}
|
||||
dependencies:
|
||||
'@popperjs/core': 2.11.8
|
||||
dev: false
|
||||
|
||||
/tiptap-solid@0.1.8(@tiptap/core@2.1.13)(@tiptap/extension-bubble-menu@2.1.13)(@tiptap/extension-floating-menu@2.1.13)(solid-js@1.8.7):
|
||||
resolution: {integrity: sha512-gUFkp9CxNVqrUkXkra3PN2NQhJoQ+wch2aJSUHE0vDW/1FloPB8Rz+P4K7RUxS0D++R3dhn0cY2dpzcZN+VXTw==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.0.0-beta.125
|
||||
'@tiptap/extension-bubble-menu': ^2.0.0-beta.42
|
||||
'@tiptap/extension-floating-menu': ^2.0.0-beta.36
|
||||
solid-js: ^1.1.6
|
||||
dependencies:
|
||||
'@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)
|
||||
'@tiptap/extension-floating-menu': 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)
|
||||
solid-js: 1.8.7
|
||||
dev: false
|
||||
|
||||
/to-fast-properties@2.0.0:
|
||||
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
|
||||
engines: {node: '>=4'}
|
||||
|
@ -1153,12 +1664,21 @@ packages:
|
|||
is-number: 7.0.0
|
||||
dev: true
|
||||
|
||||
/type-fest@2.19.0:
|
||||
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
|
||||
engines: {node: '>=12.20'}
|
||||
dev: false
|
||||
|
||||
/typescript@5.3.3:
|
||||
resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/uc.micro@1.0.6:
|
||||
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
|
||||
dev: false
|
||||
|
||||
/update-browserslist-db@1.0.13(browserslist@4.22.2):
|
||||
resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
|
||||
hasBin: true
|
||||
|
@ -1240,6 +1760,10 @@ packages:
|
|||
vite: 5.0.7(sass@1.69.5)
|
||||
dev: true
|
||||
|
||||
/w3c-keyname@2.2.8:
|
||||
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
|
||||
dev: false
|
||||
|
||||
/yallist@3.1.1:
|
||||
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
||||
dev: true
|
||||
|
|
|
@ -195,7 +195,7 @@
|
|||
}
|
||||
|
||||
& > .preview {
|
||||
padding-bottom: 2rem;
|
||||
// padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
& > footer {
|
||||
|
@ -316,6 +316,7 @@
|
|||
align-items: center;
|
||||
padding: 0 8px;
|
||||
gap: 8px;
|
||||
border-top: solid var(--background-4) 1px;
|
||||
|
||||
& > textarea {
|
||||
background: var(--background-1);
|
||||
|
@ -338,6 +339,7 @@
|
|||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
scrollbar-color: var(--background-1) var(--background-3);
|
||||
overflow-anchor: none;
|
||||
|
||||
& > .spacer {
|
||||
margin-top: auto;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { Match, Show, Switch, VoidProps, createSignal, onCleanup } from "solid-js";
|
||||
import "./App.scss";
|
||||
import { Client, Room } from "sdk";
|
||||
import { Home, RoomView, ThreadView } from "./Main";
|
||||
import { Home, RoomView } from "./Main";
|
||||
import { ThreadView } from "./Thread";
|
||||
import { Contextualizer, useGlobals } from "./Context";
|
||||
|
||||
function Wrapper() {
|
||||
|
@ -63,7 +64,7 @@ function App(props: VoidProps<{ client: Client }>) {
|
|||
<ThreadView thread={thread()!} />
|
||||
</Match>
|
||||
<Match when={!thread()}>
|
||||
empty sidebar
|
||||
<div style="padding: 4px">empty sidebar</div>
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
|
|
|
@ -21,3 +21,29 @@ export function Time(props: VoidProps<{} & ({ ts: number } | { date: Date })>) {
|
|||
|
||||
return <time datetime={date().toISOString()}>{format(date())}</time>;
|
||||
}
|
||||
|
||||
// export function Tooltip(props: ParentProps<{ tip: string }>) {
|
||||
// let tipEl: HTMLDivElement;
|
||||
// let contentEl: HTMLDivElement;
|
||||
|
||||
// onMount(() => queueMicrotask(async () => {
|
||||
// const cleanup = autoUpdate(contentEl, tipEl, () => {
|
||||
// computePosition(contentEl, tipEl, {
|
||||
// middleware: [shift(), offset()],
|
||||
// }).then(pos => {
|
||||
// tipEl.style.translate = `${pos.x}px ${pos.y}px`;
|
||||
// });
|
||||
// });
|
||||
|
||||
// onCleanup(() => cleanup());
|
||||
// }));
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// <span ref={contentEl!}>{props.children}</span>
|
||||
// <Portal>
|
||||
// <div ref={tipEl!} style="position:fixed;top:0;left:0">{props.tip}</div>
|
||||
// </Portal>
|
||||
// </>
|
||||
// )
|
||||
// }
|
||||
|
|
|
@ -70,6 +70,9 @@ export function Contextualizer(props: ParentProps) {
|
|||
locale,
|
||||
};
|
||||
|
||||
// FIXME: swap out per room state on focusRoom
|
||||
// const perRoom = new Map();
|
||||
|
||||
function redux(change: Change) {
|
||||
switch (change.type) {
|
||||
case "focusRoom":
|
||||
|
|
34
src/Editor.scss
Normal file
34
src/Editor.scss
Normal file
|
@ -0,0 +1,34 @@
|
|||
.editor {
|
||||
border: solid var(--background-3) 1px;
|
||||
background: var(--background-1);
|
||||
width: 100%;
|
||||
|
||||
& .tiptap:focus {
|
||||
outline: solid #4af 1px;
|
||||
}
|
||||
|
||||
& span[data-mention] {
|
||||
background: #9ad5;
|
||||
padding: 0 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
& p.is-editor-empty:first-child::before {
|
||||
color: var(--foreground-1);
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
height: 0;
|
||||
opacity: .3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
& .syn {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
& code {
|
||||
font-family: "Iosevka Zesty", 'Courier New', Courier, monospace;
|
||||
background: var(--background-3);
|
||||
padding: 0 2px;
|
||||
}
|
||||
}
|
114
src/Editor.tsx
Normal file
114
src/Editor.tsx
Normal file
|
@ -0,0 +1,114 @@
|
|||
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() {
|
||||
let editorEl;
|
||||
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>
|
||||
);
|
||||
}
|
126
src/Main.tsx
126
src/Main.tsx
|
@ -11,7 +11,7 @@ export function RoomView(props: VoidProps<{ room: Room }>) {
|
|||
<>
|
||||
<Threads room={props.room} />
|
||||
<div class="actions">
|
||||
<TimelineActions room={props.room} />
|
||||
<RoomActions room={props.room} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
@ -25,7 +25,7 @@ function Threads(props: VoidProps<{ room: Room }>) {
|
|||
return (
|
||||
<div class="timeline threads">
|
||||
<div class="items">
|
||||
<ThreadsHeader room={props.room} />
|
||||
<RoomHeader room={props.room} />
|
||||
<Show when={!threadChunk.loading} fallback={"loading..."}>
|
||||
{threadChunk()!.threads.map((thread: Thread) => <ThreadsItem thread={thread} />)}
|
||||
</Show>
|
||||
|
@ -34,116 +34,10 @@ function Threads(props: VoidProps<{ room: Room }>) {
|
|||
);
|
||||
}
|
||||
|
||||
export function Tooltip(props: ParentProps<{ tip: string }>) {
|
||||
let tipEl: HTMLDivElement;
|
||||
let contentEl: HTMLDivElement;
|
||||
|
||||
onMount(() => queueMicrotask(async () => {
|
||||
const cleanup = autoUpdate(contentEl, tipEl, () => {
|
||||
computePosition(contentEl, tipEl, {
|
||||
middleware: [shift(), offset()],
|
||||
}).then(pos => {
|
||||
tipEl.style.translate = `${pos.x}px ${pos.y}px`;
|
||||
});
|
||||
});
|
||||
|
||||
onCleanup(() => cleanup());
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
<span ref={contentEl!}>{props.children}</span>
|
||||
<Portal>
|
||||
<div ref={tipEl!} style="position:fixed;top:0;left:0">{props.tip}</div>
|
||||
</Portal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function ThreadView(props: VoidProps<{ thread: Thread }>) {
|
||||
let textareaEl: HTMLTextAreaElement;
|
||||
|
||||
console.log(props.thread)
|
||||
|
||||
function handleKeyDown(e: KeyboardEvent) {
|
||||
if (e.key === "Enter" && !e.shiftKey) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
props.thread.room.sendEvent("m.message", {
|
||||
text: [{ body: textareaEl.value }],
|
||||
"m.relations": [{ rel_type: "m.thread", event_id: props.thread.id }],
|
||||
});
|
||||
textareaEl.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
const [timeline, { mutate }] = createResource(() => props.thread.timeline, (tl) => tl, {
|
||||
storage: (init) => createSignal(init, { equals: false }),
|
||||
});
|
||||
|
||||
const refresh = () => mutate(props.thread.timeline);
|
||||
|
||||
let oldThread: Thread | undefined;
|
||||
createEffect(() => {
|
||||
oldThread?.room.off("timeline", refresh);
|
||||
props.thread.room.on("timeline", refresh);
|
||||
oldThread = props.thread;
|
||||
});
|
||||
onCleanup(() => oldThread?.room.off("timeline", refresh));
|
||||
|
||||
return (
|
||||
<div class="thread-view">
|
||||
<div class="scroll">
|
||||
<ThreadHeader thread={props.thread} />
|
||||
{timeline() && <EventTimeline timeline={timeline()!} />}
|
||||
<div class="spacer-bottom"></div>
|
||||
</div>
|
||||
<div class="input">
|
||||
<textarea placeholder="input text here" ref={textareaEl!} onKeyDown={handleKeyDown}></textarea>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ThreadHeader(props: VoidProps<{ thread: Thread }>) {
|
||||
const event = () => props.thread.baseEvent;
|
||||
let headerEl: HTMLHeadingElement;
|
||||
const [stuck, setStuck] = createSignal(false);
|
||||
|
||||
onMount(() => {
|
||||
const observer = new IntersectionObserver(([e]) => setStuck(!e.isIntersecting), {
|
||||
threshold: 1,
|
||||
root: headerEl.parentElement,
|
||||
});
|
||||
|
||||
observer.observe(headerEl);
|
||||
onCleanup(() => observer.unobserve(headerEl));
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="spacer"></div>
|
||||
<div class="thread-title" ref={headerEl!} classList={{ stuck: stuck() }}>
|
||||
<h1 class="real">
|
||||
<TextBlock text={event().content.title} fallback="Untitled thread" />
|
||||
</h1>
|
||||
<h1 class="fake" aria-hidden="true">
|
||||
<TextBlock text={event().content.title} fallback="Untitled thread" />
|
||||
</h1>
|
||||
</div>
|
||||
<header>
|
||||
<p>Created by {event().sender} <Time ts={event().originTs} /></p>
|
||||
<p style="color:var(--foreground-2)">Debug: event_id=<code style="user-select:all">{event().id}</code></p>
|
||||
</header>
|
||||
<Message event={event()} title={true} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function Home(props: VoidProps<{ client: Client }>) {
|
||||
function handleSubmit(e) {
|
||||
function handleSubmit(e: SubmitEvent) {
|
||||
e.preventDefault();
|
||||
const { name } = e.target.elements;
|
||||
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: ""}] });
|
||||
name.value = "";
|
||||
}
|
||||
|
@ -159,15 +53,7 @@ export function Home(props: VoidProps<{ client: Client }>) {
|
|||
);
|
||||
}
|
||||
|
||||
export function EventTimeline(props: VoidProps<{ timeline: Timeline }>) {
|
||||
return (
|
||||
<>
|
||||
{props.timeline.events.filter(ev => ev.type === "m.message").map((ev, idx) => <Message event={ev} title={idx === 0} />)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ThreadsHeader(props: VoidProps<{ room: Room }>) {
|
||||
function RoomHeader(props: VoidProps<{ room: Room }>) {
|
||||
const name = () => props.room.getState("m.room.name")?.content.name || "unnamed room";
|
||||
const topic = () => props.room.getState("m.room.topic")?.content.topic || "";
|
||||
return (
|
||||
|
@ -183,7 +69,7 @@ function ThreadsHeader(props: VoidProps<{ room: Room }>) {
|
|||
);
|
||||
}
|
||||
|
||||
function TimelineActions(props: VoidProps<{ room: Room }>) {
|
||||
function RoomActions(props: VoidProps<{ room: Room }>) {
|
||||
async function makeThread() {
|
||||
const title = prompt("title");
|
||||
const body = prompt("body");
|
||||
|
|
28
src/Room.tsx
28
src/Room.tsx
|
@ -1,23 +1,27 @@
|
|||
import { Thread } from "sdk";
|
||||
import { Show, VoidProps, createResource, createSignal, onCleanup } from "solid-js";
|
||||
import { Show, VoidProps, createEffect, createResource, createSignal, onCleanup } from "solid-js";
|
||||
import { useGlobals } from "./Context";
|
||||
import { Text, Time } from "./Atoms";
|
||||
|
||||
export function ThreadsItem(props: VoidProps<{ thread: Thread }>) {
|
||||
const [globals, change] = useGlobals();
|
||||
const [preview, { mutate }] = createResource(props.thread, async (t) => {
|
||||
await t.timeline.paginate("f", 1000);
|
||||
return t.timeline.events;
|
||||
|
||||
const [preview, { mutate }] = createResource(props.thread, async (thread) => {
|
||||
return thread.room.timelines.forThread(thread, "start");
|
||||
}, {
|
||||
storage: (init) => createSignal(init, { equals: false }),
|
||||
});
|
||||
|
||||
const refresh = () => mutate(props.thread.timeline.events);
|
||||
const refresh = () => {
|
||||
mutate(preview());
|
||||
setThread(thread());
|
||||
};
|
||||
|
||||
props.thread.room.on("timeline", refresh);
|
||||
onCleanup(() => props.thread.room.off("timeline", refresh));
|
||||
|
||||
const info = () => props.thread.baseEvent.unsigned["m.relations"]["m.thread"];
|
||||
const remaining = () => preview() && (info().count - Math.min(preview()!.length, 10));
|
||||
const [thread, setThread] = createSignal(props.thread, { equals: false });
|
||||
const remaining = () => preview() && (thread().messageCount - Math.min(preview()!.getEvents().length, 10));
|
||||
|
||||
const willOpenThread = (target: "default" | "latest") => (e: MouseEvent) => {
|
||||
// TODO: target
|
||||
|
@ -26,7 +30,7 @@ export function ThreadsItem(props: VoidProps<{ thread: Thread }>) {
|
|||
type: "focusThread",
|
||||
thread: isSame ? null : props.thread,
|
||||
});
|
||||
console.log("open thread", target);
|
||||
// console.log("open thread", target);
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
|
@ -39,15 +43,15 @@ export function ThreadsItem(props: VoidProps<{ thread: Thread }>) {
|
|||
<div class="date"><Time ts={props.thread.baseEvent.originTs} /></div>
|
||||
</div>
|
||||
<div class="bottom" onClick={willOpenThread("latest")}>
|
||||
<Text count={info().count}>message.count</Text> • <Time ts={info().latest_event.origin_server_ts} />
|
||||
<Text count={thread().messageCount}>message.count</Text> • <Time ts={thread().latestEvent.originTs} />
|
||||
</div>
|
||||
</header>
|
||||
<div class="preview">
|
||||
<Show when={!preview.loading}>
|
||||
{preview()?.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, 10).map((ev, idx) => <Message event={ev} title={idx === 0} />)}
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={remaining()}>
|
||||
<Show when={remaining() && false}>
|
||||
<footer>
|
||||
<Text count={remaining()}>message.remaining</Text>
|
||||
</footer>
|
||||
|
@ -82,7 +86,7 @@ export function Message(props: VoidProps<{ event: any, title?: boolean }>) {
|
|||
}
|
||||
|
||||
return (
|
||||
<div class="message" classList={{ title: title(), [compact() ? "compact" : "cozy"]: true }}>
|
||||
<div class="message" classList={{ title: title(), [compact() ? "compact" : "cozy"]: true }} data-event-id={props.event.id}>
|
||||
{title() && !compact() && <div class="avatar"></div>}
|
||||
{title() && compact() && <div class="name">{props.event.sender}</div>}
|
||||
<div class="content">
|
||||
|
|
159
src/Thread.tsx
Normal file
159
src/Thread.tsx
Normal file
|
@ -0,0 +1,159 @@
|
|||
import { For, ParentProps, Show, VoidProps, createEffect, createResource, createSignal, onCleanup, onMount } from "solid-js";
|
||||
import "./App.scss";
|
||||
import { Client, Room, Thread, Timeline } from "sdk";
|
||||
import { Message, TextBlock, ThreadsItem } from "./Room";
|
||||
// import { computePosition, shift, offset, autoUpdate } from "@floating-ui/dom";
|
||||
// import { Portal } from "solid-js/web";
|
||||
import { Time } from "./Atoms";
|
||||
import { ThreadTimeline } from "sdk/dist/src/timeline";
|
||||
import { debounce } from "@solid-primitives/scheduled";
|
||||
import { Editor } from "./Editor";
|
||||
// import { createVirtualizer } from "@tanstack/solid-virtual";
|
||||
|
||||
export function ThreadView(props: VoidProps<{ thread: Thread }>) {
|
||||
let textareaEl: HTMLTextAreaElement;
|
||||
|
||||
function handleKeyDown(e: KeyboardEvent) {
|
||||
if (e.key === "Enter" && !e.shiftKey) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
props.thread.room.sendEvent("m.message", {
|
||||
text: [{ body: textareaEl.value }],
|
||||
"m.relations": [{ rel_type: "m.thread", event_id: props.thread.id }],
|
||||
});
|
||||
textareaEl.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
const [timeline, { mutate }] = createResource(() => props.thread, async (thread) => {
|
||||
return thread.room.timelines.forThread(thread, "end");
|
||||
}, {
|
||||
storage: (init) => createSignal(init, { equals: false }),
|
||||
});
|
||||
|
||||
let scrollEl: HTMLDivElement;
|
||||
let isAutoscrolling = false;
|
||||
const [isPaginating, setIsPaginating] = createSignal(false);
|
||||
const [isAtBeginning, setIsAtBeginning] = createSignal(false);
|
||||
|
||||
const refresh = () => {
|
||||
mutate(timeline());
|
||||
if (isAutoscrolling) scrollEl.scrollBy(0, 999999);
|
||||
};
|
||||
|
||||
let oldTimeline: ThreadTimeline | undefined;
|
||||
createEffect(() => {
|
||||
oldTimeline?.off("timelineUpdate", refresh);
|
||||
oldTimeline?.off("timelineAppend", refresh);
|
||||
timeline()?.on("timelineUpdate", refresh);
|
||||
timeline()?.on("timelineAppend", refresh);
|
||||
oldTimeline = timeline();
|
||||
setIsAtBeginning(timeline()?.isAtBeginning || false);
|
||||
}, timeline);
|
||||
onCleanup(() => {
|
||||
oldTimeline?.off("timelineUpdate", refresh);
|
||||
oldTimeline?.off("timelineAppend", refresh);
|
||||
});
|
||||
|
||||
const AUTOSCROLL_MARGIN = 5;
|
||||
const PAGINATE_MARGIN = 500;
|
||||
|
||||
const paginate = async () => {
|
||||
isAutoscrolling = scrollEl.scrollTop + scrollEl.offsetHeight > scrollEl.scrollHeight - AUTOSCROLL_MARGIN;
|
||||
|
||||
if (scrollEl.scrollTop < PAGINATE_MARGIN && !isPaginating() && !isAtBeginning()) {
|
||||
const scrollRefEl = scrollEl.querySelector(".message") as HTMLElement | undefined;
|
||||
const oldOffsetTop = (scrollRefEl?.offsetTop || 0);
|
||||
setIsPaginating(true);
|
||||
await timeline()?.paginate("b", 10);
|
||||
const newOffsetTop = scrollRefEl?.offsetTop || 0;
|
||||
refresh();
|
||||
scrollEl.scrollBy(0, newOffsetTop - oldOffsetTop);
|
||||
// scrollEl.scrollTo(0, oldOffsetTop);
|
||||
setIsPaginating(false);
|
||||
if (timeline()?.isAtBeginning) setIsAtBeginning(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleScroll = debounce(paginate, 100);
|
||||
const events = () => timeline()?.getEvents().filter(ev => ev.type === "m.message");
|
||||
// const eventsLength = () => events()?.length || 0;
|
||||
|
||||
// const virt = createVirtualizer({
|
||||
// count: 100,
|
||||
// estimateSize: () => 50,
|
||||
// getScrollElement: () => scrollEl,
|
||||
// });
|
||||
|
||||
// const items = () => virt.getVirtualItems();
|
||||
|
||||
// <Show when={timeline() && !timeline.loading}>
|
||||
// {items().map(it => {
|
||||
// return <div style={`position:fixed;translate:0 ${it.start}px`}>
|
||||
// {it.index}
|
||||
// </div>;
|
||||
// })}
|
||||
// </Show>
|
||||
|
||||
return (
|
||||
<div class="thread-view">
|
||||
<div class="scroll" onScroll={handleScroll} ref={scrollEl!}>
|
||||
<ThreadInfo thread={props.thread} showHeader={isAtBeginning()} />
|
||||
{/* FIXME: loading placeholders */}
|
||||
{false && (timeline.loading || isPaginating()) && new Array(5).fill(0).map(() => <div style="min-height:1rem;margin:8px;background:var(--background-3)"></div>)}
|
||||
{!isAtBeginning() && <div style={`min-height:${PAGINATE_MARGIN}px`}></div>}
|
||||
<For each={events()}>
|
||||
{(ev, idx) => <Message event={ev} title={idx() === 0 && false} />}
|
||||
</For>
|
||||
<div class="spacer-bottom"></div>
|
||||
</div>
|
||||
<div class="input">
|
||||
<button onClick={handleScroll}>more</button>
|
||||
<Editor />
|
||||
<textarea placeholder="write something nice..." ref={textareaEl!} onKeyDown={handleKeyDown}></textarea>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ThreadInfo(props: VoidProps<{ thread: Thread, showHeader: boolean }>) {
|
||||
const event = () => props.thread.baseEvent;
|
||||
let headerEl: HTMLHeadingElement;
|
||||
const [stuck, setStuck] = createSignal(false);
|
||||
|
||||
// FIXME: this feels hacky, and has a flash of "unstuck" style + 1px offsets occasionally
|
||||
|
||||
onMount(() => {
|
||||
const observer = new IntersectionObserver(([e]) => setStuck(!e.isIntersecting), {
|
||||
threshold: 1,
|
||||
root: headerEl.parentElement,
|
||||
});
|
||||
|
||||
observer.observe(headerEl);
|
||||
onCleanup(() => observer.unobserve(headerEl));
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.showHeader && <div class="spacer"></div>}
|
||||
<div class="thread-title" ref={headerEl!} classList={{ stuck: stuck() || !props.showHeader }}>
|
||||
<h1 class="real">
|
||||
<TextBlock text={event().content.title} fallback="Untitled thread" />
|
||||
</h1>
|
||||
<h1 class="fake" aria-hidden="true">
|
||||
<TextBlock text={event().content.title} fallback="Untitled thread" />
|
||||
</h1>
|
||||
</div>
|
||||
<Show when={props.showHeader}>
|
||||
<header>
|
||||
<p>Created by {event().sender} <Time ts={event().originTs} /></p>
|
||||
<p style="color:var(--foreground-2)">Debug: event_id=<code style="user-select:all">{event().id}</code></p>
|
||||
</header>
|
||||
<Message event={event()} title={true} />
|
||||
</Show>
|
||||
<Show when={!props.showHeader}>
|
||||
<div style="flex:1;"></div>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -18,8 +18,6 @@
|
|||
/* Linting */
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"],
|
||||
|
|
Loading…
Reference in a new issue