vzcode 0.23.0 → 0.25.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 (30) hide show
  1. package/dist/assets/index-8628ebab.js +111 -0
  2. package/dist/assets/{index-c63b4be7.css → index-cf19de3b.css} +1 -1
  3. package/dist/assets/{worker-d5f0575a.js → worker-b775ddff.js} +1 -1
  4. package/dist/index.html +2 -2
  5. package/package.json +19 -9
  6. package/src/client/App.tsx +111 -30
  7. package/src/client/CodeEditor/getOrCreateEditor.ts +10 -1
  8. package/src/client/CodeEditor/index.tsx +9 -5
  9. package/src/client/CodeEditor/json1PresenceBroadcast.js +2 -0
  10. package/src/client/CodeEditor/json1PresenceDisplay.js +45 -1
  11. package/src/client/CodeEditor/widgets.ts +183 -10
  12. package/src/client/PresenceNotifications/index.tsx +145 -0
  13. package/src/client/PresenceNotifications/style.scss +17 -0
  14. package/src/client/PrettierErrorOverlay/index.tsx +9 -0
  15. package/src/client/PrettierErrorOverlay/style.scss +9 -0
  16. package/src/client/Settings.tsx +11 -1
  17. package/src/client/Sidebar/FileListing.tsx +0 -1
  18. package/src/client/Sidebar/Item.tsx +20 -11
  19. package/src/client/reducer.ts +119 -0
  20. package/src/client/style.scss +3 -0
  21. package/src/client/themes/index.ts +7 -0
  22. package/src/client/{themes.ts → themes/themes.ts} +5 -5
  23. package/src/client/{useDynamicTheme.ts → themes/useDynamicTheme.ts} +1 -1
  24. package/src/client/themes/vizhubTheme/colors.ts +80 -0
  25. package/src/client/themes/vizhubTheme/index.ts +166 -0
  26. package/src/client/usePrettier/index.ts +32 -14
  27. package/src/client/usePrettier/worker.ts +1 -1
  28. package/dist/assets/index-49f71876.js +0 -110
  29. package/src/client/useTabsState.ts +0 -58
  30. package/src/client/vizhubTheme.js +0 -1
@@ -4,6 +4,7 @@ import {
4
4
  ViewPlugin,
5
5
  Decoration,
6
6
  WidgetType,
7
+ keymap,
7
8
  } from '@codemirror/view';
