Skip to content

Commit

Permalink
[#48849] Add Collaboration
Browse files Browse the repository at this point in the history
  • Loading branch information
Trzcin authored and AdamOlech committed Oct 6, 2023
1 parent 603f761 commit cc8f08f
Show file tree
Hide file tree
Showing 15 changed files with 1,367 additions and 22 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module.exports = {
env: {
browser: true,
es2021: true,
node: true
},
extends: "eslint:recommended",
overrides: [
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
dist
node_modules
node_modules
.env
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ You can export the rendered markdown to a PDF file.

![PDF Demo](./assets/PDFDemo.png)

### Live Collaboration

You can work on a document with multiple people at the same time.

![Collaboration Demo](./assets/CollaborationDemo.gif)

## Usage

### Building the component
Expand Down Expand Up @@ -82,6 +88,17 @@ You can edit the source files in `src/` to modify the behavior of the component,

An analogous demo deployed with GH actions from latest main should be deployed at https://antmicro.github.io/myst-editor/

### Collaboration Server

The example server is located in the `bin` directory. To run it use:

```bash
cd bin
yarn && yarn server
```

You can change the port it runs on with setting a `PORT` environment variable.

### MystEditor Props

Here are the props you can pass to the MystEditor component:
Expand All @@ -93,6 +110,12 @@ Here are the props you can pass to the MystEditor component:
- `printCallback` *(default: window.print())* - gets called when you click the `Export to PDF` button
- `topbar` *(default: true)* - whether to show the topbar
- `templateList` - path/url to a JSON file containing your document templates. For an example see `public/linkedtemplatelist.json`.
- `collaboration` - options related to live collaboration:
- `enabled` *(default: false)*
- `wsUrl` *(example: ws://example:4444)* - url of the websocket server
- `username`
- `room` - name of the room to join, users will see others in the same room
- `color` - color of the cursor seen by other users

## License

Expand Down
Binary file added assets/CollaborationDemo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
76 changes: 76 additions & 0 deletions bin/callback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import http from 'http';

const CALLBACK_URL = process.env.CALLBACK_URL ? new URL(process.env.CALLBACK_URL) : null
const CALLBACK_TIMEOUT = process.env.CALLBACK_TIMEOUT || 5000
const CALLBACK_OBJECTS = process.env.CALLBACK_OBJECTS ? JSON.parse(process.env.CALLBACK_OBJECTS) : {}

export const isCallbackSet = !!CALLBACK_URL

/**
* @param {Uint8Array} update
* @param {any} origin
* @param {WSSharedDoc} doc
*/
export const callbackHandler = (update, origin, doc) => {
const room = doc.name
const dataToSend = {
room,
data: {}
}
const sharedObjectList = Object.keys(CALLBACK_OBJECTS)
sharedObjectList.forEach(sharedObjectName => {
const sharedObjectType = CALLBACK_OBJECTS[sharedObjectName]
dataToSend.data[sharedObjectName] = {
type: sharedObjectType,
content: getContent(sharedObjectName, sharedObjectType, doc).toJSON()
}
})
callbackRequest(CALLBACK_URL, CALLBACK_TIMEOUT, dataToSend)
}

/**
* @param {URL} url
* @param {number} timeout
* @param {Object} data
*/
const callbackRequest = (url, timeout, data) => {
data = JSON.stringify(data)
const options = {
hostname: url.hostname,
port: url.port,
path: url.pathname,
timeout,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
}
const req = http.request(options)
req.on('timeout', () => {
console.warn('Callback request timed out.')
req.destroy()
})
req.on('error', (e) => {
console.error('Callback request error.', e)
req.destroy()
})
req.write(data)
req.end()
}

/**
* @param {string} objName
* @param {string} objType
* @param {WSSharedDoc} doc
*/
const getContent = (objName, objType, doc) => {
switch (objType) {
case 'Array': return doc.getArray(objName)
case 'Map': return doc.getMap(objName)
case 'Text': return doc.getText(objName)
case 'XmlFragment': return doc.getXmlFragment(objName)
case 'XmlElement': return doc.getXmlElement(objName)
default : return {}
}
}
Loading

0 comments on commit cc8f08f

Please sign in to comment.