vzcode 0.27.0 → 0.29.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 (47) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/index-836bdce7.css +1 -0
  3. package/dist/assets/index-c7f894a9.js +115 -0
  4. package/dist/assets/worker-a2979249.js +366 -0
  5. package/dist/index.html +2 -2
  6. package/package.json +11 -5
  7. package/src/client/AIAssist.ts +137 -0
  8. package/src/client/AIAssistWidget/index.tsx +101 -0
  9. package/src/client/AIAssistWidget/style.scss +10 -0
  10. package/src/client/App/VZCodeContext.tsx +251 -0
  11. package/src/client/App/VZLeft.tsx +56 -0
  12. package/src/client/App/VZMiddle.tsx +91 -0
  13. package/src/client/App/VZRight.tsx +15 -0
  14. package/src/client/App/index.tsx +104 -0
  15. package/src/client/{style.scss → App/style.scss} +14 -3
  16. package/src/client/App/useShareDB.ts +103 -0
  17. package/src/client/CodeEditor/getOrCreateEditor.ts +70 -7
  18. package/src/client/CodeEditor/index.tsx +24 -0
  19. package/src/client/CodeEditor/json1PresenceDisplay.ts +38 -9
  20. package/src/client/CodeEditor/typeScriptCompletions.ts +108 -0
  21. package/src/client/CodeEditor/typeScriptLinter.ts +45 -0
  22. package/src/client/CodeErrorOverlay/index.tsx +44 -8
  23. package/src/client/CodeErrorOverlay/style.scss +22 -11
  24. package/src/client/SplitPaneResizeContext.tsx +195 -39
  25. package/src/client/TabList/index.tsx +1 -27
  26. package/src/client/{Resizer → VZResizer}/index.tsx +37 -13
  27. package/src/client/VZSettings.tsx +1 -1
  28. package/src/client/VZSidebar/CreateFileModal.tsx +93 -0
  29. package/src/client/VZSidebar/DeleteConfirmationModal.tsx +42 -24
  30. package/src/client/VZSidebar/index.tsx +116 -33
  31. package/src/client/VZSidebar/styles.scss +21 -1
  32. package/src/client/useFileCRUD.ts +19 -19
  33. package/src/client/useOpenDirectories.ts +12 -8
  34. package/src/client/usePrettier/index.ts +13 -1
  35. package/src/client/useTypeScript/index.ts +44 -0
  36. package/src/client/useTypeScript/requestTypes.ts +29 -0
  37. package/src/client/useTypeScript/worker.ts +222 -0
  38. package/src/client/usernameLocalStorage.ts +1 -1
  39. package/src/client/vzReducer/createInitialState.ts +4 -5
  40. package/src/index.ts +5 -1
  41. package/src/server/handleAIAssist.js +133 -0
  42. package/src/server/index.js +63 -1
  43. package/src/types.ts +78 -1
  44. package/dist/assets/index-5f9f376e.css +0 -1
  45. package/dist/assets/index-70b6d947.js +0 -111
  46. package/src/client/App.tsx +0 -286
  47. /package/src/client/{Resizer → VZResizer}/styles.scss +0 -0
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-70b6d947.js"></script>
28
- <link rel="stylesheet" href="/assets/index-5f9f376e.css">
27
+ <script type="module" crossorigin src="/assets/index-c7f894a9.js"></script>
28
+ <link rel="stylesheet" href="/assets/index-836bdce7.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.27.0",
3
+ "version": "0.29.0",
4
4
  "description": "Multiplayer code editor system",
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
@@ -45,16 +45,19 @@
45
45
  },
46
46
  "homepage": "https://github.com/vizhub-core/vzcode#readme",