8
9
  import {
9
10
  Annotation,
@@ -165,22 +166,27 @@ export const widgets = ({
165
166
  regexp: /rotate\(-?\d*\.?\d*\)/g,
166
167
  cursor: 'move',
167
168
  onDragStart(text, setText, e) {
168
- rotationOrigin = { x: e.screenX, y: e.screenY };
169
+ rotationOrigin = { x: e.clientX, y: e.clientY };
169
170
  },
170
171
 
171
172
  onDrag(text, setText, e) {
172
173
  if (rotationOrigin == null) return;
174
+ const rotationDegree = Math.round(
175
+ (Math.atan2(
176
+ rotationOrigin.y - e.clientY,
177
+ e.clientX - rotationOrigin.x,
178
+ ) *
179
+ 180) /
180
+ Math.PI,
181
+ );
173
182
  //Calculate the angle between the x axis and a line from where the user first clicks to the current location of the mouse.
174
- setText(
175
- `rotate(${Math.round(
176
- (Math.atan2(
177
- rotationOrigin.y - e.screenY,
178
- e.screenX - rotationOrigin.x,
179
- ) *
180
- 180) /
181
- Math.PI,
182
- )})`,
183
+ setText(`rotate(${rotationDegree})`);
184
+ const updateDragEvent = new CustomEvent(
185
+ 'updateRotateDrag',
186
+ { detail: rotationDegree },
183
187
  );
188
+
189
+ document.dispatchEvent(updateDragEvent);
184
190
  },
185
191
  onDragEnd(text, setText) {
186
192
  rotationOrigin = null;
@@ -312,3 +318,170 @@ class ColorWidget extends WidgetType {
312
318
  return false;
313
319
  }
314
320
  }
321
+
322
+ export const highlightWidgets = ViewPlugin.fromClass(
323
+ class {
324
+ showHighlight: boolean;
325
+ view: EditorView;
326
+ constructor(view: EditorView) {
327
+ this.showHighlight = false;
328
+ this.view = view;
329
+ }
330
+ },
331
+ {
332
+ decorations: (v) => {
333
+ if (!v.showHighlight) {
334
+ return Decoration.none;
335
+ }
336
+
337
+ const interactOpportunities = [];
338
+ const lines = v.view.state.doc.iter();
339
+ let line = lines.next();
340
+
341
+ //Offset is the number of characters before the regex so the highlighting can be placed properly.
342
+ let offset = 0;
343
+ while (!line.done) {
344
+ if (line.value === '\n') {
345
+ offset++;
346
+ line = lines.next();
347
+ continue;
348
+ }
349
+ const interactiveOccurances = line.value.matchAll(
350
+ //The below line contains all of the Regexes for all of the interactive widgets.
351
+ /\"\#([0-9]|[A-F]|[a-f]){6}\"|rotate\(-?\d*\.?\d*\)|https?:\/\/[^ "]+|vec2\(-?\b\d+\.?\d*\b\s*(,\s*-?\b\d+\.?\d*\b)?\)|true|false|(?<!\#)-?\b\d+\.?\d*\b|rgb\(.*\)/dg,
352
+ );
353
+ let interactOccurance =
354
+ interactiveOccurances.next();
355
+ while (!interactOccurance.done) {
356
+ const offsetInteract = interactOccurance.value;
357
+ offsetInteract.indices[0][0] += offset;
358
+ offsetInteract.indices[0][1] += offset;
359
+ interactOpportunities.push(offsetInteract);
360
+ interactOccurance = interactiveOccurances.next();
361
+ }
362
+ offset += line.value.length;
363
+ line = lines.next();
364
+ }
365
+ return Decoration.set(
366
+ interactOpportunities.map((opportunity) => {
367
+ return {
368
+ from: opportunity.indices[0][0],
369
+ to: opportunity.indices[0][1],
370
+ value: Decoration.mark({
371
+ class: 'cm-interact ',
372
+ }),
373
+ };
374
+ }),
375
+ );
376
+ },
377
+ eventHandlers: {
378
+ keydown(event, view) {
379
+ if (event.key == 'Alt') {
380
+ this.showHighlight = true;
381
+ }
382
+ },
383
+ keyup(event, view) {
384
+ if (event.key == 'Alt') {
385
+ this.showHighlight = false;
386
+ }
387
+ },
388
+ },
389
+ },
390
+ );
391
+
392
+ export const rotationIndicator = ViewPlugin.fromClass(
393
+ class {
394
+ view: EditorView;
395
+ textPosition?: number;
396
+ rotation: number;
397
+ constructor(view) {
398
+ this.view = view;
399
+ this.textPosition = null;
400
+ this.rotation = 0;
401
+
402
+ document.addEventListener(
403
+ 'updateRotateDrag',
404
+ (e: CustomEvent) => {
405
+ this.textPosition = this.view.posAtCoords(
406
+ rotationOrigin,
407
+ false,
408
+ );
409
+
410
+ this.rotation = e.detail;
411
+ },
412
+ );
413
+ }
414
+ },
415
+ {
416
+ decorations: (v) => {
417
+ if (rotationOrigin === null) {
418
+ return Decoration.none;
419
+ }
420
+ return Decoration.set([
421
+ {
422
+ from: v.textPosition,
423
+ to: v.textPosition,
424
+ value: Decoration.widget({
425
+ side: -1,
426
+ widget: new RotationCircle(v.rotation),
427
+ }),
428
+ },
429
+ ]);
430
+ },
431
+ },
432
+ );
433
+
434
+ class RotationCircle extends WidgetType {
435
+ angle: number;
436
+ constructor(angle: number) {
437
+ super();
438
+ this.angle = angle;
439
+ }
440
+
441
+ toDOM(view: EditorView): HTMLElement {
442
+ const parent = document.createElement('div');
443
+
444
+ parent.setAttribute('style', 'width:20px;height:20px');
445
+ parent.className = 'color-circle-parent';
446
+ const svg = document.createElementNS(
447
+ 'http://www.w3.org/2000/svg',
448
+ 'svg',
449
+ );
450
+ const colorCircle = document.createElementNS(
451
+ 'http://www.w3.org/2000/svg',
452
+ 'circle',
453
+ );
454
+ colorCircle.setAttributeNS(null, 'fill', '#808080');
455
+ colorCircle.setAttributeNS(null, 'r', '5');
456
+ colorCircle.setAttributeNS(null, 'cx', '5');
457
+ colorCircle.setAttributeNS(null, 'cy', '5');
458
+
459
+ const indicatorLine = document.createElementNS(
460
+ 'http://www.w3.org/2000/svg',
461
+ 'line',
462
+ );
463
+ indicatorLine.setAttributeNS(null, 'x1', '10');
464
+ indicatorLine.setAttributeNS(null, 'x2', '5');
465
+ indicatorLine.setAttributeNS(null, 'y1', '5');
466
+ indicatorLine.setAttributeNS(null, 'y2', '5');
467
+ indicatorLine.setAttributeNS(null, 'stroke', 'black');
468
+ indicatorLine.setAttributeNS(
469
+ null,
470
+ 'transform',
471
+ `rotate(${360 - this.angle},5,5)`,
472
+ );
473
+
474
+ svg.setAttributeNS(null, 'viewBox', '0 0 10 10');
475
+ svg.setAttributeNS(null, 'width', '20');
476
+ svg.setAttributeNS(null, 'height', '20');
477
+
478
+ svg.appendChild(colorCircle);
479
+ svg.appendChild(indicatorLine);
480
+
481
+ parent.appendChild(svg);
482
+ return parent;
483
+ }
484
+ ignoreEvent() {
485
+ return false;
486
+ }
487
+ }
@@ -0,0 +1,145 @@
1
+ import { useState, useEffect, useRef } from 'react';
2
+ import './style.scss';
3
+
4
+ type PresenceId = string;
5
+
6
+ // A presence notification is a message that is displayed when a user joins or leaves a session.
7
+ // It has the following properties:
8
+ // - `user` is the username of the user who joined or left the session.
9
+ // - `join` is a boolean value that indicates whether the user joined or left the session.
10
+ // - `true` means user has joined the session.
11
+ // - `false` means user has left the session.
12
+ type PresenceNotification = {
13
+ user: string;
14
+ join: boolean;
15
+ presenceId: PresenceId;
16
+ };
17
+
18
+ export const PresenceNotifications = ({
19
+ docPresence,
20
+ }: {
21
+ docPresence?: any;
22
+ }) => {
23
+ const [presenceNotifications, setPresenceNotifications] =
24
+ useState<Array<PresenceNotification>>([]);
25
+
26
+ // const [presenceIds, setPresenceIds] = useState<
27
+ // Array<any>
28
+ // >([]);
29
+ const alreadyJoinedPresenceIds = useRef<Set<PresenceId>>(
30
+ new Set(),
31
+ );
32
+
33
+ useEffect(() => {
34
+ // console.log('useEffect triggered.');
35
+ if (docPresence) {
36
+ // See https://share.github.io/sharedb/presence
37
+ docPresence.on('receive', (presenceId, update) => {
38
+ // Reason for notifications appearing on every cursor movement:
39
+ // code doesn't check if a notification about a user joining was previously sent
40
+
41
+ // Attempted solutions:
42
+ // - create an array called presenceIds that keeps track of the presences that join and leave
43
+ // - if presenceId is in presenceIds: local user was already alerted that a remote user joined
44
+ // - if presenceId is not in presenceIds: local user needs to be notified about a new user
45
+ // - problems:
46
+ // - the array is always empty inside useEffect (array works outside of useEffect)
47
+ // - causes check to fail
48
+ // - check docPresence.remotePresences, which keeps track of all the remote presences on the doc
49
+ // - see https://share.github.io/sharedb/api/presence
50
+ // - if presenceId is in docPresence.remotePresences: local user was already alerted that a remote user joined
51
+ // - if presenceId is not in docPresence.remotePresences: local user needs to be notified about a new user
52
+ // - problems:
53
+ // - useEffects is called when docPresence changes (in some cases, it's when the remote presence changes)
54
+ // - remotePresences already has the remote presence in the array, so check fails
55
+ // - could add another property to presence (true: already notified, false: need to be notified)
56
+ // - doesn't seem like an ideal solution
57
+
58
+ // console.log('docPresence:', docPresence);
59
+ // console.log('docPresence remotePresences:', docPresence.remotePresences);
60
+ // console.log('check for presence:', docPresence.remotePresences[presenceId]);
61
+
62
+ // TODO figure out how to get username from `presenceId`.
63
+ const user = 'someone';
64
+
65
+ // `true` means user has joined the session.
66
+ // `false` means user has left the session.
67
+ const join = update !== null;
68
+
69
+ console.log('join', join);
70
+
71
+ // Figure out if we have ever seen this user before.
72
+
73
+ // if (join && docPresence.remotePresences[presenceId] === undefined){
74
+ if (join) {
75
+ if (
76
+ !alreadyJoinedPresenceIds.current.has(
77
+ presenceId,
78
+ )
79
+ ) {
80
+ alreadyJoinedPresenceIds.current.add(
81
+ presenceId,
82
+ );
83
+ setPresenceNotifications((prev) => [
84
+ ...prev,
85
+ { user, join, presenceId },
86
+ ]);
87
+
88
+ // Remove it after 5 seconds.
89
+ setTimeout(() => {
90
+ setPresenceNotifications((prev) =>
91
+ prev.filter(
92
+ (notification) =>
93
+ notification.presenceId !==
94
+ presenceId &&
95
+ notification.join !== join,
96
+ ),
97
+ );
98
+ }, 5000);
99
+ }
100
+ } else if (!join) {
101
+ setPresenceNotifications((prev) => [
102
+ ...prev,
103
+ { user, join, presenceId },
104
+ ]);
105
+
106
+ // Remove it after 5 seconds.
107
+ // TODO clean up this duplication.
108
+ setTimeout(() => {
109
+ setPresenceNotifications((prev) =>
110
+ prev.filter(
111
+ (notification) =>
112
+ notification.presenceId !== presenceId &&
113
+ notification.join !== join,
114
+ ),
115
+ );
116
+ }, 5000);
117
+ }
118
+ });
119
+ }
120
+ }, [docPresence]);
121
+
122
+ // console.log(
123
+ // 'presenceNotifications',
124
+ // presenceNotifications,
125
+ // );
126
+ // console.log(
127
+ // 'presenceIds',
128
+ // presenceIds,
129
+ // );
130
+
131
+ return presenceNotifications.length > 0 ? (
132
+ <div className="vz-notification">
133
+ {presenceNotifications.map((notification, i) => {
134
+ const { user, join } = notification;
135
+ return (
136
+ <div key={i} className="vz-notification-item">
137
+ {join
138
+ ? `${user} has joined the session.`
139
+ : `${user} has left the session.`}
140
+ </div>
141
+ );
142
+ })}
143
+ </div>
144
+ ) : null;
145
+ };
@@ -0,0 +1,17 @@
1
+ .vz-notification {
2
+ position: fixed;
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 10px;
6
+ bottom: 20px;
7
+ right: 20px;
8
+
9
+ color: rgb(154, 160, 172);
10
+ padding: 15px;
11
+
12
+ .vz-notification-item {
13
+ background-color: rgb(54, 65, 89);
14
+ padding: 5px;
15
+ border-radius: 5px;
16
+ }
17
+ }
@@ -0,0 +1,9 @@
1
+ import './style.scss';
2
+
3
+ export const PrettierErrorOverlay = ({ prettierError }) => {
4
+ return prettierError !== null ? (
5
+ <pre className="prettier-error-overlay">
6
+ {prettierError}
7
+ </pre>
8
+ ) : null;
9
+ };
@@ -0,0 +1,9 @@
1
+ .prettier-error-overlay {
2
+ color: rgb(255, 53, 53);
3
+ background-color: rgba(0, 0, 0, 0.6);
4
+ position: absolute;
5
+ bottom: 0;
6
+ left: 0;
7
+ right: 0;
8
+ pointer-events: none;
9
+ }
@@ -1,4 +1,4 @@
1
- import { useCallback } from 'react';
1
+ import { useCallback, useRef } from 'react';
2
2
  import { Button, Modal, Form } from './bootstrap';
3
3
  import { ThemeLabel, themes } from './themes';
4
4
 
@@ -9,6 +9,8 @@ import { ThemeLabel, themes } from './themes';
9
9
  // { value: 30, label: '30 seconds' },
10
10
  // ];
11
11
 
12
+ export let username = 'Anonymous';
13
+
12
14
  export const Settings = ({
13
15
  show,
14
16
  onClose,
@@ -41,6 +43,12 @@ export const Settings = ({
41
43
  // [],
42
44
  // );
43
45
 
46
+ const usernameRef = useRef<HTMLInputElement>(null);
47
+ const handleUsernameChange = () => {
48
+ const usernameValue = usernameRef.current?.value;
49
+ username = usernameValue;
50
+ };
51
+
44
52
  return show ? (
45
53
  <Modal
46
54
  className="vz-settings"
@@ -56,6 +64,8 @@ export const Settings = ({
56
64
  <Form.Label>Username</Form.Label>
57
65
  <Form.Control
58
66
  type="text"
67
+ ref={usernameRef}
68
+ onChange={handleUsernameChange}
59
69
  placeholder="Enter username"
60
70
  />
61
71
  <Form.Text className="text-muted">
@@ -46,7 +46,6 @@ export const FileListing = ({
46
46
  handleDeleteClick={handleDeleteClick}
47
47
  handleRenameClick={handleRenameClick}
48
48
  isDir={false}
49
- name={name}
50
49
  >
51
50
  {name}
52
51
  </Item>
@@ -19,9 +19,9 @@ export const Item = ({
19
19
  const [renameValue, setRenameValue] = useState('');
20
20
 
21
21
  const onKeyDown = useCallback((event) => {
22
- // TODO make 'Escape' cancel the changes - do not commit the rename
23
- // https://github.com/vizhub-core/vzcode/issues/207
24
- if (event.key === 'Enter' || event.key === 'Escape') {
22
+ if (event.key === 'Escape') {
23
+ setIsRenaming(false);
24
+ } else if (event.key === 'Enter') {
25
25
  event.target.blur();
26
26
  }
27
27
  }, []);
@@ -42,9 +42,13 @@ export const Item = ({
42
42
  const [showModal, setShowModal] = useState(false);
43
43
 
44
44
  // Function to open the delete file confirmation modal
45
- const handleModalOpen = useCallback(() => {
46
- setShowModal(true);
47
- }, []);
45
+ const handleModalOpen = useCallback(
46
+ (event: React.MouseEvent) => {
47
+ event.stopPropagation();
48
+ setShowModal(true);
49
+ },
50
+ [],
51
+ );
48
52
 
49
53
  // Function to close the modal
50
54
  const handleModalClose = useCallback(() => {
@@ -52,10 +56,14 @@ export const Item = ({
52
56
  }, []);
53
57
 
54
58
  // Function to handle confirmation on modal
55
- const handleConfirmModal = useCallback(() => {
56
- setShowModal(false);
57
- handleDeleteClick();
58
- }, [handleDeleteClick]);
59
+ const handleConfirmModal = useCallback(
60
+ (event: React.MouseEvent) => {
61
+ event.stopPropagation();
62
+ setShowModal(false);
63
+ handleDeleteClick();
64
+ },
65
+ [handleDeleteClick],
66
+ );
59
67
 
60
68
  const handleMouseEnter = useCallback(() => {
61
69
  setIsHovered(true);
@@ -98,7 +106,8 @@ export const Item = ({
98
106
  <i
99
107
  className="bx bxs-edit utilities"
100
108
  style={{ color: '#abdafb' }}
101
- onClick={() => {
109
+ onClick={(event: React.MouseEvent) => {
110
+ event.stopPropagation();
102
111
  setIsRenaming(!isRenaming);
103
112
  setRenameValue(isDir ? name : children);
104
113
  }}
@@ -0,0 +1,119 @@
1
+ import { FileId } from '../types';
2
+ import { act } from 'react-dom/test-utils';
3
+
4
+ export const reducer = (state, action) => {
5
+ switch (action.type) {
6
+ // TODO phase this out
7
+
8
+ // The ordered list of tabs.
9
+ // TODO make this a URL param
10
+ case 'set_tab_list': {
11
+ return { ...state, tabList: action.tabList };
12
+ }
13
+ // TODO make this a URL param
14
+ case 'set_active_fileId': {
15
+ return {
16
+ ...state,
17
+ activeFileId: action.activeFileId,
18
+ };
19
+ }
20
+ case 'open_tab': {
21
+ if (!state.tabList.includes(action.fileId)) {
22
+ return {
23
+ ...state,
24
+ activeFileId: action.fileId,
25
+ tabList: [...state.tabList, action.fileId],
26
+ };
27
+ } else {
28
+ return {
29
+ ...state,
30
+ activeFileId: action.fileId,
31
+ };
32
+ }
33
+ }
34
+ case 'close_tab': {
35
+ const i = state.tabList.findIndex(
36
+ (fileId) => fileId === action.fileIdToRemove,
37
+ );
38
+ if (i != -1) {
39
+ const newTabList = [
40
+ ...state.tabList.slice(0, i),
41
+ ...state.tabList.slice(i + 1),
42
+ ];
43
+ if (state.activeFileId === action.fileIdToRemove) {
44
+ // set the new active file to the next tab over,
45
+ if (newTabList.length > 0) {
46
+ return {
47
+ ...state,
48
+ activeFileId:
49
+ i === 0 ? newTabList[i] : newTabList[i - 1],
50
+ tabList: newTabList,
51
+ };
52
+ } else {
53
+ // or clear out the active file
54
+ // if we've closed the last tab.
55
+ return {
56
+ ...state,
57
+ activeFileId: null,
58
+ tabList: newTabList,
59
+ };
60
+ }
61
+ }
62
+ return {
63
+ ...state,
64
+ tabList: newTabList,
65
+ };
66
+ }
67
+ return {
68
+ ...state,
69
+ };
70
+ }
71
+ case 'multi_close_tab': {
72
+ let newTabList = [...state.tabList]; // Create a copy of the tabList array.
73
+ let newActiveFileId: FileId = state.activeFileId;
74
+ action.idsToDelete.forEach((id) => {
75
+ const i = newTabList.findIndex(
76
+ (fileId) => fileId === id,
77
+ );
78
+ if (i !== -1) {
79
+ // Remove the tab from the tab list.
80
+ newTabList = [
81
+ ...newTabList.slice(0, i),
82
+ ...newTabList.slice(i + 1),
83
+ ];
84
+ // If we are closing the active file,
85
+ if (newActiveFileId === id) {
86
+ // set the new active file to the next tab over,
87
+ if (newTabList.length > 0) {
88
+ newActiveFileId =
89
+ i === 0 ? newTabList[i] : newTabList[i - 1];
90
+ } else {
91
+ // or clear out the active file if we've closed the last tab.
92
+ newActiveFileId = null;
93
+ }
94
+ }
95
+ }
96
+ });
97
+ return {
98
+ ...state,
99
+ tabList: newTabList,
100
+ activeFileId: newActiveFileId,
101
+ };
102
+ }
103
+ // The current theme.
104
+ // TODO persist this in local storage
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
+ throw Error('Unknown action: ' + action.type);
119
+ };
@@ -45,6 +45,9 @@ body,
45
45
  .left,
46
46
  .right {
47
47
  display: flex;
48
+
49
+ /* To suport PrettierErrorOverlay, which is position: absolute */
50
+ position: relative;
48
51
  }
49
52
 
50
53
  .right {
@@ -0,0 +1,7 @@
1
+ export {
2
+ themes,
3
+ defaultTheme,
4
+ themeOptionsByLabel,
5
+ } from './themes';
6
+ export type { ThemeLabel, ThemeOption } from './themes';
7
+ export { useDynamicTheme } from './useDynamicTheme';
@@ -7,7 +7,7 @@ import { githubDark } from '@uiw/codemirror-theme-github';
7
7
  import { material } from '@uiw/codemirror-theme-material';
8
8
  import { nord } from '@uiw/codemirror-theme-nord';
9
9
  import { xcodeLight } from '@uiw/codemirror-theme-xcode';
10
- // import { vizhub } from './vizhub-theme';
10
+ import { vizhubTheme } from './vizhubTheme';
11
11
 
12
12
  // CodeMirror themes
13
13
  export type ThemeLabel =
@@ -19,8 +19,8 @@ export type ThemeLabel =
19
19
  | 'oneDark'
20
20
  | 'okaidia'
21
21
  | 'github'
22
- | 'xcode';
23
- // | 'vizhub'
22
+ | 'xcode'
23
+ | 'vizhub';
24
24
 
25
25
  export type ThemeOption = {
26
26
  value: any;
@@ -37,7 +37,7 @@ export const themes: Array<ThemeOption> = [
37
37
  { value: okaidia, label: 'okaidia' },
38
38
  { value: githubDark, label: 'github' },
39
39
  { value: xcodeLight, label: 'xcode' },
40
- // { value: vizhub, label: 'vizhub' },
40
+ { value: vizhubTheme, label: 'vizhub' },
41
41
  ];
42
42
 
43
43
  // Map theme labels to theme values
@@ -53,4 +53,4 @@ export const themeOptionsByLabel: Record<
53
53
  );
54
54
 
55
55
  // The default theme
56
- export const defaultTheme: ThemeLabel = 'oneDark';
56
+ export const defaultTheme: ThemeLabel = 'vizhub';
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useRef } from 'react';
2
- import { EditorCache } from './useEditorCache';
2
+ import { EditorCache } from '../useEditorCache';
3
3
  import { ThemeLabel, themeOptionsByLabel } from './themes';
4
4
 
5
5
  export const useDynamicTheme = (