{"id":13630066,"url":"https://github.com/yjs/y-quill","last_synced_at":"2025-04-10T02:23:47.785Z","repository":{"id":38953493,"uuid":"169239020","full_name":"yjs/y-quill","owner":"yjs","description":"Quill Editor binding for Yjs","archived":false,"fork":false,"pushed_at":"2024-04-22T21:49:27.000Z","size":360,"stargazers_count":75,"open_issues_count":8,"forks_count":18,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-05-23T02:01:42.304Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://demos.yjs.dev/quill/quill.html","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null},"funding":{"github":"dmonad","patreon":null,"open_collective":"y-collective","ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2019-02-05T12:40:24.000Z","updated_at":"2024-05-20T05:39:38.000Z","dependencies_parsed_at":"2024-04-28T01:01:44.874Z","dependency_job_id":null,"html_url":"https://github.com/yjs/y-quill","commit_stats":{"total_commits":32,"total_committers":2,"mean_commits":16.0,"dds":0.5,"last_synced_commit":"6dc76f8554bfc06a778d0982a2e5f59b987ab0a5"},"previous_names":["y-js/y-quill"],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yjs%2Fy-quill","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yjs%2Fy-quill/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yjs%2Fy-quill/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yjs%2Fy-quill/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yjs","download_url":"https://codeload.github.com/yjs/y-quill/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248143413,"owners_count":21054778,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T22:01:28.960Z","updated_at":"2025-04-10T02:23:47.762Z","avatar_url":"https://github.com/yjs.png","language":"JavaScript","funding_links":["https://github.com/sponsors/dmonad","https://opencollective.com/y-collective"],"categories":["JavaScript"],"sub_categories":[],"readme":"# y-quill\n\n\u003e [Quill Editor](https://quilljs.com/) binding for [Yjs](https://github.com/y-js/yjs) - [Demo](https://demos.yjs.dev/quill/quill.html)\n\nThis binding maps a Y.Text to a Quill instance. It optionally supports shared cursors via\nthe [quill-cursors](https://github.com/reedsy/quill-cursors) module.\n\n## Example\n\n```js\nimport { QuillBinding } from 'y-quill'\nimport Quill from 'quill'\nimport QuillCursors from 'quill-cursors'\n\n..\n\nQuill.register('modules/cursors', QuillCursors)\n\nconst type = ydoc.getText('quill')\n\nvar editor = new Quill('#editor-container', {\n  modules: {\n    cursors: true,\n    toolbar: [\n      [{ header: [1, 2, false] }],\n      ['bold', 'italic', 'underline'],\n      ['image', 'code-block']\n    ]\n  },\n  placeholder: 'Start collaborating...',\n  theme: 'snow' // or 'bubble'\n})\n\n// Optionally specify an Awareness instance, if supported by the Provider\nconst binding = new QuillBinding(type, editor, provider.awareness)\n\n/*\n// Define user name and user name\n// Check the quill-cursors package on how to change the way cursors are rendered\nprovider.awareness.setLocalStateField('user', {\n  name: 'Typing Jimmy',\n  color: 'blue'\n})\n*/\n\n```\n\nAlso look [here](https://github.com/y-js/yjs-demos/tree/master/quill) for a working example.\n\n## Custom Embeds\n\nThe Delta format supports \"custom embeds\", a feature for embedding custom\ndata using custom data models. This feature is currently not well documented, but\nit enables us to create good data models for tables and other complex widgets\n(like drawing widgets) that can't be well represented using text+formatting.\n\ny-quill supports custom embeds. However, you need teach y-quill how to translate\nthe custom deltas to Yjs transformations and how to translate Yjs\ntransformations to custom deltas. \n\nThis package already ships with a couple of custom deltas that might be useful. \n\n**Notes:**\n- You need to register a \"Blot\" to render those embeds! This TableEmbed Blot might be\n  available in a third-party package or you can build it yourself.\n- The Quill project currently doesn't talk a lot about this feature. This might\n  mean that it is unstable, or subject to future changes. y-quill will try to\n  keep track of the changes.\n\n#### Custom Delta: table-embed\n\n```javascript\nimport { tableEmbed } from 'y-quill/embeds/table-embed'\nimport TableEmbed from 'quill/modules/tableEmbed.js'\nTableEmbed.register()\n\nconst embeds = {\n  'table-embed': tableEmbed\n}\n\nconst binding = new QuillBinding(type, editor, provider.awareness, { embeds })\n```\n\n### How to build custom deltas\n\nThe QuillBinding accepts a `embeds` option that is used as a lookup table for\ntransforming custom embed operations to Yjs operations and back.\n\nHere is an example for building a `delta` embed that simply nests another delta\nin a custom embed:\n\n```javascript\nimport Delta from 'quill-delta'\n\n// Register a custom `delta` embed in `quill-embed` module.\nDelta.registerEmbed('delta', {\n  compose: (a, b) =\u003e new Delta(a).compose(new Delta(b)).ops,\n  transform: (a, b, priority) =\u003e\n    new Delta(a).transform(new Delta(b), priority).ops,\n  invert: (a, b) =\u003e new Delta(a).invert(new Delta(b)).ops\n})\n\n\n/**\n * This object is used to translate between quill-deltas and Yjs\n * transformations.\n */\nconst embeds = {\n  /**\n   * The `delta` embed might be useful to render another editor (e.g. quill, or\n   * a code editor like codemirror) inside of a quill instance.\n   */\n  delta: {\n    /**\n     * A custom embed is always represented as a Y.XmlElement, because it is a\n     * very versatile shared type and the only one that can be \"named\" (\n     * `yxml.nodeName` will be the name of the custom embed). You must represent\n     * your data using a Y.XmlElement. However, you may embed other Yjs types\n       inside the Y.XmlElement.\n     * \n     * The `update` function is called whenever the quill editor changes the\n     * custom embed (i.e. `[ retain: { delta: op} ]`, where `op` is the second\n     * parameter of `update`).\n     *\n     * The `update` function is also called when the embed is created so we may\n     * initialize some content here if needed.\n     *\n     * @param {Y.XmlElement\u003c{ ytext: Y.Text }\u003e} yxml\n     * @param {DeltaOp} op\n     */\n    update: (yxml, op) =\u003e {\n      if (!yxml.hasAttribute('ytext')) {\n        // The \"delta\" will be represented as a Y.Text, which we will maintain\n        // on the \"ytext\" property\n        yxml.setAttribute('ytext', new Y.Text())\n      }\n      const ytext = yxml.getAttribute('ytext')\n      ytext?.applyDelta(op)\n    },\n\n    /**\n     * Translate Yjs events to a delta embed event.\n     * In this case, Y.Text (the child of yxml) already emits a quill-compatible\n     * delta event that we can simply return.\n     *\n     * @param {Y.XmlElement} yxml\n     * @param {Array\u003cY.YEvent\u003cany\u003e\u003e} events\n     * @return {DeltaOps}\n     */\n    eventsToDelta: (yxml, events) =\u003e {\n      const ytext = yxml.getAttribute('ytext')\n      const ytextevent = events.find(event =\u003e event.target === ytext)\n      if (ytextevent) {\n        return /** @type {any} */ (ytextevent.delta)\n      }\n      return []\n    },\n\n    /**\n     * Translate the Y.XmlElement to a custom embed delta.\n     * In this case, we can simply return the delta representation of the\n     * Y.Text.\n     */\n    typeToDelta: (yxml) =\u003e {\n      return yxml.getAttribute('ytext').toDelta()\n    }\n  }\n}\n\nconst binding = new QuillBinding(type, editor, provider.awareness, { embeds })\n```\n\n## License\n\n[The MIT License](./LICENSE) © Kevin Jahns\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyjs%2Fy-quill","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyjs%2Fy-quill","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyjs%2Fy-quill/lists"}