vzcode 0.43.0 → 0.45.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/dist/assets/index-iQRmsAKc.css +1 -0
- package/dist/assets/{index-WNLp-4Rf.js → index-pub_uACz.js} +44 -44
- package/dist/assets/worker-fboacSft.js +234 -0
- package/dist/index.html +2 -2
- package/package.json +12 -11
- package/src/client/CodeEditor/getOrCreateEditor.ts +14 -1
- package/src/client/VZCodeContext.tsx +5 -2
- package/src/client/VZLeft.tsx +2 -1
- package/src/client/VZMiddle.tsx +2 -2
- package/src/client/index.ts +1 -0
- package/src/client/usePrettier/index.ts +98 -30
- package/src/index.ts +1 -0
- package/src/server/computeInitialDocument.js +122 -13
- package/src/server/config.js +24 -0
- package/src/server/featureFlags.js +7 -1
- package/src/server/index.js +1 -3
- package/src/server/setupEnv.js +18 -0
- package/src/types.ts +3 -3
- package/dist/assets/index-vD_kE3p2.css +0 -1
- package/dist/assets/worker-pzCcKLRg.js +0 -237
package/dist/index.html
CHANGED
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
href="https://unpkg.com/boxicons@2.0.7/css/boxicons.min.css"
|
|
25
25
|
rel="stylesheet"
|
|
26
26
|
/>
|
|
27
|
-
<script type="module" crossorigin src="/assets/index-
|
|
28
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
27
|
+
<script type="module" crossorigin src="/assets/index-pub_uACz.js"></script>
|
|
28
|
+
<link rel="stylesheet" crossorigin href="/assets/index-iQRmsAKc.css">
|
|
29
29
|
</head>
|
|
30
30
|
<body>
|
|
31
31
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vzcode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"description": "Multiplayer code editor system",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -71,11 +71,11 @@
|
|
|
71
71
|
"@codemirror/lang-markdown": "^6.2.3",
|
|
72
72
|
"@codemirror/state": "^6.3.3",
|
|
73
73
|
"@codemirror/theme-one-dark": "^6.1.2",
|
|
74
|
-
"@codemirror/view": "^6.22.
|
|
74
|
+
"@codemirror/view": "^6.22.3",
|
|
75
75
|
"@lezer/highlight": "^1.2.0",
|
|
76
76
|
"@replit/codemirror-indentation-markers": "^6.5.0",
|
|
77
77
|
"@replit/codemirror-interact": "^6.3.0",
|
|
78
|
-
"@replit/codemirror-minimap": "^0.5.
|
|
78
|
+
"@replit/codemirror-minimap": "^0.5.2",
|
|
79
79
|
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
|
80
80
|
"@teamwork/websocket-json-stream": "^2.0.0",
|
|
81
81
|
"@typescript/vfs": "^1.5.0",
|
|
@@ -96,26 +96,27 @@
|
|
|
96
96
|
"diff-match-patch": "^1.0.5",
|
|
97
97
|
"dotenv": "^16.3.1",
|
|
98
98
|
"express": "^4.18.2",
|
|
99
|
+
"ignore": "^5.3.0",
|
|
99
100
|
"json0-ot-diff": "^1.1.2",
|
|
100
101
|
"ngrok": "^4.3.3",
|
|
101
|
-
"open": "^
|
|
102
|
-
"openai": "^4.
|
|
102
|
+
"open": "^10.0.0",
|
|
103
|
+
"openai": "^4.24.0",
|
|
103
104
|
"react": "^18.2.0",
|
|
104
105
|
"react-bootstrap": "^2.9.1",
|
|
105
106
|
"react-dom": "^18.2.0",
|
|
106
107
|
"sharedb": "^4.1.2",
|
|
107
108
|
"sharedb-client-browser": "^4.4.0",
|
|
108
|
-
"vizhub-ui": "^3.
|
|
109
|
-
"ws": "^8.15.
|
|
109
|
+
"vizhub-ui": "^3.14.0",
|
|
110
|
+
"ws": "^8.15.1"
|
|
110
111
|
},
|
|
111
112
|
"devDependencies": {
|
|
112
|
-
"@types/react": "^18.2.
|
|
113
|
-
"@types/react-dom": "^18.2.
|
|
113
|
+
"@types/react": "^18.2.45",
|
|
114
|
+
"@types/react-dom": "^18.2.18",
|
|
114
115
|
"@vitejs/plugin-react": "^4.2.1",
|
|
115
116
|
"prettier": "^3.1.1",
|
|
116
117
|
"sass": "^1.69.5",
|
|
117
118
|
"typescript": "^5.3.3",
|
|
118
|
-
"vite": "^5.0.
|
|
119
|
-
"vitest": "^1.0
|
|
119
|
+
"vite": "^5.0.10",
|
|
120
|
+
"vitest": "^1.1.0"
|
|
120
121
|
}
|
|
121
122
|
}
|
|
@@ -294,7 +294,20 @@ export const getOrCreateEditor = ({
|
|
|
294
294
|
|
|
295
295
|
// VSCode keybindings
|
|
296
296
|
// See https://github.com/replit/codemirror-vscode-keymap#usage
|
|
297
|
-
extensions.push(keymap.of(vscodeKeymap));
|
|
297
|
+
// extensions.push(keymap.of(vscodeKeymap));
|
|
298
|
+
extensions.push(
|
|
299
|
+
keymap.of(
|
|
300
|
+
vscodeKeymap.map((binding) => {
|
|
301
|
+
// Here we override the Shift+Enter behavior specifically,
|
|
302
|
+
// as that can be used to trigger a manual save/Prettier,
|
|
303
|
+
// and the default behavior from the keymap interferes.
|
|
304
|
+
if (binding.key === 'Enter') {
|
|
305
|
+
delete binding.shift;
|
|
306
|
+
}
|
|
307
|
+
return binding;
|
|
308
|
+
}),
|
|
309
|
+
),
|
|
310
|
+
);
|
|
298
311
|
|
|
299
312
|
const editor = new EditorView({
|
|
300
313
|
state: EditorState.create({
|
|
@@ -94,6 +94,7 @@ export const VZCodeProvider = ({
|
|
|
94
94
|
initialUsername,
|
|
95
95
|
children,
|
|
96
96
|
codeError = null,
|
|
97
|
+
enableManualPretter = false,
|
|
97
98
|
}: {
|
|
98
99
|
content: VZCodeContent;
|
|
99
100
|
shareDBDoc: ShareDBDoc<VZCodeContent>;
|
|
@@ -107,17 +108,19 @@ export const VZCodeProvider = ({
|
|
|
107
108
|
initialUsername: Username;
|
|
108
109
|
children: React.ReactNode;
|
|
109
110
|
codeError?: string | null;
|
|
111
|
+
enableManualPretter?: boolean;
|
|
110
112
|
}) => {
|
|
111
113
|
// Auto-run Pretter after local changes.
|
|
112
114
|
const {
|
|
113
115
|
prettierError,
|
|
114
116
|
}: {
|
|
115
117
|
prettierError: string | null;
|
|
116
|
-
} = usePrettier(
|
|
118
|
+
} = usePrettier({
|
|
117
119
|
shareDBDoc,
|
|
118
120
|
submitOperation,
|
|
119
121
|
prettierWorker,
|
|
120
|
-
|
|
122
|
+
enableManualPretter,
|
|
123
|
+
});
|
|
121
124
|
|
|
122
125
|
// The error message shows either:
|
|
123
126
|
// * `prettierError` - errors from Prettier, client-side only
|
package/src/client/VZLeft.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import { VZCodeContext } from './VZCodeContext';
|
|
|
6
6
|
// The middle portion of the VZCode environment, containing:
|
|
7
7
|
// * The sidebar
|
|
8
8
|
// * The settings modal
|
|
9
|
-
export const VZLeft = () => {
|
|
9
|
+
export const VZLeft = ({ enableUsernameField = true }) => {
|
|
10
10
|
// TODO leverage this context in deeper levels of the component tree.
|
|
11
11
|
const {
|
|
12
12
|
files,
|
|
@@ -50,6 +50,7 @@ export const VZLeft = () => {
|
|
|
50
50
|
setTheme={setTheme}
|
|
51
51
|
username={username}
|
|
52
52
|
setUsername={setUsername}
|
|
53
|
+
enableUsernameField={enableUsernameField}
|
|
53
54
|
/>
|
|
54
55
|
</div>
|
|
55
56
|
);
|
package/src/client/VZMiddle.tsx
CHANGED
|
@@ -13,7 +13,7 @@ import { VZCodeContext } from './VZCodeContext';
|
|
|
13
13
|
// * The code error overlay
|
|
14
14
|
// * The presence notifications
|
|
15
15
|
// * The UI for AI Assist
|
|
16
|
-
export const VZMiddle = () => {
|
|
16
|
+
export const VZMiddle = ({ enableAIAssist = true }) => {
|
|
17
17
|
const { codeEditorWidth } = useContext(
|
|
18
18
|
SplitPaneResizeContext,
|
|
19
19
|
);
|
|
@@ -78,7 +78,7 @@ export const VZMiddle = () => {
|
|
|
78
78
|
docPresence={docPresence}
|
|
79
79
|
localPresence={localPresence}
|
|
80
80
|
/>
|
|
81
|
-
{content && activeFileId ? (
|
|
81
|
+
{enableAIAssist && content && activeFileId ? (
|
|
82
82
|
<AIAssistWidget
|
|
83
83
|
activeFileId={activeFileId}
|
|
84
84
|
shareDBDoc={shareDBDoc}
|
package/src/client/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JSONOp } from 'ot
|
|
1
|
+
import { JSONOp } from '../../ot';
|
|
2
2
|
import {
|
|
3
3
|
FileId,
|
|
4
4
|
ShareDBDoc,
|
|
@@ -20,16 +20,58 @@ const extension = (fileName: string) => {
|
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
export const
|
|
24
|
-
|
|
23
|
+
// export const shouldTriggerRun = (event: KeyboardEvent) => {
|
|
24
|
+
// if(event.ctrlKey && event.key === 's') {
|
|
25
|
+
// return true;
|
|
26
|
+
// }
|
|
27
|
+
// }
|
|
28
|
+
|
|
29
|
+
export const shouldTriggerRun = (event: KeyboardEvent) => {
|
|
30
|
+
const isMac = /Mac|iMac|MacBook/i.test(
|
|
31
|
+
navigator.userAgent,
|
|
32
|
+
);
|
|
33
|
+
const ctrlOrCmd = isMac ? event.metaKey : event.ctrlKey;
|
|
34
|
+
|
|
35
|
+
if (ctrlOrCmd && event.key === 's') {
|
|
36
|
+
// Save
|
|
37
|
+
return true;
|
|
38
|
+
} else if (
|
|
39
|
+
event.key === 'F5' || // Run (F5)
|
|
40
|
+
(ctrlOrCmd && event.key === 'Enter') || // Ctrl+Enter or Cmd+Enter
|
|
41
|
+
(event.shiftKey && event.key === 'Enter') || // Shift+Enter
|
|
42
|
+
// Let's not override the browser's default behavior for refresh page.
|
|
43
|
+
// (ctrlOrCmd && event.key === 'r') || // Ctrl+R or Cmd+R
|
|
44
|
+
|
|
45
|
+
(event.altKey && event.key === 'Enter') || // Alt+Enter
|
|
46
|
+
(ctrlOrCmd &&
|
|
47
|
+
event.shiftKey &&
|
|
48
|
+
(event.key === 'B' || event.key === 'b')) || // Ctrl+Shift+B or Cmd+Shift+B
|
|
49
|
+
(ctrlOrCmd && event.shiftKey && event.key === 'F10') || // Ctrl+Shift+F10 or Cmd+Shift+F10
|
|
50
|
+
event.key === 'F8' ||
|
|
51
|
+
event.key === 'F9'
|
|
52
|
+
) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const usePrettier = ({
|
|
59
|
+
shareDBDoc,
|
|
60
|
+
submitOperation,
|
|
61
|
+
prettierWorker,
|
|
62
|
+
enableManualPretter,
|
|
63
|
+
}: {
|
|
64
|
+
shareDBDoc: ShareDBDoc<VZCodeContent> | null;
|
|
25
65
|
submitOperation: (
|
|
26
66
|
next: (content: VZCodeContent) => VZCodeContent,
|
|
27
|
-
) => void
|
|
28
|
-
prettierWorker: Worker
|
|
29
|
-
|
|
67
|
+
) => void;
|
|
68
|
+
prettierWorker: Worker;
|
|
69
|
+
enableManualPretter: boolean;
|
|
70
|
+
}) => {
|
|
30
71
|
// The set of files that have been modified
|
|
31
72
|
// since the last Prettier run.
|
|
32
|
-
const dirtyFiles: Set<FileId> = new Set<FileId>();
|
|
73
|
+
// const dirtyFiles: Set<FileId> = new Set<FileId>();
|
|
74
|
+
const dirtyFilesRef = useRef<Set<FileId>>(new Set());
|
|
33
75
|
|
|
34
76
|
// State to hold the error from Prettier
|
|
35
77
|
// `null` means no errors
|
|
@@ -95,6 +137,9 @@ export const usePrettier = (
|
|
|
95
137
|
// Get the files
|
|
96
138
|
const files = content.files;
|
|
97
139
|
|
|
140
|
+
// Get the set of dirty files
|
|
141
|
+
const dirtyFiles = dirtyFilesRef.current;
|
|
142
|
+
|
|
98
143
|
// Get the dirty files
|
|
99
144
|
const dirtyFileIds = Array.from(dirtyFiles);
|
|
100
145
|
|
|
@@ -118,20 +163,35 @@ export const usePrettier = (
|
|
|
118
163
|
}
|
|
119
164
|
};
|
|
120
165
|
|
|
121
|
-
|
|
166
|
+
let debouncePrettier;
|
|
122
167
|
let debounceTimeout;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
168
|
+
let handleKeyDown;
|
|
169
|
+
if (enableManualPretter) {
|
|
170
|
+
handleKeyDown = (event: KeyboardEvent) => {
|
|
171
|
+
if (shouldTriggerRun(event)) {
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
runPrettier();
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
177
|
+
} else {
|
|
178
|
+
// Function to debounce running Prettier
|
|
179
|
+
|
|
180
|
+
debouncePrettier = () => {
|
|
181
|
+
clearTimeout(debounceTimeout);
|
|
182
|
+
debounceTimeout = setTimeout(
|
|
183
|
+
runPrettier,
|
|
184
|
+
autoPrettierDebounceTimeMS,
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
//Stops prettier from running when you perform a mouse move or a keydown
|
|
188
|
+
window.addEventListener(
|
|
189
|
+
'mousemove',
|
|
190
|
+
debouncePrettier,
|
|
128
191
|
);
|
|
192
|
+
window.addEventListener('keydown', debouncePrettier);
|
|
129
193
|
}
|
|
130
194
|
|
|
131
|
-
//Stops prettier from running when you perform a mouse move or a keydown
|
|
132
|
-
window.addEventListener('mousemove', debouncePrettier);
|
|
133
|
-
window.addEventListener('keydown', debouncePrettier);
|
|
134
|
-
|
|
135
195
|
// Listen for changes
|
|
136
196
|
shareDBDoc.on(
|
|
137
197
|
'op batch',
|
|
@@ -170,11 +230,12 @@ export const usePrettier = (
|
|
|
170
230
|
const fileId: FileId = op[1];
|
|
171
231
|
|
|
172
232
|
// Add the file id to the set of dirty files
|
|
173
|
-
|
|
233
|
+
dirtyFilesRef.current.add(fileId);
|
|
174
234
|
|
|
175
235
|
// Debounce running Prettier
|
|
176
|
-
|
|
177
|
-
|
|
236
|
+
if (!enableManualPretter) {
|
|
237
|
+
debouncePrettier();
|
|
238
|
+
}
|
|
178
239
|
// console.log('Op is for file', fileId);
|
|
179
240
|
}
|
|
180
241
|
} else {
|
|
@@ -190,17 +251,24 @@ export const usePrettier = (
|
|
|
190
251
|
'message',
|
|
191
252
|
handleMessage,
|
|
192
253
|
);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
254
|
+
if (enableManualPretter) {
|
|
255
|
+
document.removeEventListener(
|
|
256
|
+
'keydown',
|
|
257
|
+
handleKeyDown,
|
|
258
|
+
);
|
|
259
|
+
} else {
|
|
260
|
+
window.removeEventListener(
|
|
261
|
+
'keydown',
|
|
262
|
+
debouncePrettier,
|
|
263
|
+
);
|
|
264
|
+
window.removeEventListener(
|
|
265
|
+
'mousemove',
|
|
266
|
+
debouncePrettier,
|
|
267
|
+
);
|
|
201
268
|
|
|
202
|
-
|
|
203
|
-
|
|
269
|
+
// Clear the timeout
|
|
270
|
+
clearTimeout(debounceTimeout);
|
|
271
|
+
}
|
|
204
272
|
};
|
|
205
273
|
}, [shareDBDoc]);
|
|
206
274
|
return { prettierError }; // Return the errors for use in other files
|
package/src/index.ts
CHANGED
|
@@ -4,10 +4,68 @@ import { randomId } from '../randomId.js';
|
|
|
4
4
|
import {
|
|
5
5
|
enableDirectories,
|
|
6
6
|
debugDirectories,
|
|
7
|
+
debugIgnore,
|
|
7
8
|
} from './featureFlags.js';
|
|
9
|
+
import createIgnore from 'ignore';
|
|
10
|
+
import { ignoreFilePattern, baseIgnore } from './config.js';
|
|
8
11
|
|
|
9
12
|
const isDirectory = (file) => file.endsWith('/');
|
|
10
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} fullPath - absolut path of the workspace root
|
|
16
|
+
* @param {string} currentDirectoryPath - path where the ignore file is found, relative to fullPath
|
|
17
|
+
* @param {string} fileName - name of the ignore file
|
|
18
|
+
* @returns {string[]} parsed lines
|
|
19
|
+
*/
|
|
20
|
+
const parseIgnoreFile = (
|
|
21
|
+
fullPath,
|
|
22
|
+
currentDirectory,
|
|
23
|
+
fileName,
|
|
24
|
+
) => {
|
|
25
|
+
const filePath = path.join(
|
|
26
|
+
fullPath,
|
|
27
|
+
currentDirectory,
|
|
28
|
+
fileName,
|
|
29
|
+
);
|
|
30
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
31
|
+
const globs = content
|
|
32
|
+
.split(/[\n\r]+/)
|
|
33
|
+
.filter(
|
|
34
|
+
// remove blank line and comments
|
|
35
|
+
(line) => line.length > 0 && !line.startsWith('#'),
|
|
36
|
+
)
|
|
37
|
+
.map((line) => {
|
|
38
|
+
const { bang, slash, glob } = line.match(
|
|
39
|
+
/^(?<bang>!?)(?<slash>\/?)(?<glob>.*)$/,
|
|
40
|
+
).groups;
|
|
41
|
+
const hasSlash =
|
|
42
|
+
Boolean(slash) || /\/.*\S/.test(glob);
|
|
43
|
+
const relativeGlob = path.posix.join(
|
|
44
|
+
currentDirectory.replace(
|
|
45
|
+
// escape characters with special meaning in glob expressions
|
|
46
|
+
/[*?!# \[\]\\]/g,
|
|
47
|
+
(char) => '\\' + char,
|
|
48
|
+
),
|
|
49
|
+
// a pattern that doesn't include a slash (not counting a trailing one) matches files in any descendant directory od the current one
|
|
50
|
+
hasSlash ? '' : '**',
|
|
51
|
+
glob,
|
|
52
|
+
);
|
|
53
|
+
// preserve leading `!` and `/` characters
|
|
54
|
+
return bang + slash + relativeGlob;
|
|
55
|
+
});
|
|
56
|
+
if (debugIgnore) {
|
|
57
|
+
console.debug(
|
|
58
|
+
'at',
|
|
59
|
+
currentDirectory,
|
|
60
|
+
'parsing',
|
|
61
|
+
fileName,
|
|
62
|
+
'obtained globs',
|
|
63
|
+
globs,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
return globs;
|
|
67
|
+
};
|
|
68
|
+
|
|
11
69
|
// Lists files from the file system,
|
|
12
70
|
// converts them into the VZCode internal
|
|
13
71
|
// ShareDB-compatible data structure.
|
|
@@ -15,8 +73,6 @@ export const computeInitialDocument = ({ fullPath }) => {
|
|
|
15
73
|
// Isolate files, not directories.
|
|
16
74
|
// Inspired by https://stackoverflow.com/questions/41472161/fs-readdir-ignore-directories
|
|
17
75
|
|
|
18
|
-
const unsearchedDirectories = [''];
|
|
19
|
-
|
|
20
76
|
// Initialize the document using our data structure for representing files.
|
|
21
77
|
const initialDocument = {
|
|
22
78
|
// `files`
|
|
@@ -34,39 +90,92 @@ export const computeInitialDocument = ({ fullPath }) => {
|
|
|
34
90
|
isInteracting: false,
|
|
35
91
|
};
|
|
36
92
|
|
|
37
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Stack for recursively traversing directories.
|
|
95
|
+
* @type {string[]}
|
|
96
|
+
*/
|
|
38
97
|
let files = [];
|
|
39
98
|
|
|
99
|
+
const ignoreFileMatcher = createIgnore().add(
|
|
100
|
+
ignoreFilePattern,
|
|
101
|
+
);
|
|
102
|
+
const isIgnoreFile = (fileName) =>
|
|
103
|
+
ignoreFileMatcher.ignores(fileName);
|
|
104
|
+
|
|
105
|
+
const unsearchedDirectories = [
|
|
106
|
+
{
|
|
107
|
+
currentDirectory: '.',
|
|
108
|
+
ignore: createIgnore().add(baseIgnore),
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
|
|
40
112
|
while (unsearchedDirectories.length !== 0) {
|
|
41
|
-
const currentDirectory =
|
|
113
|
+
const { currentDirectory, ignore: parentIgnore } =
|
|
114
|
+
unsearchedDirectories.pop();
|
|
42
115
|
const currentDirectoryPath = path.join(
|
|
43
116
|
fullPath,
|
|
44
117
|
currentDirectory,
|
|
45
118
|
);
|
|
46
|
-
const
|
|
119
|
+
const dirEntries = fs
|
|
47
120
|
.readdirSync(currentDirectoryPath, {
|
|
48
121
|
withFileTypes: true,
|
|
49
122
|
})
|
|
50
123
|
.filter((dirent) =>
|
|
51
124
|
enableDirectories ? true : dirent.isFile(),
|
|
125
|
+
);
|
|
126
|
+
// find .ignore or .gitignore files in the current directory
|
|
127
|
+
const ignoreFiles = dirEntries
|
|
128
|
+
.filter(
|
|
129
|
+
(dirent) =>
|
|
130
|
+
dirent.isFile() && isIgnoreFile(dirent.name),
|
|
52
131
|
)
|
|
53
|
-
|
|
132
|
+
.map((file) => file.name);
|
|
133
|
+
let ignore = parentIgnore;
|
|
134
|
+
if (ignoreFiles.length > 0) {
|
|
135
|
+
const globs = ignoreFiles.flatMap((fileName) =>
|
|
136
|
+
parseIgnoreFile(
|
|
137
|
+
fullPath,
|
|
138
|
+
currentDirectory,
|
|
139
|
+
fileName,
|
|
140
|
+
),
|
|
141
|
+
);
|
|
142
|
+
ignore = createIgnore().add(parentIgnore).add(globs);
|
|
143
|
+
}
|
|
144
|
+
const newFiles = dirEntries
|
|
145
|
+
.filter((dirent) => {
|
|
146
|
+
const relativePath =
|
|
147
|
+
path.posix.join(currentDirectory, dirent.name) +
|
|
148
|
+
(dirent.isDirectory() ? '/' : '');
|
|
149
|
+
const keep = !ignore.ignores(relativePath);
|
|
150
|
+
if (debugIgnore && !keep) {
|
|
151
|
+
console.debug(
|
|
152
|
+
'at',
|
|
153
|
+
currentDirectory,
|
|
154
|
+
'ignoring',
|
|
155
|
+
relativePath,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return keep;
|
|
159
|
+
})
|
|
54
160
|
// Add a trailing slash for directories
|
|
55
161
|
.map((dirent) => {
|
|
56
|
-
const relativePath =
|
|
57
|
-
currentDirectory
|
|
58
|
-
|
|
59
|
-
|
|
162
|
+
const relativePath = path.posix.join(
|
|
163
|
+
currentDirectory,
|
|
164
|
+
dirent.name,
|
|
165
|
+
);
|
|
60
166
|
|
|
61
|
-
if (dirent.
|
|
167
|
+
if (!dirent.isDirectory()) {
|
|
62
168
|
return relativePath;
|
|
63
169
|
}
|
|
64
|
-
unsearchedDirectories.push(
|
|
170
|
+
unsearchedDirectories.push({
|
|
171
|
+
currentDirectory: relativePath,
|
|
172
|
+
ignore,
|
|
173
|
+
});
|
|
65
174
|
return relativePath + '/';
|
|
66
175
|
});
|
|
67
176
|
// console.log(currentDirectory);
|
|
68
177
|
|
|
69
|
-
files
|
|
178
|
+
files.push(...newFiles);
|
|
70
179
|
}
|
|
71
180
|
|
|
72
181
|
files.forEach((file) => {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*patterns describing which files should e comsidered as ignore files when creating the file tree.
|
|
3
|
+
*
|
|
4
|
+
* Format: one pattern per line, in gitignore format as defimed by https://git-scm.com/docs/gitignore#_pattern_format
|
|
5
|
+
*/
|
|
6
|
+
export const ignoreFilePattern =
|
|
7
|
+
process.env.IGNORE_FILE_PATTERN ??
|
|
8
|
+
`
|
|
9
|
+
.vzignore
|
|
10
|
+
.ignore
|
|
11
|
+
.gitignore
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* the base ignore patterns, that should be applied before any ignore file found when creating the file tree.
|
|
16
|
+
*
|
|
17
|
+
* These patterns apply even in the absence of an ignore file.
|
|
18
|
+
*/
|
|
19
|
+
export const baseIgnore =
|
|
20
|
+
process.env.BASE_IGNORE ??
|
|
21
|
+
`
|
|
22
|
+
.git/
|
|
23
|
+
node_modules/
|
|
24
|
+
`;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
// Feature flag for directories, disabled until it's fully working.
|
|
2
2
|
export const enableDirectories = true;
|
|
3
3
|
|
|
4
|
-
export const debugDirectories =
|
|
4
|
+
export const debugDirectories = Boolean(
|
|
5
|
+
process.env.DEBUG_FILE_TREE,
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
export const debugIgnore = Boolean(
|
|
9
|
+
process.env.DEBUG_IGNORE,
|
|
10
|
+
);
|
package/src/server/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import './setupEnv.js';
|
|
2
3
|
import http from 'http';
|
|
3
4
|
import express from 'express';
|
|
4
5
|
import ShareDB from 'sharedb';
|
|
@@ -9,7 +10,6 @@ import path from 'path';
|
|
|
9
10
|
import { fileURLToPath } from 'url';
|
|
10
11
|
import open from 'open';
|
|
11
12
|
import ngrok from 'ngrok';
|
|
12
|
-
import dotenv from 'dotenv';
|
|
13
13
|
import bodyParser from 'body-parser';
|
|
14
14
|
import { json1Presence } from '../ot.js';
|
|
15
15
|
import { computeInitialDocument } from './computeInitialDocument.js';
|
|
@@ -21,8 +21,6 @@ import {
|
|
|
21
21
|
|
|
22
22
|
const { replaceOp } = json1Presence;
|
|
23
23
|
|
|
24
|
-
dotenv.config({ path: '../../.env' });
|
|
25
|
-
|
|
26
24
|
// The time in milliseconds by which auto-saving is debounced.
|
|
27
25
|
const autoSaveDebounceTimeMS = 800;
|
|
28
26
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
const dir = fileURLToPath(import.meta.url);
|
|
6
|
+
dotenv.config({
|
|
7
|
+
path: join(dir, '../../../.env'),
|
|
8
|
+
});
|
|
9
|
+
if (process.env.DEBUG_SETUP_ENV) {
|
|
10
|
+
console.log(
|
|
11
|
+
'environment variables set up',
|
|
12
|
+
Object.fromEntries(
|
|
13
|
+
Object.entries(process.env).filter(
|
|
14
|
+
(entry) => !entry[0].endsWith('PATH'),
|
|
15
|
+
),
|
|
16
|
+
),
|
|
17
|
+
);
|
|
18
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
// FileId
|
|
2
2
|
// * A unique ID for a file.
|
|
3
3
|
|
|
4
|
-
import { JSONOp } from 'ot-json1';
|
|
5
|
-
|
|
6
4
|
// * This is a random string.
|
|
7
5
|
export type FileId = string;
|
|
8
6
|
|
|
@@ -60,6 +58,8 @@ export interface FileTreeFile {
|
|
|
60
58
|
fileId: FileId;
|
|
61
59
|
}
|
|
62
60
|
|
|
61
|
+
export type JSONOp = any;
|
|
62
|
+
|
|
63
63
|
// `ShareDBDoc`
|
|
64
64
|
// A ShareDB Document.
|
|
65
65
|
// TODO better types? Integrate with upstream how?
|
|
@@ -142,7 +142,7 @@ export type AIStreamStatus = {
|
|
|
142
142
|
export type VZCodeContent = {
|
|
143
143
|
// `files`
|
|
144
144
|
// * The files in the VZCode instance.
|
|
145
|
-
files
|
|
145
|
+
files?: Files;
|
|
146
146
|
|
|
147
147
|
// `isInteracting`
|
|
148
148
|
// * `true` when the user is interacting
|