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.
Files changed (41) hide show
  1. package/dist/assets/{index-9c8bbae0.js → index-284b7339.js} +50 -50
  2. package/dist/assets/index-e160f758.css +1 -0
  3. package/dist/index.html +2 -6
  4. package/package.json +7 -5
  5. package/src/client/App.tsx +47 -213
  6. package/src/client/CodeEditor/getOrCreateEditor.ts +3 -0
  7. package/src/client/CodeEditor/index.tsx +36 -6
  8. package/src/client/CodeEditor/{json1PresenceBroadcast.js → json1PresenceBroadcast.ts} +5 -3
  9. package/src/client/CodeEditor/{json1PresenceDisplay.js → json1PresenceDisplay.ts} +9 -3
  10. package/src/client/CodeEditor/widgets.ts +112 -10
  11. package/src/client/PresenceNotifications/index.tsx +0 -2
  12. package/src/client/TabList/Tab.tsx +56 -0
  13. package/src/client/TabList/index.tsx +11 -62
  14. package/src/client/TabList/style.scss +21 -9
  15. package/src/client/{Settings.tsx → VZSettings.tsx} +1 -1
  16. package/src/client/{Sidebar → VZSidebar}/FileListing.tsx +3 -0
  17. package/src/client/{Sidebar → VZSidebar}/Item.tsx +10 -7
  18. package/src/client/{Sidebar → VZSidebar}/Listing.tsx +3 -0
  19. package/src/client/{Sidebar → VZSidebar}/index.tsx +5 -6
  20. package/src/client/{Sidebar → VZSidebar}/styles.scss +17 -12
  21. package/src/client/bootstrap.ts +3 -0
  22. package/src/client/style.scss +5 -2
  23. package/src/client/themes/index.ts +7 -0
  24. package/src/client/{themes.ts → themes/themes.ts} +5 -5
  25. package/src/client/{useDynamicTheme.ts → themes/useDynamicTheme.ts} +1 -1
  26. package/src/client/themes/vizhubTheme/colors.ts +80 -0
  27. package/src/client/themes/vizhubTheme/index.ts +166 -0
  28. package/src/client/useActions.ts +74 -0
  29. package/src/client/useFileCRUD.ts +101 -0
  30. package/src/client/useOpenDirectories.ts +25 -7
  31. package/src/client/useSubmitOperation.ts +25 -0
  32. package/src/client/vzReducer.ts +118 -0
  33. package/src/index.ts +34 -0
  34. package/src/server/index.js +13 -2
  35. package/dist/assets/index-cf19de3b.css +0 -1
  36. package/src/client/reducer.ts +0 -122
  37. package/src/client/useTabsState.ts +0 -95
  38. package/src/client/vizhubTheme.js +0 -1
  39. /package/src/client/{Sidebar → VZSidebar}/ConfirmationBox.tsx +0 -0
  40. /package/src/client/{Sidebar → VZSidebar}/DirectoryArrowSVG.jsx +0 -0
  41. /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
- // Toggle whether a directory is open.
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
- (path) => {
36
- const newOpenDirectories = new Set(openDirectories);
37
- newOpenDirectories[
38
- isDirectoryOpen(path) ? 'delete' : 'add'
39
- ](path);
40
- setOpenDirectories(newOpenDirectories);
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';
@@ -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 = 3030;
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.renamesync(previous.name, current.name);
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}