vzcode 0.1.1 → 0.2.1
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 +38 -9
- package/cli.js +51 -22
- package/dist/assets/index-5c2f5f84.css +1 -0
- package/dist/assets/index-5e86a45f.js +89 -0
- package/dist/index.html +2 -2
- package/package.json +15 -13
- package/src/App.jsx +314 -0
- package/src/CodeEditor.jsx +31 -0
- package/src/diff.js +6 -0
- package/src/getOrCreateEditor.js +85 -0
- package/src/json1PresenceBroadcast.js +26 -0
- package/src/json1PresenceDisplay.js +143 -0
- package/src/main.jsx +9 -0
- package/src/ot.js +5 -0
- package/src/randomId.js +2 -0
- package/src/settings.jsx +107 -0
- package/src/style.css +178 -0
- package/dist/assets/index-012a23a1.js +0 -73
- package/dist/assets/index-0b6fa7f4.css +0 -1
package/README.md
CHANGED
|
@@ -1,18 +1,46 @@
|
|
|
1
1
|
# VZCode
|
|
2
2
|
|
|
3
|
-
Multiplayer code editor system
|
|
3
|
+
Multiplayer code editor system | [Kanban](https://github.com/orgs/vizhub-core/projects/2/views/1)
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Features
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
This project is nascent but has enough features to actually use, a minimum viable product (MVP):
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
11
|
+
- Browser-based code editing environment
|
|
12
|
+
- Sidebar listing files from the file system
|
|
13
|
+
- Note: directories are not yet supported
|
|
14
|
+
- Real-time collaboration (multiplayer mode)
|
|
15
|
+
- Invite collaborators over LAN, or
|
|
16
|
+
- Use a service like [NGrok](https://ngrok.com/) for remote collaboration
|
|
17
|
+
- Tabs on the top for managing open files
|
|
18
|
+
- Operatons on files (create file, rename file, delete file)
|
|
19
|
+
- Syntax highlighting for several Web languages
|
|
20
|
+
- Auto-saves changes back to the file system
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
One way to use this is as an editor on your system:
|
|
25
|
+
|
|
26
|
+
- Install with `npm install -g vzcode`
|
|
27
|
+
- Launch with `cd myProject; vzcode`
|
|
28
|
+
|
|
29
|
+
Another way to use this is to set up this editor within your project by introducing a new npm script like this:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"name": "example-project",
|
|
34
|
+
"scripts": {
|
|
35
|
+
"edit": "vzcode"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"vzcode": "^0.1.0"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then run `npm run edit` to start the editor.
|
|
16
44
|
|
|
17
45
|
## Stack
|
|
18
46
|
|
|
@@ -37,6 +65,7 @@ Multiplayer code editor system
|
|
|
37
65
|
- Dogfood the product to identify ways it can be improved
|
|
38
66
|
- Iterate based on feedback
|
|
39
67
|
- Share the project with the world
|
|
68
|
+
- Core component of [VizHub](https://vizhub.com/) next generation editor
|
|
40
69
|
|
|
41
70
|
## Prior Work
|
|
42
71
|
|
package/cli.js
CHANGED
|
@@ -2,13 +2,21 @@
|
|
|
2
2
|
import http from 'http';
|
|
3
3
|
import express from 'express';
|
|
4
4
|
import ShareDB from 'sharedb';
|
|
5
|
-
import json1 from 'ot-json1';
|
|
6
5
|
import { WebSocketServer } from 'ws';
|
|
7
6
|
import WebSocketJSONStream from '@teamwork/websocket-json-stream';
|
|
8
7
|
import fs from 'fs';
|
|
9
8
|
import path from 'path';
|
|
10
9
|
import { fileURLToPath } from 'url';
|
|
10
|
+
import { json1Presence } from './src/ot.js';
|
|
11
|
+
import { randomId } from './src/randomId.js';
|
|
11
12
|
|
|
13
|
+
// The time in milliseconds by which auto-saving is debounced.
|
|
14
|
+
const autoSaveDebounceTimeMS = 800;
|
|
15
|
+
|
|
16
|
+
// Server port.
|
|
17
|
+
const port = 3030;
|
|
18
|
+
|
|
19
|
+
// Use the current working directory to look for files.
|
|
12
20
|
const fullPath = process.cwd();
|
|
13
21
|
|
|
14
22
|
// Isolate files, not directories.
|
|
@@ -18,22 +26,33 @@ const files = fs
|
|
|
18
26
|
.filter((dirent) => dirent.isFile())
|
|
19
27
|
.map((dirent) => dirent.name);
|
|
20
28
|
|
|
29
|
+
// Initialize the document using our data structure for representing files.
|
|
30
|
+
// * Keys are file ids, which are random numbers.
|
|
31
|
+
// * Values are objects with properties:
|
|
32
|
+
// * text - the text content of the file
|
|
33
|
+
// * name - the file name
|
|
21
34
|
const initialDocument = {};
|
|
22
35
|
files.forEach((file) => {
|
|
23
|
-
const id =
|
|
36
|
+
const id = randomId();
|
|
24
37
|
initialDocument[id] = {
|
|
25
38
|
text: fs.readFileSync(file, 'utf-8'),
|
|
26
39
|
name: file,
|
|
27
40
|
};
|
|
28
41
|
});
|
|
29
42
|
|
|
30
|
-
|
|
43
|
+
// Register our custom OT type,
|
|
44
|
+
// because it does not ship with ShareDB.
|
|
45
|
+
ShareDB.types.register(json1Presence.type);
|
|
31
46
|
|
|
32
47
|
const app = express();
|
|
33
|
-
const port = 3030;
|
|
34
48
|
|
|
35
49
|
// Use ShareDB over WebSocket
|
|
36
|
-
const shareDBBackend = new ShareDB(
|
|
50
|
+
const shareDBBackend = new ShareDB({
|
|
51
|
+
// Enable presence
|
|
52
|
+
// See https://github.com/share/sharedb/blob/master/examples/rich-text-presence/server.js#L9
|
|
53
|
+
presence: true,
|
|
54
|
+
doNotForwardSendPresenceErrorsToClient: true,
|
|
55
|
+
});
|
|
37
56
|
const server = http.createServer(app);
|
|
38
57
|
const wss = new WebSocketServer({ server });
|
|
39
58
|
wss.on('connection', (ws) => {
|
|
@@ -50,10 +69,7 @@ app.use(express.static(dir));
|
|
|
50
69
|
// which is a representation of files on disk.
|
|
51
70
|
const shareDBConnection = shareDBBackend.connect();
|
|
52
71
|
const shareDBDoc = shareDBConnection.get('documents', '1');
|
|
53
|
-
shareDBDoc.create(initialDocument,
|
|
54
|
-
|
|
55
|
-
// The time in milliseconds by which auto-saving is debounced.
|
|
56
|
-
const autoSaveDebounceTimeMS = 800;
|
|
72
|
+
shareDBDoc.create(initialDocument, json1Presence.type.uri);
|
|
57
73
|
|
|
58
74
|
// The state of the document when files were last auto-saved.
|
|
59
75
|
let previousDocument = initialDocument;
|
|
@@ -62,28 +78,41 @@ let previousDocument = initialDocument;
|
|
|
62
78
|
const save = () => {
|
|
63
79
|
const currentDocument = shareDBDoc.data;
|
|
64
80
|
|
|
65
|
-
// Take a look at each file
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
81
|
+
// Take a look at each file (key) previously and currently.
|
|
82
|
+
const allKeys = Object.keys({ ...currentDocument, ...previousDocument });
|
|
83
|
+
for (const key of allKeys) {
|
|
84
|
+
const previous = previousDocument[key];
|
|
85
|
+
const current = currentDocument[key];
|
|
86
|
+
|
|
87
|
+
// If this file was neither created nor deleted...
|
|
88
|
+
if (previous && current) {
|
|
89
|
+
// Handle changing of text content.
|
|
90
|
+
if (previous.text !== current.text) {
|
|
91
|
+
fs.writeFileSync(current.name, current.text);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Handle renaming files.
|
|
95
|
+
if (previous.name !== current.name) {
|
|
96
|
+
fs.renamesync(previous.name, current.name);
|
|
97
|
+
}
|
|
71
98
|
}
|
|
72
99
|
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (oldPath !== newPath) {
|
|
77
|
-
fs.renameSync(oldPath, newPath);
|
|
100
|
+
// handle deleting files.
|
|
101
|
+
if (previous && !current) {
|
|
102
|
+
fs.unlinkSync(previous.name);
|
|
78
103
|
}
|
|
79
104
|
|
|
80
|
-
//
|
|
81
|
-
|
|
105
|
+
// Handle creating files.
|
|
106
|
+
if (!previous && current) {
|
|
107
|
+
fs.writeFileSync(current.name, current.text);
|
|
108
|
+
}
|
|
82
109
|
}
|
|
83
110
|
previousDocument = currentDocument;
|
|
84
111
|
};
|
|
85
112
|
|
|
86
113
|
// Listen for when users modify files.
|
|
114
|
+
// Files get written to disk after `autoSaveDebounceTimeMS`
|
|
115
|
+
// milliseconds of inactivity.
|
|
87
116
|
let timeout;
|
|
88
117
|
shareDBDoc.subscribe(() => {
|
|
89
118
|
shareDBDoc.on('op', (op) => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap";@import"https://unpkg.com/boxicons@2.0.7/css/boxicons.min.css";@import"https://unpkg.com/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css";*{margin:0;padding:0;box-sizing:border-box}:root{--main-font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace}body{background:rgb(41,44,52);font-family:Poppins,sans-serif}.tab{width:max-content;height:30px;background:rgb(45,45,45);text-align:center;color:#fff;font-family:var(--main-font-family);justify-content:center;align-items:center;display:flex;padding-left:10px;padding-right:10px;border-top-left-radius:10px;border-top-right-radius:10px;margin-top:5px;margin-left:2px;margin-right:2px;cursor:pointer}.tab.active{background-color:#292c34}.tab-close{display:flex;cursor:pointer;margin-left:5px;justify-content:center;align-items:center}.tab-close:hover{border-radius:30%;background-color:#252526}.editor{position:fixed;margin-left:200px;width:100%;height:100%;top:30px;bottom:0;background-color:#292c34;color:#fff;font-family:var(--main-font-family)}.tab-list{position:fixed;display:flex;flex-direction:row;top:0;left:200px;height:35px;width:100%;background:rgb(37,37,38);z-index:100;transition:all .5s ease}.bottom-bar{position:absolute;bottom:0;left:0;height:20px;width:100%;background:rgb(45,45,45);z-index:100;transition:all .5s ease}.sidebar{position:fixed;top:0;left:0;height:100%;width:200px;background:rgb(54,65,89);z-index:100;transition:all .5s ease}.link-name{margin-top:30px;margin-left:10px;color:#9aa0ac;font-size:12px;text-decoration:none}.file{display:flex;flex-direction:row;align-items:center;justify-content:left;width:180px;cursor:pointer;margin-left:10px;color:#dbdde1;border-radius:5px}.full-Box{width:inherit;height:inherit;padding:7px;display:flex;flex-direction:row;align-items:center;justify-content:space-between}.utilities{justify-content:right;margin-right:3px}.noUtils{display:none}.settings{position:absolute;bottom:0;height:30px;width:180px;z-index:100;color:#dbdde1;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:14px 0;margin:10px;border-radius:5px}.file:hover,.settings:hover{background-color:#4a5469}.newBTN{justify-content:right;margin-right:10px;cursor:pointer}.themes{color:#252526}
|