forked from convergencelabs/monaco-collab-ext
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c743f8d
Showing
28 changed files
with
1,868 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"presets": [ | ||
["@babel/env", { | ||
"targets": { | ||
"browsers": ["last 2 versions"] | ||
} | ||
}], | ||
"@babel/typescript" | ||
], | ||
"plugins": [ | ||
"transform-class-properties", | ||
["module-resolver", { | ||
"extensions": [".js", ".ts"], | ||
"root": ["./src/js"] | ||
}] | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.idea | ||
node_modules | ||
dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
language: node_js | ||
|
||
node_js: | ||
- "10.10" | ||
|
||
script: npm run dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Change Log | ||
|
||
## [v0.1.0](https://github.com/convergencelabs/monaco-collab-ext/tree/0.1.0) (2019-03-03) | ||
|
||
- Initial release. | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
Copyright (c) 2019 Convergence Labs, Inc. | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
this software and associated documentation files (the "Software"), to deal in | ||
the Software without restriction, including without limitation the rights to | ||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
of the Software, and to permit persons to whom the Software is furnished to do | ||
so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | ||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH | ||
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
## Monaco Collaborative Extensions | ||
[![Build Status](https://travis-ci.org/convergencelabs/monaco-collab-ext.svg?branch=master)](https://travis-ci.org/convergencelabs/monaco-collab-ext) | ||
|
||
Enhances the [Monaco Editor](https://github.com/Microsoft/monaco-editor) by adding the ability to render cues about what remote users are doing in the system. | ||
|
||
![demo graphic](./docs/demo.gif "Shared Cursors and Selections") | ||
|
||
## Installation | ||
|
||
Install package with NPM and add it to your development dependencies: | ||
|
||
```npm install --save-dev @convergence/ace-collab-ext``` | ||
|
||
## Demo | ||
Go [here](https://examples.convergence.io/monaco/index.html) to see a live demo of multiple cursors, multiple selections, and remote scrollbars (Visit on multiple browsers, or even better, point a friend to it too). This uses [Convergence](https://convergence.io) to handle the synchronization of data and user actions. | ||
|
||
## Usage | ||
|
||
### RemoteCursorManager | ||
The RemoteCursorManager allows you to easily render the cursors of other users | ||
working in the same document. The cursor position can be represented as either | ||
a single linear index or as a 2-dimensional position in the form of | ||
```{lineNumber: 0, column: 10}```. | ||
|
||
```JavaScript | ||
const editor = monaco.editor.create(document.getElementById("editor"), { | ||
value: "function helloWolrd = () => { console.log('hello world!')", | ||
theme: "vs-dark'", | ||
language: 'javascript' | ||
}); | ||
|
||
const remoteCursorManager = new MonacoCollabExt.RemoteCursorManager({ | ||
editor: editor, | ||
tooltips: true, | ||
tooltipDuration: 2 | ||
}); | ||
|
||
const cursor = remoteCursorManager.addCursor("jDoe", "blue", "John Doe"); | ||
|
||
// Set the position of the cursor. | ||
cursor.setOffset(4); | ||
|
||
// Hide the cursor | ||
cursor.hide(); | ||
|
||
// Show the cursor | ||
cursor.show(); | ||
|
||
// Remove the cursor. | ||
cursor.dispose(); | ||
``` | ||
|
||
### RemoteSelectionManager | ||
The RemoteSelectionManager allows you to easily render the selection of other | ||
users working in the same document. | ||
|
||
```JavaScript | ||
const editor = monaco.editor.create(document.getElementById("editor"), { | ||
value: "function helloWolrd = () => { console.log('hello world!')", | ||
theme: "vs-dark'", | ||
language: 'javascript' | ||
}); | ||
|
||
const remoteSelectionManager = new MonacoCollabExt.RemoteSelectionManager({editor: editor}); | ||
|
||
const selection = remoteSelectionManager.addSelection("jDoe", "blue"); | ||
|
||
// Set the range of the selection using zero-based offsets. | ||
selection.setOffsets(45, 55); | ||
|
||
// Hide the selection | ||
selection.hide(); | ||
|
||
// Show the selection | ||
selection.show(); | ||
|
||
// Remove the selection. | ||
selection.dispose(); | ||
``` | ||
|
||
### EditorContentManager | ||
The EditorContentManager simplifies dealing with local and remote changes | ||
to the editor. | ||
|
||
```JavaScript | ||
const editor = monaco.editor.create(document.getElementById("editor"), { | ||
value: "function helloWolrd = () => { console.log('hello world!')", | ||
theme: "vs-dark'", | ||
language: 'javascript' | ||
}); | ||
|
||
const contentManager = new MonacoCollabExt.EditorContentManager({ | ||
editor: editor, | ||
onInsert(index, text) { | ||
console.log("Insert", index, text); | ||
}, | ||
onReplace(index, length, text) { | ||
console.log("Replace", index, length, text); | ||
}, | ||
onDelete(index, length) { | ||
console.log("Delete", index, length); | ||
} | ||
}); | ||
|
||
// Insert text into the editor at offset 5. | ||
contentManager.insert(5, "some text"); | ||
|
||
// Replace the text in the editor at range 5 - 10. | ||
contentManager.replace(5, 10, "some text"); | ||
|
||
// Delete the text in the editor at range 5 - 10. | ||
contentManager.delete(5, 10); | ||
|
||
// Release resources when done | ||
contentManager.dispose(); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/**! | ||
© 2019 Convergence Labs, Inc. | ||
@version <%= package.version %> | ||
@license MIT | ||
*/ |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Monaco Collaborative Extensions Example | ||
This directory contains a vanilla javascript example of using this library. Simply run `npm run dist` and then open the index.html in a web browser. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
var editorContents = `var observableProto; | ||
/** | ||
* Represents a push-style collection. | ||
*/ | ||
var Observable = Rx.Observable = (function () { | ||
function makeSubscribe(self, subscribe) { | ||
return function (o) { | ||
var oldOnError = o.onError; | ||
o.onError = function (e) { | ||
makeStackTraceLong(e, self); | ||
oldOnError.call(o, e); | ||
}; | ||
return subscribe.call(self, o); | ||
}; | ||
} | ||
function Observable() { | ||
if (Rx.config.longStackSupport && hasStacks) { | ||
var oldSubscribe = this._subscribe; | ||
var e = tryCatch(thrower)(new Error()).e; | ||
this.stack = e.stack.substring(e.stack.indexOf('\\n') + 1); | ||
this._subscribe = makeSubscribe(this, oldSubscribe); | ||
} | ||
} | ||
observableProto = Observable.prototype; | ||
/** | ||
* Determines whether the given object is an Observable | ||
* @param {Any} An object to determine whether it is an Observable | ||
* @returns {Boolean} true if an Observable, else false. | ||
*/ | ||
Observable.isObservable = function (o) { | ||
return o && isFunction(o.subscribe); | ||
}; | ||
/** | ||
* Subscribes an o to the observable sequence. | ||
* @param {Mixed} [oOrOnNext] The object that is to receive notifications or an action to invoke for each element in the observable sequence. | ||
* @param {Function} [onError] Action to invoke upon exceptional termination of the observable sequence. | ||
* @param {Function} [onCompleted] Action to invoke upon graceful termination of the observable sequence. | ||
* @returns {Diposable} A disposable handling the subscriptions and unsubscriptions. | ||
*/ | ||
observableProto.subscribe = observableProto.forEach = function (oOrOnNext, onError, onCompleted) { | ||
return this._subscribe(typeof oOrOnNext === 'object' ? | ||
oOrOnNext : | ||
observerCreate(oOrOnNext, onError, onCompleted)); | ||
}; | ||
/** | ||
* Subscribes to the next value in the sequence with an optional "this" argument. | ||
* @param {Function} onNext The function to invoke on each element in the observable sequence. | ||
* @param {Any} [thisArg] Object to use as this when executing callback. | ||
* @returns {Disposable} A disposable handling the subscriptions and unsubscriptions. | ||
*/ | ||
observableProto.subscribeOnNext = function (onNext, thisArg) { | ||
return this._subscribe(observerCreate(typeof thisArg !== 'undefined' ? function(x) { onNext.call(thisArg, x); } : onNext)); | ||
}; | ||
/** | ||
* Subscribes to an exceptional condition in the sequence with an optional "this" argument. | ||
* @param {Function} onError The function to invoke upon exceptional termination of the observable sequence. | ||
* @param {Any} [thisArg] Object to use as this when executing callback. | ||
* @returns {Disposable} A disposable handling the subscriptions and unsubscriptions. | ||
*/ | ||
observableProto.subscribeOnError = function (onError, thisArg) { | ||
return this._subscribe(observerCreate(null, typeof thisArg !== 'undefined' ? function(e) { onError.call(thisArg, e); } : onError)); | ||
}; | ||
/** | ||
* Subscribes to the next value in the sequence with an optional "this" argument. | ||
* @param {Function} onCompleted The function to invoke upon graceful termination of the observable sequence. | ||
* @param {Any} [thisArg] Object to use as this when executing callback. | ||
* @returns {Disposable} A disposable handling the subscriptions and unsubscriptions. | ||
*/ | ||
observableProto.subscribeOnCompleted = function (onCompleted, thisArg) { | ||
return this._subscribe(observerCreate(null, null, typeof thisArg !== 'undefined' ? function() { onCompleted.call(thisArg); } : onCompleted)); | ||
}; | ||
return Observable; | ||
})();`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
.body { | ||
margin: 0; | ||
} | ||
|
||
.editors { | ||
display: flex; | ||
flex-direction: row; | ||
flex: 1; | ||
} | ||
|
||
.editor-column { | ||
flex: 1; | ||
} | ||
|
||
.editor { | ||
height: 500px; | ||
border: 1px solid grey; | ||
margin-right: 20px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
const sourceUser = { | ||
id: "source", | ||
label: "Source User", | ||
color: "orange" | ||
}; | ||
|
||
const staticUser = { | ||
id: "static", | ||
label: "Static User", | ||
color: "blue" | ||
}; | ||
|
||
require.config({ paths: { 'vs': '../node_modules/monaco-editor/min/vs' }}); | ||
require(['vs/editor/editor.main', 'MonacoCollabExt'], function(m, MonacoCollabExt) { | ||
|
||
// | ||
// Create the target editor where events will be played into. | ||
// | ||
const target = monaco.editor.create(document.getElementById("target-editor"), { | ||
value: editorContents, | ||
theme: "vs-dark'", | ||
language: 'javascript' | ||
}); | ||
|
||
const remoteCursorManager = new MonacoCollabExt.RemoteCursorManager({ | ||
editor: target, | ||
tooltips: true, | ||
tooltipDuration: 2 | ||
}); | ||
const sourceUserCursor = remoteCursorManager.addCursor(sourceUser.id, sourceUser.color, sourceUser.label); | ||
const staticUserCursor = remoteCursorManager.addCursor(staticUser.id, staticUser.color, staticUser.label); | ||
|
||
const remoteSelectionManager = new MonacoCollabExt.RemoteSelectionManager({editor: target}); | ||
remoteSelectionManager.addSelection(sourceUser.id, sourceUser.color); | ||
remoteSelectionManager.addSelection(staticUser.id, staticUser.color); | ||
|
||
const targetContentManager = new MonacoCollabExt.EditorContentManager({ | ||
editor: target | ||
}); | ||
|
||
// | ||
// Faked other user. | ||
// | ||
staticUserCursor.setOffset(50); | ||
remoteSelectionManager.setSelectionOffsets(staticUser.id, 40, 50); | ||
|
||
|
||
// | ||
// Create the source editor were events will be generated. | ||
// | ||
const source = monaco.editor.create(document.getElementById("source-editor"), { | ||
value: editorContents, | ||
theme: "vs-dark'", | ||
language: 'javascript' | ||
}); | ||
|
||
source.onDidChangeCursorPosition(e => { | ||
const offset = source.getModel().getOffsetAt(e.position); | ||
sourceUserCursor.setOffset(offset); | ||
}); | ||
|
||
source.onDidChangeCursorSelection(e => { | ||
const startOffset = source.getModel().getOffsetAt(e.selection.getStartPosition()); | ||
const endOffset = source.getModel().getOffsetAt(e.selection.getEndPosition()); | ||
remoteSelectionManager.setSelectionOffsets(sourceUser.id, startOffset, endOffset); | ||
}); | ||
|
||
const sourceContentManager = new MonacoCollabExt.EditorContentManager({ | ||
editor: source, | ||
onInsert(index, text) { | ||
targetContentManager.insert(index, text); | ||
}, | ||
onReplace(index, length, text) { | ||
targetContentManager.replace(index, length, text); | ||
}, | ||
onDelete(index, length) { | ||
targetContentManager.delete(index, length); | ||
} | ||
}); | ||
}); |
Oops, something went wrong.