vzcode 0.4.0 → 0.5.0
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 +26 -0
- package/dist/assets/index-29b6a606.css +1 -0
- package/dist/assets/{index-48dbd5c3.js → index-523ecad7.js} +30 -30
- package/dist/index.html +2 -2
- package/package.json +9 -4
- package/src/{App.jsx → client/App.jsx} +29 -80
- package/src/client/Sidebar/Directory.jsx +62 -0
- package/src/client/Sidebar/DirectoryArrowSVG.jsx +5 -0
- package/src/client/Sidebar/File.jsx +32 -0
- package/src/client/Sidebar/FileOrDirectory.jsx +33 -0
- package/src/client/Sidebar/Item.jsx +43 -0
- package/src/client/Sidebar/index.jsx +64 -0
- package/src/client/Sidebar/styles.css +88 -0
- package/src/client/Sidebar/useOpenDirectories.js +39 -0
- package/src/{diff.js → client/diff.js} +1 -1
- package/src/client/featureFlags.js +3 -0
- package/src/client/getFileTree.js +49 -0
- package/src/{getOrCreateEditor.js → client/getOrCreateEditor.js} +1 -2
- package/src/{json1PresenceDisplay.js → client/json1PresenceDisplay.js} +0 -1
- package/src/{settings.jsx → client/settings.jsx} +1 -1
- package/src/client/sortFileTree.js +15 -0
- package/src/{style.css → client/style.css} +0 -95
- package/src/server/computeInitialDocument.js +69 -0
- package/src/server/featureFlags.js +4 -0
- package/{cli.js → src/server/index.js} +24 -30
- package/dist/assets/index-8d2d4b3b.css +0 -1
- /package/src/{CodeEditor.jsx → client/CodeEditor.jsx} +0 -0
- /package/src/{json1PresenceBroadcast.js → client/json1PresenceBroadcast.js} +0 -0
- /package/src/{main.jsx → client/main.jsx} +0 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Recursively sorts the file tree in-place.
|
|
2
|
+
// Sorts first to group directories before files.
|
|
3
|
+
// Sorts second alphabetically.
|
|
4
|
+
// From https://github.com/vizhub-core/vizhub/blob/main/vizhub-v2/packages/neoFrontend/src/pages/VizPage/Body/Editor/FilesSection/FileTree/sortFileTree.js
|
|
5
|
+
export const sortFileTree = (fileTree) => {
|
|
6
|
+
if (fileTree.children) {
|
|
7
|
+
fileTree.children.sort((a, b) => {
|
|
8
|
+
const aIsFile = a.file ? 1 : 0;
|
|
9
|
+
const bIsFile = b.file ? 1 : 0;
|
|
10
|
+
return aIsFile - bIsFile || a.name.localeCompare(b.name);
|
|
11
|
+
});
|
|
12
|
+
fileTree.children.forEach(sortFileTree);
|
|
13
|
+
}
|
|
14
|
+
return fileTree;
|
|
15
|
+
};
|
|
@@ -115,101 +115,6 @@ body {
|
|
|
115
115
|
transition: all 0.5s ease;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
.sidebar {
|
|
119
|
-
position: fixed;
|
|
120
|
-
top: 0;
|
|
121
|
-
left: 0;
|
|
122
|
-
height: 100%;
|
|
123
|
-
width: 200px;
|
|
124
|
-
background: rgb(54, 65, 89);
|
|
125
|
-
z-index: 100;
|
|
126
|
-
transition: all 0.5s ease;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.link-name {
|
|
130
|
-
margin-top: 30px;
|
|
131
|
-
margin-left: 10px;
|
|
132
|
-
color: rgb(154, 160, 172);
|
|
133
|
-
font-size: 12px;
|
|
134
|
-
text-decoration: none;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.file {
|
|
138
|
-
display: flex;
|
|
139
|
-
flex-direction: row;
|
|
140
|
-
align-items: center;
|
|
141
|
-
justify-content: left;
|
|
142
|
-
width: 180px;
|
|
143
|
-
cursor: pointer;
|
|
144
|
-
margin-left: 10px;
|
|
145
|
-
color: rgb(219, 221, 225);
|
|
146
|
-
border-radius: 5px;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
.full-Box {
|
|
150
|
-
width: inherit;
|
|
151
|
-
height: inherit;
|
|
152
|
-
padding: 7px;
|
|
153
|
-
display: flex;
|
|
154
|
-
flex-direction: row;
|
|
155
|
-
align-items: center;
|
|
156
|
-
justify-content: space-between;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.utilities {
|
|
160
|
-
justify-content: right;
|
|
161
|
-
margin-right: 3px;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/* TODO consistent naming "no-utils" */
|
|
165
|
-
.noUtils {
|
|
166
|
-
display: none;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.settings {
|
|
170
|
-
position: absolute;
|
|
171
|
-
bottom: 0;
|
|
172
|
-
height: 30px;
|
|
173
|
-
/* TODO overhaul CSS to use Flexbox for entire sidebar */
|
|
174
|
-
width: 180px;
|
|
175
|
-
z-index: 100;
|
|
176
|
-
color: rgb(219, 221, 225);
|
|
177
|
-
cursor: pointer;
|
|
178
|
-
display: flex;
|
|
179
|
-
align-items: center;
|
|
180
|
-
justify-content: center;
|
|
181
|
-
padding: 14px 0;
|
|
182
|
-
margin: 10px;
|
|
183
|
-
border-radius: 5px;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
.file:hover,
|
|
187
|
-
.settings:hover {
|
|
188
|
-
background-color: rgb(74, 84, 105);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/* TODO consistent naming new-btn */
|
|
192
|
-
.newBTN {
|
|
193
|
-
justify-content: right;
|
|
194
|
-
margin-right: 10px;
|
|
195
|
-
cursor: pointer;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
118
|
.themes {
|
|
199
119
|
color: rgb(37, 37, 38);
|
|
200
120
|
}
|
|
201
|
-
|
|
202
|
-
.name,
|
|
203
|
-
.hover-name {
|
|
204
|
-
text-overflow: ellipsis;
|
|
205
|
-
white-space: nowrap;
|
|
206
|
-
overflow: hidden;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
.name {
|
|
210
|
-
max-width: 200px;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.hover-name {
|
|
214
|
-
max-width: 130px;
|
|
215
|
-
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { randomId } from '../randomId.js';
|
|
4
|
+
import { enableDirectories, debugDirectories } from './featureFlags.js';
|
|
5
|
+
|
|
6
|
+
const isDirectory = (file) => file.endsWith('/');
|
|
7
|
+
|
|
8
|
+
// Lists files from the file system,
|
|
9
|
+
// converts them into the VZCode internal
|
|
10
|
+
// ShareDB-compatible data structure.
|
|
11
|
+
export const computeInitialDocument = ({ fullPath }) => {
|
|
12
|
+
// Isolate files, not directories.
|
|
13
|
+
// Inspired by https://stackoverflow.com/questions/41472161/fs-readdir-ignore-directories
|
|
14
|
+
|
|
15
|
+
const unsearchedDirectories = [''];
|
|
16
|
+
|
|
17
|
+
// Initialize the document using our data structure for representing files.
|
|
18
|
+
// * Keys are file ids, which are random numbers.
|
|
19
|
+
// * Values are objects with properties:
|
|
20
|
+
// * text - the text content of the file
|
|
21
|
+
// * name - the file name
|
|
22
|
+
const initialDocument = {};
|
|
23
|
+
|
|
24
|
+
// Stack for recursively traversing directories.
|
|
25
|
+
let files = [];
|
|
26
|
+
|
|
27
|
+
while (unsearchedDirectories.length !== 0) {
|
|
28
|
+
const currentDirectory = unsearchedDirectories.pop();
|
|
29
|
+
const currentDirectoryPath = path.join(fullPath, currentDirectory);
|
|
30
|
+
const newFiles = fs
|
|
31
|
+
.readdirSync(currentDirectoryPath, { withFileTypes: true })
|
|
32
|
+
.filter((dirent) => (enableDirectories ? true : dirent.isFile()))
|
|
33
|
+
|
|
34
|
+
// Add a trailing slash for directories
|
|
35
|
+
.map((dirent) => {
|
|
36
|
+
const relativePath =
|
|
37
|
+
currentDirectory === ''
|
|
38
|
+
? dirent.name
|
|
39
|
+
: currentDirectory + '/' + dirent.name;
|
|
40
|
+
|
|
41
|
+
if (dirent.isFile()) {
|
|
42
|
+
return relativePath;
|
|
43
|
+
}
|
|
44
|
+
unsearchedDirectories.push(relativePath);
|
|
45
|
+
return relativePath + '/';
|
|
46
|
+
});
|
|
47
|
+
// console.log(currentDirectory);
|
|
48
|
+
|
|
49
|
+
files = [...files, ...newFiles];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
files.forEach((file) => {
|
|
53
|
+
const id = randomId();
|
|
54
|
+
initialDocument[id] = {
|
|
55
|
+
text: isDirectory(file)
|
|
56
|
+
? null
|
|
57
|
+
: fs.readFileSync(path.join(fullPath, file), 'utf-8'),
|
|
58
|
+
name: file,
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (debugDirectories) {
|
|
63
|
+
console.log('files:');
|
|
64
|
+
console.log(files);
|
|
65
|
+
console.log('initialDocument:');
|
|
66
|
+
console.log(JSON.stringify(initialDocument, null, 2));
|
|
67
|
+
}
|
|
68
|
+
return initialDocument;
|
|
69
|
+
};
|
|
@@ -7,9 +7,13 @@ import WebSocketJSONStream from '@teamwork/websocket-json-stream';
|
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import { fileURLToPath } from 'url';
|
|
10
|
-
import { json1Presence } from './src/ot.js';
|
|
11
|
-
import { randomId } from './src/randomId.js';
|
|
12
10
|
import open from 'open';
|
|
11
|
+
import ngrok from 'ngrok';
|
|
12
|
+
import dotenv from 'dotenv';
|
|
13
|
+
import { computeInitialDocument } from './computeInitialDocument.js';
|
|
14
|
+
import { json1Presence } from '../ot.js';
|
|
15
|
+
|
|
16
|
+
dotenv.config({ path: '../../.env' });
|
|
13
17
|
|
|
14
18
|
// The time in milliseconds by which auto-saving is debounced.
|
|
15
19
|
const autoSaveDebounceTimeMS = 800;
|
|
@@ -17,28 +21,9 @@ const autoSaveDebounceTimeMS = 800;
|
|
|
17
21
|
// Server port.
|
|
18
22
|
const port = 3030;
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// Isolate files, not directories.
|
|
24
|
-
// Inspired by https://stackoverflow.com/questions/41472161/fs-readdir-ignore-directories
|
|
25
|
-
const files = fs
|
|
26
|
-
.readdirSync(fullPath, { withFileTypes: true })
|
|
27
|
-
.filter((dirent) => dirent.isFile())
|
|
28
|
-
.map((dirent) => dirent.name);
|
|
29
|
-
|
|
30
|
-
// Initialize the document using our data structure for representing files.
|
|
31
|
-
// * Keys are file ids, which are random numbers.
|
|
32
|
-
// * Values are objects with properties:
|
|
33
|
-
// * text - the text content of the file
|
|
34
|
-
// * name - the file name
|
|
35
|
-
const initialDocument = {};
|
|
36
|
-
files.forEach((file) => {
|
|
37
|
-
const id = randomId();
|
|
38
|
-
initialDocument[id] = {
|
|
39
|
-
text: fs.readFileSync(file, 'utf-8'),
|
|
40
|
-
name: file,
|
|
41
|
-
};
|
|
24
|
+
const initialDocument = computeInitialDocument({
|
|
25
|
+
// Use the current working directory to look for files.
|
|
26
|
+
fullPath: process.cwd(),
|
|
42
27
|
});
|
|
43
28
|
|
|
44
29
|
// Register our custom OT type,
|
|
@@ -66,9 +51,9 @@ wss.on('connection', (ws) => {
|
|
|
66
51
|
});
|
|
67
52
|
|
|
68
53
|
// Serve static files
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
const dir = path.join(
|
|
54
|
+
const filename = fileURLToPath(import.meta.url);
|
|
55
|
+
const dirname = path.dirname(filename);
|
|
56
|
+
const dir = path.join(dirname, '..', '..', 'dist');
|
|
72
57
|
app.use(express.static(dir));
|
|
73
58
|
|
|
74
59
|
// Create the initial "document",
|
|
@@ -127,7 +112,16 @@ shareDBDoc.subscribe(() => {
|
|
|
127
112
|
});
|
|
128
113
|
});
|
|
129
114
|
|
|
130
|
-
server.listen(port, () => {
|
|
131
|
-
|
|
132
|
-
|
|
115
|
+
server.listen(port, async () => {
|
|
116
|
+
if (process.env.NGROK_TOKEN) {
|
|
117
|
+
(async function () {
|
|
118
|
+
await ngrok.authtoken(process.env.NGROK_TOKEN);
|
|
119
|
+
const url = await ngrok.connect(port);
|
|
120
|
+
console.log(`Editor is live at ${url}`);
|
|
121
|
+
open(url);
|
|
122
|
+
})();
|
|
123
|
+
} else {
|
|
124
|
+
console.log(`Editor is live at http://localhost:${port}`);
|
|
125
|
+
open(`http://localhost:${port}`);
|
|
126
|
+
}
|
|
133
127
|
});
|
|
@@ -1 +0,0 @@
|
|
|
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}::-webkit-scrollbar{width:10px;height:20px}::-webkit-scrollbar-track{background:rgb(41,44,52)}::-webkit-scrollbar-thumb{background:rgb(37,37,38)}::-webkit-scrollbar-thumb:hover{background:#555}: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;overflow-x:hidden}.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:relative;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}.name,.hover-name{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.name{max-width:200px}.hover-name{max-width:130px}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|