47
47
  "dependencies": {
48
+ "@codemirror/autocomplete": "^6.11.0",
48
49
  "@codemirror/lang-css": "^6.2.1",
49
50
  "@codemirror/lang-html": "^6.4.6",
50
51
  "@codemirror/lang-javascript": "^6.2.1",
52
+ "@codemirror/lang-json": "^6.0.1",
51
53
  "@codemirror/lang-markdown": "^6.2.2",
52
54
  "@codemirror/state": "^6.3.1",
53
55
  "@codemirror/theme-one-dark": "^6.1.2",
54
- "@codemirror/view": "^6.21.4",
56
+ "@codemirror/view": "^6.22.0",
55
57
  "@lezer/highlight": "^1.1.6",
56
58
  "@replit/codemirror-interact": "^6.3.0",
57
59
  "@teamwork/websocket-json-stream": "^2.0.0",
60
+ "@typescript/vfs": "^1.5.0",
58
61
  "@uiw/codemirror-theme-abcdef": "^4.21.20",
59
62
  "@uiw/codemirror-theme-dracula": "^4.21.20",
60
63
  "@uiw/codemirror-theme-eclipse": "^4.21.20",
@@ -64,8 +67,10 @@
64
67
  "@uiw/codemirror-theme-okaidia": "^4.21.20",
65
68
  "@uiw/codemirror-theme-xcode": "^4.21.20",
66
69
  "@uiw/codemirror-themes": "^4.21.20",
70
+ "body-parser": "^1.20.2",
67
71
  "codemirror": "^6.0.1",
68
72
  "codemirror-ot": "^4.1.0",
73
+ "color-hash": "^2.0.2",
69
74
  "d3-array": "^3.2.4",
70
75
  "diff-match-patch": "^1.0.5",
71
76
  "dotenv": "^16.3.1",
@@ -73,6 +78,7 @@
73
78
  "json0-ot-diff": "^1.1.2",
74
79
  "ngrok": "^4.3.3",
75
80
  "open": "^9.1.0",
81
+ "openai": "^4.17.4",
76
82
  "ot-json1": "^1.0.2",
77
83
  "react": "^18.2.0",
78
84
  "react-bootstrap": "^2.9.1",
@@ -83,9 +89,9 @@
83
89
  "ws": "^8.14.2"
84
90
  },
