vzcode 0.21.0 → 0.23.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-49f71876.js +110 -0
- package/dist/assets/index-c63b4be7.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +9 -10
- package/src/client/App.tsx +30 -9
- package/src/client/CodeEditor/getOrCreateEditor.ts +3 -1
- package/src/client/CodeEditor/json1PresenceBroadcast.js +0 -2
- package/src/client/CodeEditor/json1PresenceDisplay.js +1 -45
- package/src/client/CodeEditor/widgets.ts +167 -3
- package/src/client/Settings.tsx +43 -45
- package/src/client/Sidebar/DirectoryListing.tsx +7 -4
- package/src/client/Sidebar/FileListing.tsx +1 -0
- package/src/client/TabList/style.scss +4 -0
- package/src/server/index.js +20 -3
- package/dist/assets/index-1547f7b8.css +0 -1
- package/dist/assets/index-37015623.js +0 -121
- package/src/client/PresenceNotifications/index.tsx +0 -128
- package/src/client/PresenceNotifications/style.scss +0 -17
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.vz-code-editor{flex:1;display:flex;font-family:var(--vzcode-font-family);overflow-x:auto}.vz-code-editor .cm-editor{flex:1}.vz-sidebar{height:100%;background:rgb(54,65,89);display:flex;flex-direction:column;justify-content:space-between}.vz-sidebar .full-box{padding:7px;display:flex;flex-direction:row;align-items:center;justify-content:space-between}.vz-sidebar .files{overflow:auto}.vz-sidebar .file-or-directory{display:flex;flex-direction:row;align-items:center;justify-content:space-between;cursor:pointer;margin:0 10px;color:#dbdde1;border-radius:5px}.vz-sidebar .link-name{margin-top:30px;margin-left:10px;color:#9aa0ac;font-size:12px;text-decoration:none}.vz-sidebar .new-btn{justify-content:right;margin-right:10px;cursor:pointer}.vz-sidebar .utils{display:flex}.vz-sidebar .utilities{margin-right:3px}.vz-sidebar .file-or-directory:hover,.vz-sidebar .settings:hover{background-color:#4a5469}.vz-sidebar .name{display:flex;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.vz-sidebar .indentation{margin-left:20px}.vz-sidebar .arrow-wrapper{margin-right:4px}.vz-sidebar .settings{height:30px;color:#dbdde1;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:14px 0;margin:10px;border-radius:5px}.vz-tab-list{display:flex;background-color:#252526;overflow-x:auto}.vz-tab-list .tab{background:rgb(45,45,45);color:#fff;font-family:var(--vzcode-font-family);align-items:center;display:flex;padding-top:2px;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;white-space:nowrap}.vz-tab-list .tab .tab-close{margin-left:5px}.vz-tab-list .tab .tab-close:hover{border-radius:30%;background-color:#252526}.vz-tab-list .tab.active{background-color:#292c34}.vz-resizer{position:absolute;top:0;left:0;bottom:0}.vz-resizer:hover{cursor:col-resize}.vz-resizer .vz-resizer-thumb{position:absolute;top:0;bottom:0;transition:background-color .15s .25s}.vz-resizer:hover .vz-resizer-thumb{background-color:#0078d4}:root{--vzcode-font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;--vz-bg-color: rgb( 41, 44, 52 )}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:var(--app-bg-color)}::-webkit-scrollbar-thumb{background:rgb(37,37,38)}::-webkit-scrollbar-thumb:hover{background:#555}html,body,#root,.app{height:100%}.app{background-color:var(--vz-bg-color);font-family:Poppins,sans-serif;display:flex;position:relative}.app .left,.app .right{display:flex}.app .right{flex:1;flex-direction:column;min-width:0}
|
package/dist/index.html
CHANGED
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
href="https://unpkg.com/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
|
29
29
|
rel="stylesheet"
|
|
30
30
|
/>
|
|
31
|
-
<script type="module" crossorigin src="/assets/index-
|
|
32
|
-
<link rel="stylesheet" href="/assets/index-
|
|
31
|
+
<script type="module" crossorigin src="/assets/index-49f71876.js"></script>
|
|
32
|
+
<link rel="stylesheet" href="/assets/index-c63b4be7.css">
|
|
33
33
|
</head>
|
|
34
34
|
<body>
|
|
35
35
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vzcode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Multiplayer code editor system",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
"@codemirror/lang-css": "^6.2.1",
|
|
50
50
|
"@codemirror/lang-html": "^6.4.6",
|
|
51
51
|
"@codemirror/lang-javascript": "^6.2.1",
|
|
52
|
-
"@codemirror/lang-markdown": "^6.2.
|
|
52
|
+
"@codemirror/lang-markdown": "^6.2.2",
|
|
53
53
|
"@codemirror/state": "^6.2.1",
|
|
54
54
|
"@codemirror/theme-one-dark": "^6.1.2",
|
|
55
55
|
"@replit/codemirror-interact": "^6.3.0",
|
|
56
56
|
"@teamwork/websocket-json-stream": "^2.0.0",
|
|
57
|
-
"@uiw/codemirror-themes-all": "^4.21.
|
|
57
|
+
"@uiw/codemirror-themes-all": "^4.21.19",
|
|
58
58
|
"codemirror": "^6.0.1",
|
|
59
59
|
"codemirror-ot": "^4.1.0",
|
|
60
60
|
"d3-array": "^3.2.4",
|
|
@@ -67,20 +67,19 @@
|
|
|
67
67
|
"react": "^18.2.0",
|
|
68
68
|
"react-bootstrap": "^2.9.0",
|
|
69
69
|
"react-dom": "^18.2.0",
|
|
70
|
-
"
|
|
71
|
-
"sharedb": "^4.0.1",
|
|
70
|
+
"sharedb": "^4.1.0",
|
|
72
71
|
"sharedb-client-browser": "^4.3.1",
|
|
73
72
|
"ot-json1": "^1.0.2",
|
|
74
73
|
"ws": "^8.14.2"
|
|
75
74
|
},
|
|
76
75
|
"devDependencies": {
|
|
77
|
-
"@types/react": "^18.2.
|
|
78
|
-
"@types/react-dom": "^18.2.
|
|
76
|
+
"@types/react": "^18.2.28",
|
|
77
|
+
"@types/react-dom": "^18.2.13",
|
|
79
78
|
"@vitejs/plugin-react": "^4.1.0",
|
|
80
79
|
"prettier": "^3.0.3",
|
|
81
|
-
"sass": "^1.
|
|
80
|
+
"sass": "^1.69.2",
|
|
82
81
|
"typescript": "^5.2.2",
|
|
83
|
-
"vite": "^4.4.
|
|
84
|
-
"vitest": "^0.34.
|
|
82
|
+
"vite": "^4.4.11",
|
|
83
|
+
"vitest": "^0.34.6"
|
|
85
84
|
}
|
|
86
85
|
}
|
package/src/client/App.tsx
CHANGED
|
@@ -28,7 +28,6 @@ import { usePrettier } from './usePrettier';
|
|
|
28
28
|
import PrettierWorker from './usePrettier/worker?worker';
|
|
29
29
|
import { SplitPaneResizeProvider } from './SplitPaneResizeContext';
|
|
30
30
|
import { Resizer } from './Resizer';
|
|
31
|
-
import { PresenceNotifications } from './PresenceNotifications';
|
|
32
31
|
import './style.scss';
|
|
33
32
|
|
|
34
33
|
// Instantiate the Prettier worker.
|
|
@@ -228,11 +227,36 @@ function App() {
|
|
|
228
227
|
[submitOperation, closeTab],
|
|
229
228
|
);
|
|
230
229
|
|
|
231
|
-
const
|
|
232
|
-
(
|
|
233
|
-
|
|
230
|
+
const deleteDirectory = useCallback(
|
|
231
|
+
(path: FileId) => {
|
|
232
|
+
submitOperation((document) => {
|
|
233
|
+
const updatedFiles = { ...document.files };
|
|
234
|
+
for (const key in updatedFiles) {
|
|
235
|
+
if (updatedFiles[key].name.includes(path)) {
|
|
236
|
+
closeTab(key);
|
|
237
|
+
delete updatedFiles[key];
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return { ...document, files: updatedFiles };
|
|
241
|
+
});
|
|
234
242
|
},
|
|
235
|
-
[
|
|
243
|
+
[submitOperation, closeTab],
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const handleDeleteClick = useCallback(
|
|
247
|
+
(key: string) => {
|
|
248
|
+
//Regex to identify if the key is a file path or a file id.
|
|
249
|
+
if (/^[0-9]*$/.test(key)) {
|
|
250
|
+
if (key.length == 8) {
|
|
251
|
+
deleteFile(key);
|
|
252
|
+
} else {
|
|
253
|
+
deleteDirectory(key);
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
deleteDirectory(key);
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
[deleteFile, deleteDirectory],
|
|
236
260
|
);
|
|
237
261
|
|
|
238
262
|
const handleSettingsClose = useCallback(() => {
|
|
@@ -276,7 +300,7 @@ function App() {
|
|
|
276
300
|
createFile={createFile}
|
|
277
301
|
files={files}
|
|
278
302
|
handleRenameFileClick={handleRenameFileClick}
|
|
279
|
-
handleDeleteFileClick={
|
|
303
|
+
handleDeleteFileClick={handleDeleteClick}
|
|
280
304
|
handleFileClick={openTab}
|
|
281
305
|
setIsSettingsOpen={setIsSettingsOpen}
|
|
282
306
|
isDirectoryOpen={isDirectoryOpen}
|
|
@@ -308,9 +332,6 @@ function App() {
|
|
|
308
332
|
editorCache={editorCache}
|
|
309
333
|
/>
|
|
310
334
|
) : null}
|
|
311
|
-
<PresenceNotifications
|
|
312
|
-
docPresence={docPresence}
|
|
313
|
-
/>
|
|
314
335
|
</div>
|
|
315
336
|
<Resizer />
|
|
316
337
|
</div>
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from '../../types';
|
|
17
17
|
import { json1PresenceBroadcast } from './json1PresenceBroadcast';
|
|
18
18
|
import { json1PresenceDisplay } from './json1PresenceDisplay';
|
|
19
|
-
import { widgets } from './widgets';
|
|
19
|
+
import { colorsInTextPlugin, widgets } from './widgets';
|
|
20
20
|
import {
|
|
21
21
|
EditorCache,
|
|
22
22
|
EditorCacheValue,
|
|
@@ -127,6 +127,8 @@ export const getOrCreateEditor = ({
|
|
|
127
127
|
json1PresenceDisplay({ path: textPath, docPresence }),
|
|
128
128
|
);
|
|
129
129
|
|
|
130
|
+
extensions.push(colorsInTextPlugin);
|
|
131
|
+
|
|
130
132
|
// This is the "basic setup" for CodeMirror,
|
|
131
133
|
// which actually adds a ton on functionality.
|
|
132
134
|
// TODO vet this functionality, and determine how much
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { EditorView } from 'codemirror';
|
|
2
|
-
import { username } from '../Settings.tsx';
|
|
3
2
|
|
|
4
3
|
// Deals with broadcasting changes in cursor location and selection.
|
|
5
4
|
export const json1PresenceBroadcast = ({
|
|
@@ -23,7 +22,6 @@ export const json1PresenceBroadcast = ({
|
|
|
23
22
|
const presence = {
|
|
24
23
|
start: [...path, from],
|
|
25
24
|
end: [...path, to],
|
|
26
|
-
username: username,
|
|
27
25
|
};
|
|
28
26
|
|
|
29
27
|
// Broadcast presence to remote clients!
|
|
@@ -32,16 +32,13 @@ export const json1PresenceDisplay = ({
|
|
|
32
32
|
// Receive remote presence changes.
|
|
33
33
|
docPresence.on('receive', (id, presence) => {
|
|
34
34
|
// If presence === null, the user has disconnected / exited
|
|
35
|
-
// notification(presence);
|
|
36
|
-
// console.log('docPresence in presenceDisplay:',docPresence);
|
|
37
|
-
// console.log('presence in presenceDisplay:', presence);
|
|
38
|
-
|
|
39
35
|
// We also check if the presence is for the current file or not.
|
|
40
36
|
if (presence && pathMatches(path, presence)) {
|
|
41
37
|
presenceState[id] = presence;
|
|
42
38
|
} else {
|
|
43
39
|
delete presenceState[id];
|
|
44
40
|
}
|
|
41
|
+
|
|
45
42
|
// Update decorations to reflect new presence state.
|
|
46
43
|
// TODO consider mutating this rather than recomputing it on each change.
|
|
47
44
|
this.decorations = Decoration.set(
|
|
@@ -102,47 +99,6 @@ export const json1PresenceDisplay = ({
|
|
|
102
99
|
presenceTheme,
|
|
103
100
|
];
|
|
104
101
|
|
|
105
|
-
// const notification = (presence) => {
|
|
106
|
-
// const notif = document.createElement('div');
|
|
107
|
-
// notif.className = 'notification-presence';
|
|
108
|
-
// if (presence === null){
|
|
109
|
-
// notif.textContent = 'User asdfgasdfgasdfg left the collaboration session.';
|
|
110
|
-
// notif.style.display = 'block';
|
|
111
|
-
// }
|
|
112
|
-
// // The following causes the popup to show every time the remote cursor moves
|
|
113
|
-
// // to a different location on the file and not when the user first joins
|
|
114
|
-
// // else {
|
|
115
|
-
// // notif.textContent = presence + 'joined the collaboration session.';
|
|
116
|
-
// // notif.style.display = 'block';
|
|
117
|
-
// // }
|
|
118
|
-
// document.body.appendChild(notif);
|
|
119
|
-
|
|
120
|
-
// const notifStyle = document.createElement('style');
|
|
121
|
-
// notifStyle.innerText = `
|
|
122
|
-
// .notification-presence {
|
|
123
|
-
// position: fixed;
|
|
124
|
-
// display: none;
|
|
125
|
-
// bottom: 20px;
|
|
126
|
-
// right: 20px;
|
|
127
|
-
// max-width: 300px;
|
|
128
|
-
// overflow-x: hidden;
|
|
129
|
-
// white-space: nowrap;
|
|
130
|
-
// background-color: rgb(54, 65, 89);
|
|
131
|
-
// color: rgb(154, 160, 172);
|
|
132
|
-
// padding: 15px;
|
|
133
|
-
// }
|
|
134
|
-
// .notification-presence:hover {
|
|
135
|
-
// overflow-x: auto;
|
|
136
|
-
// }`;
|
|
137
|
-
// document.head.appendChild(notifStyle);
|
|
138
|
-
|
|
139
|
-
// setTimeout(() => {
|
|
140
|
-
// notif.style.display = 'none';
|
|
141
|
-
// }, 10000);
|
|
142
|
-
|
|
143
|
-
// return notif;
|
|
144
|
-
// };
|
|
145
|
-
|
|
146
102
|
const presenceAnnotation = Annotation.define();
|
|
147
103
|
|
|
148
104
|
// Checks that the path of this file
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import interact from '@replit/codemirror-interact';
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
ViewPlugin,
|
|
5
|
+
Decoration,
|
|
6
|
+
WidgetType,
|
|
7
|
+
} from '@codemirror/view';
|
|
8
|
+
import {
|
|
9
|
+
Annotation,
|
|
10
|
+
Extension,
|
|
11
|
+
RangeSet,
|
|
12
|
+
} from '@codemirror/state';
|
|
13
|
+
import { EditorView } from 'codemirror';
|
|
14
|
+
|
|
3
15
|
// Interactive code widgets.
|
|
4
16
|
// * Number dragger
|
|
5
17
|
// * Boolean toggler
|
|
@@ -15,10 +27,49 @@ export const widgets = ({
|
|
|
15
27
|
}) =>
|
|
16
28
|
interact({
|
|
17
29
|
rules: [
|
|
30
|
+
// hex color picker
|
|
31
|
+
// Inspired by https://github.com/replit/codemirror-interact/blob/master/dev/index.ts#L71
|
|
32
|
+
{
|
|
33
|
+
regexp: /\"\#([0-9]|[A-F]|[a-f]){6}\"/g,
|
|
34
|
+
cursor: 'pointer',
|
|
35
|
+
onClick(text, setText, e) {
|
|
36
|
+
const res =
|
|
37
|
+
/\"(?<hex>\#([0-9]|[A-F]|[a-f]){6})\"/.exec(
|
|
38
|
+
text,
|
|
39
|
+
);
|
|
40
|
+
const startingColor = res.groups?.hex;
|
|
41
|
+
|
|
42
|
+
const sel = document.createElement('input');
|
|
43
|
+
sel.type = 'color';
|
|
44
|
+
|
|
45
|
+
sel.value = startingColor.toLowerCase();
|
|
46
|
+
|
|
47
|
+
//valueIsUpper maintains style of user's code. It keeps the case of a-f the same case as the original."
|
|
48
|
+
const valueIsUpper =
|
|
49
|
+
startingColor.toUpperCase() === startingColor;
|
|
50
|
+
|
|
51
|
+
const updateHex = (e: Event) => {
|
|
52
|
+
const el = e.target as HTMLInputElement;
|
|
53
|
+
|
|
54
|
+
if (el.value) {
|
|
55
|
+
setText(
|
|
56
|
+
`"${
|
|
57
|
+
valueIsUpper
|
|
58
|
+
? el.value.toUpperCase()
|
|
59
|
+
: el.value
|
|
60
|
+
}"`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
sel.addEventListener('input', updateHex);
|
|
65
|
+
sel.click();
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
|
|
18
69
|
// a rule for a number dragger
|
|
19
70
|
{
|
|
20
71
|
// the regexp matching the value
|
|
21
|
-
regexp:
|
|
72
|
+
regexp: /(?<!\#)-?\b\d+\.?\d*\b/g,
|
|
22
73
|
// set cursor to "ew-resize" on hover
|
|
23
74
|
cursor: 'ew-resize',
|
|
24
75
|
// change number value based on mouse X movement on drag
|
|
@@ -43,7 +94,7 @@ export const widgets = ({
|
|
|
43
94
|
}
|
|
44
95
|
},
|
|
45
96
|
},
|
|
46
|
-
//vec2 slider
|
|
97
|
+
// vec2 slider
|
|
47
98
|
// Inspired by: https://github.com/replit/codemirror-interact/blob/master/dev/index.ts#L61
|
|
48
99
|
{
|
|
49
100
|
regexp:
|
|
@@ -64,7 +115,7 @@ export const widgets = ({
|
|
|
64
115
|
);
|
|
65
116
|
},
|
|
66
117
|
},
|
|
67
|
-
//color picker
|
|
118
|
+
// rgb color picker
|
|
68
119
|
// Inspired by https://github.com/replit/codemirror-interact/blob/master/dev/index.ts#L71
|
|
69
120
|
//TODO: create color picker for hsl colors
|
|
70
121
|
{
|
|
@@ -82,6 +133,7 @@ export const widgets = ({
|
|
|
82
133
|
//sel will open the color picker when sel.click is called.
|
|
83
134
|
const sel = document.createElement('input');
|
|
84
135
|
sel.type = 'color';
|
|
136
|
+
|
|
85
137
|
if (!isNaN(r + g + b))
|
|
86
138
|
sel.value = rgb2Hex(r, g, b);
|
|
87
139
|
|
|
@@ -148,3 +200,115 @@ const hex2RGB = (hex: string): [number, number, number] => {
|
|
|
148
200
|
// Inspired by https://github.com/replit/codemirror-interact/blob/master/dev/index.ts#L117
|
|
149
201
|
const rgb2Hex = (r: number, g: number, b: number): string =>
|
|
150
202
|
'#' + r.toString(16) + g.toString(16) + b.toString(16);
|
|
203
|
+
|
|
204
|
+
const colorCircleTheme = EditorView.baseTheme({
|
|
205
|
+
'.color-circle-parent': { display: 'inline-block' },
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const colorCircleAnnotation = Annotation.define();
|
|
209
|
+
|
|
210
|
+
export const colorsInTextPlugin: Extension = [
|
|
211
|
+
ViewPlugin.fromClass(
|
|
212
|
+
class {
|
|
213
|
+
decorations: any;
|
|
214
|
+
view: EditorView;
|
|
215
|
+
constructor(view: EditorView) {
|
|
216
|
+
this.decorations = RangeSet.of([]);
|
|
217
|
+
this.view = view;
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
decorations: (v) => {
|
|
222
|
+
const colorInfos = [];
|
|
223
|
+
|
|
224
|
+
const lines = v.view.state.doc.iter();
|
|
225
|
+
let line = lines.next();
|
|
226
|
+
|
|
227
|
+
//Offset is the number of characters before the hex so the circle can be placed properly.
|
|
228
|
+
let offset = 0;
|
|
229
|
+
while (!line.done) {
|
|
230
|
+
if (line.value === '\n') {
|
|
231
|
+
offset++;
|
|
232
|
+
line = lines.next();
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const hexColorOccurances = line.value.matchAll(
|
|
236
|
+
/\"\#([0-9]|[A-F]|[a-f]){6}\"/g,
|
|
237
|
+
);
|
|
238
|
+
let hexOccurance = hexColorOccurances.next();
|
|
239
|
+
while (!hexOccurance.done) {
|
|
240
|
+
const offsetColorInfo = hexOccurance.value;
|
|
241
|
+
offsetColorInfo.index += offset;
|
|
242
|
+
colorInfos.push(offsetColorInfo);
|
|
243
|
+
hexOccurance = hexColorOccurances.next();
|
|
244
|
+
}
|
|
245
|
+
offset += line.value.length;
|
|
246
|
+
line = lines.next();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return Decoration.set(
|
|
250
|
+
colorInfos.map((colorInfo) => {
|
|
251
|
+
return {
|
|
252
|
+
//9 is the length of the hex color string including quotation marks
|
|
253
|
+
from: colorInfo.index + 9,
|
|
254
|
+
to: colorInfo.index + 9,
|
|
255
|
+
value: Decoration.widget({
|
|
256
|
+
side: -1,
|
|
257
|
+
widget: new ColorWidget(colorInfo[0]),
|
|
258
|
+
}),
|
|
259
|
+
};
|
|
260
|
+
}),
|
|
261
|
+
);
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
),
|
|
265
|
+
colorCircleTheme,
|
|
266
|
+
];
|
|
267
|
+
|
|
268
|
+
class ColorWidget extends WidgetType {
|
|
269
|
+
color: string;
|
|
270
|
+
constructor(color: string) {
|
|
271
|
+
super();
|
|
272
|
+
this.color = color;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
eq(widget: ColorWidget): boolean {
|
|
276
|
+
// TODO consider possibly adding a random ID to support multiple instances with the same color
|
|
277
|
+
return widget.color === this.color;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
toDOM(view: EditorView): HTMLElement {
|
|
281
|
+
const parent = document.createElement('div');
|
|
282
|
+
|
|
283
|
+
parent.setAttribute('style', 'width:10px;height:10px');
|
|
284
|
+
parent.className = 'color-circle-parent';
|
|
285
|
+
const svg = document.createElementNS(
|
|
286
|
+
'http://www.w3.org/2000/svg',
|
|
287
|
+
'svg',
|
|
288
|
+
);
|
|
289
|
+
const colorCircle = document.createElementNS(
|
|
290
|
+
'http://www.w3.org/2000/svg',
|
|
291
|
+
'circle',
|
|
292
|
+
);
|
|
293
|
+
colorCircle.setAttributeNS(
|
|
294
|
+
null,
|
|
295
|
+
'fill',
|
|
296
|
+
this.color.replace(/\"/g, ''),
|
|
297
|
+
);
|
|
298
|
+
colorCircle.setAttributeNS(null, 'r', '5');
|
|
299
|
+
colorCircle.setAttributeNS(null, 'cx', '5');
|
|
300
|
+
colorCircle.setAttributeNS(null, 'cy', '5');
|
|
301
|
+
|
|
302
|
+
svg.setAttributeNS(null, 'viewBox', '0 0 10 10');
|
|
303
|
+
svg.setAttributeNS(null, 'width', '10');
|
|
304
|
+
svg.setAttributeNS(null, 'height', '10');
|
|
305
|
+
|
|
306
|
+
svg.appendChild(colorCircle);
|
|
307
|
+
|
|
308
|
+
parent.appendChild(svg);
|
|
309
|
+
return parent;
|
|
310
|
+
}
|
|
311
|
+
ignoreEvent() {
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
}
|
package/src/client/Settings.tsx
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
import { useCallback
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
2
|
import { Button, Modal, Form } from './bootstrap';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
ThemeLabel,
|
|
6
|
-
themeOptionsByLabel,
|
|
7
|
-
themes,
|
|
8
|
-
} from './themes';
|
|
3
|
+
import { ThemeLabel, themes } from './themes';
|
|
9
4
|
|
|
10
|
-
const saveTimes = [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
export let username = 'Anonymous';
|
|
5
|
+
// const saveTimes = [
|
|
6
|
+
// { value: 1, label: '1 second' },
|
|
7
|
+
// { value: 5, label: '5 seconds' },
|
|
8
|
+
// { value: 10, label: '10 seconds' },
|
|
9
|
+
// { value: 30, label: '30 seconds' },
|
|
10
|
+
// ];
|
|
18
11
|
|
|
19
12
|
export const Settings = ({
|
|
20
13
|
show,
|
|
@@ -27,29 +20,26 @@ export const Settings = ({
|
|
|
27
20
|
setTheme: (theme: ThemeLabel) => void;
|
|
28
21
|
theme: ThemeLabel;
|
|
29
22
|
}) => {
|
|
30
|
-
const
|
|
31
|
-
|
|
23
|
+
const handleThemeChange = useCallback((event) => {
|
|
24
|
+
const selectedValue = event.target.value;
|
|
25
|
+
console.log(selectedValue);
|
|
26
|
+
setTheme(selectedValue);
|
|
32
27
|
}, []);
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const usernameRef = useRef<HTMLInputElement>(null);
|
|
49
|
-
const handleUsernameChange = () => {
|
|
50
|
-
const usernameValue = usernameRef.current?.value;
|
|
51
|
-
username = usernameValue;
|
|
52
|
-
};
|
|
29
|
+
// TODO https://github.com/vizhub-core/vzcode/issues/95
|
|
30
|
+
// const handleSaveTimeChange = useCallback(
|
|
31
|
+
// (selectedOption) => {
|
|
32
|
+
// const time = [{ value: selectedOption.value }];
|
|
33
|
+
// fetch('/saveTime', {
|
|
34
|
+
// method: 'POST',
|
|
35
|
+
// headers: {
|
|
36
|
+
// 'Content-Type': 'application/json',
|
|
37
|
+
// },
|
|
38
|
+
// body: JSON.stringify(time),
|
|
39
|
+
// });
|
|
40
|
+
// },
|
|
41
|
+
// [],
|
|
42
|
+
// );
|
|
53
43
|
|
|
54
44
|
return show ? (
|
|
55
45
|
<Modal
|
|
@@ -66,8 +56,6 @@ export const Settings = ({
|
|
|
66
56
|
<Form.Label>Username</Form.Label>
|
|
67
57
|
<Form.Control
|
|
68
58
|
type="text"
|
|
69
|
-
ref={usernameRef}
|
|
70
|
-
onChange={handleUsernameChange}
|
|
71
59
|
placeholder="Enter username"
|
|
72
60
|
/>
|
|
73
61
|
<Form.Text className="text-muted">
|
|
@@ -76,16 +64,26 @@ export const Settings = ({
|
|
|
76
64
|
</Form.Group>
|
|
77
65
|
<Form.Group className="mb-3" controlId="formFork">
|
|
78
66
|
<Form.Label>Theme</Form.Label>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
value={
|
|
83
|
-
|
|
67
|
+
|
|
68
|
+
<select
|
|
69
|
+
className="form-select"
|
|
70
|
+
value={theme}
|
|
71
|
+
onChange={handleThemeChange}
|
|
72
|
+
>
|
|
73
|
+
{themes.map(({ label }) => (
|
|
74
|
+
<option key={label} value={label}>
|
|
75
|
+
{label}
|
|
76
|
+
</option>
|
|
77
|
+
))}
|
|
78
|
+
</select>
|
|
79
|
+
|
|
84
80
|
<Form.Text className="text-muted">
|
|
85
81
|
Select a color theme for the editor
|
|
86
82
|
</Form.Text>
|
|
87
83
|
</Form.Group>
|
|
88
|
-
|
|
84
|
+
{/*
|
|
85
|
+
TODO https://github.com/vizhub-core/vzcode/issues/95
|
|
86
|
+
<Form.Group className="mb-3" controlId="formFork">
|
|
89
87
|
<Form.Label>Auto-Save Time</Form.Label>
|
|
90
88
|
<Select
|
|
91
89
|
options={saveTimes}
|
|
@@ -94,7 +92,7 @@ export const Settings = ({
|
|
|
94
92
|
<Form.Text className="text-muted">
|
|
95
93
|
Select an auto save time
|
|
96
94
|
</Form.Text>
|
|
97
|
-
</Form.Group>
|
|
95
|
+
</Form.Group> */}
|
|
98
96
|
</Modal.Body>
|
|
99
97
|
<Modal.Footer>
|
|
100
98
|
<Button variant="primary" onClick={onClose}>
|
|
@@ -33,10 +33,13 @@ export const DirectoryListing = ({
|
|
|
33
33
|
toggleDirectory(path);
|
|
34
34
|
}, [toggleDirectory]);
|
|
35
35
|
|
|
36
|
-
const handleDeleteClick = useCallback(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
const handleDeleteClick = useCallback(
|
|
37
|
+
(event) => {
|
|
38
|
+
// https://github.com/vizhub-core/vzcode/issues/102
|
|
39
|
+
handleDeleteFileClick(path, event);
|
|
40
|
+
},
|
|
41
|
+
[handleDeleteFileClick, path],
|
|
42
|
+
);
|
|
40
43
|
|
|
41
44
|
const handleRenameClick = useCallback(() => {
|
|
42
45
|
// https://github.com/vizhub-core/vzcode/issues/103
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
.vz-tab-list {
|
|
2
2
|
display: flex;
|
|
3
3
|
background-color: rgb(37, 37, 38);
|
|
4
|
+
// When the tab list becomes too large, a horizontal scroll bar appears.
|
|
5
|
+
overflow-x: auto;
|
|
4
6
|
|
|
5
7
|
.tab {
|
|
6
8
|
background: rgb(45, 45, 45);
|
|
@@ -17,6 +19,8 @@
|
|
|
17
19
|
margin-left: 2px;
|
|
18
20
|
margin-right: 2px;
|
|
19
21
|
cursor: pointer;
|
|
22
|
+
// Make sure text doesn't wrap within tabs.
|
|
23
|
+
white-space: nowrap;
|
|
20
24
|
|
|
21
25
|
.tab-close {
|
|
22
26
|
margin-left: 5px;
|
package/src/server/index.js
CHANGED
|
@@ -109,13 +109,30 @@ const save = () => {
|
|
|
109
109
|
|
|
110
110
|
// Handle renaming files.
|
|
111
111
|
if (previous.name !== current.name) {
|
|
112
|
-
fs.
|
|
112
|
+
fs.renamesync(previous.name, current.name);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
// handle deleting files.
|
|
116
|
+
// handle deleting files and directories.
|
|
117
117
|
if (previous && !current) {
|
|
118
|
-
fs.
|
|
118
|
+
let stats = fs.statSync(previous.name);
|
|
119
|
+
//Check if the file path we are trying to delete is a directory
|
|
120
|
+
if (!stats.isDirectory()) {
|
|
121
|
+
fs.unlinkSync(previous.name);
|
|
122
|
+
} else {
|
|
123
|
+
//Performs directory deletion.
|
|
124
|
+
fs.rm(
|
|
125
|
+
previous.name,
|
|
126
|
+
{
|
|
127
|
+
recursive: true, //Makes sure that all files in directory are deleted.
|
|
128
|
+
},
|
|
129
|
+
(error) => {
|
|
130
|
+
if (error) {
|
|
131
|
+
console.log(error);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
}
|
|
119
136
|
}
|
|
120
137
|
|
|
121
138
|
// Handle creating files.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.vz-code-editor{flex:1;display:flex;font-family:var(--vzcode-font-family);overflow-x:auto}.vz-code-editor .cm-editor{flex:1}.vz-sidebar{height:100%;background:rgb(54,65,89);display:flex;flex-direction:column;justify-content:space-between}.vz-sidebar .full-box{padding:7px;display:flex;flex-direction:row;align-items:center;justify-content:space-between}.vz-sidebar .files{overflow:auto}.vz-sidebar .file-or-directory{display:flex;flex-direction:row;align-items:center;justify-content:space-between;cursor:pointer;margin:0 10px;color:#dbdde1;border-radius:5px}.vz-sidebar .link-name{margin-top:30px;margin-left:10px;color:#9aa0ac;font-size:12px;text-decoration:none}.vz-sidebar .new-btn{justify-content:right;margin-right:10px;cursor:pointer}.vz-sidebar .utils{display:flex}.vz-sidebar .utilities{margin-right:3px}.vz-sidebar .file-or-directory:hover,.vz-sidebar .settings:hover{background-color:#4a5469}.vz-sidebar .name{display:flex;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.vz-sidebar .indentation{margin-left:20px}.vz-sidebar .arrow-wrapper{margin-right:4px}.vz-sidebar .settings{height:30px;color:#dbdde1;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:14px 0;margin:10px;border-radius:5px}.vz-tab-list{display:flex;background-color:#252526}.vz-tab-list .tab{background:rgb(45,45,45);color:#fff;font-family:var(--vzcode-font-family);align-items:center;display:flex;padding-top:2px;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}.vz-tab-list .tab .tab-close{margin-left:5px}.vz-tab-list .tab .tab-close:hover{border-radius:30%;background-color:#252526}.vz-tab-list .tab.active{background-color:#292c34}.vz-resizer{position:absolute;top:0;left:0;bottom:0}.vz-resizer:hover{cursor:col-resize}.vz-resizer .vz-resizer-thumb{position:absolute;top:0;bottom:0;transition:background-color .15s .25s}.vz-resizer:hover .vz-resizer-thumb{background-color:#0078d4}.vz-notification{position:fixed;display:flex;flex-direction:column;gap:10px;bottom:20px;right:20px;color:#9aa0ac;padding:15px}.vz-notification .vz-notification-item{background-color:#364159;padding:5px;border-radius:5px}:root{--vzcode-font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;--vz-bg-color: rgb( 41, 44, 52 )}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:var(--app-bg-color)}::-webkit-scrollbar-thumb{background:rgb(37,37,38)}::-webkit-scrollbar-thumb:hover{background:#555}html,body,#root,.app{height:100%}.app{background-color:var(--vz-bg-color);font-family:Poppins,sans-serif;display:flex;position:relative}.app .left,.app .right{display:flex}.app .right{flex:1;flex-direction:column;min-width:0}
|