diff --git a/package.json b/package.json index 9257c8d..af507a2 100644 --- a/package.json +++ b/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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d85ec3..5afd87f 100644 --- a/pnpm-lock.yaml +++ b/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 diff --git a/src/App.scss b/src/App.scss index 5a175aa..de2db80 100644 --- a/src/App.scss +++ b/src/App.scss @@ -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; diff --git a/src/App.tsx b/src/App.tsx index b1f068a..46681f2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -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 }>) { - empty sidebar +
empty sidebar
diff --git a/src/Atoms.tsx b/src/Atoms.tsx index 64defa1..c382f6e 100644 --- a/src/Atoms.tsx +++ b/src/Atoms.tsx @@ -21,3 +21,29 @@ export function Time(props: VoidProps<{} & ({ ts: number } | { date: Date })>) { return ; } + +// 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 ( +// <> +// {props.children} +// +//
{props.tip}
+//
+// +// ) +// } diff --git a/src/Context.tsx b/src/Context.tsx index 30139d2..0781ec5 100644 --- a/src/Context.tsx +++ b/src/Context.tsx @@ -69,6 +69,9 @@ export function Contextualizer(props: ParentProps) { roomState: { focusedThread }, locale, }; + + // FIXME: swap out per room state on focusRoom + // const perRoom = new Map(); function redux(change: Change) { switch (change.type) { diff --git a/src/Editor.scss b/src/Editor.scss new file mode 100644 index 0000000..5e12f55 --- /dev/null +++ b/src/Editor.scss @@ -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; + } +} diff --git a/src/Editor.tsx b/src/Editor.tsx new file mode 100644 index 0000000..7413878 --- /dev/null +++ b/src/Editor.tsx @@ -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> }>) { +// createEffect(() => console.log(props.suggestions())); +// return ( +// +// ) +// } + +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 ( +
+ + +
+ ); +} diff --git a/src/Main.tsx b/src/Main.tsx index 2e41083..c6edaee 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -11,7 +11,7 @@ export function RoomView(props: VoidProps<{ room: Room }>) { <>
- +
) @@ -25,7 +25,7 @@ function Threads(props: VoidProps<{ room: Room }>) { return (
- + {threadChunk()!.threads.map((thread: Thread) => )} @@ -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 ( - <> - {props.children} - -
{props.tip}
-
- - ) -} - -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 ( -
-
- - {timeline() && } -
-
-
- -
-
- ); -} - -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 ( - <> -
-
-

- -

-

- -

-
-
-

Created by {event().sender}

-

Debug: event_id={event().id}

-
- - - ); -} - 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) => )} - - ); -} - -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"); diff --git a/src/Room.tsx b/src/Room.tsx index 79c137a..b52a64e 100644 --- a/src/Room.tsx +++ b/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 }>) {
- message.count
- {preview()?.filter(ev => ev.type === "m.message").slice(0, 10).map((ev, idx) => )} + {preview()?.getEvents().filter(ev => ev.type === "m.message").slice(0, 10).map((ev, idx) => )}
- +
message.remaining
@@ -82,7 +86,7 @@ export function Message(props: VoidProps<{ event: any, title?: boolean }>) { } return ( -
+
{title() && !compact() &&
} {title() && compact() &&
{props.event.sender}
}
diff --git a/src/Thread.tsx b/src/Thread.tsx new file mode 100644 index 0000000..55f34ed --- /dev/null +++ b/src/Thread.tsx @@ -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(); + + // + // {items().map(it => { + // return
+ // {it.index} + //
; + // })} + //
+ + return ( +
+
+ + {/* FIXME: loading placeholders */} + {false && (timeline.loading || isPaginating()) && new Array(5).fill(0).map(() =>
)} + {!isAtBeginning() &&
} + + {(ev, idx) => } + +
+
+
+ + + +
+
+ ); +} + +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 &&
} +
+

+ +

+

+ +

+
+ +
+

Created by {event().sender}

+

Debug: event_id={event().id}

+
+ +
+ +
+
+ + ); +} diff --git a/tsconfig.json b/tsconfig.json index db334f8..c71679c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,8 +18,6 @@ /* Linting */ "strict": true, "strictNullChecks": true, - "noUnusedLocals": true, - "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, "include": ["src"],