vzcode 0.26.0 → 0.28.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 (53) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/index-a9585cbc.js +111 -0
  3. package/dist/assets/index-d30c9f1b.css +1 -0
  4. package/dist/index.html +2 -2
  5. package/package.json +6 -4
  6. package/src/client/AIAssist.ts +46 -0
  7. package/src/client/App.tsx +86 -43
  8. package/src/client/CodeEditor/getOrCreateEditor.ts +22 -2
  9. package/src/client/CodeEditor/index.tsx +75 -12
  10. package/src/client/CodeEditor/json1PresenceBroadcast.ts +6 -2
  11. package/src/client/CodeEditor/json1PresenceDisplay.ts +0 -45
  12. package/src/client/CodeEditor/style.scss +5 -1
  13. package/src/client/CodeErrorOverlay/index.tsx +37 -0
  14. package/src/client/CodeErrorOverlay/style.scss +41 -0
  15. package/src/client/PresenceNotifications/PresenceNotifications.test.ts +134 -0
  16. package/src/client/PresenceNotifications/index.tsx +118 -103
  17. package/src/client/SplitPaneResizeContext.tsx +12 -7
  18. package/src/client/TabList/Tab.tsx +20 -6
  19. package/src/client/TabList/index.tsx +18 -8
  20. package/src/client/TabList/style.scss +5 -0
  21. package/src/client/VZSettings.tsx +33 -20
  22. package/src/client/VZSidebar/{ConfirmationBox.tsx → DeleteConfirmationModal.tsx} +17 -14
  23. package/src/client/VZSidebar/DirectoryListing.tsx +27 -26
  24. package/src/client/VZSidebar/FileListing.tsx +16 -19
  25. package/src/client/VZSidebar/Item.tsx +83 -37
  26. package/src/client/VZSidebar/Listing.tsx +17 -14
  27. package/src/client/VZSidebar/index.tsx +87 -45
  28. package/src/client/VZSidebar/styles.scss +21 -1
  29. package/src/client/style.scss +3 -0
  30. package/src/client/useActions.ts +25 -6
  31. package/src/client/useEditorCache.ts +2 -1
  32. package/src/client/useFileCRUD.ts +29 -27
  33. package/src/client/usernameLocalStorage.ts +43 -0
  34. package/src/client/vzReducer/closeTabReducer.test.ts +177 -0
  35. package/src/client/vzReducer/closeTabsReducer.ts +68 -0
  36. package/src/client/vzReducer/createInitialState.ts +19 -0
  37. package/src/client/vzReducer/editorNoLongerWantsFocusReducer.ts +9 -0
  38. package/src/client/vzReducer/index.ts +107 -0
  39. package/src/client/vzReducer/openTabReducer.test.ts +110 -0
  40. package/src/client/vzReducer/openTabReducer.ts +57 -0
  41. package/src/client/vzReducer/setActiveFileIdReducer.ts +9 -0
  42. package/src/client/vzReducer/setIsSettingsOpenReducer.ts +9 -0
  43. package/src/client/vzReducer/setThemeReducer.ts +9 -0
  44. package/src/client/vzReducer/setUsernameReducer.ts +9 -0
  45. package/src/index.ts +9 -3
  46. package/src/server/handleAIAssist.js +69 -0
  47. package/src/server/index.js +9 -0
  48. package/src/types.ts +12 -5
  49. package/dist/assets/index-284b7339.js +0 -111
  50. package/dist/assets/index-e160f758.css +0 -1
  51. package/src/client/PrettierErrorOverlay/index.tsx +0 -9
  52. package/src/client/PrettierErrorOverlay/style.scss +0 -9
  53. package/src/client/vzReducer.ts +0 -118
@@ -1,33 +1,39 @@
1
- import {
2
- useState,
3
- useEffect,
4
- useCallback,
5
- useRef,
6
- useReducer,
7
- } from 'react';
1
+ import { useState, useEffect, useReducer } from 'react';
8
2
  import ShareDBClient from 'sharedb-client-browser/dist/sharedb-client-umd.cjs';
