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.
@@ -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
+ };
@@ -0,0 +1,4 @@
1
+ // Feature flag for directories, disabled until it's fully working.
2
+ export const enableDirectories = true;
3
+
4
+ export const debugDirectories = false;
@@ -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
- // Use the current working directory to look for files.
21
- const fullPath = process.cwd();
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 __filename = fileURLToPath(import.meta.url);
70
- const __dirname = path.dirname(__filename);
71
- const dir = path.join(__dirname, '/dist');
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
- console.log(`Editor is live at http://localhost:${port}`);
132
- open(`http://localhost:${port}`);
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