parent
57ee11bfea
commit
819c0e2c74
Binary file not shown.
@ -0,0 +1,115 @@
|
|||||||
|
<div id="editor">
|
||||||
|
<div id="editor-bar-header">
|
||||||
|
<div class="editor-section-bar-header">
|
||||||
|
<div class="param-editor">
|
||||||
|
<label for="fileInput">
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||||
|
<g
|
||||||
|
id="SVGRepo_tracerCarrier"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"></g>
|
||||||
|
<g id="SVGRepo_iconCarrier">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M1 5C1 3.34315 2.34315 2 4 2H8.55848C9.84977 2 10.9962 2.82629 11.4045 4.05132L11.7208 5H20C21.1046 5 22 5.89543 22 7V9.00961C23.1475 9.12163 23.9808 10.196 23.7695 11.3578L22.1332 20.3578C21.9603 21.3087 21.132 22 20.1654 22H3C1.89543 22 1 21.1046 1 20V5ZM20 9V7H11.7208C10.8599 7 10.0956 6.44914 9.82339 5.63246L9.50716 4.68377C9.37105 4.27543 8.98891 4 8.55848 4H4C3.44772 4 3 4.44772 3 5V12.2709L3.35429 10.588C3.54913 9.66249 4.36562 9 5.31139 9H20ZM3.36634 20C3.41777 19.9109 3.4562 19.8122 3.47855 19.706L5.31139 11L21 11H21.8018L20.1654 20L3.36634 20Z"
|
||||||
|
fill="#000000"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
style="display: none"
|
||||||
|
type="file"
|
||||||
|
id="fileInput"
|
||||||
|
(change)="loadFromFile($event)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="param-editor">
|
||||||
|
<button class="button-icon" type="button" (click)="saveToFile()">
|
||||||
|
<svg
|
||||||
|
width="800px"
|
||||||
|
height="800px"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M3 15C3 17.8284 3 19.2426 3.87868 20.1213C4.75736 21 6.17157 21 9 21H15C17.8284 21 19.2426 21 20.1213 20.1213C21 19.2426 21 17.8284 21 15"
|
||||||
|
stroke="#1C274C"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M12 3V16M12 16L16 11.625M12 16L8 11.625"
|
||||||
|
stroke="#1C274C"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="editor-section-bar-header">
|
||||||
|
@if (errorMessage) {
|
||||||
|
<div class="param-editor">
|
||||||
|
<p style="color: red">{{ errorMessage }}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="button-join"
|
||||||
|
type="button"
|
||||||
|
(click)="onRunButtonClicked()"
|
||||||
|
[disabled]="isLoaded">
|
||||||
|
Créer une salle
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<select id="language" [(ngModel)]="selectedLanguage">
|
||||||
|
@for (language of languages; track language.name) {
|
||||||
|
<option [ngValue]="language">{{ language.name }}</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div class="param-editor">
|
||||||
|
<button
|
||||||
|
class="button-icon button-run"
|
||||||
|
type="button"
|
||||||
|
(click)="onRunButtonClicked()"
|
||||||
|
[disabled]="isLoaded">
|
||||||
|
<div>RUN</div>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="#000000">
|
||||||
|
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||||
|
<g
|
||||||
|
id="SVGRepo_tracerCarrier"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"></g>
|
||||||
|
<g id="SVGRepo_iconCarrier">
|
||||||
|
<path
|
||||||
|
d="M2.78 2L2 2.41v12l.78.42 9-6V8l-9-6zM3 13.48V3.35l7.6 5.07L3 13.48z"></path>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M6 14.683l8.78-5.853V8L6 2.147V3.35l7.6 5.07L6 13.48v1.203z"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-center">
|
||||||
|
<div class="editor-child-element">
|
||||||
|
<codemirror6-editor [(ngModel)]="editorContent" [extensions]="extensions">
|
||||||
|
</codemirror6-editor>
|
||||||
|
</div>
|
||||||
|
<div class="editor-child-element">
|
||||||
|
<pre id="resultDiv" [innerHTML]="resultContent | safeHTML"></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,85 @@
|
|||||||
|
#editor-bar-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-section-bar-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-icon {
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 30px;
|
||||||
|
height: auto;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-run {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: #04aa6d;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 100px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-join {
|
||||||
|
background-color: #1c53bf;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 100px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
background-color: #0000f0;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*editor*/
|
||||||
|
|
||||||
|
.editor-center {
|
||||||
|
height: 700px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0px 0px 0px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-child-element {
|
||||||
|
min-height: 100px;
|
||||||
|
height: 100%;
|
||||||
|
width: 1000px;
|
||||||
|
background-color: black;
|
||||||
|
color: #fff;
|
||||||
|
border-right: 10px solid gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .codemirror6-editor {
|
||||||
|
height: 100%;
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { EditorLiveComponent } from './editor-live.component';
|
||||||
|
|
||||||
|
describe('EditorLiveComponent', () => {
|
||||||
|
let component: EditorLiveComponent;
|
||||||
|
let fixture: ComponentFixture<EditorLiveComponent>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [EditorLiveComponent],
|
||||||
|
});
|
||||||
|
fixture = TestBed.createComponent(EditorLiveComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,328 @@
|
|||||||
|
import { Component, Input, ViewChild } from '@angular/core';
|
||||||
|
import { CodeExecutionService } from 'src/app/services/codeExecution.service';
|
||||||
|
import { Compartment, StateEffect } from '@codemirror/state';
|
||||||
|
import { CodeMirrorComponent } from '@sandkasten/codemirror6-editor';
|
||||||
|
import { LanguageDescription } from '@codemirror/language';
|
||||||
|
import { CODE_DEFAULTS, LANGUAGES } from '../languages';
|
||||||
|
import { SafeHTMLPipe } from '../../safe-html.pipe';
|
||||||
|
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
|
||||||
|
import {
|
||||||
|
keymap,
|
||||||
|
highlightSpecialChars,
|
||||||
|
drawSelection,
|
||||||
|
highlightActiveLine,
|
||||||
|
dropCursor,
|
||||||
|
rectangularSelection,
|
||||||
|
crosshairCursor,
|
||||||
|
lineNumbers,
|
||||||
|
highlightActiveLineGutter,
|
||||||
|
gutter,
|
||||||
|
} from '@codemirror/view';
|
||||||
|
import { Extension, EditorState } from '@codemirror/state';
|
||||||
|
import {
|
||||||
|
defaultHighlightStyle,
|
||||||
|
syntaxHighlighting,
|
||||||
|
indentOnInput,
|
||||||
|
bracketMatching,
|
||||||
|
foldGutter,
|
||||||
|
foldKeymap,
|
||||||
|
} from '@codemirror/language';
|
||||||
|
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
|
||||||
|
import { searchKeymap, highlightSelectionMatches } from '@codemirror/search';
|
||||||
|
import {
|
||||||
|
autocompletion,
|
||||||
|
completionKeymap,
|
||||||
|
closeBrackets,
|
||||||
|
closeBracketsKeymap,
|
||||||
|
} from '@codemirror/autocomplete';
|
||||||
|
import { lintKeymap } from '@codemirror/lint';
|
||||||
|
|
||||||
|
const basicSetup: Extension = (() => [
|
||||||
|
highlightActiveLineGutter(),
|
||||||
|
highlightSpecialChars(),
|
||||||
|
history(),
|
||||||
|
foldGutter(),
|
||||||
|
drawSelection(),
|
||||||
|
dropCursor(),
|
||||||
|
EditorState.allowMultipleSelections.of(true),
|
||||||
|
indentOnInput(),
|
||||||
|
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||||||
|
bracketMatching(),
|
||||||
|
closeBrackets(),
|
||||||
|
autocompletion(),
|
||||||
|
rectangularSelection(),
|
||||||
|
crosshairCursor(),
|
||||||
|
highlightActiveLine(),
|
||||||
|
highlightSelectionMatches(),
|
||||||
|
keymap.of([
|
||||||
|
...closeBracketsKeymap,
|
||||||
|
...defaultKeymap,
|
||||||
|
...searchKeymap,
|
||||||
|
...historyKeymap,
|
||||||
|
...foldKeymap,
|
||||||
|
...completionKeymap,
|
||||||
|
...lintKeymap,
|
||||||
|
]),
|
||||||
|
])();
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-editor-live',
|
||||||
|
templateUrl: './editor-live.component.html',
|
||||||
|
styleUrls: ['./editor-live.component.scss'],
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CodeMirrorComponent,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormsModule,
|
||||||
|
SafeHTMLPipe,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class EditorLiveComponent {
|
||||||
|
isLoaded: boolean = false; // Pour vérifier si le chargement est terminé
|
||||||
|
|
||||||
|
|
||||||
|
readonly languages: LanguageDescription[] = LANGUAGES;
|
||||||
|
// Mode par défaut
|
||||||
|
private _selectedLanguage = this.languages.find(
|
||||||
|
(lang) => lang.name === 'JavaScript'
|
||||||
|
)!;
|
||||||
|
get selectedLanguage(): LanguageDescription {
|
||||||
|
return this._selectedLanguage;
|
||||||
|
}
|
||||||
|
set selectedLanguage(value: LanguageDescription) {
|
||||||
|
this._selectedLanguage = value;
|
||||||
|
if (value.name in CODE_DEFAULTS) {
|
||||||
|
this.editorContent =
|
||||||
|
CODE_DEFAULTS[value.name as keyof typeof CODE_DEFAULTS];
|
||||||
|
}
|
||||||
|
this.selectedLanguage.load().then((language) => {
|
||||||
|
this.codemirror.editor?.dispatch({
|
||||||
|
effects: this.languageCompartment.reconfigure(language),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
private _linesNumbers: boolean = true;
|
||||||
|
get linesNumbers() {
|
||||||
|
return this._linesNumbers;
|
||||||
|
}
|
||||||
|
set linesNumbers(lines: boolean) {
|
||||||
|
this._linesNumbers = lines;
|
||||||
|
this.codemirror.editor?.dispatch({
|
||||||
|
effects: this.gutterCompartment.reconfigure(
|
||||||
|
lines ? lineNumbers() : gutter({})
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contenu de l'éditeur que l'on passera au serveur
|
||||||
|
editorContent: string =
|
||||||
|
CODE_DEFAULTS[this.selectedLanguage.name as keyof typeof CODE_DEFAULTS];
|
||||||
|
resultContent: string = '';
|
||||||
|
|
||||||
|
// Message d'erreur
|
||||||
|
errorMessage: string = '';
|
||||||
|
|
||||||
|
@ViewChild(CodeMirrorComponent) private codemirror!: CodeMirrorComponent;
|
||||||
|
|
||||||
|
private readonly languageCompartment = new Compartment();
|
||||||
|
private readonly gutterCompartment = new Compartment();
|
||||||
|
protected readonly extensions: Extension[] = [
|
||||||
|
basicSetup,
|
||||||
|
this.gutterCompartment.of(lineNumbers()),
|
||||||
|
this.languageCompartment.of(this.selectedLanguage.support!),
|
||||||
|
];
|
||||||
|
|
||||||
|
private client: WebSocket | undefined;
|
||||||
|
@Input()
|
||||||
|
set idRoom(idRoom: string) {
|
||||||
|
this.client = new WebSocket(`ws://127.0.0.1:3000/live/${idRoom}`);
|
||||||
|
this.client.addEventListener('open', async () => {
|
||||||
|
let conn = new Connection(this.client!);
|
||||||
|
console.log('open')
|
||||||
|
let {version, doc} = await getDocument(conn);
|
||||||
|
console.log('res')
|
||||||
|
this.codemirror.editor?.dispatch({
|
||||||
|
changes: {
|
||||||
|
from: 0,
|
||||||
|
to: this.codemirror.editor.state.doc.length,
|
||||||
|
insert: doc
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.codemirror.editor?.dispatch({
|
||||||
|
effects: StateEffect.appendConfig.of([peerExtension(version, conn)]),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor(private codeExecutionService: CodeExecutionService) {}
|
||||||
|
|
||||||
|
// Efface le contenu de l'éditeur
|
||||||
|
clear(): void {
|
||||||
|
this.editorContent = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
onRunButtonClicked() {
|
||||||
|
// Le code à exécuter est le contenu de l'éditeur
|
||||||
|
const codeToExecute = this.editorContent;
|
||||||
|
this.codeExecutionService.executeCode(
|
||||||
|
codeToExecute,
|
||||||
|
this.selectedLanguage.name
|
||||||
|
);
|
||||||
|
|
||||||
|
this.resultContent = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFromFile(event: Event) {
|
||||||
|
const file = (event.target as HTMLInputElement).files![0];
|
||||||
|
for (const language of this.languages) {
|
||||||
|
if (language.extensions.some((ext) => file.name.endsWith(`.${ext}`))) {
|
||||||
|
this.selectedLanguage = language;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (event) => {
|
||||||
|
this.editorContent = event.target!.result as string;
|
||||||
|
this.errorMessage = '';
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const extensions = this.languages.flatMap((lang) => lang.extensions);
|
||||||
|
this.errorMessage = `Unsupported language. Please select one of the following languages: ${extensions.join(', ')}.`;
|
||||||
|
console.error(this.errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveToFile() {
|
||||||
|
const blob = new Blob([this.editorContent], { type: 'text/plain' });
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.download = `code.${this.selectedLanguage.extensions![0]}`;
|
||||||
|
a.href = URL.createObjectURL(blob);
|
||||||
|
a.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*live*/
|
||||||
|
import { ChangeSet, Text } from '@codemirror/state';
|
||||||
|
import {EditorView} from "codemirror"
|
||||||
|
import { Update, collab, getSyncedVersion, receiveUpdates, sendableUpdates } from '@codemirror/collab';
|
||||||
|
import { ViewPlugin, ViewUpdate } from '@codemirror/view';
|
||||||
|
|
||||||
|
class Connection {
|
||||||
|
private requestId = 0;
|
||||||
|
private resolves: Record<number, (value: any) => void> = {};
|
||||||
|
|
||||||
|
constructor(private client: WebSocket) {
|
||||||
|
client.addEventListener('message', (event) => {
|
||||||
|
const response = JSON.parse(event.data);
|
||||||
|
if ('_request' in response) {
|
||||||
|
const resolve = this.resolves[response._request];
|
||||||
|
if (resolve) {
|
||||||
|
resolve(response.payload);
|
||||||
|
} else {
|
||||||
|
console.error('Received response for unknown or already used request', response._request);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('Received invalid response', response._request);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
request(body: Record<string, unknown>): Promise<any> {
|
||||||
|
body['_request'] = this.requestId;
|
||||||
|
this.client.send(JSON.stringify(body));
|
||||||
|
return new Promise((resolve) => this.resolves[this.requestId++] = resolve);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pushUpdates(
|
||||||
|
connection: Connection,
|
||||||
|
version: number,
|
||||||
|
fullUpdates: readonly Update[]
|
||||||
|
): Promise<boolean> {
|
||||||
|
// Strip off transaction data
|
||||||
|
let updates = fullUpdates.map(u => ({
|
||||||
|
clientID: u.clientID,
|
||||||
|
changes: u.changes.toJSON()
|
||||||
|
}));
|
||||||
|
console.log("test1");
|
||||||
|
return connection.request({type: "pushUpdates", version, updates});
|
||||||
|
}
|
||||||
|
|
||||||
|
function pullUpdates(
|
||||||
|
connection: Connection,
|
||||||
|
version: number
|
||||||
|
): Promise<readonly Update[]> {
|
||||||
|
console.log("test2");
|
||||||
|
|
||||||
|
return connection.request({type: "pullUpdates", version})
|
||||||
|
.then(updates => updates.map((u: any) => ({
|
||||||
|
changes: ChangeSet.fromJSON(u.changes),
|
||||||
|
clientID: u.clientID
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDocument(
|
||||||
|
connection: Connection
|
||||||
|
): Promise<{version: number, doc: Text}> {
|
||||||
|
console.log("test3");
|
||||||
|
|
||||||
|
return connection.request({ type: "getDocument" }).then(data => ({
|
||||||
|
version: data.version,
|
||||||
|
doc: Text.of(data.doc.split("\n"))
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function peerExtension(startVersion: number, connection: Connection) {
|
||||||
|
console.log(connection)
|
||||||
|
let plugin = ViewPlugin.fromClass(class {
|
||||||
|
private pushing = false
|
||||||
|
private done = false
|
||||||
|
|
||||||
|
constructor(private view: EditorView) { this.pull() }
|
||||||
|
|
||||||
|
update(update: ViewUpdate) {
|
||||||
|
if (update.docChanged) this.push()
|
||||||
|
}
|
||||||
|
|
||||||
|
async push() {
|
||||||
|
let updates = sendableUpdates(this.view.state)
|
||||||
|
console.log("push");
|
||||||
|
if (this.pushing || !updates.length) return
|
||||||
|
this.pushing = true
|
||||||
|
let version = getSyncedVersion(this.view.state)
|
||||||
|
await pushUpdates(connection, version, updates)
|
||||||
|
this.pushing = false
|
||||||
|
console.log("push2");
|
||||||
|
|
||||||
|
// Regardless of whether the push failed or new updates came in
|
||||||
|
// while it was running, try again if there's updates remaining
|
||||||
|
if (sendableUpdates(this.view.state).length)
|
||||||
|
setTimeout(() => this.push(), 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
async pull() {
|
||||||
|
while (!this.done) {
|
||||||
|
let version = getSyncedVersion(this.view.state)
|
||||||
|
let updates = await pullUpdates(connection, version)
|
||||||
|
console.log(updates)
|
||||||
|
this.view.dispatch(receiveUpdates(this.view.state, updates))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() { this.done = true }
|
||||||
|
})
|
||||||
|
return [collab({startVersion}), plugin]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createPeer(connection: Connection) {
|
||||||
|
let {version, doc} = await getDocument(connection)
|
||||||
|
console.log(doc);
|
||||||
|
let state = EditorState.create({
|
||||||
|
doc,
|
||||||
|
extensions: [basicSetup, peerExtension(version, connection)],
|
||||||
|
})
|
||||||
|
return new EditorView({state, parent: document.body})
|
||||||
|
}
|
||||||
|
|
||||||
|
//const client = new WebSocket('ws://127.0.0.1:3000/live/test');
|
||||||
|
//client.addEventListener('open', () => createPeer(new Connection(client)));
|
Loading…
Reference in new issue