9
3
  import { json1Presence } from '../ot';
10
4
  import { randomId } from '../randomId';
11
5
  import { CodeEditor } from './CodeEditor';
12
6
  import { VZSettings } from './VZSettings';
13
7
  import { VZSidebar } from './VZSidebar';
14
- import { Files, ShareDBDoc, VZCodeContent } from '../types';
8
+ import {
9
+ Files,
10
+ ShareDBDoc,
11
+ Username,
12
+ VZCodeContent,
13
+ } from '../types';
15
14
  import { TabList } from './TabList';
16
15
  import { useOpenDirectories } from './useOpenDirectories';
17
16
  import { defaultTheme, useDynamicTheme } from './themes';
18
- import { useEditorCache } from './useEditorCache';
17
+ import {
18
+ EditorCache,
19
+ useEditorCache,
20
+ } from './useEditorCache';
19
21
  import { usePrettier } from './usePrettier';
20
22
  // @ts-ignore
21
23
  import PrettierWorker from './usePrettier/worker?worker';
22
24
  import { SplitPaneResizeProvider } from './SplitPaneResizeContext';
23
25
  import { Resizer } from './Resizer';
24
26
  import { PresenceNotifications } from './PresenceNotifications';
25
- import { PrettierErrorOverlay } from './PrettierErrorOverlay';
26
- import { vzReducer } from './vzReducer';
27
+ import { CodeErrorOverlay } from './CodeErrorOverlay';
28
+ import { vzReducer, createInitialState } from './vzReducer';
27
29
  import { useActions } from './useActions';
28
30
  import { useFileCRUD } from './useFileCRUD';
29
- import './style.scss';
30
31
  import { useSubmitOperation } from './useSubmitOperation';
32
+ import {
33
+ useInitialUsername,
34
+ usePersistUsername,
35
+ } from './usernameLocalStorage';
36
+ import './style.scss';
31
37
 
32
38
  // Instantiate the Prettier worker.
33
39
  const prettierWorker = new PrettierWorker();
@@ -53,17 +59,6 @@ function App() {
53
59
  const [shareDBDoc, setShareDBDoc] =
54
60
  useState<ShareDBDoc<VZCodeContent> | null>(null);
55
61
 
56
- const submitOperation: (
57
- next: (content: VZCodeContent) => VZCodeContent,
58
- ) => void = useSubmitOperation(shareDBDoc);
59
-
60
- // Auto-run Pretter after local changes.
61
- const { prettierError } = usePrettier(
62
- shareDBDoc,
63
- submitOperation,
64
- prettierWorker,
65
- );
66
-
67
62
  // Local ShareDB presence, for broadcasting our cursor position
68
63
  // so other clients can see it.
69
64
  // See https://share.github.io/sharedb/api/local-presence
@@ -127,7 +122,14 @@ function App() {
127
122
  });
128
123
 
129
124
  // Set up our local presence for broadcasting this client's presence.
130
- setLocalPresence(docPresence.create(randomId()));
125
+ const generateTimestampedId = () => {
126
+ const timestamp = Date.now().toString(36);
127
+ const randomPart = randomId();
128
+ return `${timestamp}-${randomPart}`;
129
+ };
130
+ setLocalPresence(
131
+ docPresence.create(generateTimestampedId()),
132
+ );
131
133
 
132
134
  // Store docPresence so child components can listen for changes.
133
135
  setDocPresence(docPresence);
@@ -141,17 +143,40 @@ function App() {
141
143
  };
142
144
  }, []);
143
145
 
146
+ // Get the initial username from localStorage.
147
+ const initialUsername: Username = useInitialUsername();
148
+
149
+ const submitOperation: (
150
+ next: (content: VZCodeContent) => VZCodeContent,
151
+ ) => void = useSubmitOperation(shareDBDoc);
152
+
153
+ // Auto-run Pretter after local changes.
154
+ const {
155
+ prettierError,
156
+ }: {
157
+ prettierError: string | null;
158
+ } = usePrettier(
159
+ shareDBDoc,
160
+ submitOperation,
161
+ prettierWorker,
162
+ );
163
+
144
164
  // https://react.dev/reference/react/useReducer
