├── .dockerignore ├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── .gitpod.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── codemirror.next ├── README.md ├── codemirror.next.html ├── codemirror.next.js ├── package-lock.json ├── package.json └── webpack.config.js ├── codemirror ├── README.md ├── codemirror.css ├── codemirror.html ├── codemirror.js ├── package-lock.json ├── package.json └── webpack.config.js ├── demo-server ├── README.md ├── demo-server.js ├── package-lock.json └── package.json ├── index.html ├── monaco-react ├── .gitignore ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── App.tsx │ ├── global.css │ └── main.tsx └── vite.config.ts ├── monaco ├── README.md ├── monaco.html ├── monaco.js ├── package-lock.json ├── package.json └── webpack.config.js ├── package-lock.json ├── package.json ├── prosemirror-versions ├── README.md ├── package-lock.json ├── package.json ├── prosemirror-versions.html ├── prosemirror-versions.js ├── prosemirror.css ├── schema.js ├── version.css └── webpack.config.js ├── prosemirror ├── README.md ├── package-lock.json ├── package.json ├── prosemirror.css ├── prosemirror.html ├── prosemirror.js ├── schema.js └── webpack.config.js ├── quill ├── README.md ├── package-lock.json ├── package.json ├── quill.css ├── quill.html ├── quill.js └── webpack.config.js └── react-prosemirror ├── README.md ├── index.html ├── index.jsx ├── package-lock.json ├── package.json └── prosemirror.css /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/dist 3 | .vscode 4 | .git 5 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [20.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - run: npm ci 29 | - run: npm run build --if-present 30 | - run: npm test 31 | 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/dist 3 | .vscode 4 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - init: npm i 3 | command: npm start 4 | ports: 5 | - port: 3000 6 | onOpen: open-preview 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax = docker/dockerfile:1.4 2 | 3 | # Adjust NODE_VERSION as desired 4 | ARG NODE_VERSION=20.5.1 5 | FROM node:${NODE_VERSION}-slim as base 6 | 7 | LABEL fly_launch_runtime="Node.js" 8 | 9 | # Node.js app lives here 10 | WORKDIR /app 11 | 12 | # Set production environment 13 | ENV NODE_ENV="production" 14 | 15 | 16 | # Throw-away build stage to reduce size of final image 17 | FROM base as build 18 | 19 | # Install packages needed to build node modules 20 | RUN apt-get update -qq && \ 21 | apt-get install -y build-essential pkg-config python-is-python3 22 | 23 | # Copy application code 24 | COPY . . 25 | 26 | RUN npm i -g webpack-cli 27 | RUN make static-content 28 | 29 | # Final stage for app image 30 | FROM base 31 | 32 | # Copy built application 33 | COPY --from=build /app /app 34 | 35 | # Start the server by default, this can be overwritten at runtime 36 | EXPOSE 3000 37 | CMD [ "npm", "run", "demo-server"] 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | demos = monaco codemirror codemirror.next prosemirror prosemirror-versions quill 2 | dists = $(patsubst %,%/dist,$(demos)) 3 | node_modules = $(patsubst %,%/node_modules,$(demos)) demo-server/node_modules 4 | 5 | .PHONY: help 6 | help: # Show help for each of the Makefile recipes. 7 | @grep -E '^[a-zA-Z0-9 -]+:.*#' Makefile | while read -r l; do printf "\033[1;32m$$(echo $$l | cut -f 1 -d':')\033[00m:$$(echo $$l | cut -f 2- -d'#')\n"; done 8 | 9 | .PHONY: dist 10 | dist: $(demos) # Build all distribution files 11 | 12 | .PHONY: clean 13 | clean : # remove all generated files 14 | rm -rf */dist */node_modules node_modules 15 | 16 | static-content : # Build the demos so that they can be served via a CDN 17 | make -j dist 18 | rm -rf node_modules */node_modules 19 | 20 | $(node_modules) : %/node_modules: %/package.json %/package-lock.json 21 | cd $* && npm ci 22 | @touch $@ 23 | 24 | .NOTINTERMEDIATE: $(node_modules) 25 | 26 | node_modules : package.json package-lock.json 27 | npm ci 28 | @touch node_modules 29 | 30 | .SECONDEXPANSION: 31 | 32 | ifeq ($(MAKECMDGOALS),_serve) 33 | $(dists) : %/dist : %/node_modules node_modules 34 | cd $* && npm run watch 35 | .PHONY: $(dists) 36 | else 37 | $(dists) : %/dist : node_modules %/node_modules $$(filter-out %/dist %/node_modules,$$(wildcard $$*/*)) 38 | cd $* && npm run dist 39 | @touch $@ 40 | endif 41 | 42 | $(demos) : % : %/dist # Build a specific demo 43 | .PHONY : $(demos) 44 | 45 | demo-server : demo-server/node_modules 46 | cd demo-server && npm start 47 | .PHONY : demo-server 48 | 49 | # Requires parallel execution of make targets 50 | _serve: demo-server $(demos) 51 | 52 | serve: # Start demo server and build & watch all demos in parallel 53 | @$(MAKE) -j _serve 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Yjs Demos [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/yjs/yjs-demos) 3 | > A starting point for your own ideas - PRs welcome 4 | 5 | We have a more complete [Getting Started Guide](https://docs.yjs.dev/getting-started/a-collaborative-editor) in our official documentation website. 6 | 7 | * 3D model collaboration using the [Vertex Viewer](https://developer.vertexvis.com/). [Open Demo Site](https://collaboration.vertexvis.io/). 8 | * Shared Editing using the [ProseMirror](http://prosemirror.net/) editor - [Open Directory](./prosemirror/) 9 | * Shared Editing using the [@nytimes/react-prosemirror](https://github.com/nytimes/react-prosemirror/) - [Open Directory](./react-prosemirror/) 10 | * Shared Editing using the [ProseMirror](http://prosemirror.net/) editor with 11 | versioning support - [Open Directory](./prosemirror-versions/) 12 | * Shared Editing using the [Quill](https://quilljs.com/) editor - [Open Directory](./quill/) 13 | * Shared Editing using the [Monaco](https://microsoft.github.io/monaco-editor/) 14 | editor - [Open Directory](./monaco/) 15 | * Shared Editing using the [CodeMirror](https://codemirror.net/) 16 | editor - [Open Directory](./codemirror/) 17 | * Shared Editing using the [CodeMirror.next](https://codemirror.net/6/) 18 | editor - [Open Directory](./codemirror.next/) 19 | 20 | ## Getting Started 21 | 22 | If you are new to Yjs and you just want to play around clone this repository and 23 | use one of the demo directories that interests you the most. 24 | 25 | ```sh 26 | git clone https://github.com/yjs/yjs-demos.git 27 | npm install 28 | cd yjs-demos/${demo-directory} 29 | npm install 30 | npm start 31 | ``` 32 | 33 | The demos use a public [`y-websocket`](https://github.com/yjs/y-websocket) 34 | instance for communication. Try using one of the [other connection providers](https://docs.yjs.dev/ecosystem/connection-provider) or setting up 35 | your own endpoint. 36 | 37 | ### (Un)License 38 | 39 | The demos are released to the public domain - [Unlicense](./LICENSE). 40 | -------------------------------------------------------------------------------- /codemirror.next/README.md: -------------------------------------------------------------------------------- 1 | # CodeMirror6 Demo 2 | > [y-codemirror.next](https://docs.yjs.dev/ecosystem/editor-bindings/codemirror.next) / [y-websocket](https://docs.yjs.dev/ecosystem/connection-provider/y-websocket) / [Live Demo](https://demos.yjs.dev/codemirror.next/codemirror.next.html) 3 | 4 | This is a demo of a [CodeMirror 6](https://codemirror.net/) editor that was made collaborative with Yjs & y-codemirror.next. 5 | 6 | We use the [y-websocket](https://docs.yjs.dev/ecosystem/connection-provider) provider to share document updates through a server. There are many more providers available for Yjs - try switching to another provider. [See docs](https://docs.yjs.dev/ecosystem/connection-provider). 7 | 8 | Also you could try adding offline persistence to this demo. Wouldn't it be cool if document updates are persisted in the browser, so you can load your application load faster? Try it out: https://docs.yjs.dev/getting-started/allowing-offline-editing 9 | 10 | ## Quick Start 11 | 12 | ```sh 13 | npm i 14 | npm start 15 | # Project is running at http://localhost:8080/ 16 | ``` 17 | -------------------------------------------------------------------------------- /codemirror.next/codemirror.next.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Yjs CodeMirror Example 6 | 7 | 8 | 42 | 43 | 44 | 45 |

46 |

47 | This is a demo of the Yjs ⇔ 48 | CodeMirror 6 binding: 49 | y-codemirror.next. 50 |

51 |

52 | The content of this editor is shared with every client that visits this 53 | domain. 54 |

55 |
56 | 57 | 58 | -------------------------------------------------------------------------------- /codemirror.next/codemirror.next.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | import * as Y from 'yjs' 4 | // @ts-ignore 5 | import { yCollab, yUndoManagerKeymap } from 'y-codemirror.next' 6 | import { WebsocketProvider } from 'y-websocket' 7 | 8 | import { EditorState, EditorView, basicSetup } from '@codemirror/basic-setup' 9 | import { keymap } from '@codemirror/view' 10 | import { javascript } from '@codemirror/lang-javascript' 11 | 12 | import * as random from 'lib0/random' 13 | 14 | const roomname = `codemirror-demo-${new Date().toLocaleDateString('en-CA')}` 15 | 16 | export const usercolors = [ 17 | { color: '#30bced', light: '#30bced33' }, 18 | { color: '#6eeb83', light: '#6eeb8333' }, 19 | { color: '#ffbc42', light: '#ffbc4233' }, 20 | { color: '#ecd444', light: '#ecd44433' }, 21 | { color: '#ee6352', light: '#ee635233' }, 22 | { color: '#9ac2c9', light: '#9ac2c933' }, 23 | { color: '#8acb88', light: '#8acb8833' }, 24 | { color: '#1be7ff', light: '#1be7ff33' } 25 | ] 26 | 27 | export const userColor = usercolors[random.uint32() % usercolors.length] 28 | 29 | const ydoc = new Y.Doc() 30 | // const provider = new WebrtcProvider('codemirror6-demo-room', ydoc) 31 | const provider = new WebsocketProvider( 32 | 'wss://demos.yjs.dev/ws', // use the public ws server 33 | // `ws${location.protocol.slice(4)}//${location.host}/ws`, // alternatively: use the local ws server (run `npm start` in root directory) 34 | // 'ws://localhost:3334', 35 | roomname, 36 | ydoc 37 | ) 38 | const ytext = ydoc.getText('codemirror') 39 | 40 | provider.awareness.setLocalStateField('user', { 41 | name: 'Anonymous ' + Math.floor(Math.random() * 100), 42 | color: userColor.color, 43 | colorLight: userColor.light 44 | }) 45 | 46 | const state = EditorState.create({ 47 | doc: ytext.toString(), 48 | extensions: [ 49 | keymap.of([ 50 | ...yUndoManagerKeymap 51 | ]), 52 | basicSetup, 53 | javascript(), 54 | yCollab(ytext, provider.awareness) 55 | // oneDark 56 | ] 57 | }) 58 | 59 | const view = new EditorView({ state, parent: /** @type {HTMLElement} */ (document.querySelector('#editor')) }) 60 | 61 | // @ts-ignore 62 | window.example = { provider, ydoc, ytext, view } 63 | -------------------------------------------------------------------------------- /codemirror.next/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yjs-codemirror-next-demo", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "yjs-codemirror-next-demo", 9 | "version": "1.0.0", 10 | "license": "UNLICENSE", 11 | "dependencies": { 12 | "@codemirror/basic-setup": "^0.19.0", 13 | "@codemirror/lang-javascript": "^0.19.0", 14 | "@codemirror/state": "^0.19.0", 15 | "@codemirror/view": "^0.19.0", 16 | "y-codemirror.next": "^0.1.0", 17 | "y-websocket": "^1.3.18", 18 | "yjs": "^13.5.22" 19 | } 20 | }, 21 | "node_modules/@codemirror/autocomplete": { 22 | "version": "0.19.15", 23 | "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-0.19.15.tgz", 24 | "integrity": "sha512-GQWzvvuXxNUyaEk+5gawbAD8s51/v2Chb++nx0e2eGWrphWk42isBtzOMdc3DxrxrZtPZ55q2ldNp+6G8KJLIQ==", 25 | "dependencies": { 26 | "@codemirror/language": "^0.19.0", 27 | "@codemirror/state": "^0.19.4", 28 | "@codemirror/text": "^0.19.2", 29 | "@codemirror/tooltip": "^0.19.12", 30 | "@codemirror/view": "^0.19.0", 31 | "@lezer/common": "^0.15.0" 32 | } 33 | }, 34 | "node_modules/@codemirror/basic-setup": { 35 | "version": "0.19.3", 36 | "resolved": "https://registry.npmjs.org/@codemirror/basic-setup/-/basic-setup-0.19.3.tgz", 37 | "integrity": "sha512-2hfO+QDk/HTpQzeYk1NyL1G9D5L7Sj78dtaQP8xBU42DKU9+OBPF5MdjLYnxP0jKzm6IfQfsLd89fnqW3rBVfQ==", 38 | "deprecated": "In version 6.0, this package has been renamed to just 'codemirror'", 39 | "dependencies": { 40 | "@codemirror/autocomplete": "^0.19.0", 41 | "@codemirror/closebrackets": "^0.19.0", 42 | "@codemirror/commands": "^0.19.0", 43 | "@codemirror/comment": "^0.19.0", 44 | "@codemirror/fold": "^0.19.0", 45 | "@codemirror/gutter": "^0.19.0", 46 | "@codemirror/highlight": "^0.19.0", 47 | "@codemirror/history": "^0.19.0", 48 | "@codemirror/language": "^0.19.0", 49 | "@codemirror/lint": "^0.19.0", 50 | "@codemirror/matchbrackets": "^0.19.0", 51 | "@codemirror/rectangular-selection": "^0.19.2", 52 | "@codemirror/search": "^0.19.0", 53 | "@codemirror/state": "^0.19.0", 54 | "@codemirror/view": "^0.19.31" 55 | } 56 | }, 57 | "node_modules/@codemirror/closebrackets": { 58 | "version": "0.19.2", 59 | "resolved": "https://registry.npmjs.org/@codemirror/closebrackets/-/closebrackets-0.19.2.tgz", 60 | "integrity": "sha512-ClMPzPcPP0eQiDcVjtVPl6OLxgdtZSYDazsvT0AKl70V1OJva0eHgl4/6kCW3RZ0pb2n34i9nJz4eXCmK+TYDA==", 61 | "deprecated": "As of 0.20.0, this package has been merged into @codemirror/autocomplete", 62 | "dependencies": { 63 | "@codemirror/language": "^0.19.0", 64 | "@codemirror/rangeset": "^0.19.0", 65 | "@codemirror/state": "^0.19.2", 66 | "@codemirror/text": "^0.19.0", 67 | "@codemirror/view": "^0.19.44" 68 | } 69 | }, 70 | "node_modules/@codemirror/commands": { 71 | "version": "0.19.8", 72 | "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-0.19.8.tgz", 73 | "integrity": "sha512-65LIMSGUGGpY3oH6mzV46YWRrgao6NmfJ+AuC7jNz3K5NPnH6GCV1H5I6SwOFyVbkiygGyd0EFwrWqywTBD1aw==", 74 | "dependencies": { 75 | "@codemirror/language": "^0.19.0", 76 | "@codemirror/matchbrackets": "^0.19.0", 77 | "@codemirror/state": "^0.19.2", 78 | "@codemirror/text": "^0.19.6", 79 | "@codemirror/view": "^0.19.22", 80 | "@lezer/common": "^0.15.0" 81 | } 82 | }, 83 | "node_modules/@codemirror/comment": { 84 | "version": "0.19.1", 85 | "resolved": "https://registry.npmjs.org/@codemirror/comment/-/comment-0.19.1.tgz", 86 | "integrity": "sha512-uGKteBuVWAC6fW+Yt8u27DOnXMT/xV4Ekk2Z5mRsiADCZDqYvryrJd6PLL5+8t64BVyocwQwNfz1UswYS2CtFQ==", 87 | "deprecated": "As of 0.20.0, this package has been merged into @codemirror/commands", 88 | "dependencies": { 89 | "@codemirror/state": "^0.19.9", 90 | "@codemirror/text": "^0.19.0", 91 | "@codemirror/view": "^0.19.0" 92 | } 93 | }, 94 | "node_modules/@codemirror/fold": { 95 | "version": "0.19.4", 96 | "resolved": "https://registry.npmjs.org/@codemirror/fold/-/fold-0.19.4.tgz", 97 | "integrity": "sha512-0SNSkRSOa6gymD6GauHa3sxiysjPhUC0SRVyTlvL52o0gz9GHdc8kNqNQskm3fBtGGOiSriGwF/kAsajRiGhVw==", 98 | "deprecated": "As of 0.20.0, this package has been merged into @codemirror/language", 99 | "dependencies": { 100 | "@codemirror/gutter": "^0.19.0", 101 | "@codemirror/language": "^0.19.0", 102 | "@codemirror/rangeset": "^0.19.0", 103 | "@codemirror/state": "^0.19.0", 104 | "@codemirror/view": "^0.19.22" 105 | } 106 | }, 107 | "node_modules/@codemirror/gutter": { 108 | "version": "0.19.9", 109 | "resolved": "https://registry.npmjs.org/@codemirror/gutter/-/gutter-0.19.9.tgz", 110 | "integrity": "sha512-PFrtmilahin1g6uL27aG5tM/rqR9DZzZYZsIrCXA5Uc2OFTFqx4owuhoU9hqfYxHp5ovfvBwQ+txFzqS4vog6Q==", 111 | "deprecated": "As of 0.20.0, this package has been merged into @codemirror/view", 112 | "dependencies": { 113 | "@codemirror/rangeset": "^0.19.0", 114 | "@codemirror/state": "^0.19.0", 115 | "@codemirror/view": "^0.19.23" 116 | } 117 | }, 118 | "node_modules/@codemirror/highlight": { 119 | "version": "0.19.8", 120 | "resolved": "https://registry.npmjs.org/@codemirror/highlight/-/highlight-0.19.8.tgz", 121 | "integrity": "sha512-v/lzuHjrYR8MN2mEJcUD6fHSTXXli9C1XGYpr+ElV6fLBIUhMTNKR3qThp611xuWfXfwDxeL7ppcbkM/MzPV3A==", 122 | "deprecated": "As of 0.20.0, this package has been split between @lezer/highlight and @codemirror/language", 123 | "dependencies": { 124 | "@codemirror/language": "^0.19.0", 125 | "@codemirror/rangeset": "^0.19.0", 126 | "@codemirror/state": "^0.19.3", 127 | "@codemirror/view": "^0.19.39", 128 | "@lezer/common": "^0.15.0", 129 | "style-mod": "^4.0.0" 130 | } 131 | }, 132 | "node_modules/@codemirror/history": { 133 | "version": "0.19.2", 134 | "resolved": "https://registry.npmjs.org/@codemirror/history/-/history-0.19.2.tgz", 135 | "integrity": "sha512-unhP4t3N2smzmHoo/Yio6ueWi+il8gm9VKrvi6wlcdGH5fOfVDNkmjHQ495SiR+EdOG35+3iNebSPYww0vN7ow==", 136 | "deprecated": "As of 0.20.0, this package has been merged into @codemirror/commands", 137 | "dependencies": { 138 | "@codemirror/state": "^0.19.2", 139 | "@codemirror/view": "^0.19.0" 140 | } 141 | }, 142 | "node_modules/@codemirror/lang-javascript": { 143 | "version": "0.19.7", 144 | "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-0.19.7.tgz", 145 | "integrity": "sha512-DL9f3JLqOEHH9cIwEqqjnP5bkjdVXeECksLtV+/MbPm+l4H+AG+PkwZaJQ2oR1GfPZKh8MVSIE94aGWNkJP8WQ==", 146 | "dependencies": { 147 | "@codemirror/autocomplete": "^0.19.0", 148 | "@codemirror/highlight": "^0.19.7", 149 | "@codemirror/language": "^0.19.0", 150 | "@codemirror/lint": "^0.19.0", 151 | "@codemirror/state": "^0.19.0", 152 | "@codemirror/view": "^0.19.0", 153 | "@lezer/javascript": "^0.15.1" 154 | } 155 | }, 156 | "node_modules/@codemirror/language": { 157 | "version": "0.19.10", 158 | "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-0.19.10.tgz", 159 | "integrity": "sha512-yA0DZ3RYn2CqAAGW62VrU8c4YxscMQn45y/I9sjBlqB1e2OTQLg4CCkMBuMSLXk4xaqjlsgazeOQWaJQOKfV8Q==", 160 | "dependencies": { 161 | "@codemirror/state": "^0.19.0", 162 | "@codemirror/text": "^0.19.0", 163 | "@codemirror/view": "^0.19.0", 164 | "@lezer/common": "^0.15.5", 165 | "@lezer/lr": "^0.15.0" 166 | } 167 | }, 168 | "node_modules/@codemirror/lint": { 169 | "version": "0.19.6", 170 | "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-0.19.6.tgz", 171 | "integrity": "sha512-Pbw1Y5kHVs2J+itQ0uez3dI4qY9ApYVap7eNfV81x1/3/BXgBkKfadaw0gqJ4h4FDG7OnJwb0VbPsjJQllHjaA==", 172 | "dependencies": { 173 | "@codemirror/gutter": "^0.19.4", 174 | "@codemirror/panel": "^0.19.0", 175 | "@codemirror/rangeset": "^0.19.1", 176 | "@codemirror/state": "^0.19.4", 177 | "@codemirror/tooltip": "^0.19.16", 178 | "@codemirror/view": "^0.19.22", 179 | "crelt": "^1.0.5" 180 | } 181 | }, 182 | "node_modules/@codemirror/matchbrackets": { 183 | "version": "0.19.4", 184 | "resolved": "https://registry.npmjs.org/@codemirror/matchbrackets/-/matchbrackets-0.19.4.tgz", 185 | "integrity": "sha512-VFkaOKPNudAA5sGP1zikRHCEKU0hjYmkKpr04pybUpQvfTvNJXlReCyP0rvH/1iEwAGPL990ZTT+QrLdu4MeEA==", 186 | "deprecated": "As of 0.20.0, this package has been merged into @codemirror/language", 187 | "dependencies": { 188 | "@codemirror/language": "^0.19.0", 189 | "@codemirror/state": "^0.19.0", 190 | "@codemirror/view": "^0.19.0", 191 | "@lezer/common": "^0.15.0" 192 | } 193 | }, 194 | "node_modules/@codemirror/panel": { 195 | "version": "0.19.1", 196 | "resolved": "https://registry.npmjs.org/@codemirror/panel/-/panel-0.19.1.tgz", 197 | "integrity": "sha512-sYeOCMA3KRYxZYJYn5PNlt9yNsjy3zTNTrbYSfVgjgL9QomIVgOJWPO5hZ2sTN8lufO6lw0vTBsIPL9MSidmBg==", 198 | "deprecated": "As of 0.20.0, this package has been merged into @codemirror/view", 199 | "dependencies": { 200 | "@codemirror/state": "^0.19.0", 201 | "@codemirror/view": "^0.19.0" 202 | } 203 | }, 204 | "node_modules/@codemirror/rangeset": { 205 | "version": "0.19.9", 206 | "resolved": "https://registry.npmjs.org/@codemirror/rangeset/-/rangeset-0.19.9.tgz", 207 | "integrity": "sha512-V8YUuOvK+ew87Xem+71nKcqu1SXd5QROMRLMS/ljT5/3MCxtgrRie1Cvild0G/Z2f1fpWxzX78V0U4jjXBorBQ==", 208 | "deprecated": "As of 0.20.0, this package has been merged into @codemirror/state", 209 | "dependencies": { 210 | "@codemirror/state": "^0.19.0" 211 | } 212 | }, 213 | "node_modules/@codemirror/rectangular-selection": { 214 | "version": "0.19.2", 215 | "resolved": "https://registry.npmjs.org/@codemirror/rectangular-selection/-/rectangular-selection-0.19.2.tgz", 216 | "integrity": "sha512-AXK/p5eGwFJ9GJcLfntqN4dgY+XiIF7eHfXNQJX5HhQLSped2wJE6WuC1rMEaOlcpOqlb9mrNi/ZdUjSIj9mbA==", 217 | "deprecated": "As of 0.20.0, this package has been merged into @codemirror/view", 218 | "dependencies": { 219 | "@codemirror/state": "^0.19.0", 220 | "@codemirror/text": "^0.19.4", 221 | "@codemirror/view": "^0.19.48" 222 | } 223 | }, 224 | "node_modules/@codemirror/search": { 225 | "version": "0.19.10", 226 | "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-0.19.10.tgz", 227 | "integrity": "sha512-qjubm69HJixPBWzI6HeEghTWOOD8NXiHOTRNvdizqs8xWRuFChq9zkjD3XiAJ7GXSTzCuQJnAP9DBBGCLq4ZIA==", 228 | "dependencies": { 229 | "@codemirror/panel": "^0.19.0", 230 | "@codemirror/rangeset": "^0.19.0", 231 | "@codemirror/state": "^0.19.3", 232 | "@codemirror/text": "^0.19.0", 233 | "@codemirror/view": "^0.19.34", 234 | "crelt": "^1.0.5" 235 | } 236 | }, 237 | "node_modules/@codemirror/state": { 238 | "version": "0.19.9", 239 | "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-0.19.9.tgz", 240 | "integrity": "sha512-psOzDolKTZkx4CgUqhBQ8T8gBc0xN5z4gzed109aF6x7D7umpDRoimacI/O6d9UGuyl4eYuDCZmDFr2Rq7aGOw==", 241 | "dependencies": { 242 | "@codemirror/text": "^0.19.0" 243 | } 244 | }, 245 | "node_modules/@codemirror/text": { 246 | "version": "0.19.6", 247 | "resolved": "https://registry.npmjs.org/@codemirror/text/-/text-0.19.6.tgz", 248 | "integrity": "sha512-T9jnREMIygx+TPC1bOuepz18maGq/92q2a+n4qTqObKwvNMg+8cMTslb8yxeEDEq7S3kpgGWxgO1UWbQRij0dA==", 249 | "deprecated": "As of 0.20.0, this package has been merged into @codemirror/state" 250 | }, 251 | "node_modules/@codemirror/tooltip": { 252 | "version": "0.19.16", 253 | "resolved": "https://registry.npmjs.org/@codemirror/tooltip/-/tooltip-0.19.16.tgz", 254 | "integrity": "sha512-zxKDHryUV5/RS45AQL+wOeN+i7/l81wK56OMnUPoTSzCWNITfxHn7BToDsjtrRKbzHqUxKYmBnn/4hPjpZ4WJQ==", 255 | "deprecated": "As of 0.20.0, this package has been merged into @codemirror/view", 256 | "dependencies": { 257 | "@codemirror/state": "^0.19.0", 258 | "@codemirror/view": "^0.19.0" 259 | } 260 | }, 261 | "node_modules/@codemirror/view": { 262 | "version": "0.19.48", 263 | "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-0.19.48.tgz", 264 | "integrity": "sha512-0eg7D2Nz4S8/caetCTz61rK0tkHI17V/d15Jy0kLOT8dTLGGNJUponDnW28h2B6bERmPlVHKh8MJIr5OCp1nGw==", 265 | "dependencies": { 266 | "@codemirror/rangeset": "^0.19.5", 267 | "@codemirror/state": "^0.19.3", 268 | "@codemirror/text": "^0.19.0", 269 | "style-mod": "^4.0.0", 270 | "w3c-keyname": "^2.2.4" 271 | } 272 | }, 273 | "node_modules/@lezer/common": { 274 | "version": "0.15.12", 275 | "resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz", 276 | "integrity": "sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==" 277 | }, 278 | "node_modules/@lezer/javascript": { 279 | "version": "0.15.3", 280 | "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-0.15.3.tgz", 281 | "integrity": "sha512-8jA2NpOfpWwSPZxRhd9BxK2ZPvGd7nLE3LFTJ5AbMhXAzMHeMjneV6GEVd7dAIee85dtap0jdb6bgOSO0+lfwA==", 282 | "dependencies": { 283 | "@lezer/lr": "^0.15.0" 284 | } 285 | }, 286 | "node_modules/@lezer/lr": { 287 | "version": "0.15.8", 288 | "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.15.8.tgz", 289 | "integrity": "sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==", 290 | "dependencies": { 291 | "@lezer/common": "^0.15.0" 292 | } 293 | }, 294 | "node_modules/abstract-leveldown": { 295 | "version": "6.2.3", 296 | "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz", 297 | "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==", 298 | "optional": true, 299 | "dependencies": { 300 | "buffer": "^5.5.0", 301 | "immediate": "^3.2.3", 302 | "level-concat-iterator": "~2.0.0", 303 | "level-supports": "~1.0.0", 304 | "xtend": "~4.0.0" 305 | }, 306 | "engines": { 307 | "node": ">=6" 308 | } 309 | }, 310 | "node_modules/async-limiter": { 311 | "version": "1.0.1", 312 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", 313 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", 314 | "optional": true 315 | }, 316 | "node_modules/base64-js": { 317 | "version": "1.5.1", 318 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 319 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 320 | "funding": [ 321 | { 322 | "type": "github", 323 | "url": "https://github.com/sponsors/feross" 324 | }, 325 | { 326 | "type": "patreon", 327 | "url": "https://www.patreon.com/feross" 328 | }, 329 | { 330 | "type": "consulting", 331 | "url": "https://feross.org/support" 332 | } 333 | ], 334 | "optional": true 335 | }, 336 | "node_modules/buffer": { 337 | "version": "5.7.1", 338 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 339 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 340 | "funding": [ 341 | { 342 | "type": "github", 343 | "url": "https://github.com/sponsors/feross" 344 | }, 345 | { 346 | "type": "patreon", 347 | "url": "https://www.patreon.com/feross" 348 | }, 349 | { 350 | "type": "consulting", 351 | "url": "https://feross.org/support" 352 | } 353 | ], 354 | "optional": true, 355 | "dependencies": { 356 | "base64-js": "^1.3.1", 357 | "ieee754": "^1.1.13" 358 | } 359 | }, 360 | "node_modules/crelt": { 361 | "version": "1.0.6", 362 | "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", 363 | "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" 364 | }, 365 | "node_modules/deferred-leveldown": { 366 | "version": "5.3.0", 367 | "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz", 368 | "integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==", 369 | "optional": true, 370 | "dependencies": { 371 | "abstract-leveldown": "~6.2.1", 372 | "inherits": "^2.0.3" 373 | }, 374 | "engines": { 375 | "node": ">=6" 376 | } 377 | }, 378 | "node_modules/encoding-down": { 379 | "version": "6.3.0", 380 | "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz", 381 | "integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==", 382 | "optional": true, 383 | "dependencies": { 384 | "abstract-leveldown": "^6.2.1", 385 | "inherits": "^2.0.3", 386 | "level-codec": "^9.0.0", 387 | "level-errors": "^2.0.0" 388 | }, 389 | "engines": { 390 | "node": ">=6" 391 | } 392 | }, 393 | "node_modules/errno": { 394 | "version": "0.1.8", 395 | "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", 396 | "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", 397 | "optional": true, 398 | "dependencies": { 399 | "prr": "~1.0.1" 400 | }, 401 | "bin": { 402 | "errno": "cli.js" 403 | } 404 | }, 405 | "node_modules/ieee754": { 406 | "version": "1.2.1", 407 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 408 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 409 | "funding": [ 410 | { 411 | "type": "github", 412 | "url": "https://github.com/sponsors/feross" 413 | }, 414 | { 415 | "type": "patreon", 416 | "url": "https://www.patreon.com/feross" 417 | }, 418 | { 419 | "type": "consulting", 420 | "url": "https://feross.org/support" 421 | } 422 | ], 423 | "optional": true 424 | }, 425 | "node_modules/immediate": { 426 | "version": "3.3.0", 427 | "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", 428 | "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==", 429 | "optional": true 430 | }, 431 | "node_modules/inherits": { 432 | "version": "2.0.4", 433 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 434 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 435 | "optional": true 436 | }, 437 | "node_modules/isomorphic.js": { 438 | "version": "0.2.5", 439 | "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", 440 | "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", 441 | "funding": { 442 | "type": "GitHub Sponsors ❤", 443 | "url": "https://github.com/sponsors/dmonad" 444 | } 445 | }, 446 | "node_modules/level": { 447 | "version": "6.0.1", 448 | "resolved": "https://registry.npmjs.org/level/-/level-6.0.1.tgz", 449 | "integrity": "sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==", 450 | "optional": true, 451 | "dependencies": { 452 | "level-js": "^5.0.0", 453 | "level-packager": "^5.1.0", 454 | "leveldown": "^5.4.0" 455 | }, 456 | "engines": { 457 | "node": ">=8.6.0" 458 | }, 459 | "funding": { 460 | "type": "opencollective", 461 | "url": "https://opencollective.com/level" 462 | } 463 | }, 464 | "node_modules/level-codec": { 465 | "version": "9.0.2", 466 | "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz", 467 | "integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==", 468 | "optional": true, 469 | "dependencies": { 470 | "buffer": "^5.6.0" 471 | }, 472 | "engines": { 473 | "node": ">=6" 474 | } 475 | }, 476 | "node_modules/level-concat-iterator": { 477 | "version": "2.0.1", 478 | "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz", 479 | "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==", 480 | "optional": true, 481 | "engines": { 482 | "node": ">=6" 483 | } 484 | }, 485 | "node_modules/level-errors": { 486 | "version": "2.0.1", 487 | "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", 488 | "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", 489 | "optional": true, 490 | "dependencies": { 491 | "errno": "~0.1.1" 492 | }, 493 | "engines": { 494 | "node": ">=6" 495 | } 496 | }, 497 | "node_modules/level-iterator-stream": { 498 | "version": "4.0.2", 499 | "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz", 500 | "integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==", 501 | "optional": true, 502 | "dependencies": { 503 | "inherits": "^2.0.4", 504 | "readable-stream": "^3.4.0", 505 | "xtend": "^4.0.2" 506 | }, 507 | "engines": { 508 | "node": ">=6" 509 | } 510 | }, 511 | "node_modules/level-js": { 512 | "version": "5.0.2", 513 | "resolved": "https://registry.npmjs.org/level-js/-/level-js-5.0.2.tgz", 514 | "integrity": "sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==", 515 | "optional": true, 516 | "dependencies": { 517 | "abstract-leveldown": "~6.2.3", 518 | "buffer": "^5.5.0", 519 | "inherits": "^2.0.3", 520 | "ltgt": "^2.1.2" 521 | } 522 | }, 523 | "node_modules/level-packager": { 524 | "version": "5.1.1", 525 | "resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz", 526 | "integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==", 527 | "optional": true, 528 | "dependencies": { 529 | "encoding-down": "^6.3.0", 530 | "levelup": "^4.3.2" 531 | }, 532 | "engines": { 533 | "node": ">=6" 534 | } 535 | }, 536 | "node_modules/level-supports": { 537 | "version": "1.0.1", 538 | "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", 539 | "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==", 540 | "optional": true, 541 | "dependencies": { 542 | "xtend": "^4.0.2" 543 | }, 544 | "engines": { 545 | "node": ">=6" 546 | } 547 | }, 548 | "node_modules/leveldown": { 549 | "version": "5.6.0", 550 | "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.6.0.tgz", 551 | "integrity": "sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==", 552 | "hasInstallScript": true, 553 | "optional": true, 554 | "dependencies": { 555 | "abstract-leveldown": "~6.2.1", 556 | "napi-macros": "~2.0.0", 557 | "node-gyp-build": "~4.1.0" 558 | }, 559 | "engines": { 560 | "node": ">=8.6.0" 561 | } 562 | }, 563 | "node_modules/levelup": { 564 | "version": "4.4.0", 565 | "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz", 566 | "integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==", 567 | "optional": true, 568 | "dependencies": { 569 | "deferred-leveldown": "~5.3.0", 570 | "level-errors": "~2.0.0", 571 | "level-iterator-stream": "~4.0.0", 572 | "level-supports": "~1.0.0", 573 | "xtend": "~4.0.0" 574 | }, 575 | "engines": { 576 | "node": ">=6" 577 | } 578 | }, 579 | "node_modules/lib0": { 580 | "version": "0.2.88", 581 | "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.88.tgz", 582 | "integrity": "sha512-KyroiEvCeZcZEMx5Ys+b4u4eEBbA1ch7XUaBhYpwa/nPMrzTjUhI4RfcytmQfYoTBPcdyx+FX6WFNIoNuJzJfQ==", 583 | "dependencies": { 584 | "isomorphic.js": "^0.2.4" 585 | }, 586 | "bin": { 587 | "0gentesthtml": "bin/gentesthtml.js", 588 | "0serve": "bin/0serve.js" 589 | }, 590 | "engines": { 591 | "node": ">=16" 592 | }, 593 | "funding": { 594 | "type": "GitHub Sponsors ❤", 595 | "url": "https://github.com/sponsors/dmonad" 596 | } 597 | }, 598 | "node_modules/lodash.debounce": { 599 | "version": "4.0.8", 600 | "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", 601 | "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" 602 | }, 603 | "node_modules/ltgt": { 604 | "version": "2.2.1", 605 | "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", 606 | "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", 607 | "optional": true 608 | }, 609 | "node_modules/napi-macros": { 610 | "version": "2.0.0", 611 | "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", 612 | "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", 613 | "optional": true 614 | }, 615 | "node_modules/node-gyp-build": { 616 | "version": "4.1.1", 617 | "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.1.1.tgz", 618 | "integrity": "sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==", 619 | "optional": true, 620 | "bin": { 621 | "node-gyp-build": "bin.js", 622 | "node-gyp-build-optional": "optional.js", 623 | "node-gyp-build-test": "build-test.js" 624 | } 625 | }, 626 | "node_modules/prr": { 627 | "version": "1.0.1", 628 | "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", 629 | "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", 630 | "optional": true 631 | }, 632 | "node_modules/readable-stream": { 633 | "version": "3.6.2", 634 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 635 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 636 | "optional": true, 637 | "dependencies": { 638 | "inherits": "^2.0.3", 639 | "string_decoder": "^1.1.1", 640 | "util-deprecate": "^1.0.1" 641 | }, 642 | "engines": { 643 | "node": ">= 6" 644 | } 645 | }, 646 | "node_modules/safe-buffer": { 647 | "version": "5.2.1", 648 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 649 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 650 | "funding": [ 651 | { 652 | "type": "github", 653 | "url": "https://github.com/sponsors/feross" 654 | }, 655 | { 656 | "type": "patreon", 657 | "url": "https://www.patreon.com/feross" 658 | }, 659 | { 660 | "type": "consulting", 661 | "url": "https://feross.org/support" 662 | } 663 | ], 664 | "optional": true 665 | }, 666 | "node_modules/string_decoder": { 667 | "version": "1.3.0", 668 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 669 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 670 | "optional": true, 671 | "dependencies": { 672 | "safe-buffer": "~5.2.0" 673 | } 674 | }, 675 | "node_modules/style-mod": { 676 | "version": "4.1.0", 677 | "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", 678 | "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==" 679 | }, 680 | "node_modules/util-deprecate": { 681 | "version": "1.0.2", 682 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 683 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 684 | "optional": true 685 | }, 686 | "node_modules/w3c-keyname": { 687 | "version": "2.2.8", 688 | "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", 689 | "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" 690 | }, 691 | "node_modules/ws": { 692 | "version": "6.2.2", 693 | "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", 694 | "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", 695 | "optional": true, 696 | "dependencies": { 697 | "async-limiter": "~1.0.0" 698 | } 699 | }, 700 | "node_modules/xtend": { 701 | "version": "4.0.2", 702 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 703 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 704 | "optional": true, 705 | "engines": { 706 | "node": ">=0.4" 707 | } 708 | }, 709 | "node_modules/y-codemirror.next": { 710 | "version": "0.1.4", 711 | "resolved": "https://registry.npmjs.org/y-codemirror.next/-/y-codemirror.next-0.1.4.tgz", 712 | "integrity": "sha512-0mekvY+hlqauyqvfXGc4ooT5kHSUIkROzO7WjGoE7pcEvRFhSbGmg1vJFvZcLBCy1KOvSE7NlcO0sKfQWdZenw==", 713 | "dependencies": { 714 | "lib0": "^0.2.42" 715 | }, 716 | "funding": { 717 | "type": "GitHub Sponsors ❤", 718 | "url": "https://github.com/sponsors/dmonad" 719 | }, 720 | "peerDependencies": { 721 | "@codemirror/state": "^0.19.0", 722 | "@codemirror/view": "^0.19.0", 723 | "yjs": "^13.5.6" 724 | } 725 | }, 726 | "node_modules/y-leveldb": { 727 | "version": "0.1.2", 728 | "resolved": "https://registry.npmjs.org/y-leveldb/-/y-leveldb-0.1.2.tgz", 729 | "integrity": "sha512-6ulEn5AXfXJYi89rXPEg2mMHAyyw8+ZfeMMdOtBbV8FJpQ1NOrcgi6DTAcXof0dap84NjHPT2+9d0rb6cFsjEg==", 730 | "optional": true, 731 | "dependencies": { 732 | "level": "^6.0.1", 733 | "lib0": "^0.2.31" 734 | }, 735 | "funding": { 736 | "type": "GitHub Sponsors ❤", 737 | "url": "https://github.com/sponsors/dmonad" 738 | }, 739 | "peerDependencies": { 740 | "yjs": "^13.0.0" 741 | } 742 | }, 743 | "node_modules/y-protocols": { 744 | "version": "1.0.6", 745 | "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz", 746 | "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==", 747 | "dependencies": { 748 | "lib0": "^0.2.85" 749 | }, 750 | "engines": { 751 | "node": ">=16.0.0", 752 | "npm": ">=8.0.0" 753 | }, 754 | "funding": { 755 | "type": "GitHub Sponsors ❤", 756 | "url": "https://github.com/sponsors/dmonad" 757 | }, 758 | "peerDependencies": { 759 | "yjs": "^13.0.0" 760 | } 761 | }, 762 | "node_modules/y-websocket": { 763 | "version": "1.5.0", 764 | "resolved": "https://registry.npmjs.org/y-websocket/-/y-websocket-1.5.0.tgz", 765 | "integrity": "sha512-A8AO6XtnQlYwWFytWdkDCeXg4l8ghRTIw5h2YUgUYDmEC9ugWGIwYNW80yadhSFAF7CvuWTEkQNEpevnH6EiZw==", 766 | "dependencies": { 767 | "lib0": "^0.2.52", 768 | "lodash.debounce": "^4.0.8", 769 | "y-protocols": "^1.0.5" 770 | }, 771 | "bin": { 772 | "y-websocket": "bin/server.js", 773 | "y-websocket-server": "bin/server.js" 774 | }, 775 | "funding": { 776 | "type": "GitHub Sponsors ❤", 777 | "url": "https://github.com/sponsors/dmonad" 778 | }, 779 | "optionalDependencies": { 780 | "ws": "^6.2.1", 781 | "y-leveldb": "^0.1.0" 782 | }, 783 | "peerDependencies": { 784 | "yjs": "^13.5.6" 785 | } 786 | }, 787 | "node_modules/yjs": { 788 | "version": "13.6.10", 789 | "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.10.tgz", 790 | "integrity": "sha512-1JcyQek1vaMyrDm7Fqfa+pvHg/DURSbVo4VmeN7wjnTKB/lZrfIPhdCj7d8sboK6zLfRBJXegTjc9JlaDd8/Zw==", 791 | "dependencies": { 792 | "lib0": "^0.2.86" 793 | }, 794 | "engines": { 795 | "node": ">=16.0.0", 796 | "npm": ">=8.0.0" 797 | }, 798 | "funding": { 799 | "type": "GitHub Sponsors ❤", 800 | "url": "https://github.com/sponsors/dmonad" 801 | } 802 | } 803 | } 804 | } 805 | -------------------------------------------------------------------------------- /codemirror.next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yjs-codemirror-next-demo", 3 | "version": "1.0.0", 4 | "description": "Yjs ❤ CodeMirror", 5 | "scripts": { 6 | "watch": "webpack -w --stats errors-only", 7 | "dist": "webpack --mode=production", 8 | "start": "webpack serve --open codemirror.next.html" 9 | }, 10 | "author": "Kevin Jahns ", 11 | "license": "UNLICENSE", 12 | "dependencies": { 13 | "@codemirror/basic-setup": "^0.19.0", 14 | "@codemirror/lang-javascript": "^0.19.0", 15 | "@codemirror/state": "^0.19.0", 16 | "@codemirror/view": "^0.19.0", 17 | "y-codemirror.next": "^0.1.0", 18 | "y-websocket": "^1.3.18", 19 | "yjs": "^13.5.22" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /codemirror.next/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | mode: 'development', 5 | devtool: 'source-map', 6 | entry: { 7 | codemirror: './codemirror.next.js' 8 | }, 9 | output: { 10 | globalObject: 'self', 11 | path: path.resolve(__dirname, './dist/'), 12 | filename: '[name].bundle.js', 13 | publicPath: '/codemirror/dist/' 14 | }, 15 | devServer: { 16 | static: path.join(__dirname), 17 | compress: true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /codemirror/README.md: -------------------------------------------------------------------------------- 1 | # CodeMirror 5 Demo 2 | > [y-codemirror](https://docs.yjs.dev/ecosystem/editor-bindings/codemirror) / [y-websocket](https://docs.yjs.dev/ecosystem/connection-provider/y-websocket) / [Live Demo](https://demos.yjs.dev/codemirror/codemirror.html) 3 | 4 | This is a demo of a [CodeMirror 5](https://codemirror.net/5/) editor that was made collaborative with Yjs & y-codemirror. Note that CodeMirror 6 is now the latest release. 5 | 6 | We use the [y-websocket](https://docs.yjs.dev/ecosystem/connection-provider) provider to share document updates through a server. There are many more providers available for Yjs - try switching to another provider. [See docs](https://docs.yjs.dev/ecosystem/connection-provider). 7 | 8 | Also you could try adding offline persistence to this demo. Wouldn't it be cool if document updates are persisted in the browser, so you can load your application load faster? Try it out: https://docs.yjs.dev/getting-started/allowing-offline-editing 9 | 10 | ## Quick Start 11 | 12 | ```sh 13 | npm i 14 | npm start 15 | # Project is running at http://localhost:8080/ 16 | ``` 17 | -------------------------------------------------------------------------------- /codemirror/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | color: black; 8 | direction: ltr; 9 | } 10 | 11 | /* PADDING */ 12 | 13 | .CodeMirror-lines { 14 | padding: 4px 0; /* Vertical padding around content */ 15 | } 16 | .CodeMirror pre.CodeMirror-line, 17 | .CodeMirror pre.CodeMirror-line-like { 18 | padding: 0 4px; /* Horizontal padding of content */ 19 | } 20 | 21 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 22 | background-color: white; /* The little square between H and V scrollbars */ 23 | } 24 | 25 | /* GUTTER */ 26 | 27 | .CodeMirror-gutters { 28 | border-right: 1px solid #ddd; 29 | background-color: #f7f7f7; 30 | white-space: nowrap; 31 | } 32 | .CodeMirror-linenumbers {} 33 | .CodeMirror-linenumber { 34 | padding: 0 3px 0 5px; 35 | min-width: 20px; 36 | text-align: right; 37 | color: #999; 38 | white-space: nowrap; 39 | } 40 | 41 | .CodeMirror-guttermarker { color: black; } 42 | .CodeMirror-guttermarker-subtle { color: #999; } 43 | 44 | /* CURSOR */ 45 | 46 | .CodeMirror-cursor { 47 | border-left: 1px solid black; 48 | border-right: none; 49 | width: 0; 50 | } 51 | /* Shown when moving in bi-directional text */ 52 | .CodeMirror div.CodeMirror-secondarycursor { 53 | border-left: 1px solid silver; 54 | } 55 | .cm-fat-cursor .CodeMirror-cursor { 56 | width: auto; 57 | border: 0 !important; 58 | background: #7e7; 59 | } 60 | .cm-fat-cursor div.CodeMirror-cursors { 61 | z-index: 1; 62 | } 63 | .cm-fat-cursor .CodeMirror-line::selection, 64 | .cm-fat-cursor .CodeMirror-line > span::selection, 65 | .cm-fat-cursor .CodeMirror-line > span > span::selection { background: transparent; } 66 | .cm-fat-cursor .CodeMirror-line::-moz-selection, 67 | .cm-fat-cursor .CodeMirror-line > span::-moz-selection, 68 | .cm-fat-cursor .CodeMirror-line > span > span::-moz-selection { background: transparent; } 69 | .cm-fat-cursor { caret-color: transparent; } 70 | @-moz-keyframes blink { 71 | 0% {} 72 | 50% { background-color: transparent; } 73 | 100% {} 74 | } 75 | @-webkit-keyframes blink { 76 | 0% {} 77 | 50% { background-color: transparent; } 78 | 100% {} 79 | } 80 | @keyframes blink { 81 | 0% {} 82 | 50% { background-color: transparent; } 83 | 100% {} 84 | } 85 | 86 | /* Can style cursor different in overwrite (non-insert) mode */ 87 | .CodeMirror-overwrite .CodeMirror-cursor {} 88 | 89 | .cm-tab { display: inline-block; text-decoration: inherit; } 90 | 91 | .CodeMirror-rulers { 92 | position: absolute; 93 | left: 0; right: 0; top: -50px; bottom: 0; 94 | overflow: hidden; 95 | } 96 | .CodeMirror-ruler { 97 | border-left: 1px solid #ccc; 98 | top: 0; bottom: 0; 99 | position: absolute; 100 | } 101 | 102 | /* DEFAULT THEME */ 103 | 104 | .cm-s-default .cm-header {color: blue;} 105 | .cm-s-default .cm-quote {color: #090;} 106 | .cm-negative {color: #d44;} 107 | .cm-positive {color: #292;} 108 | .cm-header, .cm-strong {font-weight: bold;} 109 | .cm-em {font-style: italic;} 110 | .cm-link {text-decoration: underline;} 111 | .cm-strikethrough {text-decoration: line-through;} 112 | 113 | .cm-s-default .cm-keyword {color: #708;} 114 | .cm-s-default .cm-atom {color: #219;} 115 | .cm-s-default .cm-number {color: #164;} 116 | .cm-s-default .cm-def {color: #00f;} 117 | .cm-s-default .cm-variable, 118 | .cm-s-default .cm-punctuation, 119 | .cm-s-default .cm-property, 120 | .cm-s-default .cm-operator {} 121 | .cm-s-default .cm-variable-2 {color: #05a;} 122 | .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} 123 | .cm-s-default .cm-comment {color: #a50;} 124 | .cm-s-default .cm-string {color: #a11;} 125 | .cm-s-default .cm-string-2 {color: #f50;} 126 | .cm-s-default .cm-meta {color: #555;} 127 | .cm-s-default .cm-qualifier {color: #555;} 128 | .cm-s-default .cm-builtin {color: #30a;} 129 | .cm-s-default .cm-bracket {color: #997;} 130 | .cm-s-default .cm-tag {color: #170;} 131 | .cm-s-default .cm-attribute {color: #00c;} 132 | .cm-s-default .cm-hr {color: #999;} 133 | .cm-s-default .cm-link {color: #00c;} 134 | 135 | .cm-s-default .cm-error {color: #f00;} 136 | .cm-invalidchar {color: #f00;} 137 | 138 | .CodeMirror-composing { border-bottom: 2px solid; } 139 | 140 | /* Default styles for common addons */ 141 | 142 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} 143 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} 144 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 145 | .CodeMirror-activeline-background {background: #e8f2ff;} 146 | 147 | /* STOP */ 148 | 149 | /* The rest of this file contains styles related to the mechanics of 150 | the editor. You probably shouldn't touch them. */ 151 | 152 | .CodeMirror { 153 | position: relative; 154 | overflow: hidden; 155 | background: white; 156 | } 157 | 158 | .CodeMirror-scroll { 159 | overflow: scroll !important; /* Things will break if this is overridden */ 160 | /* 50px is the magic margin used to hide the element's real scrollbars */ 161 | /* See overflow: hidden in .CodeMirror */ 162 | margin-bottom: -50px; margin-right: -50px; 163 | padding-bottom: 50px; 164 | height: 100%; 165 | outline: none; /* Prevent dragging from highlighting the element */ 166 | position: relative; 167 | z-index: 0; 168 | } 169 | .CodeMirror-sizer { 170 | position: relative; 171 | border-right: 50px solid transparent; 172 | } 173 | 174 | /* The fake, visible scrollbars. Used to force redraw during scrolling 175 | before actual scrolling happens, thus preventing shaking and 176 | flickering artifacts. */ 177 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 178 | position: absolute; 179 | z-index: 6; 180 | display: none; 181 | outline: none; 182 | } 183 | .CodeMirror-vscrollbar { 184 | right: 0; top: 0; 185 | overflow-x: hidden; 186 | overflow-y: scroll; 187 | } 188 | .CodeMirror-hscrollbar { 189 | bottom: 0; left: 0; 190 | overflow-y: hidden; 191 | overflow-x: scroll; 192 | } 193 | .CodeMirror-scrollbar-filler { 194 | right: 0; bottom: 0; 195 | } 196 | .CodeMirror-gutter-filler { 197 | left: 0; bottom: 0; 198 | } 199 | 200 | .CodeMirror-gutters { 201 | position: absolute; left: 0; top: 0; 202 | min-height: 100%; 203 | z-index: 3; 204 | } 205 | .CodeMirror-gutter { 206 | white-space: normal; 207 | height: 100%; 208 | display: inline-block; 209 | vertical-align: top; 210 | margin-bottom: -50px; 211 | } 212 | .CodeMirror-gutter-wrapper { 213 | position: absolute; 214 | z-index: 4; 215 | background: none !important; 216 | border: none !important; 217 | } 218 | .CodeMirror-gutter-background { 219 | position: absolute; 220 | top: 0; bottom: 0; 221 | z-index: 4; 222 | } 223 | .CodeMirror-gutter-elt { 224 | position: absolute; 225 | cursor: default; 226 | z-index: 4; 227 | } 228 | .CodeMirror-gutter-wrapper ::selection { background-color: transparent } 229 | .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } 230 | 231 | .CodeMirror-lines { 232 | cursor: text; 233 | min-height: 1px; /* prevents collapsing before first draw */ 234 | } 235 | .CodeMirror pre.CodeMirror-line, 236 | .CodeMirror pre.CodeMirror-line-like { 237 | /* Reset some styles that the rest of the page might have set */ 238 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 239 | border-width: 0; 240 | background: transparent; 241 | font-family: inherit; 242 | font-size: inherit; 243 | margin: 0; 244 | white-space: pre; 245 | word-wrap: normal; 246 | line-height: inherit; 247 | color: inherit; 248 | z-index: 2; 249 | position: relative; 250 | overflow: visible; 251 | -webkit-tap-highlight-color: transparent; 252 | -webkit-font-variant-ligatures: contextual; 253 | font-variant-ligatures: contextual; 254 | } 255 | .CodeMirror-wrap pre.CodeMirror-line, 256 | .CodeMirror-wrap pre.CodeMirror-line-like { 257 | word-wrap: break-word; 258 | white-space: pre-wrap; 259 | word-break: normal; 260 | } 261 | 262 | .CodeMirror-linebackground { 263 | position: absolute; 264 | left: 0; right: 0; top: 0; bottom: 0; 265 | z-index: 0; 266 | } 267 | 268 | .CodeMirror-linewidget { 269 | position: relative; 270 | z-index: 2; 271 | padding: 0.1px; /* Force widget margins to stay inside of the container */ 272 | } 273 | 274 | .CodeMirror-widget {} 275 | 276 | .CodeMirror-rtl pre { direction: rtl; } 277 | 278 | .CodeMirror-code { 279 | outline: none; 280 | } 281 | 282 | /* Force content-box sizing for the elements where we expect it */ 283 | .CodeMirror-scroll, 284 | .CodeMirror-sizer, 285 | .CodeMirror-gutter, 286 | .CodeMirror-gutters, 287 | .CodeMirror-linenumber { 288 | -moz-box-sizing: content-box; 289 | box-sizing: content-box; 290 | } 291 | 292 | .CodeMirror-measure { 293 | position: absolute; 294 | width: 100%; 295 | height: 0; 296 | overflow: hidden; 297 | visibility: hidden; 298 | } 299 | 300 | .CodeMirror-cursor { 301 | position: absolute; 302 | pointer-events: none; 303 | } 304 | .CodeMirror-measure pre { position: static; } 305 | 306 | div.CodeMirror-cursors { 307 | visibility: hidden; 308 | position: relative; 309 | z-index: 3; 310 | } 311 | div.CodeMirror-dragcursors { 312 | visibility: visible; 313 | } 314 | 315 | .CodeMirror-focused div.CodeMirror-cursors { 316 | visibility: visible; 317 | } 318 | 319 | .CodeMirror-selected { background: #d9d9d9; } 320 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 321 | .CodeMirror-crosshair { cursor: crosshair; } 322 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 323 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 324 | 325 | .cm-searching { 326 | background-color: #ffa; 327 | background-color: rgba(255, 255, 0, .4); 328 | } 329 | 330 | /* Used to force a border model for a node */ 331 | .cm-force-border { padding-right: .1px; } 332 | 333 | @media print { 334 | /* Hide the cursor when printing */ 335 | .CodeMirror div.CodeMirror-cursors { 336 | visibility: hidden; 337 | } 338 | } 339 | 340 | /* See issue #2901 */ 341 | .cm-tab-wrap-hack:after { content: ''; } 342 | 343 | /* Help users use markselection to safely style text background */ 344 | span.CodeMirror-selectedtext { background: none; } 345 | -------------------------------------------------------------------------------- /codemirror/codemirror.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Yjs CodeMirror Example 6 | 7 | 8 | 42 | 43 | 44 | 45 |

46 |

47 | This is a demo of the Yjs ⇔ 48 | CodeMirror binding: 49 | y-codemirror. 50 |

51 |

52 | The content of this editor is shared with every client that visits this 53 | domain. 54 |

55 | 56 | 57 | -------------------------------------------------------------------------------- /codemirror/codemirror.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | // @ts-ignore 4 | import CodeMirror from 'codemirror' 5 | import * as Y from 'yjs' 6 | import { WebsocketProvider } from 'y-websocket' 7 | import { CodemirrorBinding } from 'y-codemirror' 8 | import 'codemirror/mode/javascript/javascript.js' 9 | 10 | const roomname = `codemirror-demo-${new Date().toLocaleDateString('en-CA')}` 11 | 12 | window.addEventListener('load', () => { 13 | const ydoc = new Y.Doc() 14 | const provider = new WebsocketProvider( 15 | 'wss://demos.yjs.dev/ws', // use the public ws server 16 | // `ws${location.protocol.slice(4)}//${location.host}/ws`, // alternatively: use the local ws server (run `npm start` in root directory) 17 | roomname, 18 | ydoc 19 | ) 20 | const ytext = ydoc.getText('codemirror') 21 | const editorContainer = document.createElement('div') 22 | editorContainer.setAttribute('id', 'editor') 23 | document.body.insertBefore(editorContainer, null) 24 | 25 | const editor = CodeMirror(editorContainer, { 26 | mode: 'javascript', 27 | lineNumbers: true 28 | }) 29 | 30 | const binding = new CodemirrorBinding(ytext, editor, provider.awareness) 31 | 32 | const connectBtn = /** @type {HTMLElement} */ (document.getElementById('y-connect-btn')) 33 | connectBtn.addEventListener('click', () => { 34 | if (provider.shouldConnect) { 35 | provider.disconnect() 36 | connectBtn.textContent = 'Connect' 37 | } else { 38 | provider.connect() 39 | connectBtn.textContent = 'Disconnect' 40 | } 41 | }) 42 | 43 | // @ts-ignore 44 | window.example = { provider, ydoc, ytext, binding, Y } 45 | }) 46 | -------------------------------------------------------------------------------- /codemirror/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yjs-codemirror-demo", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "yjs-codemirror-demo", 9 | "version": "1.0.0", 10 | "license": "UNLICENSE", 11 | "dependencies": { 12 | "codemirror": "^5.64.0", 13 | "y-codemirror": "^2.1.1", 14 | "y-websocket": "^1.3.18", 15 | "yjs": "^13.5.22" 16 | } 17 | }, 18 | "node_modules/abstract-leveldown": { 19 | "version": "6.2.3", 20 | "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz", 21 | "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==", 22 | "optional": true, 23 | "dependencies": { 24 | "buffer": "^5.5.0", 25 | "immediate": "^3.2.3", 26 | "level-concat-iterator": "~2.0.0", 27 | "level-supports": "~1.0.0", 28 | "xtend": "~4.0.0" 29 | }, 30 | "engines": { 31 | "node": ">=6" 32 | } 33 | }, 34 | "node_modules/async-limiter": { 35 | "version": "1.0.1", 36 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", 37 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", 38 | "optional": true 39 | }, 40 | "node_modules/base64-js": { 41 | "version": "1.5.1", 42 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 43 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 44 | "funding": [ 45 | { 46 | "type": "github", 47 | "url": "https://github.com/sponsors/feross" 48 | }, 49 | { 50 | "type": "patreon", 51 | "url": "https://www.patreon.com/feross" 52 | }, 53 | { 54 | "type": "consulting", 55 | "url": "https://feross.org/support" 56 | } 57 | ], 58 | "optional": true 59 | }, 60 | "node_modules/buffer": { 61 | "version": "5.7.1", 62 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 63 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 64 | "funding": [ 65 | { 66 | "type": "github", 67 | "url": "https://github.com/sponsors/feross" 68 | }, 69 | { 70 | "type": "patreon", 71 | "url": "https://www.patreon.com/feross" 72 | }, 73 | { 74 | "type": "consulting", 75 | "url": "https://feross.org/support" 76 | } 77 | ], 78 | "optional": true, 79 | "dependencies": { 80 | "base64-js": "^1.3.1", 81 | "ieee754": "^1.1.13" 82 | } 83 | }, 84 | "node_modules/codemirror": { 85 | "version": "5.65.16", 86 | "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.16.tgz", 87 | "integrity": "sha512-br21LjYmSlVL0vFCPWPfhzUCT34FM/pAdK7rRIZwa0rrtrIdotvP4Oh4GUHsu2E3IrQMCfRkL/fN3ytMNxVQvg==" 88 | }, 89 | "node_modules/deferred-leveldown": { 90 | "version": "5.3.0", 91 | "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz", 92 | "integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==", 93 | "optional": true, 94 | "dependencies": { 95 | "abstract-leveldown": "~6.2.1", 96 | "inherits": "^2.0.3" 97 | }, 98 | "engines": { 99 | "node": ">=6" 100 | } 101 | }, 102 | "node_modules/encoding-down": { 103 | "version": "6.3.0", 104 | "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz", 105 | "integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==", 106 | "optional": true, 107 | "dependencies": { 108 | "abstract-leveldown": "^6.2.1", 109 | "inherits": "^2.0.3", 110 | "level-codec": "^9.0.0", 111 | "level-errors": "^2.0.0" 112 | }, 113 | "engines": { 114 | "node": ">=6" 115 | } 116 | }, 117 | "node_modules/errno": { 118 | "version": "0.1.8", 119 | "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", 120 | "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", 121 | "optional": true, 122 | "dependencies": { 123 | "prr": "~1.0.1" 124 | }, 125 | "bin": { 126 | "errno": "cli.js" 127 | } 128 | }, 129 | "node_modules/ieee754": { 130 | "version": "1.2.1", 131 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 132 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 133 | "funding": [ 134 | { 135 | "type": "github", 136 | "url": "https://github.com/sponsors/feross" 137 | }, 138 | { 139 | "type": "patreon", 140 | "url": "https://www.patreon.com/feross" 141 | }, 142 | { 143 | "type": "consulting", 144 | "url": "https://feross.org/support" 145 | } 146 | ], 147 | "optional": true 148 | }, 149 | "node_modules/immediate": { 150 | "version": "3.3.0", 151 | "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", 152 | "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==", 153 | "optional": true 154 | }, 155 | "node_modules/inherits": { 156 | "version": "2.0.4", 157 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 158 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 159 | "optional": true 160 | }, 161 | "node_modules/isomorphic.js": { 162 | "version": "0.2.5", 163 | "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", 164 | "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", 165 | "funding": { 166 | "type": "GitHub Sponsors ❤", 167 | "url": "https://github.com/sponsors/dmonad" 168 | } 169 | }, 170 | "node_modules/level": { 171 | "version": "6.0.1", 172 | "resolved": "https://registry.npmjs.org/level/-/level-6.0.1.tgz", 173 | "integrity": "sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==", 174 | "optional": true, 175 | "dependencies": { 176 | "level-js": "^5.0.0", 177 | "level-packager": "^5.1.0", 178 | "leveldown": "^5.4.0" 179 | }, 180 | "engines": { 181 | "node": ">=8.6.0" 182 | }, 183 | "funding": { 184 | "type": "opencollective", 185 | "url": "https://opencollective.com/level" 186 | } 187 | }, 188 | "node_modules/level-codec": { 189 | "version": "9.0.2", 190 | "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz", 191 | "integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==", 192 | "optional": true, 193 | "dependencies": { 194 | "buffer": "^5.6.0" 195 | }, 196 | "engines": { 197 | "node": ">=6" 198 | } 199 | }, 200 | "node_modules/level-concat-iterator": { 201 | "version": "2.0.1", 202 | "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz", 203 | "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==", 204 | "optional": true, 205 | "engines": { 206 | "node": ">=6" 207 | } 208 | }, 209 | "node_modules/level-errors": { 210 | "version": "2.0.1", 211 | "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", 212 | "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", 213 | "optional": true, 214 | "dependencies": { 215 | "errno": "~0.1.1" 216 | }, 217 | "engines": { 218 | "node": ">=6" 219 | } 220 | }, 221 | "node_modules/level-iterator-stream": { 222 | "version": "4.0.2", 223 | "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz", 224 | "integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==", 225 | "optional": true, 226 | "dependencies": { 227 | "inherits": "^2.0.4", 228 | "readable-stream": "^3.4.0", 229 | "xtend": "^4.0.2" 230 | }, 231 | "engines": { 232 | "node": ">=6" 233 | } 234 | }, 235 | "node_modules/level-js": { 236 | "version": "5.0.2", 237 | "resolved": "https://registry.npmjs.org/level-js/-/level-js-5.0.2.tgz", 238 | "integrity": "sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==", 239 | "optional": true, 240 | "dependencies": { 241 | "abstract-leveldown": "~6.2.3", 242 | "buffer": "^5.5.0", 243 | "inherits": "^2.0.3", 244 | "ltgt": "^2.1.2" 245 | } 246 | }, 247 | "node_modules/level-packager": { 248 | "version": "5.1.1", 249 | "resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz", 250 | "integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==", 251 | "optional": true, 252 | "dependencies": { 253 | "encoding-down": "^6.3.0", 254 | "levelup": "^4.3.2" 255 | }, 256 | "engines": { 257 | "node": ">=6" 258 | } 259 | }, 260 | "node_modules/level-supports": { 261 | "version": "1.0.1", 262 | "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", 263 | "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==", 264 | "optional": true, 265 | "dependencies": { 266 | "xtend": "^4.0.2" 267 | }, 268 | "engines": { 269 | "node": ">=6" 270 | } 271 | }, 272 | "node_modules/leveldown": { 273 | "version": "5.6.0", 274 | "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.6.0.tgz", 275 | "integrity": "sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==", 276 | "hasInstallScript": true, 277 | "optional": true, 278 | "dependencies": { 279 | "abstract-leveldown": "~6.2.1", 280 | "napi-macros": "~2.0.0", 281 | "node-gyp-build": "~4.1.0" 282 | }, 283 | "engines": { 284 | "node": ">=8.6.0" 285 | } 286 | }, 287 | "node_modules/levelup": { 288 | "version": "4.4.0", 289 | "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz", 290 | "integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==", 291 | "optional": true, 292 | "dependencies": { 293 | "deferred-leveldown": "~5.3.0", 294 | "level-errors": "~2.0.0", 295 | "level-iterator-stream": "~4.0.0", 296 | "level-supports": "~1.0.0", 297 | "xtend": "~4.0.0" 298 | }, 299 | "engines": { 300 | "node": ">=6" 301 | } 302 | }, 303 | "node_modules/lib0": { 304 | "version": "0.2.88", 305 | "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.88.tgz", 306 | "integrity": "sha512-KyroiEvCeZcZEMx5Ys+b4u4eEBbA1ch7XUaBhYpwa/nPMrzTjUhI4RfcytmQfYoTBPcdyx+FX6WFNIoNuJzJfQ==", 307 | "dependencies": { 308 | "isomorphic.js": "^0.2.4" 309 | }, 310 | "bin": { 311 | "0gentesthtml": "bin/gentesthtml.js", 312 | "0serve": "bin/0serve.js" 313 | }, 314 | "engines": { 315 | "node": ">=16" 316 | }, 317 | "funding": { 318 | "type": "GitHub Sponsors ❤", 319 | "url": "https://github.com/sponsors/dmonad" 320 | } 321 | }, 322 | "node_modules/lodash.debounce": { 323 | "version": "4.0.8", 324 | "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", 325 | "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" 326 | }, 327 | "node_modules/ltgt": { 328 | "version": "2.2.1", 329 | "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", 330 | "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", 331 | "optional": true 332 | }, 333 | "node_modules/napi-macros": { 334 | "version": "2.0.0", 335 | "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", 336 | "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", 337 | "optional": true 338 | }, 339 | "node_modules/node-gyp-build": { 340 | "version": "4.1.1", 341 | "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.1.1.tgz", 342 | "integrity": "sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==", 343 | "optional": true, 344 | "bin": { 345 | "node-gyp-build": "bin.js", 346 | "node-gyp-build-optional": "optional.js", 347 | "node-gyp-build-test": "build-test.js" 348 | } 349 | }, 350 | "node_modules/prr": { 351 | "version": "1.0.1", 352 | "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", 353 | "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", 354 | "optional": true 355 | }, 356 | "node_modules/readable-stream": { 357 | "version": "3.6.2", 358 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 359 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 360 | "optional": true, 361 | "dependencies": { 362 | "inherits": "^2.0.3", 363 | "string_decoder": "^1.1.1", 364 | "util-deprecate": "^1.0.1" 365 | }, 366 | "engines": { 367 | "node": ">= 6" 368 | } 369 | }, 370 | "node_modules/safe-buffer": { 371 | "version": "5.2.1", 372 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 373 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 374 | "funding": [ 375 | { 376 | "type": "github", 377 | "url": "https://github.com/sponsors/feross" 378 | }, 379 | { 380 | "type": "patreon", 381 | "url": "https://www.patreon.com/feross" 382 | }, 383 | { 384 | "type": "consulting", 385 | "url": "https://feross.org/support" 386 | } 387 | ], 388 | "optional": true 389 | }, 390 | "node_modules/string_decoder": { 391 | "version": "1.3.0", 392 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 393 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 394 | "optional": true, 395 | "dependencies": { 396 | "safe-buffer": "~5.2.0" 397 | } 398 | }, 399 | "node_modules/util-deprecate": { 400 | "version": "1.0.2", 401 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 402 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 403 | "optional": true 404 | }, 405 | "node_modules/ws": { 406 | "version": "6.2.2", 407 | "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", 408 | "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", 409 | "optional": true, 410 | "dependencies": { 411 | "async-limiter": "~1.0.0" 412 | } 413 | }, 414 | "node_modules/xtend": { 415 | "version": "4.0.2", 416 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 417 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 418 | "optional": true, 419 | "engines": { 420 | "node": ">=0.4" 421 | } 422 | }, 423 | "node_modules/y-codemirror": { 424 | "version": "2.1.1", 425 | "resolved": "https://registry.npmjs.org/y-codemirror/-/y-codemirror-2.1.1.tgz", 426 | "integrity": "sha512-QXHaOkvEJs3pB82dkW1aGfWUd4S1RA1ORtXWtprHClbqBiCOY19VKiojScSTyl8rTaOZ/zblEq+SNH2sd3Umiw==", 427 | "dependencies": { 428 | "lib0": "^0.2.41" 429 | }, 430 | "funding": { 431 | "type": "GitHub Sponsors ❤", 432 | "url": "https://github.com/sponsors/dmonad" 433 | }, 434 | "peerDependencies": { 435 | "codemirror": "^5.52.2", 436 | "yjs": "^13.0.0" 437 | } 438 | }, 439 | "node_modules/y-leveldb": { 440 | "version": "0.1.2", 441 | "resolved": "https://registry.npmjs.org/y-leveldb/-/y-leveldb-0.1.2.tgz", 442 | "integrity": "sha512-6ulEn5AXfXJYi89rXPEg2mMHAyyw8+ZfeMMdOtBbV8FJpQ1NOrcgi6DTAcXof0dap84NjHPT2+9d0rb6cFsjEg==", 443 | "optional": true, 444 | "dependencies": { 445 | "level": "^6.0.1", 446 | "lib0": "^0.2.31" 447 | }, 448 | "funding": { 449 | "type": "GitHub Sponsors ❤", 450 | "url": "https://github.com/sponsors/dmonad" 451 | }, 452 | "peerDependencies": { 453 | "yjs": "^13.0.0" 454 | } 455 | }, 456 | "node_modules/y-protocols": { 457 | "version": "1.0.6", 458 | "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz", 459 | "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==", 460 | "dependencies": { 461 | "lib0": "^0.2.85" 462 | }, 463 | "engines": { 464 | "node": ">=16.0.0", 465 | "npm": ">=8.0.0" 466 | }, 467 | "funding": { 468 | "type": "GitHub Sponsors ❤", 469 | "url": "https://github.com/sponsors/dmonad" 470 | }, 471 | "peerDependencies": { 472 | "yjs": "^13.0.0" 473 | } 474 | }, 475 | "node_modules/y-websocket": { 476 | "version": "1.5.0", 477 | "resolved": "https://registry.npmjs.org/y-websocket/-/y-websocket-1.5.0.tgz", 478 | "integrity": "sha512-A8AO6XtnQlYwWFytWdkDCeXg4l8ghRTIw5h2YUgUYDmEC9ugWGIwYNW80yadhSFAF7CvuWTEkQNEpevnH6EiZw==", 479 | "dependencies": { 480 | "lib0": "^0.2.52", 481 | "lodash.debounce": "^4.0.8", 482 | "y-protocols": "^1.0.5" 483 | }, 484 | "bin": { 485 | "y-websocket": "bin/server.js", 486 | "y-websocket-server": "bin/server.js" 487 | }, 488 | "funding": { 489 | "type": "GitHub Sponsors ❤", 490 | "url": "https://github.com/sponsors/dmonad" 491 | }, 492 | "optionalDependencies": { 493 | "ws": "^6.2.1", 494 | "y-leveldb": "^0.1.0" 495 | }, 496 | "peerDependencies": { 497 | "yjs": "^13.5.6" 498 | } 499 | }, 500 | "node_modules/yjs": { 501 | "version": "13.6.10", 502 | "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.10.tgz", 503 | "integrity": "sha512-1JcyQek1vaMyrDm7Fqfa+pvHg/DURSbVo4VmeN7wjnTKB/lZrfIPhdCj7d8sboK6zLfRBJXegTjc9JlaDd8/Zw==", 504 | "dependencies": { 505 | "lib0": "^0.2.86" 506 | }, 507 | "engines": { 508 | "node": ">=16.0.0", 509 | "npm": ">=8.0.0" 510 | }, 511 | "funding": { 512 | "type": "GitHub Sponsors ❤", 513 | "url": "https://github.com/sponsors/dmonad" 514 | } 515 | } 516 | } 517 | } 518 | -------------------------------------------------------------------------------- /codemirror/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yjs-codemirror-demo", 3 | "version": "1.0.0", 4 | "description": "Yjs ❤ CodeMirror", 5 | "scripts": { 6 | "watch": "webpack -w --stats errors-only", 7 | "dist": "webpack --mode=production", 8 | "start": "webpack serve --open codemirror.html" 9 | }, 10 | "author": "Kevin Jahns ", 11 | "license": "UNLICENSE", 12 | "dependencies": { 13 | "yjs": "^13.5.22", 14 | "y-websocket": "^1.3.18", 15 | "y-codemirror": "^2.1.1", 16 | "codemirror": "^5.64.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /codemirror/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | mode: 'development', 5 | devtool: 'source-map', 6 | entry: { 7 | codemirror: './codemirror.js' 8 | }, 9 | output: { 10 | globalObject: 'self', 11 | path: path.resolve(__dirname, './dist/'), 12 | filename: '[name].bundle.js', 13 | publicPath: '/codemirror/dist/' 14 | }, 15 | devServer: { 16 | static: path.join(__dirname), 17 | compress: true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /demo-server/README.md: -------------------------------------------------------------------------------- 1 | # demo-server 2 | 3 | All yjs-demos connect to this server to exchange document updates. It combines the y-websocket server with an http server for the files in the root directory. Of course, you can choose any of the Yjs providers instead. This is just a demo server. 4 | 5 | ```sh 6 | npm install 7 | npm run build-all # Iterates through all demos and builds them. 8 | npm start 9 | ``` 10 | 11 | -------------------------------------------------------------------------------- /demo-server/demo-server.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @type {any} 4 | */ 5 | const WebSocket = require('ws') 6 | const http = require('http') 7 | const StaticServer = require('node-static').Server 8 | const ywsUtils = require('y-websocket/bin/utils') 9 | const setupWSConnection = ywsUtils.setupWSConnection 10 | const docs = ywsUtils.docs 11 | const env = require('lib0/environment') 12 | const nostatic = env.hasParam('--nostatic') 13 | 14 | const production = process.env.PRODUCTION != null 15 | const port = process.env.PORT || 3000 16 | 17 | const staticServer = nostatic ? null : new StaticServer('../', { cache: production ? 3600 : false, gzip: production }) 18 | 19 | const server = http.createServer((request, response) => { 20 | if (request.url === '/health') { 21 | response.writeHead(200, { 'Content-Type': 'application/json' }) 22 | response.end(JSON.stringify({ 23 | response: 'ok' 24 | })) 25 | return 26 | } 27 | 28 | if (staticServer && !(request.url || '').startsWith('/ws/')) { 29 | request.addListener('end', () => { 30 | staticServer.serve(request, response) 31 | }).resume() 32 | } 33 | }) 34 | const wss = new WebSocket.Server({ server }) 35 | 36 | wss.on('connection', (conn, req) => { 37 | setupWSConnection(conn, req, { gc: req.url.slice(1) !== 'ws/prosemirror-versions' }) 38 | }) 39 | 40 | // log some stats 41 | setInterval(() => { 42 | let conns = 0 43 | docs.forEach(doc => { conns += doc.conns.size }) 44 | const stats = { 45 | conns, 46 | docs: docs.size, 47 | websocket: `ws://localhost:${port}`, 48 | http: `http://localhost:${port}` 49 | } 50 | console.log(`${new Date().toISOString()} Stats: ${JSON.stringify(stats)}`) 51 | }, 10000) 52 | 53 | server.listen(port, '0.0.0.0') 54 | 55 | console.log(`Listening to http://localhost:${port} (${production ? 'production + ' : ''} ${nostatic ? 'no static content' : 'serving static content'})`) 56 | -------------------------------------------------------------------------------- /demo-server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yjs-demos-server", 3 | "version": "0.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "yjs-demos-server", 9 | "version": "0.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "node-static": "^0.7.11", 13 | "ws": "^7.4.2", 14 | "y-websocket": "^1.3.9", 15 | "yjs": "^13.4.12" 16 | }, 17 | "engines": { 18 | "node": ">=16.0.0" 19 | }, 20 | "funding": { 21 | "type": "GitHub Sponsors ❤", 22 | "url": "https://github.com/sponsors/dmonad" 23 | } 24 | }, 25 | "node_modules/abstract-leveldown": { 26 | "version": "6.2.3", 27 | "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz", 28 | "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==", 29 | "optional": true, 30 | "dependencies": { 31 | "buffer": "^5.5.0", 32 | "immediate": "^3.2.3", 33 | "level-concat-iterator": "~2.0.0", 34 | "level-supports": "~1.0.0", 35 | "xtend": "~4.0.0" 36 | }, 37 | "engines": { 38 | "node": ">=6" 39 | } 40 | }, 41 | "node_modules/async-limiter": { 42 | "version": "1.0.1", 43 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", 44 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", 45 | "optional": true 46 | }, 47 | "node_modules/base64-js": { 48 | "version": "1.5.1", 49 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 50 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 51 | "funding": [ 52 | { 53 | "type": "github", 54 | "url": "https://github.com/sponsors/feross" 55 | }, 56 | { 57 | "type": "patreon", 58 | "url": "https://www.patreon.com/feross" 59 | }, 60 | { 61 | "type": "consulting", 62 | "url": "https://feross.org/support" 63 | } 64 | ], 65 | "optional": true 66 | }, 67 | "node_modules/buffer": { 68 | "version": "5.7.1", 69 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 70 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 71 | "funding": [ 72 | { 73 | "type": "github", 74 | "url": "https://github.com/sponsors/feross" 75 | }, 76 | { 77 | "type": "patreon", 78 | "url": "https://www.patreon.com/feross" 79 | }, 80 | { 81 | "type": "consulting", 82 | "url": "https://feross.org/support" 83 | } 84 | ], 85 | "optional": true, 86 | "dependencies": { 87 | "base64-js": "^1.3.1", 88 | "ieee754": "^1.1.13" 89 | } 90 | }, 91 | "node_modules/colors": { 92 | "version": "1.4.0", 93 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", 94 | "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", 95 | "engines": { 96 | "node": ">=0.1.90" 97 | } 98 | }, 99 | "node_modules/deferred-leveldown": { 100 | "version": "5.3.0", 101 | "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz", 102 | "integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==", 103 | "optional": true, 104 | "dependencies": { 105 | "abstract-leveldown": "~6.2.1", 106 | "inherits": "^2.0.3" 107 | }, 108 | "engines": { 109 | "node": ">=6" 110 | } 111 | }, 112 | "node_modules/encoding-down": { 113 | "version": "6.3.0", 114 | "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz", 115 | "integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==", 116 | "optional": true, 117 | "dependencies": { 118 | "abstract-leveldown": "^6.2.1", 119 | "inherits": "^2.0.3", 120 | "level-codec": "^9.0.0", 121 | "level-errors": "^2.0.0" 122 | }, 123 | "engines": { 124 | "node": ">=6" 125 | } 126 | }, 127 | "node_modules/errno": { 128 | "version": "0.1.8", 129 | "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", 130 | "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", 131 | "optional": true, 132 | "dependencies": { 133 | "prr": "~1.0.1" 134 | }, 135 | "bin": { 136 | "errno": "cli.js" 137 | } 138 | }, 139 | "node_modules/ieee754": { 140 | "version": "1.2.1", 141 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 142 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 143 | "funding": [ 144 | { 145 | "type": "github", 146 | "url": "https://github.com/sponsors/feross" 147 | }, 148 | { 149 | "type": "patreon", 150 | "url": "https://www.patreon.com/feross" 151 | }, 152 | { 153 | "type": "consulting", 154 | "url": "https://feross.org/support" 155 | } 156 | ], 157 | "optional": true 158 | }, 159 | "node_modules/immediate": { 160 | "version": "3.3.0", 161 | "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", 162 | "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==", 163 | "optional": true 164 | }, 165 | "node_modules/inherits": { 166 | "version": "2.0.4", 167 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 168 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 169 | "optional": true 170 | }, 171 | "node_modules/isomorphic.js": { 172 | "version": "0.2.5", 173 | "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", 174 | "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", 175 | "funding": { 176 | "type": "GitHub Sponsors ❤", 177 | "url": "https://github.com/sponsors/dmonad" 178 | } 179 | }, 180 | "node_modules/level": { 181 | "version": "6.0.1", 182 | "resolved": "https://registry.npmjs.org/level/-/level-6.0.1.tgz", 183 | "integrity": "sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==", 184 | "optional": true, 185 | "dependencies": { 186 | "level-js": "^5.0.0", 187 | "level-packager": "^5.1.0", 188 | "leveldown": "^5.4.0" 189 | }, 190 | "engines": { 191 | "node": ">=8.6.0" 192 | }, 193 | "funding": { 194 | "type": "opencollective", 195 | "url": "https://opencollective.com/level" 196 | } 197 | }, 198 | "node_modules/level-codec": { 199 | "version": "9.0.2", 200 | "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz", 201 | "integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==", 202 | "optional": true, 203 | "dependencies": { 204 | "buffer": "^5.6.0" 205 | }, 206 | "engines": { 207 | "node": ">=6" 208 | } 209 | }, 210 | "node_modules/level-concat-iterator": { 211 | "version": "2.0.1", 212 | "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz", 213 | "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==", 214 | "optional": true, 215 | "engines": { 216 | "node": ">=6" 217 | } 218 | }, 219 | "node_modules/level-errors": { 220 | "version": "2.0.1", 221 | "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", 222 | "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", 223 | "optional": true, 224 | "dependencies": { 225 | "errno": "~0.1.1" 226 | }, 227 | "engines": { 228 | "node": ">=6" 229 | } 230 | }, 231 | "node_modules/level-iterator-stream": { 232 | "version": "4.0.2", 233 | "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz", 234 | "integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==", 235 | "optional": true, 236 | "dependencies": { 237 | "inherits": "^2.0.4", 238 | "readable-stream": "^3.4.0", 239 | "xtend": "^4.0.2" 240 | }, 241 | "engines": { 242 | "node": ">=6" 243 | } 244 | }, 245 | "node_modules/level-js": { 246 | "version": "5.0.2", 247 | "resolved": "https://registry.npmjs.org/level-js/-/level-js-5.0.2.tgz", 248 | "integrity": "sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==", 249 | "optional": true, 250 | "dependencies": { 251 | "abstract-leveldown": "~6.2.3", 252 | "buffer": "^5.5.0", 253 | "inherits": "^2.0.3", 254 | "ltgt": "^2.1.2" 255 | } 256 | }, 257 | "node_modules/level-packager": { 258 | "version": "5.1.1", 259 | "resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz", 260 | "integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==", 261 | "optional": true, 262 | "dependencies": { 263 | "encoding-down": "^6.3.0", 264 | "levelup": "^4.3.2" 265 | }, 266 | "engines": { 267 | "node": ">=6" 268 | } 269 | }, 270 | "node_modules/level-supports": { 271 | "version": "1.0.1", 272 | "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", 273 | "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==", 274 | "optional": true, 275 | "dependencies": { 276 | "xtend": "^4.0.2" 277 | }, 278 | "engines": { 279 | "node": ">=6" 280 | } 281 | }, 282 | "node_modules/leveldown": { 283 | "version": "5.6.0", 284 | "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.6.0.tgz", 285 | "integrity": "sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==", 286 | "hasInstallScript": true, 287 | "optional": true, 288 | "dependencies": { 289 | "abstract-leveldown": "~6.2.1", 290 | "napi-macros": "~2.0.0", 291 | "node-gyp-build": "~4.1.0" 292 | }, 293 | "engines": { 294 | "node": ">=8.6.0" 295 | } 296 | }, 297 | "node_modules/levelup": { 298 | "version": "4.4.0", 299 | "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz", 300 | "integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==", 301 | "optional": true, 302 | "dependencies": { 303 | "deferred-leveldown": "~5.3.0", 304 | "level-errors": "~2.0.0", 305 | "level-iterator-stream": "~4.0.0", 306 | "level-supports": "~1.0.0", 307 | "xtend": "~4.0.0" 308 | }, 309 | "engines": { 310 | "node": ">=6" 311 | } 312 | }, 313 | "node_modules/lib0": { 314 | "version": "0.2.88", 315 | "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.88.tgz", 316 | "integrity": "sha512-KyroiEvCeZcZEMx5Ys+b4u4eEBbA1ch7XUaBhYpwa/nPMrzTjUhI4RfcytmQfYoTBPcdyx+FX6WFNIoNuJzJfQ==", 317 | "dependencies": { 318 | "isomorphic.js": "^0.2.4" 319 | }, 320 | "bin": { 321 | "0gentesthtml": "bin/gentesthtml.js", 322 | "0serve": "bin/0serve.js" 323 | }, 324 | "engines": { 325 | "node": ">=16" 326 | }, 327 | "funding": { 328 | "type": "GitHub Sponsors ❤", 329 | "url": "https://github.com/sponsors/dmonad" 330 | } 331 | }, 332 | "node_modules/lodash.debounce": { 333 | "version": "4.0.8", 334 | "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", 335 | "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" 336 | }, 337 | "node_modules/ltgt": { 338 | "version": "2.2.1", 339 | "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", 340 | "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", 341 | "optional": true 342 | }, 343 | "node_modules/mime": { 344 | "version": "1.6.0", 345 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 346 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 347 | "bin": { 348 | "mime": "cli.js" 349 | }, 350 | "engines": { 351 | "node": ">=4" 352 | } 353 | }, 354 | "node_modules/minimist": { 355 | "version": "0.0.10", 356 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", 357 | "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==" 358 | }, 359 | "node_modules/napi-macros": { 360 | "version": "2.0.0", 361 | "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", 362 | "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", 363 | "optional": true 364 | }, 365 | "node_modules/node-gyp-build": { 366 | "version": "4.1.1", 367 | "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.1.1.tgz", 368 | "integrity": "sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==", 369 | "optional": true, 370 | "bin": { 371 | "node-gyp-build": "bin.js", 372 | "node-gyp-build-optional": "optional.js", 373 | "node-gyp-build-test": "build-test.js" 374 | } 375 | }, 376 | "node_modules/node-static": { 377 | "version": "0.7.11", 378 | "resolved": "https://registry.npmjs.org/node-static/-/node-static-0.7.11.tgz", 379 | "integrity": "sha512-zfWC/gICcqb74D9ndyvxZWaI1jzcoHmf4UTHWQchBNuNMxdBLJMDiUgZ1tjGLEIe/BMhj2DxKD8HOuc2062pDQ==", 380 | "dependencies": { 381 | "colors": ">=0.6.0", 382 | "mime": "^1.2.9", 383 | "optimist": ">=0.3.4" 384 | }, 385 | "bin": { 386 | "static": "bin/cli.js" 387 | }, 388 | "engines": { 389 | "node": ">= 0.4.1" 390 | } 391 | }, 392 | "node_modules/optimist": { 393 | "version": "0.6.1", 394 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 395 | "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==", 396 | "dependencies": { 397 | "minimist": "~0.0.1", 398 | "wordwrap": "~0.0.2" 399 | } 400 | }, 401 | "node_modules/prr": { 402 | "version": "1.0.1", 403 | "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", 404 | "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", 405 | "optional": true 406 | }, 407 | "node_modules/readable-stream": { 408 | "version": "3.6.2", 409 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 410 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 411 | "optional": true, 412 | "dependencies": { 413 | "inherits": "^2.0.3", 414 | "string_decoder": "^1.1.1", 415 | "util-deprecate": "^1.0.1" 416 | }, 417 | "engines": { 418 | "node": ">= 6" 419 | } 420 | }, 421 | "node_modules/safe-buffer": { 422 | "version": "5.2.1", 423 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 424 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 425 | "funding": [ 426 | { 427 | "type": "github", 428 | "url": "https://github.com/sponsors/feross" 429 | }, 430 | { 431 | "type": "patreon", 432 | "url": "https://www.patreon.com/feross" 433 | }, 434 | { 435 | "type": "consulting", 436 | "url": "https://feross.org/support" 437 | } 438 | ], 439 | "optional": true 440 | }, 441 | "node_modules/string_decoder": { 442 | "version": "1.3.0", 443 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 444 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 445 | "optional": true, 446 | "dependencies": { 447 | "safe-buffer": "~5.2.0" 448 | } 449 | }, 450 | "node_modules/util-deprecate": { 451 | "version": "1.0.2", 452 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 453 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 454 | "optional": true 455 | }, 456 | "node_modules/wordwrap": { 457 | "version": "0.0.3", 458 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 459 | "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==", 460 | "engines": { 461 | "node": ">=0.4.0" 462 | } 463 | }, 464 | "node_modules/ws": { 465 | "version": "7.5.9", 466 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", 467 | "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", 468 | "engines": { 469 | "node": ">=8.3.0" 470 | }, 471 | "peerDependencies": { 472 | "bufferutil": "^4.0.1", 473 | "utf-8-validate": "^5.0.2" 474 | }, 475 | "peerDependenciesMeta": { 476 | "bufferutil": { 477 | "optional": true 478 | }, 479 | "utf-8-validate": { 480 | "optional": true 481 | } 482 | } 483 | }, 484 | "node_modules/xtend": { 485 | "version": "4.0.2", 486 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 487 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 488 | "optional": true, 489 | "engines": { 490 | "node": ">=0.4" 491 | } 492 | }, 493 | "node_modules/y-leveldb": { 494 | "version": "0.1.2", 495 | "resolved": "https://registry.npmjs.org/y-leveldb/-/y-leveldb-0.1.2.tgz", 496 | "integrity": "sha512-6ulEn5AXfXJYi89rXPEg2mMHAyyw8+ZfeMMdOtBbV8FJpQ1NOrcgi6DTAcXof0dap84NjHPT2+9d0rb6cFsjEg==", 497 | "optional": true, 498 | "dependencies": { 499 | "level": "^6.0.1", 500 | "lib0": "^0.2.31" 501 | }, 502 | "funding": { 503 | "type": "GitHub Sponsors ❤", 504 | "url": "https://github.com/sponsors/dmonad" 505 | }, 506 | "peerDependencies": { 507 | "yjs": "^13.0.0" 508 | } 509 | }, 510 | "node_modules/y-protocols": { 511 | "version": "1.0.6", 512 | "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz", 513 | "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==", 514 | "dependencies": { 515 | "lib0": "^0.2.85" 516 | }, 517 | "engines": { 518 | "node": ">=16.0.0", 519 | "npm": ">=8.0.0" 520 | }, 521 | "funding": { 522 | "type": "GitHub Sponsors ❤", 523 | "url": "https://github.com/sponsors/dmonad" 524 | }, 525 | "peerDependencies": { 526 | "yjs": "^13.0.0" 527 | } 528 | }, 529 | "node_modules/y-websocket": { 530 | "version": "1.5.0", 531 | "resolved": "https://registry.npmjs.org/y-websocket/-/y-websocket-1.5.0.tgz", 532 | "integrity": "sha512-A8AO6XtnQlYwWFytWdkDCeXg4l8ghRTIw5h2YUgUYDmEC9ugWGIwYNW80yadhSFAF7CvuWTEkQNEpevnH6EiZw==", 533 | "dependencies": { 534 | "lib0": "^0.2.52", 535 | "lodash.debounce": "^4.0.8", 536 | "y-protocols": "^1.0.5" 537 | }, 538 | "bin": { 539 | "y-websocket": "bin/server.js", 540 | "y-websocket-server": "bin/server.js" 541 | }, 542 | "funding": { 543 | "type": "GitHub Sponsors ❤", 544 | "url": "https://github.com/sponsors/dmonad" 545 | }, 546 | "optionalDependencies": { 547 | "ws": "^6.2.1", 548 | "y-leveldb": "^0.1.0" 549 | }, 550 | "peerDependencies": { 551 | "yjs": "^13.5.6" 552 | } 553 | }, 554 | "node_modules/y-websocket/node_modules/ws": { 555 | "version": "6.2.2", 556 | "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", 557 | "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", 558 | "optional": true, 559 | "dependencies": { 560 | "async-limiter": "~1.0.0" 561 | } 562 | }, 563 | "node_modules/yjs": { 564 | "version": "13.6.10", 565 | "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.10.tgz", 566 | "integrity": "sha512-1JcyQek1vaMyrDm7Fqfa+pvHg/DURSbVo4VmeN7wjnTKB/lZrfIPhdCj7d8sboK6zLfRBJXegTjc9JlaDd8/Zw==", 567 | "dependencies": { 568 | "lib0": "^0.2.86" 569 | }, 570 | "engines": { 571 | "node": ">=16.0.0", 572 | "npm": ">=8.0.0" 573 | }, 574 | "funding": { 575 | "type": "GitHub Sponsors ❤", 576 | "url": "https://github.com/sponsors/dmonad" 577 | } 578 | } 579 | } 580 | } 581 | -------------------------------------------------------------------------------- /demo-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yjs-demos-server", 3 | "version": "0.0.0", 4 | "private": true, 5 | "description": "Demo server that serves the static assets and provides a shared websocket backend.", 6 | "funding": { 7 | "type": "GitHub Sponsors ❤", 8 | "url": "https://github.com/sponsors/dmonad" 9 | }, 10 | "scripts": { 11 | "start": "export PRODUCTION=1; node ./demo-server.js", 12 | "start:nostatic": "export PRODUCTION=1; node ./demo-server.js --nostatic" 13 | }, 14 | "author": "Kevin Jahns =16.0.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

Yjs Demos

7 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /monaco-react/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /monaco-react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Playground for Monaco React 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /monaco-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playground", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "start": "concurrently \"npm run lib-build-dev\" \"vite\"" 6 | }, 7 | "dependencies": { 8 | "@monaco-editor/react": "^4.6.0", 9 | "react": "^18.2.0", 10 | "react-dom": "^18.2.0", 11 | "state-local": "^1.0.7", 12 | "y-monaco": "^0.1.5", 13 | "y-websocket": "^2.0.3", 14 | "yjs": "^13.6.16" 15 | }, 16 | "devDependencies": { 17 | "@vitejs/plugin-react-swc": "^3.2.0", 18 | "concurrently": "^7.0.0", 19 | "vite": "^4.1.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /monaco-react/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as Y from 'yjs' 2 | import { WebsocketProvider } from 'y-websocket' 3 | import { MonacoBinding } from 'y-monaco' 4 | 5 | import React, { useEffect, useMemo, useState } from 'react' 6 | import Editor from '@monaco-editor/react' 7 | 8 | const roomname = `monaco-react-demo-${new Date().toLocaleDateString('en-CA')}` 9 | 10 | function App() { 11 | const ydoc = useMemo(() => new Y.Doc(), []) 12 | const [editor, setEditor] = useState(null) 13 | const [provider, setProvider] = useState(null) 14 | const [binding, setBinding] = useState(null) 15 | 16 | // this effect manages the lifetime of the Yjs document and the provider 17 | useEffect(() => { 18 | const provider = new WebsocketProvider('wss://demos.yjs.dev/ws', roomname, ydoc) 19 | setProvider(provider) 20 | return () => { 21 | provider?.destroy() 22 | ydoc.destroy() 23 | } 24 | }, [ydoc]) 25 | 26 | // this effect manages the lifetime of the editor binding 27 | useEffect(() => { 28 | if (provider == null || editor == null) { 29 | return 30 | } 31 | console.log('reached', provider) 32 | const binding = new MonacoBinding(ydoc.getText(), editor.getModel()!, new Set([editor]), provider?.awareness) 33 | setBinding(binding) 34 | return () => { 35 | binding.destroy() 36 | } 37 | }, [ydoc, provider, editor]) 38 | 39 | return { setEditor(editor) }} /> 40 | } 41 | 42 | export default App 43 | -------------------------------------------------------------------------------- /monaco-react/src/global.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | 15 | .yRemoteSelection { 16 | background-color: rgb(250, 129, 0, .5) 17 | } 18 | .yRemoteSelectionHead { 19 | position: absolute; 20 | border-left: orange solid 2px; 21 | border-top: orange solid 2px; 22 | border-bottom: orange solid 2px; 23 | height: 100%; 24 | box-sizing: border-box; 25 | } 26 | .yRemoteSelectionHead::after { 27 | position: absolute; 28 | content: ' '; 29 | border: 3px solid orange; 30 | border-radius: 4px; 31 | left: -4px; 32 | top: -5px; 33 | } 34 | -------------------------------------------------------------------------------- /monaco-react/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import App from './App' 4 | 5 | import './global.css' 6 | 7 | const root = createRoot(document.getElementById('root') as HTMLElement) 8 | 9 | root.render() 10 | -------------------------------------------------------------------------------- /monaco-react/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react-swc"; 3 | 4 | export default defineConfig({ 5 | plugins: [react()], 6 | }); 7 | -------------------------------------------------------------------------------- /monaco/README.md: -------------------------------------------------------------------------------- 1 | # Monaco Demo 2 | > [y-monaco](https://docs.yjs.dev/ecosystem/editor-bindings/monaco) / [y-websocket](https://docs.yjs.dev/ecosystem/connection-provider/y-websocket) / [Live Demo](https://demos.yjs.dev/monaco/monaco.html) 3 | 4 | This is a demo of a [Monaco](https://microsoft.github.io/monaco-editor/) editor that was made collaborative with Yjs & `y-monaco`. 5 | 6 | We use the [y-websocket](https://docs.yjs.dev/ecosystem/connection-provider/y-websocket) provider to share document updates through a server. There are many more providers available for Yjs - try switching to another provider. [See docs](https://docs.yjs.dev/ecosystem/connection-provider). 7 | 8 | Also you could try adding offline persistence to this demo. Wouldn't it be cool if document updates were persisted in the browser so you could load your application load faster? Try it out: https://docs.yjs.dev/getting-started/allowing-offline-editing 9 | 10 | ## Quick Start 11 | 12 | ```sh 13 | npm i 14 | npm start 15 | # Project is running at http://localhost:8080/ 16 | ``` 17 | -------------------------------------------------------------------------------- /monaco/monaco.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Yjs Monaco Example 6 | 32 | 33 | 34 | 35 |

36 |

This is a demo of the YjsMonaco binding: y-monaco.

37 |

The content of this editor is shared with every client that visits this domain.

38 |
39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /monaco/monaco.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | import * as Y from 'yjs' 4 | import { WebsocketProvider } from 'y-websocket' 5 | import { MonacoBinding } from 'y-monaco' 6 | import * as monaco from 'monaco-editor' 7 | 8 | // // @ts-ignore 9 | // window.MonacoEnvironment = { 10 | // getWorkerUrl: function (moduleId, label) { 11 | // if (label === 'json') { 12 | // return '/monaco/dist/json.worker.bundle.js' 13 | // } 14 | // if (label === 'css') { 15 | // return '/monaco/dist/css.worker.bundle.js' 16 | // } 17 | // if (label === 'html') { 18 | // return '/monaco/dist/html.worker.bundle.js' 19 | // } 20 | // if (label === 'typescript' || label === 'javascript') { 21 | // return '/monaco/dist/ts.worker.bundle.js' 22 | // } 23 | // return '/monaco/dist/editor.worker.bundle.js' 24 | // } 25 | // } 26 | 27 | const roomname = `monaco-demo-${new Date().toLocaleDateString('en-CA')}` 28 | 29 | window.addEventListener('load', () => { 30 | const ydoc = new Y.Doc() 31 | const provider = new WebsocketProvider( 32 | 'wss://demos.yjs.dev/ws', // use the public ws server 33 | // `ws${location.protocol.slice(4)}//${location.host}/ws`, // alternatively: use the local ws server (run `npm start` in root directory) 34 | roomname, 35 | ydoc 36 | ) 37 | const ytext = ydoc.getText('monaco') 38 | 39 | const editor = monaco.editor.create(/** @type {HTMLElement} */ (document.getElementById('monaco-editor')), { 40 | value: '', 41 | language: 'javascript', 42 | theme: 'vs-dark' 43 | }) 44 | const monacoBinding = new MonacoBinding(ytext, /** @type {monaco.editor.ITextModel} */ (editor.getModel()), new Set([editor]), provider.awareness) 45 | 46 | const connectBtn = /** @type {HTMLElement} */ (document.getElementById('y-connect-btn')) 47 | connectBtn.addEventListener('click', () => { 48 | if (provider.shouldConnect) { 49 | provider.disconnect() 50 | connectBtn.textContent = 'Connect' 51 | } else { 52 | provider.connect() 53 | connectBtn.textContent = 'Disconnect' 54 | } 55 | }) 56 | 57 | // @ts-ignore 58 | window.example = { provider, ydoc, ytext, monacoBinding } 59 | }) 60 | -------------------------------------------------------------------------------- /monaco/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yjs-monaco-demo", 3 | "version": "1.0.0", 4 | "description": "Yjs ❤ Monaco", 5 | "main": "index.js", 6 | "scripts": { 7 | "ensureBundle": "[ -f ./dist/monaco.bundle.js ] || npm run dist", 8 | "watch": "webpack -w --stats errors-only", 9 | "dist": "webpack --mode=production", 10 | "start": "npm run ensureBundle && webpack serve --open monaco.html" 11 | }, 12 | "author": "Kevin Jahns ", 13 | "license": "UNLICENSE", 14 | "dependencies": { 15 | "yjs": "^13.5.8", 16 | "y-monaco": "0.1.2", 17 | "y-websocket": "^1.3.15", 18 | "monaco-editor": "^0.44.0", 19 | "css-loader": "^6.8.1", 20 | "monaco-editor-webpack-plugin": "^7.1.0", 21 | "style-loader": "^3.3.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /monaco/webpack.config.js: -------------------------------------------------------------------------------- 1 | const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin') 2 | const path = require('path') 3 | 4 | module.exports = { 5 | mode: 'development', 6 | devtool: 'source-map', 7 | entry: { 8 | monaco: './monaco.js' 9 | }, 10 | output: { 11 | globalObject: 'self', 12 | path: path.resolve(__dirname, './dist/'), 13 | filename: '[name].bundle.js', 14 | publicPath: './dist/' 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.css$/, 20 | use: ['style-loader', 'css-loader'] 21 | }, 22 | { 23 | test: /\.ttf$/, 24 | type: 'asset/resource' 25 | } 26 | ] 27 | }, 28 | devServer: { 29 | static: path.join(__dirname), 30 | compress: true 31 | }, 32 | plugins: [new MonacoWebpackPlugin({ 33 | publicPath: './dist/' 34 | })] 35 | } 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yjs-demos", 3 | "version": "0.0.0", 4 | "private": true, 5 | "description": "You can ignore this package.json. We only need it for eploying the demos to our CDN.", 6 | "funding": { 7 | "type": "GitHub Sponsors ❤", 8 | "url": "https://github.com/sponsors/dmonad" 9 | }, 10 | "scripts": { 11 | "clean": "make clean", 12 | "test": "standard", 13 | "start": "make serve", 14 | "demo-server": "cd demo-server && npm ci && npm start", 15 | "dist": "make dist", 16 | "lint": "standard", 17 | "container:build": "docker build . -t yjs-demos", 18 | "container:run": "docker run -p 3000:3000 --name yjs-demos yjs-demos", 19 | "container:stop": "docker stop yjs-demos && docker remove yjs-demos" 20 | }, 21 | "author": "Kevin Jahns =16.0.0" 31 | }, 32 | "dependencies": { 33 | "standard": "^16.0.3", 34 | "webpack": "^5.91.0", 35 | "webpack-cli": "^5.1.4", 36 | "webpack-dev-server": "^5.0.4" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /prosemirror-versions/README.md: -------------------------------------------------------------------------------- 1 | # ProseMirror + Versions Demo 2 | > [y-prosemirror](https://docs.yjs.dev/ecosystem/editor-bindings/prosemirror) / [y-websocket](https://docs.yjs.dev/ecosystem/connection-provider/y-websocket) / [Live Demo](https://demos.yjs.dev/prosemirror-versions/prosemirror-versions.html) 3 | 4 | This is a demo of a [ProseMirror](https://prosemirror.net/) editor that was made collaborative with Yjs & y-prosemirror. This demo uses the [prosemirror-example-setup](https://github.com/ProseMirror/prosemirror-example-setup) as a configuration. Learn more about how you can build your own custom editor with ProseMirror in [their documentation](https://github.com/yjs/y-prosemirror/). 5 | 6 | In this variation of the ProseMirror demo, we allow you to create versions and render the differences and highlight the changes by a unique user-color. When you enable "Live Tracking", your editor becomes unresponsive and you can see diff created from edits from other users happening in real-time. Click on a created snapshot to render the differences to the previous snapshot. 7 | 8 | The snapshot API is very versatile and allows you to render the differences between any two snapshots. In this examples, we simply created a linear version history. But using the same API you can model branching & merging as well. 9 | 10 | ## Quick Start 11 | 12 | ```sh 13 | npm i 14 | npm start 15 | # Project is running at http://localhost:8080/ 16 | ``` 17 | -------------------------------------------------------------------------------- /prosemirror-versions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yjs-prosemirror-versions", 3 | "version": "1.0.0", 4 | "description": "Yjs ❤ ProseMirror + versioning support", 5 | "scripts": { 6 | "watch": "webpack -w --stats errors-only", 7 | "dist": "webpack --mode=production", 8 | "start": "webpack serve --open prosemirror-versions.html" 9 | }, 10 | "author": "Kevin Jahns ", 11 | "license": "UNLICENSE", 12 | "dependencies": { 13 | "yjs": "^13.4.12", 14 | "y-prosemirror": "^0.3.7", 15 | "y-websocket": "^1.3.9", 16 | "lit-html": "^1.4.1", 17 | "prosemirror-example-setup": "^1.1.2", 18 | "prosemirror-model": "^1.13.1", 19 | "prosemirror-state": "^1.3.4", 20 | "prosemirror-view": "^1.17.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /prosemirror-versions/prosemirror-versions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Yjs Prosemirror Versions Example 6 | 7 | 8 | 9 | 62 | 63 | 64 |
65 |
66 | 67 |
68 |

69 |

This is a demo of the YjsProseMirror binding: y-prosemirror.

70 |

The content of this editor is shared with every client that visits this domain.

71 | 72 | 73 | -------------------------------------------------------------------------------- /prosemirror-versions/prosemirror-versions.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | import * as Y from 'yjs' 4 | import { WebsocketProvider } from 'y-websocket' 5 | import { ySyncPlugin, ySyncPluginKey, yCursorPlugin, yUndoPlugin, undo, redo } from 'y-prosemirror' 6 | import { EditorState } from 'prosemirror-state' 7 | import { EditorView } from 'prosemirror-view' 8 | import { schema } from './schema.js' 9 | import { exampleSetup } from 'prosemirror-example-setup' 10 | import { keymap } from 'prosemirror-keymap' 11 | import * as random from 'lib0/random.js' 12 | import { html, render } from 'lit-html' 13 | import * as dom from 'lib0/dom.js' 14 | import * as pair from 'lib0/pair.js' 15 | 16 | const roomname = `prosemirror-versions-demo-${new Date().toLocaleDateString('en-CA')}` 17 | 18 | /** 19 | * @typedef {Object} Version 20 | * @property {number} date 21 | * @property {Uint8Array} snapshot 22 | * @property {number} clientID 23 | */ 24 | 25 | /** 26 | * @param {Y.Doc} doc 27 | */ 28 | const addVersion = doc => { 29 | const versions = doc.getArray('versions') 30 | const prevVersion = versions.length === 0 ? null : versions.get(versions.length - 1) 31 | const prevSnapshot = prevVersion === null ? Y.emptySnapshot : Y.decodeSnapshot(prevVersion.snapshot) 32 | const snapshot = Y.snapshot(doc) 33 | if (prevVersion != null) { 34 | // account for the action of adding a version to ydoc 35 | prevSnapshot.sv.set(prevVersion.clientID, /** @type {number} */ (prevSnapshot.sv.get(prevVersion.clientID)) + 1) 36 | } 37 | if (!Y.equalSnapshots(prevSnapshot, snapshot)) { 38 | versions.push([{ 39 | date: new Date().getTime(), 40 | snapshot: Y.encodeSnapshot(snapshot), 41 | clientID: doc.clientID 42 | }]) 43 | } 44 | } 45 | 46 | const liveTracking = /** @type {HTMLInputElement} */ (dom.element('input', [ 47 | pair.create('type', 'checkbox'), 48 | pair.create('name', 'yjs-live-tracking'), 49 | pair.create('value', 'Live Tracking ') 50 | ])) 51 | 52 | const updateLiveTrackingState = editorstate => { 53 | setTimeout(() => { 54 | const syncState = ySyncPluginKey.getState(editorstate.state) 55 | liveTracking.checked = syncState.prevSnapshot != null && syncState.snapshot == null 56 | }, 500) 57 | } 58 | 59 | const renderVersion = (editorview, version, prevSnapshot) => { 60 | editorview.dispatch(editorview.state.tr.setMeta(ySyncPluginKey, { snapshot: Y.decodeSnapshot(version.snapshot), prevSnapshot: prevSnapshot == null ? Y.emptySnapshot : Y.decodeSnapshot(prevSnapshot) })) 61 | updateLiveTrackingState(editorview) 62 | } 63 | 64 | const unrenderVersion = editorview => { 65 | const binding = ySyncPluginKey.getState(editorview.state).binding 66 | if (binding != null) { 67 | binding.unrenderSnapshot() 68 | } 69 | updateLiveTrackingState(editorview) 70 | } 71 | 72 | /** 73 | * @param {EditorView} editorview 74 | * @param {Version} version 75 | * @param {Version|null} prevSnapshot 76 | */ 77 | const versionTemplate = (editorview, version, prevSnapshot) => html`
renderVersion(editorview, version, prevSnapshot)}>${new Date(version.date).toLocaleString()}
` 78 | 79 | const versionList = (editorview, doc) => { 80 | const versions = doc.getArray('versions') 81 | return html`
${versions.length > 0 ? versions.map((version, i) => versionTemplate(editorview, version, i > 0 ? versions.get(i - 1).snapshot : null)) : html`
No snapshots..
`}
` 82 | } 83 | 84 | const snapshotButton = doc => { 85 | return html`` 86 | } 87 | 88 | /** 89 | * @param {HTMLElement} parent 90 | * @param {Y.Doc} doc 91 | * @param {EditorView} editorview 92 | */ 93 | export const attachVersion = (parent, doc, editorview) => { 94 | let open = false 95 | const rerender = () => { 96 | render(html`
${snapshotButton(doc)}${versionList(editorview, doc)}
`, vContainer) 97 | } 98 | updateLiveTrackingState(editorview) 99 | liveTracking.addEventListener('click', () => { 100 | if (liveTracking.checked) { 101 | const versions = doc.getArray('versions') 102 | const lastVersion = versions.length > 0 ? Y.decodeSnapshot(versions.get(versions.length - 1).snapshot) : Y.emptySnapshot 103 | editorview.dispatch(editorview.state.tr.setMeta(ySyncPluginKey, { snapshot: null, prevSnapshot: lastVersion })) 104 | } else { 105 | unrenderVersion(editorview) 106 | } 107 | }) 108 | parent.insertBefore(liveTracking, null) 109 | parent.insertBefore(dom.element('label', [ 110 | pair.create('for', 'yjs-live-tracking') 111 | ], [ 112 | dom.text('Live Tracking ') 113 | ]), null) 114 | const btn = document.createElement('button') 115 | btn.setAttribute('type', 'button') 116 | btn.textContent = 'Versions' 117 | btn.addEventListener('click', () => { 118 | open = !open 119 | unrenderVersion(editorview) 120 | rerender() 121 | }) 122 | const vContainer = document.createElement('div') 123 | parent.insertBefore(btn, null) 124 | parent.insertBefore(vContainer, null) 125 | doc.getArray('versions').observe(rerender) 126 | rerender() 127 | } 128 | 129 | const testUsers = [ 130 | { username: 'Alice', color: '#ecd444', lightColor: '#ecd44433' }, 131 | { username: 'Bob', color: '#ee6352', lightColor: '#ee635233' }, 132 | { username: 'Max', color: '#6eeb83', lightColor: '#6eeb8333' } 133 | ] 134 | 135 | const colors = [ 136 | { light: '#ecd44433', dark: '#ecd444' }, 137 | { light: '#ee635233', dark: '#ee6352' }, 138 | { light: '#6eeb8333', dark: '#6eeb83' } 139 | ] 140 | 141 | const user = random.oneOf(testUsers) 142 | 143 | window.addEventListener('load', () => { 144 | const ydoc = new Y.Doc() 145 | const permanentUserData = new Y.PermanentUserData(ydoc) 146 | permanentUserData.setUserMapping(ydoc, ydoc.clientID, user.username) 147 | ydoc.gc = false 148 | const provider = new WebsocketProvider( 149 | 'wss://demos.yjs.dev/ws', // use the public ws server 150 | // `ws${location.protocol.slice(4)}//${location.host}/ws`, // alternatively: use the local ws server (run `npm start` in root directory) 151 | roomname, 152 | ydoc 153 | ) 154 | const yXmlFragment = ydoc.get('prosemirror', Y.XmlFragment) 155 | 156 | const editor = document.createElement('div') 157 | editor.setAttribute('id', 'editor') 158 | const editorContainer = document.createElement('div') 159 | editorContainer.insertBefore(editor, null) 160 | const prosemirrorView = new EditorView(editor, { 161 | state: EditorState.create({ 162 | schema, 163 | plugins: [ 164 | ySyncPlugin(yXmlFragment, { permanentUserData, colors }), 165 | yCursorPlugin(provider.awareness), 166 | yUndoPlugin(), 167 | keymap({ 168 | 'Mod-z': undo, 169 | 'Mod-y': redo, 170 | 'Mod-Shift-z': redo 171 | }) 172 | ].concat(exampleSetup({ schema })) 173 | }) 174 | }) 175 | document.body.insertBefore(editorContainer, null) 176 | 177 | attachVersion(document.getElementById('y-version'), ydoc, prosemirrorView) 178 | 179 | const connectBtn = document.getElementById('y-connect-btn') 180 | connectBtn.addEventListener('click', () => { 181 | if (provider.shouldConnect) { 182 | provider.disconnect() 183 | connectBtn.textContent = 'Connect' 184 | } else { 185 | provider.connect() 186 | connectBtn.textContent = 'Disconnect' 187 | } 188 | }) 189 | 190 | // @ts-ignore 191 | window.example = { provider, ydoc, yXmlFragment, prosemirrorView } 192 | }) 193 | -------------------------------------------------------------------------------- /prosemirror-versions/prosemirror.css: -------------------------------------------------------------------------------- 1 | .ProseMirror { 2 | position: relative; 3 | } 4 | 5 | .ProseMirror { 6 | word-wrap: break-word; 7 | white-space: pre-wrap; 8 | -webkit-font-variant-ligatures: none; 9 | font-variant-ligatures: none; 10 | } 11 | 12 | .ProseMirror pre { 13 | white-space: pre-wrap; 14 | } 15 | 16 | .ProseMirror li { 17 | position: relative; 18 | } 19 | 20 | .ProseMirror-hideselection *::selection { background: transparent; } 21 | .ProseMirror-hideselection *::-moz-selection { background: transparent; } 22 | .ProseMirror-hideselection { caret-color: transparent; } 23 | 24 | .ProseMirror-selectednode { 25 | outline: 2px solid #8cf; 26 | } 27 | 28 | /* Make sure li selections wrap around markers */ 29 | 30 | li.ProseMirror-selectednode { 31 | outline: none; 32 | } 33 | 34 | li.ProseMirror-selectednode:after { 35 | content: ""; 36 | position: absolute; 37 | left: -32px; 38 | right: -2px; top: -2px; bottom: -2px; 39 | border: 2px solid #8cf; 40 | pointer-events: none; 41 | } 42 | .ProseMirror-textblock-dropdown { 43 | min-width: 3em; 44 | } 45 | 46 | .ProseMirror-menu { 47 | margin: 0 -4px; 48 | line-height: 1; 49 | } 50 | 51 | .ProseMirror-tooltip .ProseMirror-menu { 52 | width: -webkit-fit-content; 53 | width: fit-content; 54 | white-space: pre; 55 | } 56 | 57 | .ProseMirror-menuitem { 58 | margin-right: 3px; 59 | display: inline-block; 60 | } 61 | 62 | .ProseMirror-menuseparator { 63 | border-right: 1px solid #ddd; 64 | margin-right: 3px; 65 | } 66 | 67 | .ProseMirror-menu-dropdown, .ProseMirror-menu-dropdown-menu { 68 | font-size: 90%; 69 | white-space: nowrap; 70 | } 71 | 72 | .ProseMirror-menu-dropdown { 73 | vertical-align: 1px; 74 | cursor: pointer; 75 | position: relative; 76 | padding-right: 15px; 77 | } 78 | 79 | .ProseMirror-menu-dropdown-wrap { 80 | padding: 1px 0 1px 4px; 81 | display: inline-block; 82 | position: relative; 83 | } 84 | 85 | .ProseMirror-menu-dropdown:after { 86 | content: ""; 87 | border-left: 4px solid transparent; 88 | border-right: 4px solid transparent; 89 | border-top: 4px solid currentColor; 90 | opacity: .6; 91 | position: absolute; 92 | right: 4px; 93 | top: calc(50% - 2px); 94 | } 95 | 96 | .ProseMirror-menu-dropdown-menu, .ProseMirror-menu-submenu { 97 | position: absolute; 98 | background: white; 99 | color: #666; 100 | border: 1px solid #aaa; 101 | padding: 2px; 102 | } 103 | 104 | .ProseMirror-menu-dropdown-menu { 105 | z-index: 15; 106 | min-width: 6em; 107 | } 108 | 109 | .ProseMirror-menu-dropdown-item { 110 | cursor: pointer; 111 | padding: 2px 8px 2px 4px; 112 | } 113 | 114 | .ProseMirror-menu-dropdown-item:hover { 115 | background: #f2f2f2; 116 | } 117 | 118 | .ProseMirror-menu-submenu-wrap { 119 | position: relative; 120 | margin-right: -4px; 121 | } 122 | 123 | .ProseMirror-menu-submenu-label:after { 124 | content: ""; 125 | border-top: 4px solid transparent; 126 | border-bottom: 4px solid transparent; 127 | border-left: 4px solid currentColor; 128 | opacity: .6; 129 | position: absolute; 130 | right: 4px; 131 | top: calc(50% - 4px); 132 | } 133 | 134 | .ProseMirror-menu-submenu { 135 | display: none; 136 | min-width: 4em; 137 | left: 100%; 138 | top: -3px; 139 | } 140 | 141 | .ProseMirror-menu-active { 142 | background: #eee; 143 | border-radius: 4px; 144 | } 145 | 146 | .ProseMirror-menu-active { 147 | background: #eee; 148 | border-radius: 4px; 149 | } 150 | 151 | .ProseMirror-menu-disabled { 152 | opacity: .3; 153 | } 154 | 155 | .ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu, .ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu { 156 | display: block; 157 | } 158 | 159 | .ProseMirror-menubar { 160 | border-top-left-radius: inherit; 161 | border-top-right-radius: inherit; 162 | position: relative; 163 | min-height: 1em; 164 | color: #666; 165 | padding: 1px 6px; 166 | top: 0; left: 0; right: 0; 167 | border-bottom: 1px solid silver; 168 | background: white; 169 | z-index: 10; 170 | -moz-box-sizing: border-box; 171 | box-sizing: border-box; 172 | overflow: visible; 173 | } 174 | 175 | .ProseMirror-icon { 176 | display: inline-block; 177 | line-height: .8; 178 | vertical-align: -2px; /* Compensate for padding */ 179 | padding: 2px 8px; 180 | cursor: pointer; 181 | } 182 | 183 | .ProseMirror-menu-disabled.ProseMirror-icon { 184 | cursor: default; 185 | } 186 | 187 | .ProseMirror-icon svg { 188 | fill: currentColor; 189 | height: 1em; 190 | } 191 | 192 | .ProseMirror-icon span { 193 | vertical-align: text-top; 194 | } 195 | .ProseMirror-gapcursor { 196 | display: none; 197 | pointer-events: none; 198 | position: absolute; 199 | } 200 | 201 | .ProseMirror-gapcursor:after { 202 | content: ""; 203 | display: block; 204 | position: absolute; 205 | top: -2px; 206 | width: 20px; 207 | border-top: 1px solid black; 208 | animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite; 209 | } 210 | 211 | @keyframes ProseMirror-cursor-blink { 212 | to { 213 | visibility: hidden; 214 | } 215 | } 216 | 217 | .ProseMirror-focused .ProseMirror-gapcursor { 218 | display: block; 219 | } 220 | /* Add space around the hr to make clicking it easier */ 221 | 222 | .ProseMirror-example-setup-style hr { 223 | padding: 2px 10px; 224 | border: none; 225 | margin: 1em 0; 226 | } 227 | 228 | .ProseMirror-example-setup-style hr:after { 229 | content: ""; 230 | display: block; 231 | height: 1px; 232 | background-color: silver; 233 | line-height: 2px; 234 | } 235 | 236 | .ProseMirror ul, .ProseMirror ol { 237 | padding-left: 30px; 238 | } 239 | 240 | .ProseMirror blockquote { 241 | padding-left: 1em; 242 | border-left: 3px solid #eee; 243 | margin-left: 0; margin-right: 0; 244 | } 245 | 246 | .ProseMirror-example-setup-style img { 247 | cursor: default; 248 | } 249 | 250 | .ProseMirror-prompt { 251 | background: white; 252 | padding: 5px 10px 5px 15px; 253 | border: 1px solid silver; 254 | position: fixed; 255 | border-radius: 3px; 256 | z-index: 11; 257 | box-shadow: -.5px 2px 5px rgba(0, 0, 0, .2); 258 | } 259 | 260 | .ProseMirror-prompt h5 { 261 | margin: 0; 262 | font-weight: normal; 263 | font-size: 100%; 264 | color: #444; 265 | } 266 | 267 | .ProseMirror-prompt input[type="text"], 268 | .ProseMirror-prompt textarea { 269 | background: #eee; 270 | border: none; 271 | outline: none; 272 | } 273 | 274 | .ProseMirror-prompt input[type="text"] { 275 | padding: 0 4px; 276 | } 277 | 278 | .ProseMirror-prompt-close { 279 | position: absolute; 280 | left: 2px; top: 1px; 281 | color: #666; 282 | border: none; background: transparent; padding: 0; 283 | } 284 | 285 | .ProseMirror-prompt-close:after { 286 | content: "✕"; 287 | font-size: 12px; 288 | } 289 | 290 | .ProseMirror-invalid { 291 | background: #ffc; 292 | border: 1px solid #cc7; 293 | border-radius: 4px; 294 | padding: 5px 10px; 295 | position: absolute; 296 | min-width: 10em; 297 | } 298 | 299 | .ProseMirror-prompt-buttons { 300 | margin-top: 5px; 301 | display: none; 302 | } 303 | #editor, .editor { 304 | background: white; 305 | color: black; 306 | background-clip: padding-box; 307 | border-radius: 4px; 308 | border: 2px solid rgba(0, 0, 0, 0.2); 309 | padding: 5px 0; 310 | margin-bottom: 23px; 311 | } 312 | 313 | .ProseMirror p:first-child, 314 | .ProseMirror h1:first-child, 315 | .ProseMirror h2:first-child, 316 | .ProseMirror h3:first-child, 317 | .ProseMirror h4:first-child, 318 | .ProseMirror h5:first-child, 319 | .ProseMirror h6:first-child { 320 | margin-top: 10px; 321 | } 322 | 323 | .ProseMirror { 324 | padding: 4px 8px 4px 14px; 325 | line-height: 1.2; 326 | outline: none; 327 | } 328 | 329 | .ProseMirror p { margin-bottom: 1em } 330 | 331 | -------------------------------------------------------------------------------- /prosemirror-versions/schema.js: -------------------------------------------------------------------------------- 1 | import { Schema } from 'prosemirror-model' 2 | 3 | const brDOM = ['br'] 4 | 5 | const calcYChangeStyle = ychange => { 6 | switch (ychange.type) { 7 | case 'removed': 8 | return `color:${ychange.color.dark}` 9 | case 'added': 10 | return `background-color:${ychange.color.light}` 11 | case null: 12 | return '' 13 | } 14 | } 15 | 16 | const calcYchangeDomAttrs = (attrs, domAttrs = {}) => { 17 | domAttrs = Object.assign({}, domAttrs) 18 | if (attrs.ychange !== null) { 19 | domAttrs.ychange_user = attrs.ychange.user 20 | domAttrs.ychange_type = attrs.ychange.type 21 | domAttrs.ychange_color = attrs.ychange.color.light 22 | domAttrs.style = calcYChangeStyle(attrs.ychange) 23 | } 24 | return domAttrs 25 | } 26 | 27 | /** 28 | * @param {any} ychange 29 | * @param {Array} 30 | */ 31 | const hoverWrapper = (ychange, els) => 32 | ychange === null ? els : [['span', { class: 'ychange-hover', style: `background-color:${ychange.color.dark}` }, ychange.user || 'Unknown'], ['span', ...els]] 33 | 34 | // :: Object 35 | // [Specs](#model.NodeSpec) for the nodes defined in this schema. 36 | export const nodes = { 37 | // :: NodeSpec The top level document node. 38 | doc: { 39 | content: 'block+' 40 | }, 41 | 42 | // :: NodeSpec A plain paragraph textblock. Represented in the DOM 43 | // as a `

` element. 44 | paragraph: { 45 | attrs: { ychange: { default: null } }, 46 | content: 'inline*', 47 | group: 'block', 48 | parseDOM: [{ tag: 'p' }], 49 | toDOM (node) { 50 | // only render changes if no child nodes 51 | const renderChanges = node.content.size === 0 52 | const attrs = renderChanges ? calcYchangeDomAttrs(node.attrs) : node.attrs 53 | const defChildren = [0] 54 | const children = renderChanges ? hoverWrapper(node.attrs.ychange, defChildren) : defChildren 55 | return ['p', attrs, ...children] 56 | } 57 | }, 58 | 59 | // :: NodeSpec A blockquote (`

`) wrapping one or more blocks. 60 | blockquote: { 61 | attrs: { ychange: { default: null } }, 62 | content: 'block+', 63 | group: 'block', 64 | defining: true, 65 | parseDOM: [{ tag: 'blockquote' }], 66 | toDOM (node) { return ['blockquote', calcYchangeDomAttrs(node.attrs), ...hoverWrapper(node.attrs.ychange, [0])] } 67 | }, 68 | 69 | // :: NodeSpec A horizontal rule (`
`). 70 | horizontal_rule: { 71 | attrs: { ychange: { default: null } }, 72 | group: 'block', 73 | parseDOM: [{ tag: 'hr' }], 74 | toDOM (node) { 75 | return ['hr', calcYchangeDomAttrs(node.attrs), ...hoverWrapper(node.attrs.ychange, [])] 76 | } 77 | }, 78 | 79 | // :: NodeSpec A heading textblock, with a `level` attribute that 80 | // should hold the number 1 to 6. Parsed and serialized as `

` to 81 | // `

` elements. 82 | heading: { 83 | attrs: { 84 | level: { default: 1 }, 85 | ychange: { default: null } 86 | }, 87 | content: 'inline*', 88 | group: 'block', 89 | defining: true, 90 | parseDOM: [{ tag: 'h1', attrs: { level: 1 } }, 91 | { tag: 'h2', attrs: { level: 2 } }, 92 | { tag: 'h3', attrs: { level: 3 } }, 93 | { tag: 'h4', attrs: { level: 4 } }, 94 | { tag: 'h5', attrs: { level: 5 } }, 95 | { tag: 'h6', attrs: { level: 6 } }], 96 | toDOM (node) { return ['h' + node.attrs.level, calcYchangeDomAttrs(node.attrs), ...hoverWrapper(node.attrs.ychange, [0])] } 97 | }, 98 | 99 | // :: NodeSpec A code listing. Disallows marks or non-text inline 100 | // nodes by default. Represented as a `
` element with a
101 |   // `` element inside of it.
102 |   code_block: {
103 |     attrs: { ychange: { default: null } },
104 |     content: 'text*',
105 |     marks: '',
106 |     group: 'block',
107 |     code: true,
108 |     defining: true,
109 |     parseDOM: [{ tag: 'pre', preserveWhitespace: 'full' }],
110 |     toDOM (node) { return ['pre', calcYchangeDomAttrs(node.attrs), ...hoverWrapper(node.attrs.ychange, [['code', 0]])] }
111 |   },
112 | 
113 |   // :: NodeSpec The text node.
114 |   text: {
115 |     group: 'inline'
116 |   },
117 | 
118 |   // :: NodeSpec An inline image (``) node. Supports `src`,
119 |   // `alt`, and `href` attributes. The latter two default to the empty
120 |   // string.
121 |   image: {
122 |     inline: true,
123 |     attrs: {
124 |       ychange: { default: null },
125 |       src: {},
126 |       alt: { default: null },
127 |       title: { default: null }
128 |     },
129 |     group: 'inline',
130 |     draggable: true,
131 |     parseDOM: [{
132 |       tag: 'img[src]',
133 |       getAttrs (dom) {
134 |         return {
135 |           src: dom.getAttribute('src'),
136 |           title: dom.getAttribute('title'),
137 |           alt: dom.getAttribute('alt')
138 |         }
139 |       }
140 |     }],
141 |     toDOM (node) {
142 |       const domAttrs = {
143 |         src: node.attrs.src,
144 |         title: node.attrs.title,
145 |         alt: node.attrs.alt
146 |       }
147 |       return ['img', calcYchangeDomAttrs(node.attrs, domAttrs), ...hoverWrapper(node.attrs.ychange, [])]
148 |     }
149 |   },
150 | 
151 |   // :: NodeSpec A hard line break, represented in the DOM as `
`. 152 | hard_break: { 153 | inline: true, 154 | group: 'inline', 155 | selectable: false, 156 | parseDOM: [{ tag: 'br' }], 157 | toDOM () { return brDOM } 158 | } 159 | } 160 | 161 | const emDOM = ['em', 0]; const strongDOM = ['strong', 0]; const codeDOM = ['code', 0] 162 | 163 | // :: Object [Specs](#model.MarkSpec) for the marks in the schema. 164 | export const marks = { 165 | // :: MarkSpec A link. Has `href` and `title` attributes. `title` 166 | // defaults to the empty string. Rendered and parsed as an `` 167 | // element. 168 | link: { 169 | attrs: { 170 | href: {}, 171 | title: { default: null } 172 | }, 173 | inclusive: false, 174 | parseDOM: [{ 175 | tag: 'a[href]', 176 | getAttrs (dom) { 177 | return { href: dom.getAttribute('href'), title: dom.getAttribute('title') } 178 | } 179 | }], 180 | toDOM (node) { return ['a', node.attrs, 0] } 181 | }, 182 | 183 | // :: MarkSpec An emphasis mark. Rendered as an `` element. 184 | // Has parse rules that also match `` and `font-style: italic`. 185 | em: { 186 | parseDOM: [{ tag: 'i' }, { tag: 'em' }, { style: 'font-style=italic' }], 187 | toDOM () { return emDOM } 188 | }, 189 | 190 | // :: MarkSpec A strong mark. Rendered as ``, parse rules 191 | // also match `` and `font-weight: bold`. 192 | strong: { 193 | parseDOM: [{ tag: 'strong' }, 194 | // This works around a Google Docs misbehavior where 195 | // pasted content will be inexplicably wrapped in `` 196 | // tags with a font-weight normal. 197 | { tag: 'b', getAttrs: node => node.style.fontWeight !== 'normal' && null }, 198 | { style: 'font-weight', getAttrs: value => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null }], 199 | toDOM () { return strongDOM } 200 | }, 201 | 202 | // :: MarkSpec Code font mark. Represented as a `` element. 203 | code: { 204 | parseDOM: [{ tag: 'code' }], 205 | toDOM () { return codeDOM } 206 | }, 207 | ychange: { 208 | attrs: { 209 | user: { default: null }, 210 | type: { default: null }, 211 | color: { default: null } 212 | }, 213 | inclusive: false, 214 | parseDOM: [{ tag: 'ychange' }], 215 | toDOM (node) { 216 | return ['ychange', { ychange_user: node.attrs.user, ychange_type: node.attrs.type, style: calcYChangeStyle(node.attrs), ychange_color: node.attrs.color.light }, ...hoverWrapper(node.attrs, [0])] 217 | } 218 | } 219 | } 220 | 221 | // :: Schema 222 | // This schema rougly corresponds to the document schema used by 223 | // [CommonMark](http://commonmark.org/), minus the list elements, 224 | // which are defined in the [`prosemirror-schema-list`](#schema-list) 225 | // module. 226 | // 227 | // To reuse elements from this schema, extend or read from its 228 | // `spec.nodes` and `spec.marks` [properties](#model.Schema.spec). 229 | export const schema = new Schema({ nodes, marks }) 230 | -------------------------------------------------------------------------------- /prosemirror-versions/version.css: -------------------------------------------------------------------------------- 1 | #y-version { 2 | position: relative; 3 | } 4 | 5 | .version-modal[hidden] { 6 | display: none; 7 | } 8 | 9 | .version-modal:not([hidden]) { 10 | position: absolute; 11 | top: 30px; 12 | right: 0; 13 | width: 300px; 14 | min-height: 300px; 15 | height: fit-content; 16 | z-index: 20; 17 | background-color: white; 18 | border: 1px solid #ccc; 19 | border-radius: 11px; 20 | border-top-right-radius: 0; 21 | box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); 22 | } 23 | 24 | .version-modal > button { 25 | position: absolute; 26 | right: 0; 27 | top: 0; 28 | } 29 | 30 | .version-list { 31 | line-height: 30px; 32 | padding: 0 8px; 33 | cursor: pointer; 34 | } 35 | 36 | .version-list:hover { 37 | background-color: #eee; 38 | } 39 | 40 | [ychange_type] { 41 | position: relative; 42 | } 43 | .ychange-hover { 44 | display: none; 45 | } 46 | *:hover > .ychange-hover { 47 | display: inline; 48 | position: absolute; 49 | top: -14px; 50 | left: 0; 51 | font-size: 12px; 52 | padding: 0 2px; 53 | border-radius: 3px 3px 0 0; 54 | color: #fdfdfe; 55 | user-select: none; 56 | word-break: normal; 57 | } 58 | 59 | ychange[ychange_type='removed'], p[ychange_type='removed'] { 60 | text-decoration: line-through; 61 | } 62 | /* 63 | p[ychange_type='removed'] > span > br { 64 | display: none; 65 | } 66 | */ 67 | *:not(ychange)[ychange_type='removed'] { 68 | background-color: #ff5a56; 69 | color: inherit !important; 70 | } 71 | img[ychange_type='removed'] { 72 | padding: 2px; 73 | } 74 | -------------------------------------------------------------------------------- /prosemirror-versions/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | mode: 'development', 5 | devtool: 'source-map', 6 | entry: { 7 | 'prosemirror-versions': './prosemirror-versions.js' 8 | }, 9 | output: { 10 | globalObject: 'self', 11 | path: path.resolve(__dirname, './dist/'), 12 | filename: '[name].bundle.js', 13 | publicPath: '/dist/' 14 | }, 15 | devServer: { 16 | static: path.join(__dirname), 17 | compress: true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /prosemirror/README.md: -------------------------------------------------------------------------------- 1 | # ProseMirror Demo 2 | > [y-prosemirror](https://docs.yjs.dev/ecosystem/editor-bindings/prosemirror) / [y-websocket](https://docs.yjs.dev/ecosystem/connection-provider/y-websocket) / [Live Demo](https://demos.yjs.dev/prosemirror/prosemirror.html) 3 | 4 | This is a demo of a [ProseMirror](https://prosemirror.net/) editor that was made collaborative with Yjs & y-prosemirror. This demo uses the [prosemirror-example-setup](https://github.com/ProseMirror/prosemirror-example-setup) as a configuration. Learn more about how you can build your own custom editor with ProseMirror in [their documentation](https://github.com/yjs/y-prosemirror/). 5 | 6 | We use the [y-websocket](https://docs.yjs.dev/ecosystem/connection-provider/y-websocket) provider to share document updates through a server. There are many more providers available for Yjs - try switching to another provider. [See docs](https://docs.yjs.dev/ecosystem/connection-provider). 7 | 8 | Also you could try adding offline persistence to this demo. Wouldn't it be cool if document updates are persisted in the browser, so you can load your application load faster? Try it out: https://docs.yjs.dev/getting-started/allowing-offline-editing 9 | 10 | ## Quick Start 11 | 12 | ```sh 13 | npm i 14 | npm start 15 | # Project is running at http://localhost:8080/ 16 | ``` 17 | -------------------------------------------------------------------------------- /prosemirror/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yjs-prosemirror-demo", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "yjs-prosemirror-demo", 9 | "version": "1.0.0", 10 | "license": "UNLICENSE", 11 | "dependencies": { 12 | "prosemirror-example-setup": "^1.1.2", 13 | "prosemirror-model": "^1.13.1", 14 | "prosemirror-state": "^1.3.4", 15 | "prosemirror-view": "^1.18.7", 16 | "y-prosemirror": "^1.2.8", 17 | "y-websocket": "^1.3.15", 18 | "yjs": "^13.5.8" 19 | } 20 | }, 21 | "node_modules/abstract-leveldown": { 22 | "version": "6.2.3", 23 | "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz", 24 | "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==", 25 | "optional": true, 26 | "dependencies": { 27 | "buffer": "^5.5.0", 28 | "immediate": "^3.2.3", 29 | "level-concat-iterator": "~2.0.0", 30 | "level-supports": "~1.0.0", 31 | "xtend": "~4.0.0" 32 | }, 33 | "engines": { 34 | "node": ">=6" 35 | } 36 | }, 37 | "node_modules/async-limiter": { 38 | "version": "1.0.1", 39 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", 40 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", 41 | "optional": true 42 | }, 43 | "node_modules/base64-js": { 44 | "version": "1.5.1", 45 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 46 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 47 | "funding": [ 48 | { 49 | "type": "github", 50 | "url": "https://github.com/sponsors/feross" 51 | }, 52 | { 53 | "type": "patreon", 54 | "url": "https://www.patreon.com/feross" 55 | }, 56 | { 57 | "type": "consulting", 58 | "url": "https://feross.org/support" 59 | } 60 | ], 61 | "optional": true 62 | }, 63 | "node_modules/buffer": { 64 | "version": "5.7.1", 65 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 66 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 67 | "funding": [ 68 | { 69 | "type": "github", 70 | "url": "https://github.com/sponsors/feross" 71 | }, 72 | { 73 | "type": "patreon", 74 | "url": "https://www.patreon.com/feross" 75 | }, 76 | { 77 | "type": "consulting", 78 | "url": "https://feross.org/support" 79 | } 80 | ], 81 | "optional": true, 82 | "dependencies": { 83 | "base64-js": "^1.3.1", 84 | "ieee754": "^1.1.13" 85 | } 86 | }, 87 | "node_modules/crelt": { 88 | "version": "1.0.6", 89 | "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", 90 | "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" 91 | }, 92 | "node_modules/deferred-leveldown": { 93 | "version": "5.3.0", 94 | "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz", 95 | "integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==", 96 | "optional": true, 97 | "dependencies": { 98 | "abstract-leveldown": "~6.2.1", 99 | "inherits": "^2.0.3" 100 | }, 101 | "engines": { 102 | "node": ">=6" 103 | } 104 | }, 105 | "node_modules/encoding-down": { 106 | "version": "6.3.0", 107 | "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz", 108 | "integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==", 109 | "optional": true, 110 | "dependencies": { 111 | "abstract-leveldown": "^6.2.1", 112 | "inherits": "^2.0.3", 113 | "level-codec": "^9.0.0", 114 | "level-errors": "^2.0.0" 115 | }, 116 | "engines": { 117 | "node": ">=6" 118 | } 119 | }, 120 | "node_modules/errno": { 121 | "version": "0.1.8", 122 | "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", 123 | "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", 124 | "optional": true, 125 | "dependencies": { 126 | "prr": "~1.0.1" 127 | }, 128 | "bin": { 129 | "errno": "cli.js" 130 | } 131 | }, 132 | "node_modules/ieee754": { 133 | "version": "1.2.1", 134 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 135 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 136 | "funding": [ 137 | { 138 | "type": "github", 139 | "url": "https://github.com/sponsors/feross" 140 | }, 141 | { 142 | "type": "patreon", 143 | "url": "https://www.patreon.com/feross" 144 | }, 145 | { 146 | "type": "consulting", 147 | "url": "https://feross.org/support" 148 | } 149 | ], 150 | "optional": true 151 | }, 152 | "node_modules/immediate": { 153 | "version": "3.3.0", 154 | "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", 155 | "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==", 156 | "optional": true 157 | }, 158 | "node_modules/inherits": { 159 | "version": "2.0.4", 160 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 161 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 162 | "optional": true 163 | }, 164 | "node_modules/isomorphic.js": { 165 | "version": "0.2.5", 166 | "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", 167 | "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", 168 | "funding": { 169 | "type": "GitHub Sponsors ❤", 170 | "url": "https://github.com/sponsors/dmonad" 171 | } 172 | }, 173 | "node_modules/level": { 174 | "version": "6.0.1", 175 | "resolved": "https://registry.npmjs.org/level/-/level-6.0.1.tgz", 176 | "integrity": "sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==", 177 | "optional": true, 178 | "dependencies": { 179 | "level-js": "^5.0.0", 180 | "level-packager": "^5.1.0", 181 | "leveldown": "^5.4.0" 182 | }, 183 | "engines": { 184 | "node": ">=8.6.0" 185 | }, 186 | "funding": { 187 | "type": "opencollective", 188 | "url": "https://opencollective.com/level" 189 | } 190 | }, 191 | "node_modules/level-codec": { 192 | "version": "9.0.2", 193 | "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz", 194 | "integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==", 195 | "optional": true, 196 | "dependencies": { 197 | "buffer": "^5.6.0" 198 | }, 199 | "engines": { 200 | "node": ">=6" 201 | } 202 | }, 203 | "node_modules/level-concat-iterator": { 204 | "version": "2.0.1", 205 | "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz", 206 | "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==", 207 | "optional": true, 208 | "engines": { 209 | "node": ">=6" 210 | } 211 | }, 212 | "node_modules/level-errors": { 213 | "version": "2.0.1", 214 | "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", 215 | "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", 216 | "optional": true, 217 | "dependencies": { 218 | "errno": "~0.1.1" 219 | }, 220 | "engines": { 221 | "node": ">=6" 222 | } 223 | }, 224 | "node_modules/level-iterator-stream": { 225 | "version": "4.0.2", 226 | "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz", 227 | "integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==", 228 | "optional": true, 229 | "dependencies": { 230 | "inherits": "^2.0.4", 231 | "readable-stream": "^3.4.0", 232 | "xtend": "^4.0.2" 233 | }, 234 | "engines": { 235 | "node": ">=6" 236 | } 237 | }, 238 | "node_modules/level-js": { 239 | "version": "5.0.2", 240 | "resolved": "https://registry.npmjs.org/level-js/-/level-js-5.0.2.tgz", 241 | "integrity": "sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==", 242 | "optional": true, 243 | "dependencies": { 244 | "abstract-leveldown": "~6.2.3", 245 | "buffer": "^5.5.0", 246 | "inherits": "^2.0.3", 247 | "ltgt": "^2.1.2" 248 | } 249 | }, 250 | "node_modules/level-packager": { 251 | "version": "5.1.1", 252 | "resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz", 253 | "integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==", 254 | "optional": true, 255 | "dependencies": { 256 | "encoding-down": "^6.3.0", 257 | "levelup": "^4.3.2" 258 | }, 259 | "engines": { 260 | "node": ">=6" 261 | } 262 | }, 263 | "node_modules/level-supports": { 264 | "version": "1.0.1", 265 | "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", 266 | "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==", 267 | "optional": true, 268 | "dependencies": { 269 | "xtend": "^4.0.2" 270 | }, 271 | "engines": { 272 | "node": ">=6" 273 | } 274 | }, 275 | "node_modules/leveldown": { 276 | "version": "5.6.0", 277 | "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.6.0.tgz", 278 | "integrity": "sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==", 279 | "hasInstallScript": true, 280 | "optional": true, 281 | "dependencies": { 282 | "abstract-leveldown": "~6.2.1", 283 | "napi-macros": "~2.0.0", 284 | "node-gyp-build": "~4.1.0" 285 | }, 286 | "engines": { 287 | "node": ">=8.6.0" 288 | } 289 | }, 290 | "node_modules/levelup": { 291 | "version": "4.4.0", 292 | "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz", 293 | "integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==", 294 | "optional": true, 295 | "dependencies": { 296 | "deferred-leveldown": "~5.3.0", 297 | "level-errors": "~2.0.0", 298 | "level-iterator-stream": "~4.0.0", 299 | "level-supports": "~1.0.0", 300 | "xtend": "~4.0.0" 301 | }, 302 | "engines": { 303 | "node": ">=6" 304 | } 305 | }, 306 | "node_modules/lib0": { 307 | "version": "0.2.94", 308 | "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.94.tgz", 309 | "integrity": "sha512-hZ3p54jL4Wpu7IOg26uC7dnEWiMyNlUrb9KoG7+xYs45WkQwpVvKFndVq2+pqLYKe1u8Fp3+zAfZHVvTK34PvQ==", 310 | "dependencies": { 311 | "isomorphic.js": "^0.2.4" 312 | }, 313 | "bin": { 314 | "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", 315 | "0gentesthtml": "bin/gentesthtml.js", 316 | "0serve": "bin/0serve.js" 317 | }, 318 | "engines": { 319 | "node": ">=16" 320 | }, 321 | "funding": { 322 | "type": "GitHub Sponsors ❤", 323 | "url": "https://github.com/sponsors/dmonad" 324 | } 325 | }, 326 | "node_modules/lodash.debounce": { 327 | "version": "4.0.8", 328 | "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", 329 | "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" 330 | }, 331 | "node_modules/ltgt": { 332 | "version": "2.2.1", 333 | "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", 334 | "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", 335 | "optional": true 336 | }, 337 | "node_modules/napi-macros": { 338 | "version": "2.0.0", 339 | "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", 340 | "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", 341 | "optional": true 342 | }, 343 | "node_modules/node-gyp-build": { 344 | "version": "4.1.1", 345 | "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.1.1.tgz", 346 | "integrity": "sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==", 347 | "optional": true, 348 | "bin": { 349 | "node-gyp-build": "bin.js", 350 | "node-gyp-build-optional": "optional.js", 351 | "node-gyp-build-test": "build-test.js" 352 | } 353 | }, 354 | "node_modules/orderedmap": { 355 | "version": "2.1.1", 356 | "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", 357 | "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==" 358 | }, 359 | "node_modules/prosemirror-commands": { 360 | "version": "1.5.2", 361 | "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.5.2.tgz", 362 | "integrity": "sha512-hgLcPaakxH8tu6YvVAaILV2tXYsW3rAdDR8WNkeKGcgeMVQg3/TMhPdVoh7iAmfgVjZGtcOSjKiQaoeKjzd2mQ==", 363 | "dependencies": { 364 | "prosemirror-model": "^1.0.0", 365 | "prosemirror-state": "^1.0.0", 366 | "prosemirror-transform": "^1.0.0" 367 | } 368 | }, 369 | "node_modules/prosemirror-dropcursor": { 370 | "version": "1.8.1", 371 | "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.1.tgz", 372 | "integrity": "sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==", 373 | "dependencies": { 374 | "prosemirror-state": "^1.0.0", 375 | "prosemirror-transform": "^1.1.0", 376 | "prosemirror-view": "^1.1.0" 377 | } 378 | }, 379 | "node_modules/prosemirror-example-setup": { 380 | "version": "1.2.2", 381 | "resolved": "https://registry.npmjs.org/prosemirror-example-setup/-/prosemirror-example-setup-1.2.2.tgz", 382 | "integrity": "sha512-pHJc656IgYm249RNp0eQaWNmnyWGk6OrzysWfYI4+NwE14HQ7YNYOlRBLErUS6uCAHIYJLNXf0/XCmf1OCtNbQ==", 383 | "dependencies": { 384 | "prosemirror-commands": "^1.0.0", 385 | "prosemirror-dropcursor": "^1.0.0", 386 | "prosemirror-gapcursor": "^1.0.0", 387 | "prosemirror-history": "^1.0.0", 388 | "prosemirror-inputrules": "^1.0.0", 389 | "prosemirror-keymap": "^1.0.0", 390 | "prosemirror-menu": "^1.0.0", 391 | "prosemirror-schema-list": "^1.0.0", 392 | "prosemirror-state": "^1.0.0" 393 | } 394 | }, 395 | "node_modules/prosemirror-gapcursor": { 396 | "version": "1.3.2", 397 | "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz", 398 | "integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==", 399 | "dependencies": { 400 | "prosemirror-keymap": "^1.0.0", 401 | "prosemirror-model": "^1.0.0", 402 | "prosemirror-state": "^1.0.0", 403 | "prosemirror-view": "^1.0.0" 404 | } 405 | }, 406 | "node_modules/prosemirror-history": { 407 | "version": "1.4.0", 408 | "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.4.0.tgz", 409 | "integrity": "sha512-UUiGzDVcqo1lovOPdi9YxxUps3oBFWAIYkXLu3Ot+JPv1qzVogRbcizxK3LhHmtaUxclohgiOVesRw5QSlMnbQ==", 410 | "dependencies": { 411 | "prosemirror-state": "^1.2.2", 412 | "prosemirror-transform": "^1.0.0", 413 | "prosemirror-view": "^1.31.0", 414 | "rope-sequence": "^1.3.0" 415 | } 416 | }, 417 | "node_modules/prosemirror-inputrules": { 418 | "version": "1.4.0", 419 | "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.4.0.tgz", 420 | "integrity": "sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==", 421 | "dependencies": { 422 | "prosemirror-state": "^1.0.0", 423 | "prosemirror-transform": "^1.0.0" 424 | } 425 | }, 426 | "node_modules/prosemirror-keymap": { 427 | "version": "1.2.2", 428 | "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz", 429 | "integrity": "sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==", 430 | "dependencies": { 431 | "prosemirror-state": "^1.0.0", 432 | "w3c-keyname": "^2.2.0" 433 | } 434 | }, 435 | "node_modules/prosemirror-menu": { 436 | "version": "1.2.4", 437 | "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.4.tgz", 438 | "integrity": "sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==", 439 | "dependencies": { 440 | "crelt": "^1.0.0", 441 | "prosemirror-commands": "^1.0.0", 442 | "prosemirror-history": "^1.0.0", 443 | "prosemirror-state": "^1.0.0" 444 | } 445 | }, 446 | "node_modules/prosemirror-model": { 447 | "version": "1.21.1", 448 | "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.21.1.tgz", 449 | "integrity": "sha512-IVBAuMqOfltTr7yPypwpfdGT+6rGAteVOw2FO6GEvCGGa1ZwxLseqC1Eax/EChDvG/xGquB2d/hLdgh3THpsYg==", 450 | "dependencies": { 451 | "orderedmap": "^2.0.0" 452 | } 453 | }, 454 | "node_modules/prosemirror-schema-list": { 455 | "version": "1.4.0", 456 | "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.4.0.tgz", 457 | "integrity": "sha512-nZOIq/AkBSzCENxUyLm5ltWE53e2PLk65ghMN8qLQptOmDVixZlPqtMeQdiNw0odL9vNpalEjl3upgRkuJ/Jyw==", 458 | "dependencies": { 459 | "prosemirror-model": "^1.0.0", 460 | "prosemirror-state": "^1.0.0", 461 | "prosemirror-transform": "^1.7.3" 462 | } 463 | }, 464 | "node_modules/prosemirror-state": { 465 | "version": "1.4.3", 466 | "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz", 467 | "integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==", 468 | "dependencies": { 469 | "prosemirror-model": "^1.0.0", 470 | "prosemirror-transform": "^1.0.0", 471 | "prosemirror-view": "^1.27.0" 472 | } 473 | }, 474 | "node_modules/prosemirror-transform": { 475 | "version": "1.9.0", 476 | "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.9.0.tgz", 477 | "integrity": "sha512-5UXkr1LIRx3jmpXXNKDhv8OyAOeLTGuXNwdVfg8x27uASna/wQkr9p6fD3eupGOi4PLJfbezxTyi/7fSJypXHg==", 478 | "dependencies": { 479 | "prosemirror-model": "^1.21.0" 480 | } 481 | }, 482 | "node_modules/prosemirror-view": { 483 | "version": "1.33.8", 484 | "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.33.8.tgz", 485 | "integrity": "sha512-4PhMr/ufz2cdvFgpUAnZfs+0xij3RsFysreeG9V/utpwX7AJtYCDVyuRxzWoMJIEf4C7wVihuBNMPpFLPCiLQw==", 486 | "dependencies": { 487 | "prosemirror-model": "^1.20.0", 488 | "prosemirror-state": "^1.0.0", 489 | "prosemirror-transform": "^1.1.0" 490 | } 491 | }, 492 | "node_modules/prr": { 493 | "version": "1.0.1", 494 | "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", 495 | "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", 496 | "optional": true 497 | }, 498 | "node_modules/readable-stream": { 499 | "version": "3.6.2", 500 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 501 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 502 | "optional": true, 503 | "dependencies": { 504 | "inherits": "^2.0.3", 505 | "string_decoder": "^1.1.1", 506 | "util-deprecate": "^1.0.1" 507 | }, 508 | "engines": { 509 | "node": ">= 6" 510 | } 511 | }, 512 | "node_modules/rope-sequence": { 513 | "version": "1.3.4", 514 | "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", 515 | "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==" 516 | }, 517 | "node_modules/safe-buffer": { 518 | "version": "5.2.1", 519 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 520 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 521 | "funding": [ 522 | { 523 | "type": "github", 524 | "url": "https://github.com/sponsors/feross" 525 | }, 526 | { 527 | "type": "patreon", 528 | "url": "https://www.patreon.com/feross" 529 | }, 530 | { 531 | "type": "consulting", 532 | "url": "https://feross.org/support" 533 | } 534 | ], 535 | "optional": true 536 | }, 537 | "node_modules/string_decoder": { 538 | "version": "1.3.0", 539 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 540 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 541 | "optional": true, 542 | "dependencies": { 543 | "safe-buffer": "~5.2.0" 544 | } 545 | }, 546 | "node_modules/util-deprecate": { 547 | "version": "1.0.2", 548 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 549 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 550 | "optional": true 551 | }, 552 | "node_modules/w3c-keyname": { 553 | "version": "2.2.8", 554 | "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", 555 | "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" 556 | }, 557 | "node_modules/ws": { 558 | "version": "6.2.3", 559 | "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", 560 | "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", 561 | "optional": true, 562 | "dependencies": { 563 | "async-limiter": "~1.0.0" 564 | } 565 | }, 566 | "node_modules/xtend": { 567 | "version": "4.0.2", 568 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 569 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 570 | "optional": true, 571 | "engines": { 572 | "node": ">=0.4" 573 | } 574 | }, 575 | "node_modules/y-leveldb": { 576 | "version": "0.1.2", 577 | "resolved": "https://registry.npmjs.org/y-leveldb/-/y-leveldb-0.1.2.tgz", 578 | "integrity": "sha512-6ulEn5AXfXJYi89rXPEg2mMHAyyw8+ZfeMMdOtBbV8FJpQ1NOrcgi6DTAcXof0dap84NjHPT2+9d0rb6cFsjEg==", 579 | "optional": true, 580 | "dependencies": { 581 | "level": "^6.0.1", 582 | "lib0": "^0.2.31" 583 | }, 584 | "funding": { 585 | "type": "GitHub Sponsors ❤", 586 | "url": "https://github.com/sponsors/dmonad" 587 | }, 588 | "peerDependencies": { 589 | "yjs": "^13.0.0" 590 | } 591 | }, 592 | "node_modules/y-prosemirror": { 593 | "version": "1.2.9", 594 | "resolved": "https://registry.npmjs.org/y-prosemirror/-/y-prosemirror-1.2.9.tgz", 595 | "integrity": "sha512-fThGIVmSqrqnG/ckywEGlHM9ElfILC4TcMZd5zxWPe/i+UuP97TEr4swsopRKG3Y+KHBVt4Y/5NVBC3AAsUoUg==", 596 | "dependencies": { 597 | "lib0": "^0.2.42" 598 | }, 599 | "engines": { 600 | "node": ">=16.0.0", 601 | "npm": ">=8.0.0" 602 | }, 603 | "funding": { 604 | "type": "GitHub Sponsors ❤", 605 | "url": "https://github.com/sponsors/dmonad" 606 | }, 607 | "peerDependencies": { 608 | "prosemirror-model": "^1.7.1", 609 | "prosemirror-state": "^1.2.3", 610 | "prosemirror-view": "^1.9.10", 611 | "y-protocols": "^1.0.1", 612 | "yjs": "^13.5.38" 613 | } 614 | }, 615 | "node_modules/y-protocols": { 616 | "version": "1.0.6", 617 | "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz", 618 | "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==", 619 | "dependencies": { 620 | "lib0": "^0.2.85" 621 | }, 622 | "engines": { 623 | "node": ">=16.0.0", 624 | "npm": ">=8.0.0" 625 | }, 626 | "funding": { 627 | "type": "GitHub Sponsors ❤", 628 | "url": "https://github.com/sponsors/dmonad" 629 | }, 630 | "peerDependencies": { 631 | "yjs": "^13.0.0" 632 | } 633 | }, 634 | "node_modules/y-websocket": { 635 | "version": "1.5.4", 636 | "resolved": "https://registry.npmjs.org/y-websocket/-/y-websocket-1.5.4.tgz", 637 | "integrity": "sha512-Y3021uy0anOIHqAPyAZbNDoR05JuMEGjRNI8c+K9MHzVS8dWoImdJUjccljAznc8H2L7WkIXhRHZ1igWNRSgPw==", 638 | "dependencies": { 639 | "lib0": "^0.2.52", 640 | "lodash.debounce": "^4.0.8", 641 | "y-protocols": "^1.0.5" 642 | }, 643 | "bin": { 644 | "y-websocket": "bin/server.js", 645 | "y-websocket-server": "bin/server.js" 646 | }, 647 | "engines": { 648 | "node": ">=16.0.0", 649 | "npm": ">=8.0.0" 650 | }, 651 | "funding": { 652 | "type": "GitHub Sponsors ❤", 653 | "url": "https://github.com/sponsors/dmonad" 654 | }, 655 | "optionalDependencies": { 656 | "ws": "^6.2.1", 657 | "y-leveldb": "^0.1.0" 658 | }, 659 | "peerDependencies": { 660 | "yjs": "^13.5.6" 661 | } 662 | }, 663 | "node_modules/yjs": { 664 | "version": "13.6.18", 665 | "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.18.tgz", 666 | "integrity": "sha512-GBTjO4QCmv2HFKFkYIJl7U77hIB1o22vSCSQD1Ge8ZxWbIbn8AltI4gyXbtL+g5/GJep67HCMq3Y5AmNwDSyEg==", 667 | "dependencies": { 668 | "lib0": "^0.2.86" 669 | }, 670 | "engines": { 671 | "node": ">=16.0.0", 672 | "npm": ">=8.0.0" 673 | }, 674 | "funding": { 675 | "type": "GitHub Sponsors ❤", 676 | "url": "https://github.com/sponsors/dmonad" 677 | } 678 | } 679 | } 680 | } 681 | -------------------------------------------------------------------------------- /prosemirror/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yjs-prosemirror-demo", 3 | "version": "1.0.0", 4 | "description": "Yjs ❤ ProseMirror", 5 | "scripts": { 6 | "watch": "webpack -w --stats errors-only", 7 | "dist": "webpack --mode=production", 8 | "start": "webpack server --open prosemirror.html" 9 | }, 10 | "author": "Kevin Jahns ", 11 | "license": "UNLICENSE", 12 | "dependencies": { 13 | "yjs": "^13.5.8", 14 | "y-prosemirror": "^1.2.8", 15 | "y-websocket": "^1.3.15", 16 | "prosemirror-example-setup": "^1.1.2", 17 | "prosemirror-model": "^1.13.1", 18 | "prosemirror-state": "^1.3.4", 19 | "prosemirror-view": "^1.18.7" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /prosemirror/prosemirror.css: -------------------------------------------------------------------------------- 1 | .ProseMirror { 2 | position: relative; 3 | } 4 | 5 | .ProseMirror { 6 | word-wrap: break-word; 7 | white-space: pre-wrap; 8 | -webkit-font-variant-ligatures: none; 9 | font-variant-ligatures: none; 10 | } 11 | 12 | .ProseMirror pre { 13 | white-space: pre-wrap; 14 | } 15 | 16 | .ProseMirror li { 17 | position: relative; 18 | } 19 | 20 | .ProseMirror-hideselection *::selection { background: transparent; } 21 | .ProseMirror-hideselection *::-moz-selection { background: transparent; } 22 | .ProseMirror-hideselection { caret-color: transparent; } 23 | 24 | .ProseMirror-selectednode { 25 | outline: 2px solid #8cf; 26 | } 27 | 28 | /* Make sure li selections wrap around markers */ 29 | 30 | li.ProseMirror-selectednode { 31 | outline: none; 32 | } 33 | 34 | li.ProseMirror-selectednode:after { 35 | content: ""; 36 | position: absolute; 37 | left: -32px; 38 | right: -2px; top: -2px; bottom: -2px; 39 | border: 2px solid #8cf; 40 | pointer-events: none; 41 | } 42 | .ProseMirror-textblock-dropdown { 43 | min-width: 3em; 44 | } 45 | 46 | .ProseMirror-menu { 47 | margin: 0 -4px; 48 | line-height: 1; 49 | } 50 | 51 | .ProseMirror-tooltip .ProseMirror-menu { 52 | width: -webkit-fit-content; 53 | width: fit-content; 54 | white-space: pre; 55 | } 56 | 57 | .ProseMirror-menuitem { 58 | margin-right: 3px; 59 | display: inline-block; 60 | } 61 | 62 | .ProseMirror-menuseparator { 63 | border-right: 1px solid #ddd; 64 | margin-right: 3px; 65 | } 66 | 67 | .ProseMirror-menu-dropdown, .ProseMirror-menu-dropdown-menu { 68 | font-size: 90%; 69 | white-space: nowrap; 70 | } 71 | 72 | .ProseMirror-menu-dropdown { 73 | vertical-align: 1px; 74 | cursor: pointer; 75 | position: relative; 76 | padding-right: 15px; 77 | } 78 | 79 | .ProseMirror-menu-dropdown-wrap { 80 | padding: 1px 0 1px 4px; 81 | display: inline-block; 82 | position: relative; 83 | } 84 | 85 | .ProseMirror-menu-dropdown:after { 86 | content: ""; 87 | border-left: 4px solid transparent; 88 | border-right: 4px solid transparent; 89 | border-top: 4px solid currentColor; 90 | opacity: .6; 91 | position: absolute; 92 | right: 4px; 93 | top: calc(50% - 2px); 94 | } 95 | 96 | .ProseMirror-menu-dropdown-menu, .ProseMirror-menu-submenu { 97 | position: absolute; 98 | background: white; 99 | color: #666; 100 | border: 1px solid #aaa; 101 | padding: 2px; 102 | } 103 | 104 | .ProseMirror-menu-dropdown-menu { 105 | z-index: 15; 106 | min-width: 6em; 107 | } 108 | 109 | .ProseMirror-menu-dropdown-item { 110 | cursor: pointer; 111 | padding: 2px 8px 2px 4px; 112 | } 113 | 114 | .ProseMirror-menu-dropdown-item:hover { 115 | background: #f2f2f2; 116 | } 117 | 118 | .ProseMirror-menu-submenu-wrap { 119 | position: relative; 120 | margin-right: -4px; 121 | } 122 | 123 | .ProseMirror-menu-submenu-label:after { 124 | content: ""; 125 | border-top: 4px solid transparent; 126 | border-bottom: 4px solid transparent; 127 | border-left: 4px solid currentColor; 128 | opacity: .6; 129 | position: absolute; 130 | right: 4px; 131 | top: calc(50% - 4px); 132 | } 133 | 134 | .ProseMirror-menu-submenu { 135 | display: none; 136 | min-width: 4em; 137 | left: 100%; 138 | top: -3px; 139 | } 140 | 141 | .ProseMirror-menu-active { 142 | background: #eee; 143 | border-radius: 4px; 144 | } 145 | 146 | .ProseMirror-menu-active { 147 | background: #eee; 148 | border-radius: 4px; 149 | } 150 | 151 | .ProseMirror-menu-disabled { 152 | opacity: .3; 153 | } 154 | 155 | .ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu, .ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu { 156 | display: block; 157 | } 158 | 159 | .ProseMirror-menubar { 160 | border-top-left-radius: inherit; 161 | border-top-right-radius: inherit; 162 | position: relative; 163 | min-height: 1em; 164 | color: #666; 165 | padding: 1px 6px; 166 | top: 0; left: 0; right: 0; 167 | border-bottom: 1px solid silver; 168 | background: white; 169 | z-index: 10; 170 | -moz-box-sizing: border-box; 171 | box-sizing: border-box; 172 | overflow: visible; 173 | } 174 | 175 | .ProseMirror-icon { 176 | display: inline-block; 177 | line-height: .8; 178 | vertical-align: -2px; /* Compensate for padding */ 179 | padding: 2px 8px; 180 | cursor: pointer; 181 | } 182 | 183 | .ProseMirror-menu-disabled.ProseMirror-icon { 184 | cursor: default; 185 | } 186 | 187 | .ProseMirror-icon svg { 188 | fill: currentColor; 189 | height: 1em; 190 | } 191 | 192 | .ProseMirror-icon span { 193 | vertical-align: text-top; 194 | } 195 | .ProseMirror-gapcursor { 196 | display: none; 197 | pointer-events: none; 198 | position: absolute; 199 | } 200 | 201 | .ProseMirror-gapcursor:after { 202 | content: ""; 203 | display: block; 204 | position: absolute; 205 | top: -2px; 206 | width: 20px; 207 | border-top: 1px solid black; 208 | animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite; 209 | } 210 | 211 | @keyframes ProseMirror-cursor-blink { 212 | to { 213 | visibility: hidden; 214 | } 215 | } 216 | 217 | .ProseMirror-focused .ProseMirror-gapcursor { 218 | display: block; 219 | } 220 | /* Add space around the hr to make clicking it easier */ 221 | 222 | .ProseMirror-example-setup-style hr { 223 | padding: 2px 10px; 224 | border: none; 225 | margin: 1em 0; 226 | } 227 | 228 | .ProseMirror-example-setup-style hr:after { 229 | content: ""; 230 | display: block; 231 | height: 1px; 232 | background-color: silver; 233 | line-height: 2px; 234 | } 235 | 236 | .ProseMirror ul, .ProseMirror ol { 237 | padding-left: 30px; 238 | } 239 | 240 | .ProseMirror blockquote { 241 | padding-left: 1em; 242 | border-left: 3px solid #eee; 243 | margin-left: 0; margin-right: 0; 244 | } 245 | 246 | .ProseMirror-example-setup-style img { 247 | cursor: default; 248 | } 249 | 250 | .ProseMirror-prompt { 251 | background: white; 252 | padding: 5px 10px 5px 15px; 253 | border: 1px solid silver; 254 | position: fixed; 255 | border-radius: 3px; 256 | z-index: 11; 257 | box-shadow: -.5px 2px 5px rgba(0, 0, 0, .2); 258 | } 259 | 260 | .ProseMirror-prompt h5 { 261 | margin: 0; 262 | font-weight: normal; 263 | font-size: 100%; 264 | color: #444; 265 | } 266 | 267 | .ProseMirror-prompt input[type="text"], 268 | .ProseMirror-prompt textarea { 269 | background: #eee; 270 | border: none; 271 | outline: none; 272 | } 273 | 274 | .ProseMirror-prompt input[type="text"] { 275 | padding: 0 4px; 276 | } 277 | 278 | .ProseMirror-prompt-close { 279 | position: absolute; 280 | left: 2px; top: 1px; 281 | color: #666; 282 | border: none; background: transparent; padding: 0; 283 | } 284 | 285 | .ProseMirror-prompt-close:after { 286 | content: "✕"; 287 | font-size: 12px; 288 | } 289 | 290 | .ProseMirror-invalid { 291 | background: #ffc; 292 | border: 1px solid #cc7; 293 | border-radius: 4px; 294 | padding: 5px 10px; 295 | position: absolute; 296 | min-width: 10em; 297 | } 298 | 299 | .ProseMirror-prompt-buttons { 300 | margin-top: 5px; 301 | display: none; 302 | } 303 | #editor, .editor { 304 | background: white; 305 | color: black; 306 | background-clip: padding-box; 307 | border-radius: 4px; 308 | border: 2px solid rgba(0, 0, 0, 0.2); 309 | padding: 5px 0; 310 | margin-bottom: 23px; 311 | } 312 | 313 | .ProseMirror p:first-child, 314 | .ProseMirror h1:first-child, 315 | .ProseMirror h2:first-child, 316 | .ProseMirror h3:first-child, 317 | .ProseMirror h4:first-child, 318 | .ProseMirror h5:first-child, 319 | .ProseMirror h6:first-child { 320 | margin-top: 10px; 321 | } 322 | 323 | .ProseMirror { 324 | padding: 4px 8px 4px 14px; 325 | line-height: 1.2; 326 | outline: none; 327 | } 328 | 329 | .ProseMirror p { margin-bottom: 1em } 330 | 331 | -------------------------------------------------------------------------------- /prosemirror/prosemirror.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Yjs Prosemirror Example 6 | 7 | 8 | 66 | 67 | 68 |
69 |
70 | 71 |
72 |

73 |

This is a demo of the YjsProseMirror binding: y-prosemirror.

74 |

The content of this editor is shared with every client that visits this domain.

75 | 76 | 77 | -------------------------------------------------------------------------------- /prosemirror/prosemirror.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | import * as Y from 'yjs' 4 | import { WebsocketProvider } from 'y-websocket' 5 | import { ySyncPlugin, yCursorPlugin, yUndoPlugin, undo, redo, initProseMirrorDoc } from 'y-prosemirror' 6 | import { EditorState } from 'prosemirror-state' 7 | import { EditorView } from 'prosemirror-view' 8 | import { schema } from './schema.js' 9 | import { exampleSetup } from 'prosemirror-example-setup' 10 | import { keymap } from 'prosemirror-keymap' 11 | 12 | const roomname = `prosemirror-demo-${new Date().toLocaleDateString('en-CA')}` 13 | 14 | window.addEventListener('load', () => { 15 | const ydoc = new Y.Doc() 16 | const provider = new WebsocketProvider( 17 | 'wss://demos.yjs.dev/ws', // use the public ws server 18 | // `ws${location.protocol.slice(4)}//${location.host}/ws`, // alternatively: use the local ws server (run `npm start` in root directory) 19 | roomname, 20 | ydoc 21 | ) 22 | const yXmlFragment = ydoc.getXmlFragment('prosemirror') 23 | 24 | const editor = document.createElement('div') 25 | editor.setAttribute('id', 'editor') 26 | const editorContainer = document.createElement('div') 27 | editorContainer.insertBefore(editor, null) 28 | const { doc, mapping } = initProseMirrorDoc(yXmlFragment, schema) 29 | const prosemirrorView = new EditorView(editor, { 30 | state: EditorState.create({ 31 | doc, 32 | schema, 33 | plugins: [ 34 | ySyncPlugin(yXmlFragment, { mapping }), 35 | yCursorPlugin(provider.awareness), 36 | yUndoPlugin(), 37 | keymap({ 38 | 'Mod-z': undo, 39 | 'Mod-y': redo, 40 | 'Mod-Shift-z': redo 41 | }) 42 | ].concat(exampleSetup({ schema })) 43 | }) 44 | }) 45 | document.body.insertBefore(editorContainer, null) 46 | 47 | const connectBtn = /** @type {HTMLElement} */ (document.getElementById('y-connect-btn')) 48 | connectBtn.addEventListener('click', () => { 49 | if (provider.shouldConnect) { 50 | provider.disconnect() 51 | connectBtn.textContent = 'Connect' 52 | } else { 53 | provider.connect() 54 | connectBtn.textContent = 'Disconnect' 55 | } 56 | }) 57 | 58 | // @ts-ignore 59 | window.example = { provider, ydoc, yXmlFragment, prosemirrorView } 60 | }) 61 | -------------------------------------------------------------------------------- /prosemirror/schema.js: -------------------------------------------------------------------------------- 1 | import { Schema } from 'prosemirror-model' 2 | 3 | const brDOM = ['br'] 4 | 5 | const calcYchangeDomAttrs = (attrs, domAttrs = {}) => { 6 | domAttrs = Object.assign({}, domAttrs) 7 | if (attrs.ychange !== null) { 8 | domAttrs.ychange_user = attrs.ychange.user 9 | domAttrs.ychange_state = attrs.ychange.state 10 | } 11 | return domAttrs 12 | } 13 | 14 | // :: Object 15 | // [Specs](#model.NodeSpec) for the nodes defined in this schema. 16 | export const nodes = { 17 | // :: NodeSpec The top level document node. 18 | doc: { 19 | content: 'block+' 20 | }, 21 | 22 | // :: NodeSpec A plain paragraph textblock. Represented in the DOM 23 | // as a `

` element. 24 | paragraph: { 25 | attrs: { ychange: { default: null } }, 26 | content: 'inline*', 27 | group: 'block', 28 | parseDOM: [{ tag: 'p' }], 29 | toDOM (node) { return ['p', calcYchangeDomAttrs(node.attrs), 0] } 30 | }, 31 | 32 | // :: NodeSpec A blockquote (`

`) wrapping one or more blocks. 33 | blockquote: { 34 | attrs: { ychange: { default: null } }, 35 | content: 'block+', 36 | group: 'block', 37 | defining: true, 38 | parseDOM: [{ tag: 'blockquote' }], 39 | toDOM (node) { return ['blockquote', calcYchangeDomAttrs(node.attrs), 0] } 40 | }, 41 | 42 | // :: NodeSpec A horizontal rule (`
`). 43 | horizontal_rule: { 44 | attrs: { ychange: { default: null } }, 45 | group: 'block', 46 | parseDOM: [{ tag: 'hr' }], 47 | toDOM (node) { 48 | return ['hr', calcYchangeDomAttrs(node.attrs)] 49 | } 50 | }, 51 | 52 | // :: NodeSpec A heading textblock, with a `level` attribute that 53 | // should hold the number 1 to 6. Parsed and serialized as `

` to 54 | // `

` elements. 55 | heading: { 56 | attrs: { 57 | level: { default: 1 }, 58 | ychange: { default: null } 59 | }, 60 | content: 'inline*', 61 | group: 'block', 62 | defining: true, 63 | parseDOM: [{ tag: 'h1', attrs: { level: 1 } }, 64 | { tag: 'h2', attrs: { level: 2 } }, 65 | { tag: 'h3', attrs: { level: 3 } }, 66 | { tag: 'h4', attrs: { level: 4 } }, 67 | { tag: 'h5', attrs: { level: 5 } }, 68 | { tag: 'h6', attrs: { level: 6 } }], 69 | toDOM (node) { return ['h' + node.attrs.level, calcYchangeDomAttrs(node.attrs), 0] } 70 | }, 71 | 72 | // :: NodeSpec A code listing. Disallows marks or non-text inline 73 | // nodes by default. Represented as a `
` element with a
 74 |   // `` element inside of it.
 75 |   code_block: {
 76 |     attrs: { ychange: { default: null } },
 77 |     content: 'text*',
 78 |     marks: '',
 79 |     group: 'block',
 80 |     code: true,
 81 |     defining: true,
 82 |     parseDOM: [{ tag: 'pre', preserveWhitespace: 'full' }],
 83 |     toDOM (node) { return ['pre', calcYchangeDomAttrs(node.attrs), ['code', 0]] }
 84 |   },
 85 | 
 86 |   // :: NodeSpec The text node.
 87 |   text: {
 88 |     group: 'inline'
 89 |   },
 90 | 
 91 |   // :: NodeSpec An inline image (``) node. Supports `src`,
 92 |   // `alt`, and `href` attributes. The latter two default to the empty
 93 |   // string.
 94 |   image: {
 95 |     inline: true,
 96 |     attrs: {
 97 |       ychange: { default: null },
 98 |       src: {},
 99 |       alt: { default: null },
100 |       title: { default: null }
101 |     },
102 |     group: 'inline',
103 |     draggable: true,
104 |     parseDOM: [{
105 |       tag: 'img[src]',
106 |       getAttrs (dom) {
107 |         return {
108 |           src: dom.getAttribute('src'),
109 |           title: dom.getAttribute('title'),
110 |           alt: dom.getAttribute('alt')
111 |         }
112 |       }
113 |     }],
114 |     toDOM (node) {
115 |       const domAttrs = {
116 |         src: node.attrs.src,
117 |         title: node.attrs.title,
118 |         alt: node.attrs.alt
119 |       }
120 |       return ['img', calcYchangeDomAttrs(node.attrs, domAttrs)]
121 |     }
122 |   },
123 | 
124 |   // :: NodeSpec A hard line break, represented in the DOM as `
`. 125 | hard_break: { 126 | inline: true, 127 | group: 'inline', 128 | selectable: false, 129 | parseDOM: [{ tag: 'br' }], 130 | toDOM () { return brDOM } 131 | } 132 | } 133 | 134 | const emDOM = ['em', 0]; const strongDOM = ['strong', 0]; const codeDOM = ['code', 0] 135 | 136 | // :: Object [Specs](#model.MarkSpec) for the marks in the schema. 137 | export const marks = { 138 | // :: MarkSpec A link. Has `href` and `title` attributes. `title` 139 | // defaults to the empty string. Rendered and parsed as an `` 140 | // element. 141 | link: { 142 | attrs: { 143 | href: {}, 144 | title: { default: null } 145 | }, 146 | inclusive: false, 147 | parseDOM: [{ 148 | tag: 'a[href]', 149 | getAttrs (dom) { 150 | return { href: dom.getAttribute('href'), title: dom.getAttribute('title') } 151 | } 152 | }], 153 | toDOM (node) { return ['a', node.attrs, 0] } 154 | }, 155 | 156 | // :: MarkSpec An emphasis mark. Rendered as an `` element. 157 | // Has parse rules that also match `` and `font-style: italic`. 158 | em: { 159 | parseDOM: [{ tag: 'i' }, { tag: 'em' }, { style: 'font-style=italic' }], 160 | toDOM () { return emDOM } 161 | }, 162 | 163 | // :: MarkSpec A strong mark. Rendered as ``, parse rules 164 | // also match `` and `font-weight: bold`. 165 | strong: { 166 | parseDOM: [{ tag: 'strong' }, 167 | // This works around a Google Docs misbehavior where 168 | // pasted content will be inexplicably wrapped in `` 169 | // tags with a font-weight normal. 170 | { tag: 'b', getAttrs: node => node.style.fontWeight !== 'normal' && null }, 171 | { style: 'font-weight', getAttrs: value => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null }], 172 | toDOM () { return strongDOM } 173 | }, 174 | 175 | // :: MarkSpec Code font mark. Represented as a `` element. 176 | code: { 177 | parseDOM: [{ tag: 'code' }], 178 | toDOM () { return codeDOM } 179 | }, 180 | ychange: { 181 | attrs: { 182 | user: { default: null }, 183 | state: { default: null } 184 | }, 185 | inclusive: false, 186 | parseDOM: [{ tag: 'ychange' }], 187 | toDOM (node) { 188 | return ['ychange', { ychange_user: node.attrs.user, ychange_state: node.attrs.state }, 0] 189 | } 190 | } 191 | } 192 | 193 | // :: Schema 194 | // This schema rougly corresponds to the document schema used by 195 | // [CommonMark](http://commonmark.org/), minus the list elements, 196 | // which are defined in the [`prosemirror-schema-list`](#schema-list) 197 | // module. 198 | // 199 | // To reuse elements from this schema, extend or read from its 200 | // `spec.nodes` and `spec.marks` [properties](#model.Schema.spec). 201 | export const schema = new Schema({ nodes, marks }) 202 | -------------------------------------------------------------------------------- /prosemirror/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | mode: 'development', 5 | devtool: 'source-map', 6 | entry: { 7 | prosemirror: './prosemirror.js' 8 | }, 9 | output: { 10 | globalObject: 'self', 11 | path: path.resolve(__dirname, './dist/'), 12 | filename: '[name].bundle.js', 13 | publicPath: '/dist/' 14 | }, 15 | devServer: { 16 | static: path.join(__dirname), 17 | compress: true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /quill/README.md: -------------------------------------------------------------------------------- 1 | # Quill Demo 2 | > [y-quill](https://docs.yjs.dev/ecosystem/editor-bindings/quill) / [y-websocket](https://docs.yjs.dev/ecosystem/connection-provider/y-websocket) / [Live Demo](https://demos.yjs.dev/quill/quill.html) 3 | 4 | This is a demo of a [Quill](https://quilljs.com/) editor that was made collaborative with Yjs & y-quill. Learn more about how you can customize Quill in [their documentation](https://quilljs.com/). 5 | 6 | We use the [y-websocket](https://docs.yjs.dev/ecosystem/connection-provider/y-websocket) provider to share document updates through a server. There are many more providers available for Yjs - try switching to another provider. [See docs](https://docs.yjs.dev/ecosystem/connection-provider). 7 | 8 | Also you could try adding offline persistence to this demo. Wouldn't it be cool if document updates are persisted in the browser, so you can load your application faster? Try it out: https://docs.yjs.dev/getting-started/allowing-offline-editing 9 | 10 | ## Quick Start 11 | 12 | ```sh 13 | npm i 14 | npm start 15 | # Project is running at http://localhost:8080/ 16 | ``` 17 | -------------------------------------------------------------------------------- /quill/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yjs-quill-demo", 3 | "version": "1.0.0", 4 | "description": "Yjs ❤ Quill", 5 | "author": "Kevin Jahns ", 6 | "license": "UNLICENSE", 7 | "scripts": { 8 | "watch": "webpack -w --stats errors-only", 9 | "dist": "webpack --mode=production", 10 | "start": "webpack serve --open ./quill.html" 11 | }, 12 | "dependencies": { 13 | "yjs": "^13.5.8", 14 | "y-quill": "^0.1.4", 15 | "y-websocket": "^1.4.1", 16 | "quill": "^1.3.7", 17 | "quill-cursors": "^2.2.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /quill/quill.css: -------------------------------------------------------------------------------- 1 | #editor { 2 | min-height: 500px; 3 | } -------------------------------------------------------------------------------- /quill/quill.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Yjs Quill Example 6 | 7 | 8 | 10 | 12 | 14 | 15 | 16 | 17 |

18 |

This is a demo of the YjsQuill binding: y-quill.

19 |

The content of this editor is shared with every client that visits this domain.

20 | 21 | 22 | -------------------------------------------------------------------------------- /quill/quill.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | import * as Y from 'yjs' 4 | import { WebsocketProvider } from 'y-websocket' 5 | import { QuillBinding } from 'y-quill' 6 | import Quill from 'quill' 7 | import QuillCursors from 'quill-cursors' 8 | 9 | Quill.register('modules/cursors', QuillCursors) 10 | const roomname = `codemirror-demo-${new Date().toLocaleDateString('en-CA')}` 11 | 12 | window.addEventListener('load', () => { 13 | const ydoc = new Y.Doc() 14 | const provider = new WebsocketProvider( 15 | 'wss://demos.yjs.dev/ws', // use the public ws server 16 | // `ws${location.protocol.slice(4)}//${location.host}/ws`, // alternatively: use the local ws server (run `npm start` in root directory) 17 | roomname, 18 | ydoc 19 | ) 20 | const ytext = ydoc.getText('quill') 21 | const editorContainer = document.createElement('div') 22 | editorContainer.setAttribute('id', 'editor') 23 | document.body.insertBefore(editorContainer, null) 24 | 25 | const editor = new Quill(editorContainer, { 26 | modules: { 27 | cursors: true, 28 | toolbar: [ 29 | [{ header: [1, 2, false] }], 30 | ['bold', 'italic', 'underline'], 31 | ['image', 'code-block'] 32 | ], 33 | history: { 34 | userOnly: true 35 | } 36 | }, 37 | placeholder: 'Start collaborating...', 38 | theme: 'snow' // or 'bubble' 39 | }) 40 | 41 | const binding = new QuillBinding(ytext, editor, provider.awareness) 42 | 43 | /* 44 | // Define user name and user name 45 | // Check the quill-cursors package on how to change the way cursors are rendered 46 | provider.awareness.setLocalStateField('user', { 47 | name: 'Typing Jimmy', 48 | color: 'blue' 49 | }) 50 | */ 51 | 52 | const connectBtn = document.getElementById('y-connect-btn') 53 | connectBtn.addEventListener('click', () => { 54 | if (provider.shouldConnect) { 55 | provider.disconnect() 56 | connectBtn.textContent = 'Connect' 57 | } else { 58 | provider.connect() 59 | connectBtn.textContent = 'Disconnect' 60 | } 61 | }) 62 | 63 | // @ts-ignore 64 | window.example = { provider, ydoc, ytext, binding, Y } 65 | }) 66 | -------------------------------------------------------------------------------- /quill/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | mode: 'development', 5 | devtool: 'source-map', 6 | entry: { 7 | quill: './quill.js' 8 | }, 9 | output: { 10 | globalObject: 'self', 11 | path: path.resolve(__dirname, './dist/'), 12 | filename: '[name].bundle.js', 13 | publicPath: '/dist/' 14 | }, 15 | devServer: { 16 | static: path.join(__dirname), 17 | compress: true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /react-prosemirror/README.md: -------------------------------------------------------------------------------- 1 | # react-prosemirror 2 | > Short example on how to set up `@handlewithcare/prosemirror` with y-prosemirror 3 | -------------------------------------------------------------------------------- /react-prosemirror/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React-ProseMirror Demo 7 | 8 | 66 | 67 | 125 | 181 | 182 | 183 |
184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /react-prosemirror/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import { EditorState } from 'prosemirror-state' 4 | import { ProseMirror, ProseMirrorDoc, reactKeys } from '@handlewithcare/react-prosemirror' 5 | 6 | import { exampleSetup } from 'prosemirror-example-setup' 7 | import { schema } from 'prosemirror-schema-basic' 8 | import { keymap } from 'prosemirror-keymap' 9 | import { baseKeymap } from 'prosemirror-commands' 10 | 11 | import * as Y from 'yjs' 12 | import { WebsocketProvider } from 'y-websocket' 13 | import { ySyncPlugin, yCursorPlugin, yUndoPlugin, undo, redo, initProseMirrorDoc } from 'y-prosemirror' 14 | 15 | const roomname = `prosemirror-react-demo-${new Date().toLocaleDateString('en-CA')}` 16 | 17 | const ydoc = new Y.Doc() 18 | const provider = new WebsocketProvider( 19 | 'wss://demos.yjs.dev/ws', // use the public ws server 20 | // `ws${location.protocol.slice(4)}//${location.host}/ws`, // alternatively: use the local ws server (run `npm start` in root directory) 21 | roomname, 22 | ydoc 23 | ) 24 | 25 | function App () { 26 | const yXmlFragment = ydoc.getXmlFragment('prosemirror') 27 | const { doc, mapping } = initProseMirrorDoc(yXmlFragment, schema) 28 | const defaultState = EditorState.create({ 29 | doc, 30 | schema, 31 | plugins: [ 32 | ySyncPlugin(yXmlFragment, { mapping }), 33 | yCursorPlugin(provider.awareness), 34 | yUndoPlugin(), 35 | keymap({ 36 | 'Mod-z': undo, 37 | 'Mod-y': redo, 38 | 'Mod-Shift-z': redo 39 | }), 40 | keymap(baseKeymap), 41 | reactKeys() 42 | ].concat(exampleSetup({ schema })) 43 | }) 44 | 45 | window.example = { ydoc, provider, yXmlFragment, pmDoc: doc } 46 | 47 | return ( 48 | 49 | 50 | 51 | ) 52 | } 53 | 54 | const root = createRoot(document.getElementById('root')) 55 | root.render() 56 | -------------------------------------------------------------------------------- /react-prosemirror/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-prosemirror", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "npx esbuild --bundle index.jsx --serve --outdir=dist --servedir=." 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@types/react": "^19.0.10", 14 | "@types/react-dom": "^19.0.4", 15 | "esbuild": "^0.25.0", 16 | "react": "^19.0.0", 17 | "react-dom": "^19.0.0" 18 | }, 19 | "dependencies": { 20 | "@handlewithcare/react-prosemirror": "^2.2.4", 21 | "prosemirror-commands": "^1.7.0", 22 | "prosemirror-example-setup": "^1.2.3", 23 | "prosemirror-keymap": "^1.2.2", 24 | "prosemirror-schema-basic": "^1.2.3", 25 | "prosemirror-schema-list": "^1.5.0", 26 | "y-prosemirror": "^1.2.16", 27 | "y-websocket": "^2.1.0", 28 | "yjs": "^13.6.23" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /react-prosemirror/prosemirror.css: -------------------------------------------------------------------------------- 1 | .ProseMirror { 2 | position: relative; 3 | } 4 | 5 | .ProseMirror { 6 | word-wrap: break-word; 7 | white-space: pre-wrap; 8 | -webkit-font-variant-ligatures: none; 9 | font-variant-ligatures: none; 10 | } 11 | 12 | .ProseMirror pre { 13 | white-space: pre-wrap; 14 | } 15 | 16 | .ProseMirror li { 17 | position: relative; 18 | } 19 | 20 | .ProseMirror-hideselection *::selection { background: transparent; } 21 | .ProseMirror-hideselection *::-moz-selection { background: transparent; } 22 | .ProseMirror-hideselection { caret-color: transparent; } 23 | 24 | .ProseMirror-selectednode { 25 | outline: 2px solid #8cf; 26 | } 27 | 28 | /* Make sure li selections wrap around markers */ 29 | 30 | li.ProseMirror-selectednode { 31 | outline: none; 32 | } 33 | 34 | li.ProseMirror-selectednode:after { 35 | content: ""; 36 | position: absolute; 37 | left: -32px; 38 | right: -2px; top: -2px; bottom: -2px; 39 | border: 2px solid #8cf; 40 | pointer-events: none; 41 | } 42 | .ProseMirror-textblock-dropdown { 43 | min-width: 3em; 44 | } 45 | 46 | .ProseMirror-menu { 47 | margin: 0 -4px; 48 | line-height: 1; 49 | } 50 | 51 | .ProseMirror-tooltip .ProseMirror-menu { 52 | width: -webkit-fit-content; 53 | width: fit-content; 54 | white-space: pre; 55 | } 56 | 57 | .ProseMirror-menuitem { 58 | margin-right: 3px; 59 | display: inline-block; 60 | } 61 | 62 | .ProseMirror-menuseparator { 63 | border-right: 1px solid #ddd; 64 | margin-right: 3px; 65 | } 66 | 67 | .ProseMirror-menu-dropdown, .ProseMirror-menu-dropdown-menu { 68 | font-size: 90%; 69 | white-space: nowrap; 70 | } 71 | 72 | .ProseMirror-menu-dropdown { 73 | vertical-align: 1px; 74 | cursor: pointer; 75 | position: relative; 76 | padding-right: 15px; 77 | } 78 | 79 | .ProseMirror-menu-dropdown-wrap { 80 | padding: 1px 0 1px 4px; 81 | display: inline-block; 82 | position: relative; 83 | } 84 | 85 | .ProseMirror-menu-dropdown:after { 86 | content: ""; 87 | border-left: 4px solid transparent; 88 | border-right: 4px solid transparent; 89 | border-top: 4px solid currentColor; 90 | opacity: .6; 91 | position: absolute; 92 | right: 4px; 93 | top: calc(50% - 2px); 94 | } 95 | 96 | .ProseMirror-menu-dropdown-menu, .ProseMirror-menu-submenu { 97 | position: absolute; 98 | background: white; 99 | color: #666; 100 | border: 1px solid #aaa; 101 | padding: 2px; 102 | } 103 | 104 | .ProseMirror-menu-dropdown-menu { 105 | z-index: 15; 106 | min-width: 6em; 107 | } 108 | 109 | .ProseMirror-menu-dropdown-item { 110 | cursor: pointer; 111 | padding: 2px 8px 2px 4px; 112 | } 113 | 114 | .ProseMirror-menu-dropdown-item:hover { 115 | background: #f2f2f2; 116 | } 117 | 118 | .ProseMirror-menu-submenu-wrap { 119 | position: relative; 120 | margin-right: -4px; 121 | } 122 | 123 | .ProseMirror-menu-submenu-label:after { 124 | content: ""; 125 | border-top: 4px solid transparent; 126 | border-bottom: 4px solid transparent; 127 | border-left: 4px solid currentColor; 128 | opacity: .6; 129 | position: absolute; 130 | right: 4px; 131 | top: calc(50% - 4px); 132 | } 133 | 134 | .ProseMirror-menu-submenu { 135 | display: none; 136 | min-width: 4em; 137 | left: 100%; 138 | top: -3px; 139 | } 140 | 141 | .ProseMirror-menu-active { 142 | background: #eee; 143 | border-radius: 4px; 144 | } 145 | 146 | .ProseMirror-menu-active { 147 | background: #eee; 148 | border-radius: 4px; 149 | } 150 | 151 | .ProseMirror-menu-disabled { 152 | opacity: .3; 153 | } 154 | 155 | .ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu, .ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu { 156 | display: block; 157 | } 158 | 159 | .ProseMirror-menubar { 160 | border-top-left-radius: inherit; 161 | border-top-right-radius: inherit; 162 | position: relative; 163 | min-height: 1em; 164 | color: #666; 165 | padding: 1px 6px; 166 | top: 0; left: 0; right: 0; 167 | border-bottom: 1px solid silver; 168 | background: white; 169 | z-index: 10; 170 | -moz-box-sizing: border-box; 171 | box-sizing: border-box; 172 | overflow: visible; 173 | } 174 | 175 | .ProseMirror-icon { 176 | display: inline-block; 177 | line-height: .8; 178 | vertical-align: -2px; /* Compensate for padding */ 179 | padding: 2px 8px; 180 | cursor: pointer; 181 | } 182 | 183 | .ProseMirror-menu-disabled.ProseMirror-icon { 184 | cursor: default; 185 | } 186 | 187 | .ProseMirror-icon svg { 188 | fill: currentColor; 189 | height: 1em; 190 | } 191 | 192 | .ProseMirror-icon span { 193 | vertical-align: text-top; 194 | } 195 | .ProseMirror-gapcursor { 196 | display: none; 197 | pointer-events: none; 198 | position: absolute; 199 | } 200 | 201 | .ProseMirror-gapcursor:after { 202 | content: ""; 203 | display: block; 204 | position: absolute; 205 | top: -2px; 206 | width: 20px; 207 | border-top: 1px solid black; 208 | animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite; 209 | } 210 | 211 | @keyframes ProseMirror-cursor-blink { 212 | to { 213 | visibility: hidden; 214 | } 215 | } 216 | 217 | .ProseMirror-focused .ProseMirror-gapcursor { 218 | display: block; 219 | } 220 | /* Add space around the hr to make clicking it easier */ 221 | 222 | .ProseMirror-example-setup-style hr { 223 | padding: 2px 10px; 224 | border: none; 225 | margin: 1em 0; 226 | } 227 | 228 | .ProseMirror-example-setup-style hr:after { 229 | content: ""; 230 | display: block; 231 | height: 1px; 232 | background-color: silver; 233 | line-height: 2px; 234 | } 235 | 236 | .ProseMirror ul, .ProseMirror ol { 237 | padding-left: 30px; 238 | } 239 | 240 | .ProseMirror blockquote { 241 | padding-left: 1em; 242 | border-left: 3px solid #eee; 243 | margin-left: 0; margin-right: 0; 244 | } 245 | 246 | .ProseMirror-example-setup-style img { 247 | cursor: default; 248 | } 249 | 250 | .ProseMirror-prompt { 251 | background: white; 252 | padding: 5px 10px 5px 15px; 253 | border: 1px solid silver; 254 | position: fixed; 255 | border-radius: 3px; 256 | z-index: 11; 257 | box-shadow: -.5px 2px 5px rgba(0, 0, 0, .2); 258 | } 259 | 260 | .ProseMirror-prompt h5 { 261 | margin: 0; 262 | font-weight: normal; 263 | font-size: 100%; 264 | color: #444; 265 | } 266 | 267 | .ProseMirror-prompt input[type="text"], 268 | .ProseMirror-prompt textarea { 269 | background: #eee; 270 | border: none; 271 | outline: none; 272 | } 273 | 274 | .ProseMirror-prompt input[type="text"] { 275 | padding: 0 4px; 276 | } 277 | 278 | .ProseMirror-prompt-close { 279 | position: absolute; 280 | left: 2px; top: 1px; 281 | color: #666; 282 | border: none; background: transparent; padding: 0; 283 | } 284 | 285 | .ProseMirror-prompt-close:after { 286 | content: "✕"; 287 | font-size: 12px; 288 | } 289 | 290 | .ProseMirror-invalid { 291 | background: #ffc; 292 | border: 1px solid #cc7; 293 | border-radius: 4px; 294 | padding: 5px 10px; 295 | position: absolute; 296 | min-width: 10em; 297 | } 298 | 299 | .ProseMirror-prompt-buttons { 300 | margin-top: 5px; 301 | display: none; 302 | } 303 | #editor, .editor { 304 | background: white; 305 | color: black; 306 | background-clip: padding-box; 307 | border-radius: 4px; 308 | border: 2px solid rgba(0, 0, 0, 0.2); 309 | padding: 5px 0; 310 | margin-bottom: 23px; 311 | } 312 | 313 | .ProseMirror p:first-child, 314 | .ProseMirror h1:first-child, 315 | .ProseMirror h2:first-child, 316 | .ProseMirror h3:first-child, 317 | .ProseMirror h4:first-child, 318 | .ProseMirror h5:first-child, 319 | .ProseMirror h6:first-child { 320 | margin-top: 10px; 321 | } 322 | 323 | .ProseMirror { 324 | padding: 4px 8px 4px 14px; 325 | line-height: 1.2; 326 | outline: none; 327 | } 328 | 329 | .ProseMirror p { margin-bottom: 1em } 330 | 331 | --------------------------------------------------------------------------------