85
91
  "devDependencies": {
86
- "@types/react": "^18.2.33",
87
- "@types/react-dom": "^18.2.14",
88
- "@vitejs/plugin-react": "^4.1.0",
92
+ "@types/react": "^18.2.37",
93
+ "@types/react-dom": "^18.2.15",
94
+ "@vitejs/plugin-react": "^4.1.1",
89
95
  "prettier": "^3.0.3",
90
96
  "sass": "^1.69.5",
91
97
  "typescript": "^5.2.2",
@@ -0,0 +1,137 @@
1
+ import { EditorView, keymap } from '@codemirror/view';
2
+ import {
3
+ FileId,
4
+ File,
5
+ ShareDBDoc,
6
+ VZCodeContent,
7
+ } from '../types';
8
+ import { generateRequestId } from './CodeEditor/typeScriptCompletions';
9
+ import { insertOp, replaceOp } from 'ot-json1';
10
+
11
+ import { TabState } from './vzReducer';
12
+
13
+ export const AIAssist = ({
14
+ shareDBDoc,
15
+ // The file id of the file the AI should assist with.
16
+ fileId,
17
+ tabList,
18
+
19
+ // Optional endpoint override.
20
+ aiAssistEndpoint = '/AIAssist',
21
+
22
+ // Optional additional options to pass to the endpoint.
23
+ aiAssistOptions = {},
24
+ }: {
25
+ shareDBDoc: ShareDBDoc<VZCodeContent>;
26
+ fileId: string;
27
+ aiAssistEndpoint: string;
28
+ aiAssistOptions?: {
29
+ [key: string]: any;
30
+ };
31
+ tabList: Array<TabState>;
32
+ }) =>
33
+ keymap.of([
34
+ {
35
+ key: 'control-m',
36
+ run: (view: EditorView) => {
37
+ if (
38
+ shareDBDoc.data.aiStreams === undefined ||
39
+ shareDBDoc.data.aiStreams[mostRecentStreamId] ===
40
+ null ||
41
+ shareDBDoc.data.aiStreams[mostRecentStreamId]
42
+ ?.AIStreamStatus.serverIsRunning !== true
43
+ ) {
44
+ startAIAssist(view, shareDBDoc, fileId, tabList);
45
+ } else {
46
+ haltAIAssist(shareDBDoc);
47
+ }
48
+
49
+ return true;
50
+ },
51
+ },
52
+ ]);
53
+
54
+ // TODO use ref forwarding instead of `export let`
55
+ export let mostRecentStreamId = null;
56
+
57
+ export const startAIAssist = async (
58
+ view: EditorView,
59
+ shareDBDoc: ShareDBDoc<VZCodeContent>,
60
+ fileId: FileId,
61
+ tabList: Array<TabState>,
62
+ // TODO mostRecentStreamIdRef: React.MutableRefObject<string>,
63
+ ) => {
64
+ const textToSend =
65
+ (await generateFilesContext(
66
+ tabList.map(
67
+ (tabState) =>
68
+ shareDBDoc.data.files[tabState.fileId],
69
+ ),
70
+ )) +
71
+ 'Current File:\n' +
72
+ view.state.sliceDoc(0, view.state.selection.main.to);
73
+
74
+ console.log(textToSend);
75
+
76
+ // TODO mostRecentStreamIdRef.current = generateRequestId();
77
+ mostRecentStreamId = generateRequestId();
78
+
79
+ if (shareDBDoc.data['aiStreams'] === undefined) {
80
+ shareDBDoc.submitOp(insertOp(['aiStreams'], {}), {
81
+ source: 'AIClient',
82
+ });
83
+ }
84
+
85
+ shareDBDoc.submitOp(
86
+ insertOp(['aiStreams', mostRecentStreamId], {
87
+ AIStreamStatus: {
88
+ clientWantsToStart: true,
89
+ serverIsRunning: false,
90
+ text: textToSend,
91
+ insertionCursor: view.state.selection.main.to,
92
+ fileId: fileId,
93
+ },
94
+ }),
95
+ { source: 'AIClient' },
96
+ );
97
+ };
98
+
99
+ export const haltAIAssist = (
100
+ shareDBDoc: ShareDBDoc<VZCodeContent>,
101
+ ) => {
102
+ const haltGenerationOp = replaceOp(
103
+ [
104
+ 'aiStreams',
105
+ mostRecentStreamId,
106
+ 'AIStreamStatus',
107
+ 'clientWantsToStart',
108
+ ],
109
+ true,
110
+ false,
111
+ );
112
+
113
+ shareDBDoc.submitOp(haltGenerationOp, {
114
+ source: 'AIClient',
115
+ });
116
+ };
117
+
118
+ export const generateFilesContext = async (
119
+ goodFiles: File[],
120
+ ): Promise<string> => {
121
+ const input = goodFiles
122
+ .map((file) => {
123
+ const nameSubstring = file.name
124
+ // ?.substring(0, maxFileNameLength)
125
+ .trim();
126
+
127
+ const textSubstring = file.text
128
+ // ?.substring(0, maxFileTextLength)
129
+ .trim();
130
+
131
+ // Generate Markdown that AI will understand.
132
+ return `File \`${nameSubstring}\`:\n\`\`\`${textSubstring}\`\`\``;
133
+ })
134
+ .join('\n\n');
135
+
136
+ return input;
137
+ };
@@ -0,0 +1,101 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ import { Button } from '../bootstrap';
3
+ import './style.scss';
4
+ import {
5
+ mostRecentStreamId,
6
+ haltAIAssist,
7
+ startAIAssist,
8
+ } from '../AIAssist';
9
+ import {
10
+ FileId,
11
+ ShareDBDoc,
12
+ VZCodeContent,
13
+ } from '../../types';
14
+ import { EditorCache } from '../useEditorCache';
15
+ import { TabState } from '../vzReducer';
16
+ // From
17
+ // https://primer.style/foundations/icons/zap-24
18
+ const ZapSVG = () => (
19
+ <svg
20
+ xmlns="http://www.w3.org/2000/svg"
21
+ viewBox="0 0 24 24"
22
+ width="24"
23
+ height="24"
24
+ >
25
+ <path d="M15.716 1.329a1.341 1.341 0 0 1 2.109 1.55L15.147 9h4.161c1.623 0 2.372 2.016 1.143 3.075L8.102 22.721a1.148 1.148 0 0 1-1.81-1.317L8.996 15H4.674c-1.619 0-2.37-2.008-1.148-3.07l12.19-10.6Zm.452 1.595L4.51 13.061a.25.25 0 0 0 .164.439h5.45a.749.749 0 0 1 .692 1.041l-2.559 6.066 11.215-9.668a.25.25 0 0 0-.164-.439H14a.75.75 0 0 1-.687-1.05Z"></path>
26
+ </svg>
27
+ );
28
+
29
+ // From Wikimedia
30
+ // https://commons.wikimedia.org/wiki/File:Stopsign.svg
31
+ const StopSVG = () => (
32
+ <svg
33
+ width="15pt"
34
+ height="15pt"
35
+ version="1.1"
36
+ viewBox="0 0 15 15"
37
+ xmlns="http://www.w3.org/2000/svg"
38
+ >
39
+ <g transform="scale(.75)" fill="#fff" stroke="#000">
40
+ <path d="m17.5 0.5c1.1055 0 2 0.89453 2 2v15c0 1.1055-0.89453 2-2 2h-15c-1.1055 0-2-0.89453-2-2v-15c0-1.1055 0.89453-2 2-2z" />
41
+ </g>
42
+ </svg>
43
+ );
44
+
45
+ export const AIAssistWidget = ({
46
+ activeFileId,
47
+ shareDBDoc,
48
+ editorCache,
49
+ tabList,
50
+ }: {
51
+ activeFileId: FileId;
52
+ shareDBDoc: ShareDBDoc<VZCodeContent>;
53
+ editorCache: EditorCache;
54
+ tabList: Array<TabState>;
55
+ }) => {
56
+ const [AIAssistRunning, setAIAssistRunning] =
57
+ useState(false);
58
+
59
+ useEffect(() => {
60
+ const handleOp = (op, source) => {
61
+ if (op !== null && op[0] === 'aiStreams') {
62
+ // The check if the value is true is required
63
+ // because the value could be null rather than false.
64
+ setAIAssistRunning(
65
+ shareDBDoc.data.aiStreams[mostRecentStreamId]
66
+ ?.AIStreamStatus.serverIsRunning === true,
67
+ );
68
+ }
69
+ };
70
+ shareDBDoc.on('op', handleOp);
71
+
72
+ return () => {
73
+ shareDBDoc.off('op', handleOp);
74
+ };
75
+ }, [shareDBDoc]);
76
+
77
+ const handleClick = useCallback(() => {
78
+ if (!AIAssistRunning) {
79
+ startAIAssist(
80
+ editorCache.get(activeFileId).editor,
81
+ shareDBDoc,
82
+ activeFileId,
83
+ tabList,
84
+ );
85
+ } else {
86
+ haltAIAssist(shareDBDoc);
87
+ }
88
+ }, [activeFileId, AIAssistRunning]);
89
+
90
+ return (
91
+ <div className="vz-code-ai-assist-widget">
92
+ <Button
93
+ variant="light"
94
+ title="Start AI Assist"
95
+ onClick={handleClick}
96
+ >
97
+ {AIAssistRunning ? <StopSVG /> : <ZapSVG />}
98
+ </Button>
99
+ </div>
100
+ );
101
+ };
@@ -0,0 +1,10 @@
1
+ .vz-code-ai-assist-widget {
2
+ position: absolute;
3
+ bottom: 20px;
4
+ right: 20px;
5
+
6
+ .btn {
7
+ /* Center the icon */
8
+ padding: 6px 6px 7px 6px;
9
+ }
10
+ }
@@ -0,0 +1,251 @@
1
+ import { createContext, useReducer } from 'react';
2
+ import {
3
+ Files,
4
+ ShareDBDoc,
5
+ Username,
6
+ VZCodeContent,
7
+ } from '../../types';
8
+ import { useSubmitOperation } from '../useSubmitOperation';
9
+ import { usePrettier } from '../usePrettier';
10
+ import { useTypeScript } from '../useTypeScript';
11
+ import {
12
+ TabState,
13
+ createInitialState,
14
+ vzReducer,
15
+ } from '../vzReducer';
16
+ import {
17
+ ThemeLabel,
18
+ defaultTheme,
19
+ useDynamicTheme,
20
+ } from '../themes';
21
+ import { useActions } from '../useActions';
22
+ import { useOpenDirectories } from '../useOpenDirectories';
23
+ import { useFileCRUD } from '../useFileCRUD';
24
+ import {
25
+ EditorCache,
26
+ useEditorCache,
27
+ } from '../useEditorCache';
28
+
29
+ // This context centralizes all the "smart" logic
30
+ // to do with the application state. This includes
31
+ // * Accessing and manipulating ShareDB data
32
+ // * Centralized application state
33
+ export const VZCodeContext =
34
+ createContext<VZCodeContextValue>(null);
35
+
36
+ // The type of the object provided by this context.
37
+ export type VZCodeContextValue = {
38
+ content: VZCodeContent | null;
39
+ shareDBDoc: ShareDBDoc<VZCodeContent> | null;
40
+ submitOperation: (
41
+ next: (content: VZCodeContent) => VZCodeContent,
42
+ ) => void;
43
+ localPresence: any;
44
+ docPresence: any;
45
+
46
+ files: Files | null;
47
+ createFile: (fileName: string) => void;
48
+ renameFile: (fileId: string, fileName: string) => void;
49
+ deleteFile: (fileId: string) => void;
50
+ deleteDirectory: (directoryId: string) => void;
51
+
52
+ activeFileId: string | null;
53
+ setActiveFileId: (fileId: string | null) => void;
54
+
55
+ tabList: Array<TabState>;
56
+ openTab: ({
57
+ fileId,
58
+ isTransient,
59
+ }: {
60
+ fileId: string;
61
+ isTransient?: boolean;
62
+ }) => void;
63
+ closeTabs: (fileIds: string[]) => void;
64
+
65
+ isSettingsOpen: boolean;
66
+ setIsSettingsOpen: (isSettingsOpen: boolean) => void;
67
+ closeSettings: () => void;
68
+
69
+ theme: ThemeLabel;
70
+ setTheme: (theme: ThemeLabel) => void;
71
+
72
+ username: Username;
73
+ setUsername: (username: Username) => void;
74
+
75
+ isDirectoryOpen: (path: string) => boolean;
76
+ toggleDirectory: (path: string) => void;
77
+
78
+ editorCache: EditorCache;
79
+ editorWantsFocus: boolean;
80
+ editorNoLongerWantsFocus: () => void;
81
+
82
+ errorMessage: string | null;
83
+
84
+ typeScriptWorker: Worker | null;
85
+ };
86
+
87
+ export const VZCodeProvider = ({
88
+ content,
89
+ shareDBDoc,
90
+ localPresence,
91
+ docPresence,
92
+ prettierWorker,
93
+ typeScriptWorker,
94
+ initialUsername,
95
+ children,
96
+ codeError = null,
97
+ }: {
98
+ content: VZCodeContent;
99
+ shareDBDoc: ShareDBDoc<VZCodeContent>;
100
+ localPresence: any;
101
+ docPresence: any;
102
+ prettierWorker: Worker;
103
+ typeScriptWorker: Worker;
104
+ initialUsername: Username;
105
+ children: React.ReactNode;
106
+ codeError?: string | null;
107
+ }) => {
108
+ // A convenience function for mutating the ShareDB document
109
+ // based submitting OT ops generated by diffing the JSON.
110
+ const submitOperation: (
111
+ next: (content: VZCodeContent) => VZCodeContent,
112
+ ) => void = useSubmitOperation(shareDBDoc);
113
+
114
+ // Auto-run Pretter after local changes.
115
+ const {
116
+ prettierError,
117
+ }: {
118
+ prettierError: string | null;
119
+ } = usePrettier(
120
+ shareDBDoc,
121
+ submitOperation,
122
+ prettierWorker,
123
+ );
124
+
125
+ // The error message shows either:
126
+ // * `prettierError` - errors from Prettier, client-side only
127
+ // * `codeError` - errors from an external source, such as
128
+ // build-time errors or intercepted runtime errors.
129
+ // Since `prettierError` surfaces syntax errors, it's more likely to be
130
+ // useful to the user, so we prioritize it.
131
+ const errorMessage: string | null = prettierError
132
+ ? prettierError
133
+ : codeError;
134
+
135
+ // Set up the TypeScript Language Server worker.
136
+ // This acts as a central "brain" for features powered
137
+ // by the TypeScript Language Server including:
138
+ // * Completions
139
+ // * Linting
140
+ useTypeScript({
141
+ content,
142
+ typeScriptWorker,
143
+ });
144
+
145
+ // Set up the reducer that manages much of the application state.
146
+ // See https://react.dev/reference/react/useReducer
147
+ const [state, dispatch] = useReducer(
148
+ vzReducer,
149
+ { defaultTheme, initialUsername },
150
+ createInitialState,
151
+ );
152
+
153
+ // Unpack state.
154
+ const {
155
+ tabList,
156
+ activeFileId,
157
+ theme,
158
+ isSettingsOpen,
159
+ editorWantsFocus,
160
+ username,
161
+ } = state;
162
+
163
+ // Functions for dispatching actions to the reducer.
164
+ const {
165
+ setActiveFileId,
166
+ openTab,
167
+ closeTabs,
168
+ setTheme,
169
+ setIsSettingsOpen,
170
+ closeSettings,
171
+ editorNoLongerWantsFocus,
172
+ setUsername,
173
+ } = useActions(dispatch);
174
+
175
+ // The set of open directories.
176
+ // TODO move this into reducer/useActions
177
+ const { isDirectoryOpen, toggleDirectory } =
178
+ useOpenDirectories();
179
+
180
+ // Cache of CodeMirror editors by file id.
181
+ const editorCache: EditorCache = useEditorCache();
182
+
183
+ // Handle dynamic theme changes.
184
+ useDynamicTheme(editorCache, theme);
185
+
186
+ // Handle file CRUD operations (Create, Read, Update, Delete)
187
+ const {
188
+ createFile,
189
+ renameFile,
190
+ deleteFile,
191
+ deleteDirectory,
192
+ } = useFileCRUD({
193
+ submitOperation,
194
+ closeTabs,
195
+ openTab,
196
+ });
197
+
198
+ // Isolate the files object from the document.
199
+ const files: Files | null = content
200
+ ? content.files
201
+ : null;
202
+
203
+ // The value provided by this context.
204
+ const value: VZCodeContextValue = {
205
+ content,
206
+ shareDBDoc,
207
+ submitOperation,
208
+ localPresence,
209
+ docPresence,
210
+
211
+ files,
212
+ createFile,
213
+ renameFile,
214
+ deleteFile,
215
+ deleteDirectory,
216
+
217
+ activeFileId,
218
+ setActiveFileId,
219
+
220
+ tabList,
221
+ openTab,
222
+ closeTabs,
223
+
224
+ isSettingsOpen,
225
+ setIsSettingsOpen,
226
+ closeSettings,
227
+
228
+ theme,
229
+ setTheme,
230
+
231
+ username,
232
+ setUsername,
233
+
234
+ isDirectoryOpen,
235
+ toggleDirectory,
236
+
237
+ editorCache,
238
+ editorWantsFocus,
239
+ editorNoLongerWantsFocus,
240
+
241
+ errorMessage,
242
+
243
+ typeScriptWorker,
244
+ };
245
+
246
+ return (
247
+ <VZCodeContext.Provider value={value}>
248
+ {children}
249
+ </VZCodeContext.Provider>
250
+ );
251
+ };
@@ -0,0 +1,56 @@
1
+ import { useContext } from 'react';
2
+ import { VZSettings } from '../VZSettings';
3
+ import { VZSidebar } from '../VZSidebar';
4
+ import { VZCodeContext } from './VZCodeContext';
5
+
6
+ // The middle portion of the VZCode environment, containing:
7
+ // * The sidebar
8
+ // * The settings modal
9
+ export const VZLeft = () => {
10
+ // TODO leverage this context in deeper levels of the component tree.
11
+ const {
12
+ files,
13
+ createFile,
14
+ renameFile,
15
+ deleteFile,
16
+ deleteDirectory,
17
+ activeFileId,
18
+ openTab,
19
+ closeTabs,
20
+ isSettingsOpen,
21
+ setIsSettingsOpen,
22
+ closeSettings,
23
+ theme,
24
+ setTheme,
25
+ username,
26
+ setUsername,
27
+ isDirectoryOpen,
28
+ toggleDirectory,
29
+ } = useContext(VZCodeContext);
30
+
31
+ return (
32
+ <div className="left">
33
+ <VZSidebar
34
+ files={files}
35
+ createFile={createFile}
36
+ renameFile={renameFile}
37
+ deleteFile={deleteFile}
38
+ deleteDirectory={deleteDirectory}
39
+ openTab={openTab}
40
+ closeTabs={closeTabs}
41
+ setIsSettingsOpen={setIsSettingsOpen}
42
+ isDirectoryOpen={isDirectoryOpen}
43
+ toggleDirectory={toggleDirectory}
44
+ activeFileId={activeFileId}
45
+ />
46
+ <VZSettings
47
+ show={isSettingsOpen}
48
+ onClose={closeSettings}
49
+ theme={theme}
50
+ setTheme={setTheme}
51
+ username={username}
52
+ setUsername={setUsername}
53
+ />
54
+ </div>
55
+ );
56
+ };