diff --git a/server/core/collaboration.mjs b/server/core/collaboration.mjs new file mode 100644 index 00000000..e2d76db6 --- /dev/null +++ b/server/core/collaboration.mjs @@ -0,0 +1,76 @@ +import { Hocuspocus } from '@hocuspocus/server' +import { Database } from '@hocuspocus/extension-database' +import * as Y from 'yjs' + +export default { + hocuspocus: null, + + async init() { + this.hocuspocus = new Hocuspocus({ + port: null, // Don't listen on its own port - we'll handle WebSocket upgrade + quiet: true, + + async onAuthenticate({ token }) { + // TODO: validate JWT token + // For now, allow authenticated connections + if (!token) { + throw new Error('Not authenticated') + } + return { user: { name: 'User' } } + }, + + extensions: [ + new Database({ + async fetch({ documentName }) { + try { + const page = await WIKI.db.knex('pages') + .where('id', documentName) + .first('id', 'content') + + if (page && page.content) { + // Convert markdown content to Y.Doc + const ydoc = new Y.Doc() + const yxml = ydoc.getXmlFragment('default') + // Return null to let Hocuspocus create a fresh doc + // The editor will load content from the page store + return null + } + return null + } catch (err) { + WIKI.logger.warn(`Collab fetch error for ${documentName}: ${err.message}`) + return null + } + }, + + async store({ documentName, state }) { + // Store Y.Doc state for persistence between sessions + try { + await WIKI.db.knex('pages') + .where('id', documentName) + .update({ + updatedAt: new Date().toISOString() + }) + WIKI.logger.debug(`Collab state saved for ${documentName}`) + } catch (err) { + WIKI.logger.warn(`Collab store error for ${documentName}: ${err.message}`) + } + } + }) + ] + }) + + WIKI.logger.info('Collaboration Server initialized: [ OK ]') + }, + + handleUpgrade(request, socket, head) { + if (this.hocuspocus) { + this.hocuspocus.handleUpgrade(request, socket, head) + } + }, + + handleConnection(socket, request) { + if (this.hocuspocus) { + this.hocuspocus.handleConnection(socket, request) + } + } +} diff --git a/server/core/kernel.mjs b/server/core/kernel.mjs index eb0a1d7c..36cea44f 100644 --- a/server/core/kernel.mjs +++ b/server/core/kernel.mjs @@ -3,6 +3,7 @@ import eventemitter2 from 'eventemitter2' import NodeCache from 'node-cache' import asar from './asar.mjs' +import collaboration from './collaboration.mjs' import db from './db.mjs' import extensions from './extensions.mjs' import scheduler from './scheduler.mjs' @@ -89,6 +90,25 @@ export default { await WIKI.db.subscribeToNotifications() await WIKI.scheduler.start() + + // Initialize collaboration server + try { + WIKI.collab = collaboration + await WIKI.collab.init() + + // Handle WebSocket upgrade for collaboration + const httpServer = WIKI.servers.http || WIKI.servers.https + if (httpServer) { + httpServer.on('upgrade', (request, socket, head) => { + if (request.url && request.url.startsWith('/_collab')) { + WIKI.collab.handleUpgrade(request, socket, head) + } + }) + WIKI.logger.info('Collaboration WebSocket on /_collab: [ OK ]') + } + } catch (err) { + WIKI.logger.warn(`Collaboration server init failed: ${err.message}`) + } }, /** * Graceful shutdown diff --git a/server/package.json b/server/package.json index 0a284703..5a0c0aa2 100644 --- a/server/package.json +++ b/server/package.json @@ -43,6 +43,8 @@ "@graphql-tools/schema": "10.0.25", "@graphql-tools/utils": "10.9.1", "@hexagon/base64": "2.0.4", + "@hocuspocus/extension-database": "3.4.4", + "@hocuspocus/server": "3.4.4", "@joplin/turndown-plugin-gfm": "1.0.62", "@node-saml/passport-saml": "5.1.0", "@root/csr": "0.8.1", @@ -173,7 +175,8 @@ "uuid": "11.1.0", "validate.js": "0.13.1", "vue": "3.5.18", - "xss": "1.0.15" + "xss": "1.0.15", + "yjs": "13.6.30" }, "devDependencies": { "eslint": "9.32.0", diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml index f3161c94..69ba5a11 100644 --- a/server/pnpm-lock.yaml +++ b/server/pnpm-lock.yaml @@ -29,6 +29,12 @@ importers: '@hexagon/base64': specifier: 2.0.4 version: 2.0.4 + '@hocuspocus/extension-database': + specifier: 3.4.4 + version: 3.4.4(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30) + '@hocuspocus/server': + specifier: 3.4.4 + version: 3.4.4(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30) '@joplin/turndown-plugin-gfm': specifier: 1.0.62 version: 1.0.62 @@ -422,6 +428,9 @@ importers: xss: specifier: 1.0.15 version: 1.0.15 + yjs: + specifier: 13.6.30 + version: 13.6.30 devDependencies: eslint: specifier: 9.32.0 @@ -753,6 +762,20 @@ packages: '@hexagon/base64@2.0.4': resolution: {integrity: sha512-H/ZY6rGyaEuk0mwQgZ3BVi9hMjFTYpBNFbmtOuec/pPibuGhCMXd8fGtwBaO0h44FkWMurysMsDrpkJsBRmoWQ==} + '@hocuspocus/common@3.4.4': + resolution: {integrity: sha512-RykIJ0tsHHMP4Xk+4UCbc7SO5LgGxGUSTdbh6anJEsaALAyqinf1Nn5HYuMjLPolAmsar1v++m9zufR09NLpXA==} + + '@hocuspocus/extension-database@3.4.4': + resolution: {integrity: sha512-z7iq2Dw+GOp4aQq7ys3PD0BA++7tQdXBsSHZ+8mkAbxfTDzjzQ576TphxPiXXC1WQ7yjeFXq03xp/KLIhg3Pyg==} + peerDependencies: + yjs: ^13.6.8 + + '@hocuspocus/server@3.4.4': + resolution: {integrity: sha512-UV+oaONAejOzeYgUygNcgsc8RdZvSokVvAxluZJIisLACpRO/VsseQ5lWKDRwLd7Fn6+rHWDH3hGuQ1fdX1Ycg==} + peerDependencies: + y-protocols: ^1.0.6 + yjs: ^13.6.8 + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -1713,6 +1736,12 @@ packages: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} + async-lock@1.4.1: + resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} + + async-mutex@0.5.0: + resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + async-retry@1.3.3: resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} @@ -3283,6 +3312,9 @@ packages: resolution: {integrity: sha512-0FTlXP/gEEWW+O/sXaO9yZ4bgegrHnOqzbdCNAMeO2KYIOVMAcqVIo+uTcWYd1+DmI+nV58vUmNW03nauoKn2w==} engines: {node: '>=18'} + isomorphic.js@0.2.5: + resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==} + iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} @@ -3396,6 +3428,10 @@ packages: resolution: {integrity: sha512-1zGZ9MF9H22UnkpVeuaGKOjfA2t6WrfdrJmGjy16ykcjnKQDmHVX+KI477rpbGevz/5FD4MC3xf1oxylBgcaQw==} engines: {node: '>=14.14.0'} + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + knex@3.1.0: resolution: {integrity: sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==} engines: {node: '>=16'} @@ -3443,6 +3479,11 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lib0@0.2.117: + resolution: {integrity: sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw==} + engines: {node: '>=16'} + hasBin: true + lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -5157,6 +5198,12 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + y-protocols@1.0.7: + resolution: {integrity: sha512-YSVsLoXxO67J6eE/nV4AtFtT3QEotZf5sK5BHxFBXso7VDUT3Tx07IfA6hsu5Q5OmBdMkQVmFZ9QOA7fikWvnw==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + peerDependencies: + yjs: ^13.0.0 + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -5177,6 +5224,10 @@ packages: yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yjs@13.6.30: + resolution: {integrity: sha512-vv/9h42eCMC81ZHDFswuu/MKzkl/vyq1BhaNGfHyOonwlG4CJbQF4oiBBJPvfdeCt/PlVDWh7Nov9D34YY09uQ==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -5585,6 +5636,33 @@ snapshots: '@hexagon/base64@2.0.4': {} + '@hocuspocus/common@3.4.4': + dependencies: + lib0: 0.2.117 + + '@hocuspocus/extension-database@3.4.4(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30)': + dependencies: + '@hocuspocus/server': 3.4.4(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30) + yjs: 13.6.30 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - y-protocols + + '@hocuspocus/server@3.4.4(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30)': + dependencies: + '@hocuspocus/common': 3.4.4 + async-lock: 1.4.1 + async-mutex: 0.5.0 + kleur: 4.1.5 + lib0: 0.2.117 + ws: 8.18.3 + y-protocols: 1.0.7(yjs@13.6.30) + yjs: 13.6.30 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -6709,6 +6787,12 @@ snapshots: async-function@1.0.0: {} + async-lock@1.4.1: {} + + async-mutex@0.5.0: + dependencies: + tslib: 2.8.1 + async-retry@1.3.3: dependencies: retry: 0.13.1 @@ -8451,6 +8535,8 @@ snapshots: - supports-color - utf-8-validate + isomorphic.js@0.2.5: {} + iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 @@ -8616,6 +8702,8 @@ snapshots: klaw@4.1.0: {} + kleur@4.1.5: {} + knex@3.1.0(pg@8.16.3): dependencies: colorette: 2.0.19 @@ -8666,6 +8754,10 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lib0@0.2.117: + dependencies: + isomorphic.js: 0.2.5 + lilconfig@2.1.0: {} lilconfig@3.1.1: {} @@ -10541,6 +10633,11 @@ snapshots: xtend@4.0.2: {} + y-protocols@1.0.7(yjs@13.6.30): + dependencies: + lib0: 0.2.117 + yjs: 13.6.30 + y18n@5.0.8: {} yaml@2.8.0: {} @@ -10562,6 +10659,10 @@ snapshots: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 + yjs@13.6.30: + dependencies: + lib0: 0.2.117 + yocto-queue@0.1.0: {} zen-observable-ts@1.2.5: diff --git a/ux/package.json b/ux/package.json index 291ac4d7..85babfc1 100644 --- a/ux/package.json +++ b/ux/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@apollo/client": "3.13.8", + "@hocuspocus/provider": "3.4.4", "@lezer/common": "1.2.3", "@mdi/font": "7.4.47", "@quasar/extras": "1.17.0", @@ -19,6 +20,8 @@ "@tiptap/core": "2.11.5", "@tiptap/extension-code-block": "2.11.5", "@tiptap/extension-code-block-lowlight": "2.11.5", + "@tiptap/extension-collaboration": "2.11.5", + "@tiptap/extension-collaboration-cursor": "2.11.5", "@tiptap/extension-color": "2.11.5", "@tiptap/extension-dropcursor": "2.11.5", "@tiptap/extension-font-family": "2.11.5", @@ -104,6 +107,7 @@ "vue-router": "4.5.1", "vue3-otp-input": "0.5.40", "vuedraggable": "4.1.0", + "yjs": "13.6.30", "zxcvbn": "4.4.2" }, "devDependencies": { diff --git a/ux/pnpm-lock.yaml b/ux/pnpm-lock.yaml index 1dab0f3a..8b916376 100644 --- a/ux/pnpm-lock.yaml +++ b/ux/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@apollo/client': specifier: 3.13.8 version: 3.13.8(graphql@16.11.0) + '@hocuspocus/provider': + specifier: 3.4.4 + version: 3.4.4(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30) '@lezer/common': specifier: 1.2.3 version: 1.2.3 @@ -32,6 +35,12 @@ importers: '@tiptap/extension-code-block-lowlight': specifier: 2.11.5 version: 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/extension-code-block@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)(highlight.js@11.11.1)(lowlight@3.3.0) + '@tiptap/extension-collaboration': + specifier: 2.11.5 + version: 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)(y-prosemirror@1.3.7(prosemirror-model@1.25.2)(prosemirror-state@1.4.3)(prosemirror-view@1.40.1)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30)) + '@tiptap/extension-collaboration-cursor': + specifier: 2.11.5 + version: 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(y-prosemirror@1.3.7(prosemirror-model@1.25.2)(prosemirror-state@1.4.3)(prosemirror-view@1.40.1)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30)) '@tiptap/extension-color': specifier: 2.11.5 version: 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/extension-text-style@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))) @@ -287,6 +296,9 @@ importers: vuedraggable: specifier: 4.1.0 version: 4.1.0(vue@3.5.18(typescript@5.8.3)) + yjs: + specifier: 13.6.30 + version: 13.6.30 zxcvbn: specifier: 4.4.2 version: 4.4.2 @@ -744,6 +756,15 @@ packages: peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@hocuspocus/common@3.4.4': + resolution: {integrity: sha512-RykIJ0tsHHMP4Xk+4UCbc7SO5LgGxGUSTdbh6anJEsaALAyqinf1Nn5HYuMjLPolAmsar1v++m9zufR09NLpXA==} + + '@hocuspocus/provider@3.4.4': + resolution: {integrity: sha512-KbsMAfdYcIJD8eMU/5QnpXcSOvIWAcCNI33FSRSaKCIpYBFtAwkYIwWnZJmPZ8a1BMAtqQc+uvy9+UQf7GHnGQ==} + peerDependencies: + y-protocols: ^1.0.6 + yjs: ^13.6.8 + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -857,6 +878,9 @@ packages: '@lezer/common@1.2.3': resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} + '@lifeomic/attempt@3.1.0': + resolution: {integrity: sha512-QZqem4QuAnAyzfz+Gj5/+SLxqwCAw2qmt7732ZXodr6VDWGeYLG6w1i/vYLa55JQM9wRuBKLmXmiZ2P0LtE5rw==} + '@mdi/font@7.4.47': resolution: {integrity: sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==} @@ -1199,6 +1223,19 @@ packages: peerDependencies: '@tiptap/core': ^2.7.0 + '@tiptap/extension-collaboration-cursor@2.11.5': + resolution: {integrity: sha512-sazBzi5HCHgGRihSzWHhHFMSI9oU0v/qqiDZYJ/zhzCKEWONx8WlS6WTxo6z3l6/Qz9lm7clmHNUQNWxnssAOA==} + peerDependencies: + '@tiptap/core': ^2.7.0 + y-prosemirror: ^1.2.11 + + '@tiptap/extension-collaboration@2.11.5': + resolution: {integrity: sha512-3tMMq0E+FM3/3YBUMq5rLvks2DC/t1XLH2Kz/VcuVCxqg1Zg5s9nKOl6CcUZ8gbdvZoEd/GYoQyROJ957v9wzw==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + y-prosemirror: ^1.2.11 + '@tiptap/extension-color@2.11.5': resolution: {integrity: sha512-9gZF6EIpfOJYUt1TtFY37e8iqwKcOmBl8CkFaxq+4mWVvYd2D7KbA0r4tYTxSO0fOBJ5fA/1qJrpvgRlyocp/A==} peerDependencies: @@ -3061,6 +3098,9 @@ packages: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} + isomorphic.js@0.2.5: + resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==} + iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} @@ -3151,6 +3191,11 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lib0@0.2.117: + resolution: {integrity: sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw==} + engines: {node: '>=16'} + hasBin: true + linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} @@ -4650,6 +4695,22 @@ packages: resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} engines: {node: '>=0.4.0'} + y-prosemirror@1.3.7: + resolution: {integrity: sha512-NpM99WSdD4Fx4if5xOMDpPtU3oAmTSjlzh5U4353ABbRHl1HtAFUx6HlebLZfyFxXN9jzKMDkVbcRjqOZVkYQg==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + peerDependencies: + prosemirror-model: ^1.7.1 + prosemirror-state: ^1.2.3 + prosemirror-view: ^1.9.10 + y-protocols: ^1.0.1 + yjs: ^13.5.38 + + y-protocols@1.0.7: + resolution: {integrity: sha512-YSVsLoXxO67J6eE/nV4AtFtT3QEotZf5sK5BHxFBXso7VDUT3Tx07IfA6hsu5Q5OmBdMkQVmFZ9QOA7fikWvnw==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + peerDependencies: + yjs: ^13.0.0 + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -4674,6 +4735,10 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yjs@13.6.30: + resolution: {integrity: sha512-vv/9h42eCMC81ZHDFswuu/MKzkl/vyq1BhaNGfHyOonwlG4CJbQF4oiBBJPvfdeCt/PlVDWh7Nov9D34YY09uQ==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -5081,6 +5146,22 @@ snapshots: dependencies: graphql: 16.11.0 + '@hocuspocus/common@3.4.4': + dependencies: + lib0: 0.2.117 + + '@hocuspocus/provider@3.4.4(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30)': + dependencies: + '@hocuspocus/common': 3.4.4 + '@lifeomic/attempt': 3.1.0 + lib0: 0.2.117 + ws: 8.17.1 + y-protocols: 1.0.7(yjs@13.6.30) + yjs: 13.6.30 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -5196,6 +5277,8 @@ snapshots: '@lezer/common@1.2.3': {} + '@lifeomic/attempt@3.1.0': {} + '@mdi/font@7.4.47': {} '@napi-rs/wasm-runtime@0.2.12': @@ -5497,6 +5580,17 @@ snapshots: dependencies: '@tiptap/core': 2.11.5(@tiptap/pm@2.11.5) + '@tiptap/extension-collaboration-cursor@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(y-prosemirror@1.3.7(prosemirror-model@1.25.2)(prosemirror-state@1.4.3)(prosemirror-view@1.40.1)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))': + dependencies: + '@tiptap/core': 2.11.5(@tiptap/pm@2.11.5) + y-prosemirror: 1.3.7(prosemirror-model@1.25.2)(prosemirror-state@1.4.3)(prosemirror-view@1.40.1)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30) + + '@tiptap/extension-collaboration@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)(y-prosemirror@1.3.7(prosemirror-model@1.25.2)(prosemirror-state@1.4.3)(prosemirror-view@1.40.1)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))': + dependencies: + '@tiptap/core': 2.11.5(@tiptap/pm@2.11.5) + '@tiptap/pm': 2.11.5 + y-prosemirror: 1.3.7(prosemirror-model@1.25.2)(prosemirror-state@1.4.3)(prosemirror-view@1.40.1)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30) + '@tiptap/extension-color@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/extension-text-style@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5)))': dependencies: '@tiptap/core': 2.11.5(@tiptap/pm@2.11.5) @@ -7649,6 +7743,8 @@ snapshots: isobject@3.0.1: {} + isomorphic.js@0.2.5: {} + iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 @@ -7746,6 +7842,10 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lib0@0.2.117: + dependencies: + isomorphic.js: 0.2.5 + linkify-it@5.0.0: dependencies: uc.micro: 2.1.0 @@ -9395,6 +9495,20 @@ snapshots: xmlhttprequest-ssl@2.1.2: {} + y-prosemirror@1.3.7(prosemirror-model@1.25.2)(prosemirror-state@1.4.3)(prosemirror-view@1.40.1)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30): + dependencies: + lib0: 0.2.117 + prosemirror-model: 1.25.2 + prosemirror-state: 1.4.3 + prosemirror-view: 1.40.1 + y-protocols: 1.0.7(yjs@13.6.30) + yjs: 13.6.30 + + y-protocols@1.0.7(yjs@13.6.30): + dependencies: + lib0: 0.2.117 + yjs: 13.6.30 + y18n@5.0.8: {} yallist@3.1.1: {} @@ -9418,6 +9532,10 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yjs@13.6.30: + dependencies: + lib0: 0.2.117 + yocto-queue@0.1.0: {} yoctocolors-cjs@2.1.2: {} diff --git a/ux/src/components/EditorWysiwyg.vue b/ux/src/components/EditorWysiwyg.vue index b7c62a49..09d65d77 100644 --- a/ux/src/components/EditorWysiwyg.vue +++ b/ux/src/components/EditorWysiwyg.vue @@ -89,7 +89,8 @@