@hereugo/open-collaboration-monaco 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -0
- package/lib/collaboration-connection.d.ts +10 -0
- package/lib/collaboration-connection.d.ts.map +1 -0
- package/lib/collaboration-connection.js +64 -0
- package/lib/collaboration-connection.js.map +1 -0
- package/lib/collaboration-instance.d.ts +98 -0
- package/lib/collaboration-instance.d.ts.map +1 -0
- package/lib/collaboration-instance.js +606 -0
- package/lib/collaboration-instance.js.map +1 -0
- package/lib/collaboration-peer.d.ts +19 -0
- package/lib/collaboration-peer.d.ts.map +1 -0
- package/lib/collaboration-peer.js +113 -0
- package/lib/collaboration-peer.js.map +1 -0
- package/lib/example.d.ts +2 -0
- package/lib/example.d.ts.map +1 -0
- package/lib/example.js +84 -0
- package/lib/example.js.map +1 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +11 -0
- package/lib/index.js.map +1 -0
- package/lib/monaco-api.d.ts +49 -0
- package/lib/monaco-api.d.ts.map +1 -0
- package/lib/monaco-api.js +168 -0
- package/lib/monaco-api.js.map +1 -0
- package/lib/types.d.ts +4 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +7 -0
- package/lib/types.js.map +1 -0
- package/package.json +75 -0
- package/src/collaboration-connection.ts +69 -0
- package/src/collaboration-instance.ts +688 -0
- package/src/collaboration-peer.ts +133 -0
- package/src/example.ts +96 -0
- package/src/index.ts +11 -0
- package/src/monaco-api.ts +235 -0
- package/src/types.ts +9 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// ******************************************************************************
|
|
2
|
+
// Copyright 2024 TypeFox GmbH
|
|
3
|
+
// This program and the accompanying materials are made available under the
|
|
4
|
+
// terms of the MIT License, which is available in the project root.
|
|
5
|
+
// ******************************************************************************
|
|
6
|
+
|
|
7
|
+
import * as types from '@hereugo/open-collaboration-protocol';
|
|
8
|
+
import * as awarenessProtocol from 'y-protocols/awareness';
|
|
9
|
+
|
|
10
|
+
type PeerDecorationOptions = {
|
|
11
|
+
selectionClassName: string;
|
|
12
|
+
cursorClassName: string;
|
|
13
|
+
cursorInvertedClassName: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export class DisposablePeer {
|
|
17
|
+
|
|
18
|
+
readonly peer: types.Peer;
|
|
19
|
+
color: string | undefined;
|
|
20
|
+
|
|
21
|
+
private yjsAwareness: awarenessProtocol.Awareness;
|
|
22
|
+
|
|
23
|
+
readonly decoration: PeerDecorationOptions;
|
|
24
|
+
|
|
25
|
+
get clientId(): number | undefined {
|
|
26
|
+
const states = this.yjsAwareness.getStates() as Map<number, types.ClientAwareness>;
|
|
27
|
+
for (const [clientID, state] of states.entries()) {
|
|
28
|
+
if (state.peer === this.peer.id) {
|
|
29
|
+
return clientID;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get lastUpdated(): number | undefined {
|
|
36
|
+
const clientId = this.clientId;
|
|
37
|
+
if (clientId !== undefined) {
|
|
38
|
+
const meta = this.yjsAwareness.meta.get(clientId);
|
|
39
|
+
if (meta) {
|
|
40
|
+
return meta.lastUpdated;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
constructor(yAwareness: awarenessProtocol.Awareness, peer: types.Peer) {
|
|
47
|
+
this.peer = peer;
|
|
48
|
+
this.yjsAwareness = yAwareness;
|
|
49
|
+
this.decoration = this.createDecorations();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private createDecorations(): PeerDecorationOptions {
|
|
53
|
+
const color = createColor();
|
|
54
|
+
const colorCss = typeof color === 'string' ? color : `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
|
|
55
|
+
this.color = colorCss;
|
|
56
|
+
const className = `peer-${this.peer.id}`;
|
|
57
|
+
const cursorClassName = `${className}-cursor`;
|
|
58
|
+
const cursorInvertedClassName = `${className}-cursor-inverted`;
|
|
59
|
+
const selectionClassName = `${className}-selection`;
|
|
60
|
+
const cursorCss = `.${cursorClassName} {
|
|
61
|
+
background-color: ${colorCss} !important;
|
|
62
|
+
border-color: ${colorCss} !important;
|
|
63
|
+
position: absolute;
|
|
64
|
+
border-right: solid 2px;
|
|
65
|
+
border-top: solid 2px;
|
|
66
|
+
border-bottom: solid 2px;
|
|
67
|
+
height: 100%;
|
|
68
|
+
box-sizing: border-box;
|
|
69
|
+
}`;
|
|
70
|
+
generateCSS(cursorCss);
|
|
71
|
+
const cursorAfterCss = `.${cursorClassName}::after {
|
|
72
|
+
content: "${this.peer.name}";
|
|
73
|
+
position: absolute;
|
|
74
|
+
transform: translateY(-100%);
|
|
75
|
+
padding: 0 4px;
|
|
76
|
+
border-radius: 4px 4px 4px 0px;
|
|
77
|
+
background-color: ${colorCss};
|
|
78
|
+
}`;
|
|
79
|
+
generateCSS(cursorAfterCss);
|
|
80
|
+
const cursorAfterInvertedCss = `.${cursorClassName}.${cursorInvertedClassName}::after {
|
|
81
|
+
transform: translateY(100%);
|
|
82
|
+
margin-top: -2px;
|
|
83
|
+
border-radius: 0px 4px 4px 4px;
|
|
84
|
+
z-index: 1;
|
|
85
|
+
}`;
|
|
86
|
+
generateCSS(cursorAfterInvertedCss);
|
|
87
|
+
const selectionCss = `.${selectionClassName} {
|
|
88
|
+
background: ${colorCss} !important;
|
|
89
|
+
opacity: 0.25;
|
|
90
|
+
}`;
|
|
91
|
+
generateCSS(selectionCss);
|
|
92
|
+
return {
|
|
93
|
+
cursorClassName,
|
|
94
|
+
cursorInvertedClassName,
|
|
95
|
+
selectionClassName
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let colorIndex = 0;
|
|
102
|
+
const defaultColors: Array<[number, number, number] | string> = [
|
|
103
|
+
'yellow', // Yellow
|
|
104
|
+
'green', // Green
|
|
105
|
+
'magenta', // Magenta
|
|
106
|
+
'lightGreen', // Light green
|
|
107
|
+
[255, 178, 123], // Light orange
|
|
108
|
+
[255, 157, 242], // Light magenta
|
|
109
|
+
[92, 45, 145], // Purple
|
|
110
|
+
[0, 178, 148], // Light teal
|
|
111
|
+
[255, 241, 0], // Light yellow
|
|
112
|
+
[180, 160, 255] // Light purple
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
const knownColors = new Set<string>();
|
|
116
|
+
function createColor(): [number, number, number] | string {
|
|
117
|
+
if (colorIndex < defaultColors.length) {
|
|
118
|
+
return defaultColors[colorIndex++];
|
|
119
|
+
}
|
|
120
|
+
const o = Math.round, r = Math.random, s = 255;
|
|
121
|
+
let color: [number, number, number];
|
|
122
|
+
do {
|
|
123
|
+
color = [o(r() * s), o(r() * s), o(r() * s)];
|
|
124
|
+
} while (knownColors.has(JSON.stringify(color)));
|
|
125
|
+
knownColors.add(JSON.stringify(color));
|
|
126
|
+
return color;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function generateCSS(cssText: string) {
|
|
130
|
+
const style: HTMLStyleElement = document.createElement('style');
|
|
131
|
+
style.textContent = cssText;
|
|
132
|
+
document.head.appendChild(style);
|
|
133
|
+
}
|
package/src/example.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// ******************************************************************************
|
|
2
|
+
// Copyright 2024 TypeFox GmbH
|
|
3
|
+
// This program and the accompanying materials are made available under the
|
|
4
|
+
// terms of the MIT License, which is available in the project root.
|
|
5
|
+
// ******************************************************************************
|
|
6
|
+
|
|
7
|
+
import * as monaco from 'monaco-editor';
|
|
8
|
+
import { monacoCollab } from './monaco-api.js';
|
|
9
|
+
import { User } from '@hereugo/open-collaboration-protocol';
|
|
10
|
+
|
|
11
|
+
const value = `function sayHello(): string {
|
|
12
|
+
return "Hello";
|
|
13
|
+
};`;
|
|
14
|
+
|
|
15
|
+
export type WorkerLoader = () => Worker
|
|
16
|
+
const workerLoaders: Partial<Record<string, WorkerLoader>> = {
|
|
17
|
+
editorWorkerService: () =>
|
|
18
|
+
new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker.js', import.meta.url), {
|
|
19
|
+
type: 'module'
|
|
20
|
+
}),
|
|
21
|
+
typescript: () =>
|
|
22
|
+
new Worker(new URL('monaco-editor/esm/vs/language/typescript/ts.worker.js', import.meta.url), {
|
|
23
|
+
type: 'module'
|
|
24
|
+
})
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
window.MonacoEnvironment = {
|
|
28
|
+
getWorker: function(moduleId, label) {
|
|
29
|
+
const workerFactory = workerLoaders[label];
|
|
30
|
+
if (workerFactory !== undefined && workerFactory !== null) {
|
|
31
|
+
return workerFactory();
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`Unimplemented worker ${label} (${moduleId})`);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const container = document.getElementById('container');
|
|
38
|
+
if (container) {
|
|
39
|
+
const myEditor = monaco.editor.create(container, {
|
|
40
|
+
value,
|
|
41
|
+
language: 'typescript'
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const monacoCollabApi = monacoCollab({
|
|
45
|
+
serverUrl: 'http://localhost:8100',
|
|
46
|
+
callbacks: {
|
|
47
|
+
onUserRequestsAccess: (user: User) => {
|
|
48
|
+
console.log('User requests access', user);
|
|
49
|
+
return Promise.resolve(true);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// on click of button with id create create room, call createRoom, take the value from response and set it in textfield with id token
|
|
55
|
+
const createRoomButton = document.getElementById('create');
|
|
56
|
+
createRoomButton?.addEventListener('click', () => {
|
|
57
|
+
monacoCollabApi.createRoom().then(token => {
|
|
58
|
+
if (token) {
|
|
59
|
+
monacoCollabApi.setEditor(myEditor);
|
|
60
|
+
(document.getElementById('token') as HTMLInputElement).value = token ?? '';
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// on click of join room button take value from textfield with id room and call joinRoom
|
|
66
|
+
const joinRoomButton = document.getElementById('join');
|
|
67
|
+
joinRoomButton?.addEventListener('click', () => {
|
|
68
|
+
const roomToken = (document.getElementById('room') as HTMLInputElement).value;
|
|
69
|
+
monacoCollabApi.joinRoom(roomToken).then(state => {
|
|
70
|
+
if (state) {
|
|
71
|
+
monacoCollabApi.setEditor(myEditor);
|
|
72
|
+
monacoCollabApi.onUsersChanged(() => {
|
|
73
|
+
monacoCollabApi.getUserData().then(userData => {
|
|
74
|
+
const host = userData?.others.find(u => u.peer.host);
|
|
75
|
+
if (host && monacoCollabApi.getFollowedUser() === undefined) {
|
|
76
|
+
monacoCollabApi.followUser(host.peer.id);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
console.log('Joined room');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// on click of button with id login call login
|
|
86
|
+
const loginButton = document.getElementById('login');
|
|
87
|
+
loginButton?.addEventListener('click', () => {
|
|
88
|
+
monacoCollabApi.login().then((userAuthToken?: string) => {
|
|
89
|
+
let loginText = 'Failed to login';
|
|
90
|
+
if (userAuthToken) {
|
|
91
|
+
loginText = 'Successfully logged in';
|
|
92
|
+
}
|
|
93
|
+
document.getElementById('user')!.innerText = loginText;
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// ******************************************************************************
|
|
2
|
+
// Copyright 2024 TypeFox GmbH
|
|
3
|
+
// This program and the accompanying materials are made available under the
|
|
4
|
+
// terms of the MIT License, which is available in the project root.
|
|
5
|
+
// ******************************************************************************
|
|
6
|
+
|
|
7
|
+
export * from './collaboration-connection.js';
|
|
8
|
+
export * from './collaboration-instance.js';
|
|
9
|
+
export * from './collaboration-peer.js';
|
|
10
|
+
export * from './types.js';
|
|
11
|
+
export * from './monaco-api.js';
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// ******************************************************************************
|
|
2
|
+
// Copyright 2024 TypeFox GmbH
|
|
3
|
+
// This program and the accompanying materials are made available under the
|
|
4
|
+
// terms of the MIT License, which is available in the project root.
|
|
5
|
+
// ******************************************************************************
|
|
6
|
+
|
|
7
|
+
import { ConnectionProvider, SocketIoTransportProvider } from '@hereugo/open-collaboration-protocol';
|
|
8
|
+
import { CollaborationInstance, UsersChangeEvent, FileNameChangeEvent } from './collaboration-instance.js';
|
|
9
|
+
import * as types from '@hereugo/open-collaboration-protocol';
|
|
10
|
+
import { createRoom, joinRoom, login } from './collaboration-connection.js';
|
|
11
|
+
import * as monaco from 'monaco-editor';
|
|
12
|
+
|
|
13
|
+
let connectionProvider: ConnectionProvider | undefined;
|
|
14
|
+
let instance: CollaborationInstance | undefined;
|
|
15
|
+
|
|
16
|
+
types.initializeProtocol({
|
|
17
|
+
cryptoModule: globalThis.crypto
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export type MonacoCollabCallbacks = {
|
|
21
|
+
onUserRequestsAccess: (user: types.User) => Promise<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* reports the status when joining or creating a room
|
|
24
|
+
* @param info information about the changed status
|
|
25
|
+
*/
|
|
26
|
+
statusReporter?: (info: types.Info) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type MonacoCollabOptions = {
|
|
30
|
+
serverUrl: string;
|
|
31
|
+
callbacks: MonacoCollabCallbacks;
|
|
32
|
+
userToken?: string;
|
|
33
|
+
roomToken?: string;
|
|
34
|
+
useCookieAuth?: boolean;
|
|
35
|
+
loginPageOpener?: (token: string, authenticationMetadata: types.AuthMetadata) => Promise<boolean>;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type OtherUserData = {peer: types.Peer, color: string};
|
|
39
|
+
export type UserData = {me: types.Peer, others: OtherUserData[]};
|
|
40
|
+
|
|
41
|
+
export type MonacoCollabApi = {
|
|
42
|
+
createRoom: () => Promise<string | undefined>
|
|
43
|
+
joinRoom: (roomToken: string) => Promise<string | undefined>
|
|
44
|
+
leaveRoom: () => void
|
|
45
|
+
login: () => Promise<string | undefined>
|
|
46
|
+
logout: () => Promise<void | undefined>
|
|
47
|
+
isLoggedIn: () => Promise<boolean>
|
|
48
|
+
setEditor: (editor: monaco.editor.IStandaloneCodeEditor) => void
|
|
49
|
+
getUserData: () => Promise<UserData | undefined>
|
|
50
|
+
onUsersChanged: (evt: UsersChangeEvent) => void
|
|
51
|
+
onFileNameChange: (callback: FileNameChangeEvent) => void
|
|
52
|
+
getCurrentConnection: () => types.ProtocolBroadcastConnection | undefined
|
|
53
|
+
followUser: (id?: string) => void
|
|
54
|
+
getFollowedUser: () => string | undefined
|
|
55
|
+
setFileName: (fileName: string) => void
|
|
56
|
+
getFileName: () => string | undefined
|
|
57
|
+
setWorkspaceName: (workspaceName: string) => void
|
|
58
|
+
getWorkspaceName: () => string | undefined
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function monacoCollab(options: MonacoCollabOptions): MonacoCollabApi {
|
|
62
|
+
connectionProvider = new ConnectionProvider({
|
|
63
|
+
url: options.serverUrl,
|
|
64
|
+
authenticationHandler: options.loginPageOpener ?? (async (_token, metaData) => {
|
|
65
|
+
// If this returns null, it means the window could not be opened and the authentication failed
|
|
66
|
+
return window.open(metaData.loginPageUrl, '_blank') !== null;
|
|
67
|
+
}),
|
|
68
|
+
transports: [SocketIoTransportProvider],
|
|
69
|
+
userToken: options.userToken,
|
|
70
|
+
useCookieAuth: options.useCookieAuth,
|
|
71
|
+
fetch: async (url, options) => {
|
|
72
|
+
const response = await fetch(url, options);
|
|
73
|
+
return {
|
|
74
|
+
ok: response.ok,
|
|
75
|
+
status: response.status,
|
|
76
|
+
json: async () => response.json(),
|
|
77
|
+
text: async () => response.text()
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const doCreateRoom = async () => {
|
|
83
|
+
console.log('Creating room');
|
|
84
|
+
|
|
85
|
+
if (!connectionProvider) {
|
|
86
|
+
console.log('No OCT Server configured.');
|
|
87
|
+
throw new Error('No OCT Server configured.');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
instance = await createRoom(connectionProvider, options.callbacks);
|
|
91
|
+
if (instance) {
|
|
92
|
+
return instance.roomId;
|
|
93
|
+
}
|
|
94
|
+
throw new Error('Failed to create room');
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const doJoinRoom = async (roomToken: string) => {
|
|
98
|
+
console.log('Joining room', roomToken);
|
|
99
|
+
|
|
100
|
+
if (!connectionProvider) {
|
|
101
|
+
console.log('No OCT Server configured.');
|
|
102
|
+
throw new Error('No OCT Server configured.');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const res = await joinRoom(connectionProvider, options.callbacks, roomToken);
|
|
106
|
+
if (res && 'message' in res) {
|
|
107
|
+
console.log('Failed to join room: ', res.message);
|
|
108
|
+
throw new Error('Failed to join room: ' + res.message);
|
|
109
|
+
} else {
|
|
110
|
+
instance = res;
|
|
111
|
+
return instance.roomId;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const doLogin = async () => {
|
|
116
|
+
if (!connectionProvider) {
|
|
117
|
+
console.log('No OCT Server configured.');
|
|
118
|
+
throw new Error('No OCT Server configured.');
|
|
119
|
+
}
|
|
120
|
+
await login(connectionProvider);
|
|
121
|
+
return connectionProvider.authToken;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const doSetEditor = (editor: monaco.editor.IStandaloneCodeEditor) => {
|
|
125
|
+
if (instance) {
|
|
126
|
+
instance.setEditor(editor);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const doGetUserData = async () => {
|
|
131
|
+
let data: UserData | undefined;
|
|
132
|
+
if (instance) {
|
|
133
|
+
const me: types.Peer = await instance.ownUserData;
|
|
134
|
+
const others = instance.connectedUsers.map(
|
|
135
|
+
user => ({
|
|
136
|
+
peer: user.peer,
|
|
137
|
+
color: user.color ?? 'rgba(0, 0, 0, 0.5)'
|
|
138
|
+
}));
|
|
139
|
+
data = {me, others};
|
|
140
|
+
}
|
|
141
|
+
return data;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const registerUserChangeHandler = (evt: UsersChangeEvent) => {
|
|
145
|
+
if (instance) {
|
|
146
|
+
instance.onUsersChanged(evt);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const doFollowUser = (id?: string) => {
|
|
151
|
+
if (instance) {
|
|
152
|
+
instance.followUser(id);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const doGetFollowedUser = () => {
|
|
157
|
+
if (instance) {
|
|
158
|
+
return instance.following;
|
|
159
|
+
}
|
|
160
|
+
return undefined;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const doSetFileName = (fileName: string) => {
|
|
164
|
+
if (instance) {
|
|
165
|
+
instance.setFileName(fileName);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const doGetWorkspaceName = () => {
|
|
170
|
+
if (instance) {
|
|
171
|
+
return instance.workspaceName;
|
|
172
|
+
}
|
|
173
|
+
return undefined;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const doGetFileName = () => {
|
|
177
|
+
if (instance) {
|
|
178
|
+
return instance.fileName;
|
|
179
|
+
}
|
|
180
|
+
return undefined;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const registerFileNameChangeHandler = (callback: FileNameChangeEvent) => {
|
|
184
|
+
if (instance) {
|
|
185
|
+
instance.onFileNameChange(callback);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const doSetWorkspaceName = (workspaceName: string) => {
|
|
190
|
+
if (instance) {
|
|
191
|
+
instance.workspaceName = workspaceName;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const isLoggedIn = async () => {
|
|
196
|
+
if (!connectionProvider) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (options.useCookieAuth) {
|
|
201
|
+
const valid = await fetch(options.serverUrl + '/api/login/validate', {
|
|
202
|
+
credentials: 'include',
|
|
203
|
+
method: 'POST',
|
|
204
|
+
});
|
|
205
|
+
return valid.ok && (await valid.json())?.valid;
|
|
206
|
+
} else {
|
|
207
|
+
return !!connectionProvider.authToken;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
createRoom: doCreateRoom,
|
|
213
|
+
joinRoom: doJoinRoom,
|
|
214
|
+
leaveRoom: () => instance?.leaveRoom(),
|
|
215
|
+
login: doLogin,
|
|
216
|
+
logout: async () => connectionProvider?.logout(),
|
|
217
|
+
isLoggedIn: isLoggedIn,
|
|
218
|
+
setEditor: doSetEditor,
|
|
219
|
+
getUserData: doGetUserData,
|
|
220
|
+
onUsersChanged: registerUserChangeHandler,
|
|
221
|
+
onFileNameChange: registerFileNameChangeHandler,
|
|
222
|
+
followUser: doFollowUser,
|
|
223
|
+
getFollowedUser: doGetFollowedUser,
|
|
224
|
+
getCurrentConnection: () => instance?.getCurrentConnection(),
|
|
225
|
+
setFileName: doSetFileName,
|
|
226
|
+
getFileName: doGetFileName,
|
|
227
|
+
getWorkspaceName: doGetWorkspaceName,
|
|
228
|
+
setWorkspaceName: doSetWorkspaceName
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function deactivate() {
|
|
234
|
+
instance?.dispose();
|
|
235
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// ******************************************************************************
|
|
2
|
+
// Copyright 2025 TypeFox GmbH
|
|
3
|
+
// This program and the accompanying materials are made available under the
|
|
4
|
+
// terms of the MIT License, which is available in the project root.
|
|
5
|
+
// ******************************************************************************
|
|
6
|
+
|
|
7
|
+
export type { Peer } from '@hereugo/open-collaboration-protocol';
|
|
8
|
+
export type { User } from '@hereugo/open-collaboration-protocol';
|
|
9
|
+
export type { AuthMetadata } from '@hereugo/open-collaboration-protocol';
|