vzcode 0.24.0 → 0.26.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-9c8bbae0.js → index-284b7339.js} +50 -50
- package/dist/assets/index-e160f758.css +1 -0
- package/dist/index.html +2 -6
- package/package.json +7 -5
- package/src/client/App.tsx +47 -213
- package/src/client/CodeEditor/getOrCreateEditor.ts +3 -0
- package/src/client/CodeEditor/index.tsx +36 -6
- package/src/client/CodeEditor/{json1PresenceBroadcast.js → json1PresenceBroadcast.ts} +5 -3
- package/src/client/CodeEditor/{json1PresenceDisplay.js → json1PresenceDisplay.ts} +9 -3
- package/src/client/CodeEditor/widgets.ts +112 -10
- package/src/client/PresenceNotifications/index.tsx +0 -2
- package/src/client/TabList/Tab.tsx +56 -0
- package/src/client/TabList/index.tsx +11 -62
- package/src/client/TabList/style.scss +21 -9
- package/src/client/{Settings.tsx → VZSettings.tsx} +1 -1
- package/src/client/{Sidebar → VZSidebar}/FileListing.tsx +3 -0
- package/src/client/{Sidebar → VZSidebar}/Item.tsx +10 -7
- package/src/client/{Sidebar → VZSidebar}/Listing.tsx +3 -0
- package/src/client/{Sidebar → VZSidebar}/index.tsx +5 -6
- package/src/client/{Sidebar → VZSidebar}/styles.scss +17 -12
- package/src/client/bootstrap.ts +3 -0
- package/src/client/style.scss +5 -2
- package/src/client/themes/index.ts +7 -0
- package/src/client/{themes.ts → themes/themes.ts} +5 -5
- package/src/client/{useDynamicTheme.ts → themes/useDynamicTheme.ts} +1 -1
- package/src/client/themes/vizhubTheme/colors.ts +80 -0
- package/src/client/themes/vizhubTheme/index.ts +166 -0
- package/src/client/useActions.ts +74 -0
- package/src/client/useFileCRUD.ts +101 -0
- package/src/client/useOpenDirectories.ts +25 -7
- package/src/client/useSubmitOperation.ts +25 -0
- package/src/client/vzReducer.ts +118 -0
- package/src/index.ts +34 -0
- package/src/server/index.js +13 -2
- package/dist/assets/index-cf19de3b.css +0 -1
- package/src/client/reducer.ts +0 -122
- package/src/client/useTabsState.ts +0 -95
- package/src/client/vizhubTheme.js +0 -1
- /package/src/client/{Sidebar → VZSidebar}/ConfirmationBox.tsx +0 -0
- /package/src/client/{Sidebar → VZSidebar}/DirectoryArrowSVG.jsx +0 -0
- /package/src/client/{Sidebar → VZSidebar}/DirectoryListing.tsx +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// A CodeMirror 6 theme for the VizHub syntax highlighting colors.
|
|
2
|
+
|
|
3
|
+
// Inspired by https://github.com/uiwjs/react-codemirror/blob/master/themes/vscode/src/index.ts
|
|
4
|
+
|
|
5
|
+
import { tags as t } from '@lezer/highlight';
|
|
6
|
+
import {
|
|
7
|
+
createTheme,
|
|
8
|
+
CreateThemeOptions,
|
|
9
|
+
} from '@uiw/codemirror-themes';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
// Colors for various things outside the code itself
|
|
13
|
+
backgroundColor,
|
|
14
|
+
lineHighlight,
|
|
15
|
+
light,
|
|
16
|
+
dark,
|
|
17
|
+
lineNumbers,
|
|
18
|
+
lineNumbersActive,
|
|
19
|
+
selectionBackground,
|
|
20
|
+
selectionBackgroundMatch,
|
|
21
|
+
caretColor,
|
|
22
|
+
|
|
23
|
+
// Syntax highlighting colors
|
|
24
|
+
MINT,
|
|
25
|
+
AQUA,
|
|
26
|
+
SKY,
|
|
27
|
+
LAVENDER,
|
|
28
|
+
SALMON,
|
|
29
|
+
GOLDENROD,
|
|
30
|
+
PANIC,
|
|
31
|
+
} from './colors';
|
|
32
|
+
|
|
33
|
+
const defaultSettingsVizhubTheme: CreateThemeOptions['settings'] =
|
|
34
|
+
{
|
|
35
|
+
background: backgroundColor,
|
|
36
|
+
foreground: light,
|
|
37
|
+
caret: caretColor,
|
|
38
|
+
selection: selectionBackground,
|
|
39
|
+
selectionMatch: selectionBackgroundMatch,
|
|
40
|
+
lineHighlight,
|
|
41
|
+
gutterBackground: backgroundColor,
|
|
42
|
+
gutterForeground: lineNumbers,
|
|
43
|
+
gutterActiveForeground: lineNumbersActive,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function vizhubThemeInit(
|
|
47
|
+
options?: Partial<CreateThemeOptions>,
|
|
48
|
+
) {
|
|
49
|
+
const {
|
|
50
|
+
theme = 'dark',
|
|
51
|
+
settings = {},
|
|
52
|
+
styles = [],
|
|
53
|
+
} = options || {};
|
|
54
|
+
return createTheme({
|
|
55
|
+
theme: theme,
|
|
56
|
+
settings: {
|
|
57
|
+
...defaultSettingsVizhubTheme,
|
|
58
|
+
...settings,
|
|
59
|
+
},
|
|
60
|
+
styles: [
|
|
61
|
+
{
|
|
62
|
+
tag: [
|
|
63
|
+
t.keyword,
|
|
64
|
+
t.operatorKeyword,
|
|
65
|
+
t.modifier,
|
|
66
|
+
t.color,
|
|
67
|
+
t.constant(t.name),
|
|
68
|
+
t.standard(t.name),
|
|
69
|
+
t.standard(t.tagName),
|
|
70
|
+
t.special(t.brace),
|
|
71
|
+
t.atom,
|
|
72
|
+
t.bool,
|
|
73
|
+
t.special(t.variableName),
|
|
74
|
+
],
|
|
75
|
+
color: MINT,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
tag: [t.controlKeyword, t.moduleKeyword],
|
|
79
|
+
color: MINT,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
tag: [
|
|
83
|
+
t.name,
|
|
84
|
+
t.deleted,
|
|
85
|
+
t.character,
|
|
86
|
+
t.macroName,
|
|
87
|
+
|
|
88
|
+
t.variableName,
|
|
89
|
+
t.labelName,
|
|
90
|
+
t.definition(t.name),
|
|
91
|
+
],
|
|
92
|
+
color: AQUA,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
tag: [t.propertyName],
|
|
96
|
+
color: GOLDENROD,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
tag: t.heading,
|
|
100
|
+
fontWeight: 'bold',
|
|
101
|
+
color: SKY,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
tag: [
|
|
105
|
+
t.typeName,
|
|
106
|
+
t.className,
|
|
107
|
+
t.tagName,
|
|
108
|
+
t.number,
|
|
109
|
+
t.changed,
|
|
110
|
+
t.annotation,
|
|
111
|
+
t.self,
|
|
112
|
+
t.namespace,
|
|
113
|
+
],
|
|
114
|
+
color: AQUA,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
tag: [
|
|
118
|
+
t.function(t.variableName),
|
|
119
|
+
t.function(t.propertyName),
|
|
120
|
+
],
|
|
121
|
+
color: AQUA,
|
|
122
|
+
},
|
|
123
|
+
{ tag: [t.number], color: SALMON },
|
|
124
|
+
{
|
|
125
|
+
tag: [
|
|
126
|
+
t.operator,
|
|
127
|
+
t.punctuation,
|
|
128
|
+
t.separator,
|
|
129
|
+
t.url,
|
|
130
|
+
t.escape,
|
|
131
|
+
t.regexp,
|
|
132
|
+
],
|
|
133
|
+
color: light,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
tag: [t.regexp],
|
|
137
|
+
color: LAVENDER,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
tag: [t.special(t.string), t.string, t.inserted],
|
|
141
|
+
color: LAVENDER,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
tag: [t.processingInstruction],
|
|
145
|
+
color: AQUA,
|
|
146
|
+
},
|
|
147
|
+
{ tag: [t.angleBracket], color: AQUA },
|
|
148
|
+
{ tag: t.strong, fontWeight: 'bold' },
|
|
149
|
+
{ tag: t.emphasis, fontStyle: 'italic' },
|
|
150
|
+
{
|
|
151
|
+
tag: t.strikethrough,
|
|
152
|
+
textDecoration: 'line-through',
|
|
153
|
+
},
|
|
154
|
+
{ tag: [t.meta, t.comment], color: dark },
|
|
155
|
+
{
|
|
156
|
+
tag: t.link,
|
|
157
|
+
color: GOLDENROD,
|
|
158
|
+
textDecoration: 'underline',
|
|
159
|
+
},
|
|
160
|
+
{ tag: t.invalid, color: PANIC },
|
|
161
|
+
...styles,
|
|
162
|
+
],
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export const vizhubTheme = vizhubThemeInit();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { ThemeLabel } from './themes';
|
|
3
|
+
import { FileId } from '../types';
|
|
4
|
+
import { VZAction } from './vzReducer';
|
|
5
|
+
|
|
6
|
+
// This is a custom hook that returns a set of functions
|
|
7
|
+
// that can be used to dispatch actions to the reducer.
|
|
8
|
+
export const useActions = (
|
|
9
|
+
dispatch: (action: VZAction) => void,
|
|
10
|
+
) => {
|
|
11
|
+
const setActiveFileId = useCallback(
|
|
12
|
+
(activeFileId: FileId) => {
|
|
13
|
+
dispatch({
|
|
14
|
+
type: 'set_active_file_id',
|
|
15
|
+
activeFileId,
|
|
16
|
+
});
|
|
17
|
+
},
|
|
18
|
+
[dispatch],
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const openTab = useCallback(
|
|
22
|
+
(fileId: FileId) => {
|
|
23
|
+
dispatch({
|
|
24
|
+
type: 'open_tab',
|
|
25
|
+
fileId,
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
[dispatch],
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const closeTabs = useCallback(
|
|
32
|
+
(idsToDelete: Array<FileId>) => {
|
|
33
|
+
dispatch({
|
|
34
|
+
type: 'close_tabs',
|
|
35
|
+
idsToDelete,
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
[dispatch],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const setTheme = useCallback(
|
|
42
|
+
(themeLabel: ThemeLabel) => {
|
|
43
|
+
dispatch({
|
|
44
|
+
type: 'set_theme',
|
|
45
|
+
themeLabel: themeLabel,
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
[dispatch],
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// True to show the settings modal.
|
|
52
|
+
const setIsSettingsOpen = useCallback(
|
|
53
|
+
(value: boolean) => {
|
|
54
|
+
dispatch({
|
|
55
|
+
type: 'set_is_settings_open',
|
|
56
|
+
value: value,
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
[dispatch],
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const closeSettings = useCallback(() => {
|
|
63
|
+
setIsSettingsOpen(false);
|
|
64
|
+
}, [setIsSettingsOpen]);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
setActiveFileId,
|
|
68
|
+
openTab,
|
|
69
|
+
closeTabs,
|
|
70
|
+
setTheme,
|
|
71
|
+
setIsSettingsOpen,
|
|
72
|
+
closeSettings,
|
|
73
|
+
};
|
|
74
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { FileId, VZCodeContent } from '../types';
|
|
3
|
+
import { randomId } from '../randomId';
|
|
4
|
+
|
|
5
|
+
// CRUD operations for files and directories
|
|
6
|
+
// CRUD = Create, Read, Update, Delete
|
|
7
|
+
export const useFileCRUD = ({
|
|
8
|
+
submitOperation,
|
|
9
|
+
closeTabs,
|
|
10
|
+
}) => {
|
|
11
|
+
// Create a new file
|
|
12
|
+
const createFile = useCallback(() => {
|
|
13
|
+
// TODO use Bootstrap modals not native prompts
|
|
14
|
+
// See https://github.com/vizhub-core/vzcode/issues/252
|
|
15
|
+
const name = prompt('Enter new file name');
|
|
16
|
+
if (name) {
|
|
17
|
+
submitOperation((document: VZCodeContent) => ({
|
|
18
|
+
...document,
|
|
19
|
+
files: {
|
|
20
|
+
...document.files,
|
|
21
|
+
[randomId()]: { name, text: '' },
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
}, [submitOperation]);
|
|
26
|
+
|
|
27
|
+
// Called when a file in the sidebar is renamed.
|
|
28
|
+
const handleRenameFileClick = useCallback(
|
|
29
|
+
(fileId: FileId, newName: string) => {
|
|
30
|
+
submitOperation((document) => ({
|
|
31
|
+
...document,
|
|
32
|
+
files: {
|
|
33
|
+
...document.files,
|
|
34
|
+
[fileId]: {
|
|
35
|
+
...document.files[fileId],
|
|
36
|
+
name: newName,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
}));
|
|
40
|
+
},
|
|
41
|
+
[submitOperation],
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Deletes a file
|
|
45
|
+
const deleteFile = useCallback(
|
|
46
|
+
(fileId: FileId) => {
|
|
47
|
+
closeTabs([fileId]);
|
|
48
|
+
submitOperation((document) => {
|
|
49
|
+
const updatedFiles = { ...document.files };
|
|
50
|
+
delete updatedFiles[fileId];
|
|
51
|
+
return { ...document, files: updatedFiles };
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
[submitOperation, closeTabs],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Deletes a directory and all files within it
|
|
58
|
+
const deleteDirectory = useCallback(
|
|
59
|
+
(path: FileId) => {
|
|
60
|
+
const tabsToClose: Array<FileId> = [];
|
|
61
|
+
submitOperation((document: VZCodeContent) => {
|
|
62
|
+
const updatedFiles = { ...document.files };
|
|
63
|
+
for (const key in updatedFiles) {
|
|
64
|
+
if (updatedFiles[key].name.includes(path)) {
|
|
65
|
+
tabsToClose.push(key);
|
|
66
|
+
delete updatedFiles[key];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { ...document, files: updatedFiles };
|
|
70
|
+
});
|
|
71
|
+
closeTabs(tabsToClose);
|
|
72
|
+
},
|
|
73
|
+
[submitOperation, closeTabs],
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Generic delete function that calls either deleteFile or deleteDirectory
|
|
77
|
+
// TODO consider if there is a cleaner way to do this
|
|
78
|
+
// - Ideally we would have a separate function for deleting files and directories
|
|
79
|
+
// - When we click the delete button, we should be able to tell if it is a file or directory
|
|
80
|
+
const handleDeleteClick = useCallback(
|
|
81
|
+
(key: string) => {
|
|
82
|
+
// Regex to identify if the key is a file path or a file id.
|
|
83
|
+
if (/^[0-9]*$/.test(key)) {
|
|
84
|
+
if (key.length == 8) {
|
|
85
|
+
deleteFile(key);
|
|
86
|
+
} else {
|
|
87
|
+
deleteDirectory(key);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
deleteDirectory(key);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
[deleteFile, deleteDirectory],
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
createFile,
|
|
98
|
+
handleRenameFileClick,
|
|
99
|
+
handleDeleteClick,
|
|
100
|
+
};
|
|
101
|
+
};
|
|
@@ -17,6 +17,7 @@ import { useState, useCallback } from 'react';
|
|
|
17
17
|
|
|
18
18
|
// Inspired by
|
|
19
19
|
// https://github.com/vizhub-core/vizhub/blob/main/vizhub-v2/packages/neoFrontend/src/pages/VizPage/Body/Editor/FilesSection/useOpenDirectories.js
|
|
20
|
+
// TODO move this into reducer
|
|
20
21
|
export const useOpenDirectories = () => {
|
|
21
22
|
// The set of open directories by path/
|
|
22
23
|
const [openDirectories, setOpenDirectories] = useState<
|
|
@@ -30,14 +31,31 @@ export const useOpenDirectories = () => {
|
|
|
30
31
|
[openDirectories],
|
|
31
32
|
);
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Toggles the state of a directory (open/close).
|
|
36
|
+
* If the directory is currently open, it will close it.
|
|
37
|
+
* If it's closed, it will open it.
|
|
38
|
+
*/
|
|
34
39
|
const toggleDirectory = useCallback(
|
|
35
|
-
(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
(directoryPath: string) => {
|
|
41
|
+
// Create a copy of the currently open directories
|
|
42
|
+
const updatedOpenDirectories = new Set(
|
|
43
|
+
openDirectories,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// Check if the directory is currently open
|
|
47
|
+
const directoryIsOpen =
|
|
48
|
+
isDirectoryOpen(directoryPath);
|
|
49
|
+
|
|
50
|
+
// Toggle the directory's state
|
|
51
|
+
if (directoryIsOpen) {
|
|
52
|
+
updatedOpenDirectories.delete(directoryPath);
|
|
53
|
+
} else {
|
|
54
|
+
updatedOpenDirectories.add(directoryPath);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Update the state with the modified set of open directories
|
|
58
|
+
setOpenDirectories(updatedOpenDirectories);
|
|
41
59
|
},
|
|
42
60
|
[openDirectories, isDirectoryOpen],
|
|
43
61
|
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { ShareDBDoc, VZCodeContent } from '../types';
|
|
3
|
+
import { diff } from './diff';
|
|
4
|
+
|
|
5
|
+
// A helper function to submit diff-based operations to ShareDB
|
|
6
|
+
export const useSubmitOperation = (
|
|
7
|
+
shareDBDoc: ShareDBDoc<VZCodeContent>,
|
|
8
|
+
): ((
|
|
9
|
+
next: (content: VZCodeContent) => VZCodeContent,
|
|
10
|
+
) => void) => {
|
|
11
|
+
const submitOperation: (
|
|
12
|
+
next: (content: VZCodeContent) => VZCodeContent,
|
|
13
|
+
) => void = useCallback(
|
|
14
|
+
(next) => {
|
|
15
|
+
const content: VZCodeContent = shareDBDoc.data;
|
|
16
|
+
const op = diff(content, next(content));
|
|
17
|
+
if (op && shareDBDoc) {
|
|
18
|
+
shareDBDoc.submitOp(op);
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
[shareDBDoc],
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return submitOperation;
|
|
25
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { FileId } from '../types';
|
|
2
|
+
import { ThemeLabel } from './themes';
|
|
3
|
+
|
|
4
|
+
export type VZState = {
|
|
5
|
+
tabList: Array<FileId>;
|
|
6
|
+
activeFileId: FileId | null;
|
|
7
|
+
theme: ThemeLabel;
|
|
8
|
+
isSettingsOpen: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type VZAction =
|
|
12
|
+
| { type: 'set_active_file_id'; activeFileId: FileId }
|
|
13
|
+
| { type: 'open_tab'; fileId: FileId }
|
|
14
|
+
| { type: 'close_tab'; fileIdToRemove: FileId }
|
|
15
|
+
| { type: 'close_tabs'; idsToDelete: Array<FileId> }
|
|
16
|
+
| { type: 'set_theme'; themeLabel: ThemeLabel }
|
|
17
|
+
| { type: 'set_is_settings_open'; value: boolean };
|
|
18
|
+
|
|
19
|
+
export const vzReducer = (
|
|
20
|
+
state: VZState,
|
|
21
|
+
action: VZAction,
|
|
22
|
+
) => {
|
|
23
|
+
switch (action.type) {
|
|
24
|
+
case 'set_active_file_id': {
|
|
25
|
+
return {
|
|
26
|
+
...state,
|
|
27
|
+
activeFileId: action.activeFileId,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
case 'open_tab': {
|
|
31
|
+
if (!state.tabList.includes(action.fileId)) {
|
|
32
|
+
return {
|
|
33
|
+
...state,
|
|
34
|
+
activeFileId: action.fileId,
|
|
35
|
+
tabList: [...state.tabList, action.fileId],
|
|
36
|
+
};
|
|
37
|
+
} else {
|
|
38
|
+
return {
|
|
39
|
+
...state,
|
|
40
|
+
activeFileId: action.fileId,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
case 'close_tab': {
|
|
45
|
+
const i = state.tabList.findIndex(
|
|
46
|
+
(fileId: FileId) =>
|
|
47
|
+
fileId === action.fileIdToRemove,
|
|
48
|
+
);
|
|
49
|
+
if (i != -1) {
|
|
50
|
+
const newTabList = [
|
|
51
|
+
...state.tabList.slice(0, i),
|
|
52
|
+
...state.tabList.slice(i + 1),
|
|
53
|
+
];
|
|
54
|
+
if (state.activeFileId === action.fileIdToRemove) {
|
|
55
|
+
// set the new active file to the next tab over,
|
|
56
|
+
if (newTabList.length > 0) {
|
|
57
|
+
return {
|
|
58
|
+
...state,
|
|
59
|
+
activeFileId:
|
|
60
|
+
i === 0 ? newTabList[i] : newTabList[i - 1],
|
|
61
|
+
tabList: newTabList,
|
|
62
|
+
};
|
|
63
|
+
} else {
|
|
64
|
+
// or clear out the active file
|
|
65
|
+
// if we've closed the last tab.
|
|
66
|
+
return {
|
|
67
|
+
...state,
|
|
68
|
+
activeFileId: null,
|
|
69
|
+
tabList: newTabList,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
...state,
|
|
75
|
+
tabList: newTabList,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
...state,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
case 'close_tabs': {
|
|
83
|
+
const newTabList = state.tabList.filter(
|
|
84
|
+
(fileId: FileId) =>
|
|
85
|
+
!action.idsToDelete.includes(fileId),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
let newActiveFileId: FileId | null =
|
|
89
|
+
state.activeFileId;
|
|
90
|
+
|
|
91
|
+
if (action.idsToDelete.includes(newActiveFileId)) {
|
|
92
|
+
const index =
|
|
93
|
+
state.tabList.indexOf(newActiveFileId);
|
|
94
|
+
newActiveFileId = newTabList[index]
|
|
95
|
+
? newTabList[index]
|
|
96
|
+
: newTabList[index - 1] || null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
...state,
|
|
101
|
+
tabList: newTabList,
|
|
102
|
+
activeFileId: newActiveFileId,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
case 'set_theme': {
|
|
106
|
+
return {
|
|
107
|
+
...state,
|
|
108
|
+
theme: action.themeLabel,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
case 'set_is_settings_open': {
|
|
112
|
+
return {
|
|
113
|
+
...state,
|
|
114
|
+
isSettingsOpen: action.value,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
FileId,
|
|
3
|
+
Files,
|
|
4
|
+
File,
|
|
5
|
+
FileTree,
|
|
6
|
+
FileTreeFile,
|
|
7
|
+
ShareDBDoc,
|
|
8
|
+
VZCodeContent,
|
|
9
|
+
} from './types';
|
|
10
|
+
export type { EditorCache } from './client/useEditorCache';
|
|
11
|
+
export type {
|
|
12
|
+
ThemeLabel,
|
|
13
|
+
ThemeOption,
|
|
14
|
+
} from './client/themes';
|
|
15
|
+
|
|
16
|
+
export { VZSidebar } from './client/VZSidebar';
|
|
17
|
+
export { VZSettings } from './client/VZSettings';
|
|
18
|
+
export { SplitPaneResizeContext } from './client/SplitPaneResizeContext';
|
|
19
|
+
export { Resizer } from './client/Resizer';
|
|
20
|
+
export { TabList } from './client/TabList';
|
|
21
|
+
export { CodeEditor } from './client/CodeEditor';
|
|
22
|
+
export { usePrettier } from './client/usePrettier';
|
|
23
|
+
export { PrettierErrorOverlay } from './client/PrettierErrorOverlay';
|
|
24
|
+
export { PresenceNotifications } from './client/PresenceNotifications';
|
|
25
|
+
export { useFileCRUD } from './client/useFileCRUD';
|
|
26
|
+
export { useSubmitOperation } from './client/useSubmitOperation';
|
|
27
|
+
export { vzReducer } from './client/vzReducer';
|
|
28
|
+
export { useActions } from './client/useActions';
|
|
29
|
+
export { useOpenDirectories } from './client/useOpenDirectories';
|
|
30
|
+
export {
|
|
31
|
+
defaultTheme,
|
|
32
|
+
useDynamicTheme,
|
|
33
|
+
} from './client/themes';
|
|
34
|
+
export { useEditorCache } from './client/useEditorCache';
|
package/src/server/index.js
CHANGED
|
@@ -22,8 +22,19 @@ const autoSaveDebounceTimeMS = 800;
|
|
|
22
22
|
// when the user is interacting with the widgets in the editor.
|
|
23
23
|
const throttleTimeMS = 100;
|
|
24
24
|
|
|
25
|
+
// Helper function to get the port from command line arguments or use default.
|
|
26
|
+
function getPortFromArgs(defaultPort = 3030) {
|
|
27
|
+
const portArg = process.argv.find((arg) =>
|
|
28
|
+
arg.startsWith('--port='),
|
|
29
|
+
);
|
|
30
|
+
if (portArg) {
|
|
31
|
+
return parseInt(portArg.split('=')[1], 10);
|
|
32
|
+
}
|
|
33
|
+
return defaultPort;
|
|
34
|
+
}
|
|
35
|
+
|
|
25
36
|
// Server port.
|
|
26
|
-
const port =
|
|
37
|
+
const port = getPortFromArgs();
|
|
27
38
|
|
|
28
39
|
const initialDocument = computeInitialDocument({
|
|
29
40
|
// Use the current working directory to look for files.
|
|
@@ -109,7 +120,7 @@ const save = () => {
|
|
|
109
120
|
|
|
110
121
|
// Handle renaming files.
|
|
111
122
|
if (previous.name !== current.name) {
|
|
112
|
-
fs.
|
|
123
|
+
fs.renameSync(previous.name, current.name);
|
|
113
124
|
}
|
|
114
125
|
}
|
|
115
126
|
|
|
@@ -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;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}.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;position:relative}.app .right{flex:1;flex-direction:column;min-width:0}.prettier-error-overlay{color:#ff3535;background-color:#0009;position:absolute;bottom:0;left:0;right:0;pointer-events:none}
|