145
- const [state, dispatch] = useReducer(vzReducer, {
146
- tabList: [],
147
- activeFileId: null,
148
- theme: defaultTheme,
149
- isSettingsOpen: false,
150
- });
165
+ const [state, dispatch] = useReducer(
166
+ vzReducer,
167
+ { defaultTheme, initialUsername },
168
+ createInitialState,
169
+ );
151
170
 
152
171
  // Unpack state.
153
- const { tabList, activeFileId, theme, isSettingsOpen } =
154
- state;
172
+ const {
173
+ tabList,
174
+ activeFileId,
175
+ theme,
176
+ isSettingsOpen,
177
+ editorWantsFocus,
178
+ username,
179
+ } = state;
155
180
 
156
181
  // Functions for dispatching actions to the reducer.
157
182
  const {
@@ -161,6 +186,8 @@ function App() {
161
186
  setTheme,
162
187
  setIsSettingsOpen,
163
188
  closeSettings,
189
+ editorNoLongerWantsFocus,
190
+ setUsername,
164
191
  } = useActions(dispatch);
165
192
 
166
193
  // The set of open directories.
@@ -168,8 +195,10 @@ function App() {
168
195
  const { isDirectoryOpen, toggleDirectory } =
169
196
  useOpenDirectories();
170
197
 
198
+ usePersistUsername(username);
199
+
171
200
  // Cache of CodeMirror editors by file id.
172
- const editorCache = useEditorCache();
201
+ const editorCache: EditorCache = useEditorCache();
173
202
 
174
203
  // Handle dynamic theme changes.
175
204
  useDynamicTheme(editorCache, theme);
@@ -177,9 +206,14 @@ function App() {
177
206
  // Handle file CRUD operations.
178
207
  const {
179
208
  createFile,
180
- handleRenameFileClick,
181
- handleDeleteClick,
182
- } = useFileCRUD({ submitOperation, closeTabs });
209
+ renameFile,
210
+ deleteFile,
211
+ deleteDirectory,
212
+ } = useFileCRUD({
213
+ submitOperation,
214
+ closeTabs,
215
+ openTab,
216
+ });
183
217
 
184
218
  // Isolate the files object from the document.
185
219
  const files: Files | null = content
@@ -191,11 +225,12 @@ function App() {
191
225
  <div className="app">
192
226
  <div className="left">
193
227
  <VZSidebar
194
- createFile={createFile}
195
228
  files={files}
196
- handleRenameFileClick={handleRenameFileClick}
197
- handleDeleteFileClick={handleDeleteClick}
198
- handleFileClick={openTab}
229
+ createFile={createFile}
230
+ renameFile={renameFile}
231
+ deleteFile={deleteFile}
232
+ deleteDirectory={deleteDirectory}
233
+ openTab={openTab}
199
234
  setIsSettingsOpen={setIsSettingsOpen}
200
235
  isDirectoryOpen={isDirectoryOpen}
201
236
  toggleDirectory={toggleDirectory}
@@ -206,6 +241,8 @@ function App() {
206
241
  onClose={closeSettings}
207
242
  theme={theme}
208
243
  setTheme={setTheme}
244
+ username={username}
245
+ setUsername={setUsername}
209
246
  />
210
247
  </div>
211
248
  <div className="right">
@@ -214,7 +251,9 @@ function App() {
214
251
  tabList={tabList}
215
252
  activeFileId={activeFileId}
216
253
  setActiveFileId={setActiveFileId}
254
+ openTab={openTab}
217
255
  closeTabs={closeTabs}
256
+ createFile={createFile}
218
257
  />
219
258
  {content && activeFileId ? (
220
259
  <CodeEditor
@@ -225,13 +264,17 @@ function App() {
225
264
  activeFileId={activeFileId}
226
265
  theme={theme}
227
266
  editorCache={editorCache}
267
+ editorWantsFocus={editorWantsFocus}
268
+ editorNoLongerWantsFocus={
269
+ editorNoLongerWantsFocus
270
+ }
271
+ username={username}
228
272
  />
229
273
  ) : null}
230
- <PrettierErrorOverlay
231
- prettierError={prettierError}
232
- />
274
+ <CodeErrorOverlay errorMessage={prettierError} />
233
275
  <PresenceNotifications
234
276
  docPresence={docPresence}
277
+ localPresence={localPresence}
235
278
  />
236
279
  </div>
237
280
  <Resizer />
@@ -12,6 +12,7 @@ import { json1Presence, textUnicode } from '../../ot';
12
12
  import {
13
13
  FileId,
14
14
  ShareDBDoc,
15
+ Username,
15
16
  VZCodeContent,
16
17
  } from '../../types';
17
18
  import { json1PresenceBroadcast } from './json1PresenceBroadcast';
@@ -27,6 +28,7 @@ import {
27
28
  EditorCacheValue,
28
29
  } from '../useEditorCache';
29
30
  import { ThemeLabel, themeOptionsByLabel } from '../themes';
31
+ import { AIAssist } from '../AIAssist';
30
32
 
31
33
  // Language extensions for CodeMirror.
32
34
  // Keys are file extensions.
@@ -64,6 +66,9 @@ export const getOrCreateEditor = ({
64
66
  theme,
65
67
  onInteract,
66
68
  editorCache,
69
+ usernameRef,
70
+ aiAssistEndpoint,
71
+ aiAssistOptions,
67
72
  }: {
68
73
  fileId: FileId;
69
74
 
@@ -82,6 +87,11 @@ export const getOrCreateEditor = ({
82
87
  theme: ThemeLabel;
83
88
  onInteract?: () => void;
84
89
  editorCache: EditorCache;
90
+ usernameRef: React.MutableRefObject<Username>;
91
+ aiAssistEndpoint?: string;
92
+ aiAssistOptions?: {
93
+ [key: string]: any;
94
+ };
85
95
  }): EditorCacheValue => {
86
96
  // Cache hit
87
97
  if (editorCache.has(fileId)) {
@@ -118,13 +128,15 @@ export const getOrCreateEditor = ({
118
128
  );
119
129
 
120
130
  // Deals with broadcasting changes in cursor location and selection.
121
- if (localPresence)
131
+ if (localPresence) {
122
132
  extensions.push(
123
133
  json1PresenceBroadcast({
124
134
  path: textPath,
125
135
  localPresence,
136
+ usernameRef,
126
137
  }),
127
138
  );
139
+ }
128
140
 
129
141
  // Deals with receiving the broadcas from other clients and displaying them.
130
142
  if (docPresence)
@@ -152,7 +164,7 @@ export const getOrCreateEditor = ({
152
164
  // using a Compartment.
153
165
  const languageCompartment = new Compartment();
154
166
  // See https://github.com/vizhub-core/vzcode/issues/55
155
- const getLanguageExtension = (fileExtension) => {
167
+ const getLanguageExtension = (fileExtension: string) => {
156
168
  const languageFunc = languageExtensions[fileExtension];
157
169
  return languageFunc ? languageFunc() : undefined;
158
170
  };
@@ -195,6 +207,14 @@ export const getOrCreateEditor = ({
195
207
 
196
208
  extensions.push(rotationIndicator);
197
209
 
210
+ extensions.push(
211
+ AIAssist({
212
+ fileId,
213
+ aiAssistEndpoint,
214
+ aiAssistOptions,
215
+ }),
216
+ );
217
+
198
218
  const editor = new EditorView({
199
219
  state: EditorState.create({
200
220
  doc: text,
@@ -2,10 +2,12 @@ import {
2
2
  useRef,
3
3
  useLayoutEffect,
4
4
  useCallback,
5
+ useMemo,
5
6
  } from 'react';
6
7
  import {
7
8
  FileId,
8
9
  ShareDBDoc,
10
+ Username,
9
11
  VZCodeContent,
10
12
  } from '../../types';
11
13
  import { ThemeLabel, defaultTheme } from '../themes';
@@ -25,6 +27,11 @@ export const CodeEditor = ({
25
27
  theme = defaultTheme,
26
28
  filesPath = ['files'],
27
29
  editorCache,
30
+ editorWantsFocus,
31
+ editorNoLongerWantsFocus,
32
+ username,
33
+ aiAssistEndpoint,
34
+ aiAssistOptions,
28
35
  }: {
29
36
  activeFileId: FileId;
30
37
  shareDBDoc: ShareDBDoc<VZCodeContent> | null;
@@ -39,6 +46,22 @@ export const CodeEditor = ({
39
46
  // Defaults to `files` if not provided.
40
47
  filesPath?: string[];
41
48
  editorCache: EditorCache;
49
+
50
+ // Whether the editor should be focused.
51
+ editorWantsFocus: boolean;
52
+
53
+ // Signals that the editor no longer wants focus.
54
+ editorNoLongerWantsFocus: () => void;
55
+ username: Username;
56
+
57
+ // The server endpoint for the AI Assist service.
58
+ aiAssistEndpoint?: string;
59
+
60
+ // Additional options to pass to the AI Assist service,
61
+ // an object with string values.
62
+ aiAssistOptions?: {
63
+ [key: string]: string;
64
+ };
42
65
  }) => {
43
66
  const ref = useRef<HTMLDivElement>(null);
44
67
 
@@ -67,17 +90,15 @@ export const CodeEditor = ({
67
90
  }, 800);
68
91
  }, [submitOperation]);
69
92
 
70
- // Every time the active file switches from one file to another,
71
- // the editor corresponding to the old file is removed from the DOM,
72
- // and the editor corresponding to the new file is added to the DOM.
73
- useLayoutEffect(() => {
74
- // Guard against cases where page is still loading.
75
- if (!ref.current) return;
76
- if (!shareDBDoc) return;
93
+ // Track username on a ref, so that we can use it in the
94
+ // presence object's `username` property.
95
+ const usernameRef = useRef<Username>(username);
96
+ usernameRef.current = username;
77
97
 
78
- // Get the editor corresponding to the active file.
79
- // Looks in `editorCache` first, and if not found, creates a new editor.
80
- const editorCacheValue: EditorCacheValue =
98
+ // Get the editor corresponding to the active file.
99
+ // Looks in `editorCache` first, and if not found, creates a new editor.
100
+ const editorCacheValue: EditorCacheValue = useMemo(
101
+ () =>
81
102
  getOrCreateEditor({
82
103
  fileId: activeFileId,
83
104
  shareDBDoc,
@@ -87,7 +108,32 @@ export const CodeEditor = ({
87
108
  theme,
88
109
  onInteract,
89
110
  editorCache,
90
- });
111
+ usernameRef,
112
+ aiAssistEndpoint,
113
+ aiAssistOptions,
114
+ }),
115
+ [
116
+ activeFileId,
117
+ shareDBDoc,
118
+ filesPath,
119
+ localPresence,
120
+ docPresence,
121
+ theme,
122
+ onInteract,
123
+ editorCache,
124
+ usernameRef,
125
+ aiAssistEndpoint,
126
+ aiAssistOptions,
127
+ ],
128
+ );
129
+
130
+ // Every time the active file switches from one file to another,
131
+ // the editor corresponding to the old file is removed from the DOM,
132
+ // and the editor corresponding to the new file is added to the DOM.
133
+ useLayoutEffect(() => {
134
+ // Guard against cases where page is still loading.
135
+ if (!ref.current) return;
136
+ if (!shareDBDoc) return;
91
137
 
92
138
  // Add the editor to the DOM.
93
139
  ref.current.appendChild(editorCacheValue.editor.dom);
@@ -97,7 +143,24 @@ export const CodeEditor = ({
97
143
  // This happens every time `activeFileId` changes.
98
144
  ref.current.removeChild(editorCacheValue.editor.dom);
99
145
  };
100
- }, [shareDBDoc, activeFileId, onInteract]);
146
+ }, [shareDBDoc, editorCacheValue]);
147
+
148
+ // useEffect(() => {
149
+ // if (editorWantsFocus) {
150
+ // editorCacheValue.editor.focus();
151
+ // }
152
+
153
+ // Focus the editor
154
+ useLayoutEffect(() => {
155
+ if (editorWantsFocus) {
156
+ editorCacheValue.editor.focus();
157
+ editorNoLongerWantsFocus();
158
+ }
159
+ }, [
160
+ editorWantsFocus,
161
+ editorCacheValue,
162
+ editorNoLongerWantsFocus,
163
+ ]);
101
164
 
102
165
  return <div className="vz-code-editor" ref={ref}></div>;
103
166
  };
@@ -5,11 +5,13 @@ import { EditorView } from 'codemirror';
5
5
  export const json1PresenceBroadcast = ({
6
6
  path,
7
7
  localPresence,
8
+ usernameRef,
8
9
  }) =>
9
10
  // See https://discuss.codemirror.net/t/codemirror-6-proper-way-to-listen-for-changes/2395/10
10
11
  EditorView.updateListener.of((viewUpdate: ViewUpdate) => {
11
12
  // If this update modified the cursor / selection,
12
13
  // we broadcast the selection update via ShareDB presence.
14
+
13
15
  if (viewUpdate.selectionSet) {
14
16
  // Isolate the single selection to use for presence.
15
17
  // Unfortunately JSON1 with presence does not yet
@@ -24,8 +26,10 @@ export const json1PresenceBroadcast = ({
24
26
  start: [...path, from],
25
27
  end: [...path, to],
26
28
 
27
- // TODO fill this in with the user's actual username.
28
- username: 'anonymous',
29
+ // We use a ref here so that the presence always broadcasts
30
+ // the latest username, in the case that it has been changed
31
+ // in the Settings mid-session.
32
+ username: usernameRef.current,
29
33
  };
30
34
 
31
35
  // Broadcast presence to remote clients!
@@ -37,10 +37,6 @@ export const json1PresenceDisplay = ({
37
37
  // Receive remote presence changes.
38
38
  docPresence.on('receive', (id, presence) => {
39
39
  // If presence === null, the user has disconnected / exited
40
- // notification(presence);
41
- // console.log('docPresence in presenceDisplay:',docPresence);
42
- // console.log('presence in presenceDisplay:', presence);
43
-
44
40
  // We also check if the presence is for the current file or not.
45
41
  if (presence && pathMatches(path, presence)) {
46
42
  presenceState[id] = presence;
@@ -107,47 +103,6 @@ export const json1PresenceDisplay = ({
107
103
  presenceTheme,
108
104
  ];
109
105
 
110
- // const notification = (presence) => {
111
- // const notif = document.createElement('div');
112
- // notif.className = 'notification-presence';
113
- // if (presence === null){
114
- // notif.textContent = 'User asdfgasdfgasdfg left the collaboration session.';
115
- // notif.style.display = 'block';
116
- // }
117
- // // The following causes the popup to show every time the remote cursor moves
118
- // // to a different location on the file and not when the user first joins
119
- // // else {
120
- // // notif.textContent = presence + 'joined the collaboration session.';
121
- // // notif.style.display = 'block';
122
- // // }
123
- // document.body.appendChild(notif);
124
-
125
- // const notifStyle = document.createElement('style');
126
- // notifStyle.innerText = `
127
- // .notification-presence {
128
- // position: fixed;
129
- // display: none;
130
- // bottom: 20px;
131
- // right: 20px;
132
- // max-width: 300px;
133
- // overflow-x: hidden;
134
- // white-space: nowrap;
135
- // background-color: rgb(54, 65, 89);
136
- // color: rgb(154, 160, 172);
137
- // padding: 15px;
138
- // }
139
- // .notification-presence:hover {
140
- // overflow-x: auto;
141
- // }`;
142
- // document.head.appendChild(notifStyle);
143
-
144
- // setTimeout(() => {
145
- // notif.style.display = 'none';
146
- // }, 10000);
147
-
148
- // return notif;
149
- // };
150
-
151
106
  const presenceAnnotation = Annotation.define();
152
107
 
153
108
  // Checks that the path of this file
@@ -4,8 +4,12 @@
4
4
  font-family: var(--vzcode-font-family);
5
5
  overflow-x: auto;
6
6
 
7
- /* TODO QA this */
8
7
  .cm-editor {
9
8
  flex: 1;
10
9
  }
10
+
11
+ .cm-scroller {
12
+ font-family: var(--vzcode-font-family);
13
+ font-size: var(--vzcode-font-size);
14
+ }
11
15
  }
@@ -0,0 +1,37 @@
1
+ import { useState, useCallback, useEffect } from 'react';
2
+ import './style.scss';
3
+
4
+ const enableErrorDismiss = true;
5
+
6
+ export const CodeErrorOverlay = ({
7
+ errorMessage,
8
+ }: {
9
+ errorMessage: string | null;
10
+ }) => {
11
+ const [isOverlayVisible, setIsOverlayVisible] =
12
+ useState(true);
13
+
14
+ // If errorMessage changes, set the overlay to be visible
15
+ useEffect(() => {
16
+ if (errorMessage !== null) {
17
+ setIsOverlayVisible(true);
18
+ }
19
+ }, [errorMessage]);
20
+
21
+ const handleCloseClick = useCallback(() => {
22
+ // Set the visibility state to false when the button is clicked
23
+ setIsOverlayVisible(false);
24
+ }, []);
25
+
26
+ return isOverlayVisible && errorMessage !== null ? (
27
+ <div className="vz-code-error-overlay">
28
+ {errorMessage}
29
+ {enableErrorDismiss ? (
30
+ <div
31
+ className={'bx bx-x error-dismiss-button'}
32
+ onClick={handleCloseClick}
33
+ ></div>
34
+ ) : null}
35
+ </div>
36
+ ) : null;
37
+ };
@@ -0,0 +1,41 @@
1
+ .vz-code-error-overlay {
2
+ color: rgb(255, 35, 35);
3
+ font-family: var(--vzcode-font-family);
4
+ font-size: var(--vzcode-font-size);
5
+ background-color: rgba(0, 0, 0, 0.6);
6
+ position: absolute;
7
+ bottom: 0;
8
+ left: 0;
9
+ right: 0;
10
+ // pointer-events: none;
11
+ margin: 0;
12
+ padding: 8px;
13
+ border-top-left-radius: 12px;
14
+ border-top-right-radius: 12px;
15
+
16
+ /* Define your custom styles here */
17
+ /* For example, change the font size and color */
18
+ font-size: 16px;
19
+ color: #FF5733;
20
+
21
+ /* Add a box shadow for a subtle effect */
22
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
23
+
24
+ /* Customize the dismiss button style */
25
+ .error-dismiss-button {
26
+ position: absolute;
27
+ top: 5px; /* Adjust the top position */
28
+ right: 5px; /* Adjust the right position */
29
+ margin: 0;
30
+ padding: 5px; /* Adjust padding for the button */
31
+ cursor: pointer;
32
+ color: white;
33
+ background-color: #FF5733; /* Change the button's background color */
34
+ border-radius: 50%; /* Make it circular */
35
+ }
36
+
37
+ /* Style the button on hover */
38
+ .error-dismiss-button:hover {
39
+ background-color: #FF826D; /* Change the background color on hover */
40
+ }
41
+ }