vzcode 0.17.0 → 0.19.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 (38) hide show
  1. package/README.md +47 -39
  2. package/dist/assets/index-0053b846.css +1 -0
  3. package/dist/assets/index-34134182.js +118 -0
  4. package/dist/index.html +15 -5
  5. package/package.json +4 -4
  6. package/src/client/App.tsx +92 -52
  7. package/src/client/CodeEditor/getOrCreateEditor.ts +33 -9
  8. package/src/client/CodeEditor/index.tsx +15 -11
  9. package/src/client/CodeEditor/json1PresenceBroadcast.js +10 -3
  10. package/src/client/CodeEditor/json1PresenceDisplay.js +7 -2
  11. package/src/client/CodeEditor/widgets.ts +5 -1
  12. package/src/client/Resizer/index.tsx +96 -0
  13. package/src/client/Resizer/styles.scss +29 -0
  14. package/src/client/Settings.tsx +29 -21
  15. package/src/client/Sidebar/DirectoryArrowSVG.jsx +4 -1
  16. package/src/client/Sidebar/DirectoryListing.tsx +17 -5
  17. package/src/client/Sidebar/FileListing.tsx +4 -1
  18. package/src/client/Sidebar/Listing.tsx +9 -2
  19. package/src/client/Sidebar/index.tsx +30 -8
  20. package/src/client/Sidebar/styles.scss +0 -1
  21. package/src/client/SplitPaneResizeContext.tsx +77 -0
  22. package/src/client/TabList/index.tsx +7 -2
  23. package/src/client/diff.js +7 -1
  24. package/src/client/featureFlags.js +2 -1
  25. package/src/client/sortFileTree.ts +3 -1
  26. package/src/client/style.scss +18 -12
  27. package/src/client/themes.ts +10 -8
  28. package/src/client/useDynamicTheme.ts +5 -2
  29. package/src/client/useOpenDirectories.ts +11 -8
  30. package/src/client/usePrettier/index.ts +76 -57
  31. package/src/client/usePrettier/worker.ts +4 -1
  32. package/src/client/useTabsState.ts +10 -3
  33. package/src/randomId.js +14 -1
  34. package/src/server/computeInitialDocument.js +18 -5
  35. package/src/server/index.js +7 -2
  36. package/src/types.ts +11 -4
  37. package/dist/assets/index-ab3ae13e.js +0 -115
  38. package/dist/assets/index-ccd52a89.css +0 -1
@@ -3,7 +3,11 @@ import Modal from 'react-bootstrap/Modal';
3
3
  import Form from 'react-bootstrap/Form';
4
4
  import Button from 'react-bootstrap/Button';
5
5
  import Select from 'react-select';
6
- import { ThemeLabel, themeOptionsByLabel, themes } from './themes';
6
+ import {
7
+ ThemeLabel,
8
+ themeOptionsByLabel,
9
+ themes,
10
+ } from './themes';
7
11
 
8
12
  const saveTimes = [
9
13
  { value: 1, label: '1 second' },
@@ -25,25 +29,21 @@ export const Settings = ({
25
29
  }) => {
26
30
  const handleChange = useCallback((selectedOption) => {
27
31
  setTheme(selectedOption.label);
28
- // TODO make this work.
29
- // Sketch of algorithm:
30
- // for each editor in the editor cache,
31
- // update the theme
32
- // editor.dispatch({
33
- // effects: themeSet.reconfigure(selectedOption.value),
34
- // });
35
32
  }, []);
36
33
 
37
- const handleSaveTimeChange = useCallback((selectedOption) => {
38
- const time = [{ value: selectedOption.value }];
39
- fetch('/saveTime', {
40
- method: 'POST',
41
- headers: {
42
- 'Content-Type': 'application/json',
43
- },
44
- body: JSON.stringify(time),
45
- });
46
- }, []);
34
+ const handleSaveTimeChange = useCallback(
35
+ (selectedOption) => {
36
+ const time = [{ value: selectedOption.value }];
37
+ fetch('/saveTime', {
38
+ method: 'POST',
39
+ headers: {
40
+ 'Content-Type': 'application/json',
41
+ },
42
+ body: JSON.stringify(time),
43
+ });
44
+ },
45
+ [],
46
+ );
47
47
 
48
48
  return show ? (
49
49
  <Modal
@@ -58,7 +58,10 @@ export const Settings = ({
58
58
  <Modal.Body>
59
59
  <Form.Group className="mb-3" controlId="formFork">
60
60
  <Form.Label>Username</Form.Label>
61
- <Form.Control type="text" placeholder="Enter username" />
61
+ <Form.Control
62
+ type="text"
63
+ placeholder="Enter username"
64
+ />
62
65
  <Form.Text className="text-muted">
63
66
  Enter a username to be displayed on your cursor
64
67
  </Form.Text>
@@ -76,8 +79,13 @@ export const Settings = ({
76
79
  </Form.Group>
77
80
  <Form.Group className="mb-3" controlId="formFork">
78
81
  <Form.Label>Auto-Save Time</Form.Label>
79
- <Select options={saveTimes} onChange={handleSaveTimeChange} />
80
- <Form.Text className="text-muted">Select an auto save time</Form.Text>
82
+ <Select
83
+ options={saveTimes}
84
+ onChange={handleSaveTimeChange}
85
+ />
86
+ <Form.Text className="text-muted">
87
+ Select an auto save time
88
+ </Form.Text>
81
89
  </Form.Group>
82
90
  </Modal.Body>
83
91
  <Modal.Footer>
@@ -1,4 +1,7 @@
1
- export const DirectoryArrowSVG = ({ height = 10, fill = 'currentcolor' }) => (
1
+ export const DirectoryArrowSVG = ({
2
+ height = 10,
3
+ fill = 'currentcolor',
4
+ }) => (
2
5
  <svg height={height} viewBox={'4 4 16 16'}>
3
6
  <path fill={fill} d="M8 17l9-5-9-5v10z" />
4
7
  </svg>
@@ -18,7 +18,10 @@ export const DirectoryListing = ({
18
18
  path: string;
19
19
  children: Array<FileTree | FileTreeFile>;
20
20
  handleRenameFileClick: (fileId: string) => void;
21
- handleDeleteFileClick: (fileId: string, event: React.MouseEvent) => void;
21
+ handleDeleteFileClick: (
22
+ fileId: string,
23
+ event: React.MouseEvent,
24
+ ) => void;
22
25
  handleFileClick: (fileId: string) => void;
23
26
  isDirectoryOpen: (path: string) => boolean;
24
27
  toggleDirectory: (path: string) => void;
@@ -35,7 +38,10 @@ export const DirectoryListing = ({
35
38
  console.log('TODO handleRenameDirectoryClick');
36
39
  }, []);
37
40
 
38
- const isOpen = useMemo(() => isDirectoryOpen(path), [isDirectoryOpen, path]);
41
+ const isOpen = useMemo(
42
+ () => isDirectoryOpen(path),
43
+ [isDirectoryOpen, path],
44
+ );
39
45
 
40
46
  return (
41
47
  <>
@@ -47,7 +53,9 @@ export const DirectoryListing = ({
47
53
  >
48
54
  <div
49
55
  className="arrow-wrapper"
50
- style={{ transform: `rotate(${isOpen ? 90 : 0}deg)` }}
56
+ style={{
57
+ transform: `rotate(${isOpen ? 90 : 0}deg)`,
58
+ }}
51
59
  >
52
60
  <DirectoryArrowSVG />
53
61
  </div>
@@ -62,8 +70,12 @@ export const DirectoryListing = ({
62
70
  <Listing
63
71
  entity={entity}
64
72
  key={fileId || path}
65
- handleRenameFileClick={handleRenameFileClick}
66
- handleDeleteFileClick={handleDeleteFileClick}
73
+ handleRenameFileClick={
74
+ handleRenameFileClick
75
+ }
76
+ handleDeleteFileClick={
77
+ handleDeleteFileClick
78
+ }
67
79
  handleFileClick={handleFileClick}
68
80
  isDirectoryOpen={isDirectoryOpen}
69
81
  toggleDirectory={toggleDirectory}
@@ -12,7 +12,10 @@ export const FileListing = ({
12
12
  name: string;
13
13
  fileId: FileId;
14
14
  handleRenameFileClick: (fileId: FileId) => void;
15
- handleDeleteFileClick: (fileId: FileId, event: React.MouseEvent) => void;
15
+ handleDeleteFileClick: (
16
+ fileId: FileId,
17
+ event: React.MouseEvent,
18
+ ) => void;
16
19
  handleFileClick: (fileId: FileId) => void;
17
20
  }) => {
18
21
  const handleClick = useCallback(() => {
@@ -1,5 +1,9 @@
1
1
  import { DirectoryListing } from './DirectoryListing';
2
- import { FileId, FileTree, FileTreeFile } from '../../types';
2
+ import {
3
+ FileId,
4
+ FileTree,
5
+ FileTreeFile,
6
+ } from '../../types';
3
7
  import { FileListing } from './FileListing';
4
8
 
5
9
  // A "Listing" is a "FileListing" or a "DirectoryListing"
@@ -14,7 +18,10 @@ export const Listing = ({
14
18
  }: {
15
19
  entity: FileTree | FileTreeFile;
16
20
  handleRenameFileClick: (fileId: FileId) => void;
17
- handleDeleteFileClick: (fileId: FileId, event: React.MouseEvent) => void;
21
+ handleDeleteFileClick: (
22
+ fileId: FileId,
23
+ event: React.MouseEvent,
24
+ ) => void;
18
25
  handleFileClick: (fileId: FileId) => void;
19
26
  isDirectoryOpen: (path: string) => boolean;
20
27
  toggleDirectory: (path: string) => void;
@@ -1,11 +1,16 @@
1
- import { useCallback, useMemo } from 'react';
1
+ import { useCallback, useContext, useMemo } from 'react';
2
+ import {
3
+ FileId,
4
+ FileTree,
5
+ FileTreeFile,
6
+ Files,
7
+ } from '../../types';
2
8
  import { getFileTree } from '../getFileTree';
3
9
  import { sortFileTree } from '../sortFileTree';
4
10
  import { disableSettings } from '../featureFlags';
11
+ import { SplitPaneResizeContext } from '../SplitPaneResizeContext';
5
12
  import { Listing } from './Listing';
6
-
7
13
  import './styles.scss';
8
- import { FileId, FileTree, FileTreeFile, Files } from '../../types';
9
14
 
10
15
  export const Sidebar = ({
11
16
  createFile,
@@ -20,7 +25,10 @@ export const Sidebar = ({
20
25
  files: Files;
21
26
  createFile?: () => void;
22
27
  handleRenameFileClick?: (fileId: FileId) => void;
23
- handleDeleteFileClick?: (fileId: FileId, event: React.MouseEvent) => void;
28
+ handleDeleteFileClick?: (
29
+ fileId: FileId,
30
+ event: React.MouseEvent,
31
+ ) => void;
24
32
  handleFileClick?: (fileId: FileId) => void;
25
33
  setIsSettingsOpen?: (isSettingsOpen: boolean) => void;
26
34
  isDirectoryOpen?: (path: string) => boolean;
@@ -35,8 +43,15 @@ export const Sidebar = ({
35
43
  setIsSettingsOpen(true);
36
44
  }, []);
37
45
 
46
+ const { codeEditorWidth } = useContext(
47
+ SplitPaneResizeContext,
48
+ );
49
+
38
50
  return (
39
- <div className="vz-sidebar">
51
+ <div
52
+ className="vz-sidebar"
53
+ style={{ width: codeEditorWidth + 'px' }}
54
+ >
40
55
  <div className="files">
41
56
  <div className="full-box">
42
57
  <div>
@@ -61,8 +76,12 @@ export const Sidebar = ({
61
76
  <Listing
62
77
  entity={entity}
63
78
  key={key}
64
- handleRenameFileClick={handleRenameFileClick}
65
- handleDeleteFileClick={handleDeleteFileClick}
79
+ handleRenameFileClick={
80
+ handleRenameFileClick
81
+ }
82
+ handleDeleteFileClick={
83
+ handleDeleteFileClick
84
+ }
66
85
  handleFileClick={handleFileClick}
67
86
  isDirectoryOpen={isDirectoryOpen}
68
87
  toggleDirectory={toggleDirectory}
@@ -72,7 +91,10 @@ export const Sidebar = ({
72
91
  : null}
73
92
  </div>
74
93
  {disableSettings ? null : (
75
- <div className="settings" onClick={handleSettingsClick}>
94
+ <div
95
+ className="settings"
96
+ onClick={handleSettingsClick}
97
+ >
76
98
  Settings
77
99
  </div>
78
100
  )}
@@ -1,6 +1,5 @@
1
1
  .vz-sidebar {
2
2
  height: 100%;
3
- width: 200px;
4
3
  background: rgb(54, 65, 89);
5
4
  display: flex;
6
5
  flex-direction: column;
@@ -0,0 +1,77 @@
1
+ // From https://github.com/vizhub-core/vizhub/edit/main/vizhub-v2/packages/neoFrontend/src/pages/VizPage/SplitPaneResizeContext/index.js
2
+ import {
3
+ useState,
4
+ createContext,
5
+ useReducer,
6
+ useEffect,
7
+ } from 'react';
8
+
9
+ export const SplitPaneResizeContext = createContext<{
10
+ codeEditorWidth: number;
11
+ moveSplitPane: (a: number) => void;
12
+ isDragging: boolean;
13
+ setIsDragging: (a: boolean) => void;
14
+ }>(null);
15
+
16
+ // The amount of time to wait idle before writing to localStorage.
17
+ // This is to avoid writing to localStorage on every resize event.
18
+ // MS = milliseconds
19
+ const localStorageWriteDebounceMS = 800;
20
+
21
+ const localStoragePropertyName = 'vizHubCodeEditorWidth';
22
+
23
+ const initialWidthDefault = 220;
24
+ let initialWidth: number = initialWidthDefault;
25
+
26
+ // If we're in the browser,
27
+ if (typeof window !== 'undefined') {
28
+ //check localStorage for a previously stored width.
29
+ const initialWidthFromLocalStorage: string | null =
30
+ window.localStorage.getItem(localStoragePropertyName);
31
+
32
+ // If there is a previously stored width,
33
+ if (initialWidthFromLocalStorage !== null) {
34
+ // use it as the initial width.
35
+ initialWidth = +initialWidthFromLocalStorage;
36
+ }
37
+ } else {
38
+ // If we're not in the browser, use the default initial width.
39
+ }
40
+
41
+ const add = (a: number, b: number) => a + b;
42
+
43
+ export const SplitPaneResizeProvider = ({ children }) => {
44
+ const [codeEditorWidth, moveSplitPane] = useReducer(
45
+ add,
46
+ initialWidth,
47
+ );
48
+ const [isDragging, setIsDragging] = useState(false);
49
+
50
+ useEffect(() => {
51
+ if (codeEditorWidth !== initialWidth) {
52
+ const timeout = setTimeout(() => {
53
+ window.localStorage.setItem(
54
+ localStoragePropertyName,
55
+ '' + codeEditorWidth,
56
+ );
57
+ }, localStorageWriteDebounceMS);
58
+
59
+ return () => {
60
+ clearTimeout(timeout);
61
+ };
62
+ }
63
+ }, [codeEditorWidth]);
64
+
65
+ return (
66
+ <SplitPaneResizeContext.Provider
67
+ value={{
68
+ codeEditorWidth,
69
+ moveSplitPane,
70
+ isDragging,
71
+ setIsDragging,
72
+ }}
73
+ >
74
+ {children}
75
+ </SplitPaneResizeContext.Provider>
76
+ );
77
+ };
@@ -6,7 +6,9 @@ import './style.scss';
6
6
  const fileNameSplit = (fileName) => {
7
7
  const split = fileName.split('/');
8
8
  if (split.length === 1) return split[split.length - 1];
9
- return split[split.length - 2] + '/' + split[split.length - 1];
9
+ return (
10
+ split[split.length - 2] + '/' + split[split.length - 1]
11
+ );
10
12
  };
11
13
 
12
14
  const Tab = ({
@@ -32,7 +34,10 @@ const Tab = ({
32
34
  [closeTab, fileId],
33
35
  );
34
36
 
35
- const tabName = useMemo(() => fileNameSplit(fileName), [fileName]);
37
+ const tabName = useMemo(
38
+ () => fileNameSplit(fileName),
39
+ [fileName],
40
+ );
36
41
 
37
42
  return (
38
43
  <div
@@ -3,4 +3,10 @@ import diffMatchPatch from 'diff-match-patch';
3
3
  import { json1Presence, textUnicode } from '../ot';
4
4
 
5
5
  export const diff = (a, b) =>
6
- jsondiff(a, b, diffMatchPatch, json1Presence, textUnicode);
6
+ jsondiff(
7
+ a,
8
+ b,
9
+ diffMatchPatch,
10
+ json1Presence,
11
+ textUnicode,
12
+ );
@@ -1,3 +1,4 @@
1
1
  // A feature flag to disable settings for release, until it's completed.
2
2
  // See https://vitejs.dev/guide/env-and-mode.html
3
- export const disableSettings = import.meta.env.VITE_DISABLE_SETTINGS === 'true';
3
+ export const disableSettings =
4
+ import.meta.env.VITE_DISABLE_SETTINGS === 'true';
@@ -11,7 +11,9 @@ export const sortFileTree = (fileTree: FileTree) => {
11
11
  const aIsFile = (a as FileTreeFile).file ? 1 : 0;
12
12
  const bIsFile = (b as FileTreeFile).file ? 1 : 0;
13
13
  // Directories before files.
14
- return aIsFile - bIsFile || a.name.localeCompare(b.name);
14
+ return (
15
+ aIsFile - bIsFile || a.name.localeCompare(b.name)
16
+ );
15
17
  });
16
18
  fileTree.children.forEach(sortFileTree);
17
19
  }
@@ -1,47 +1,53 @@
1
1
  :root {
2
- --vzcode-font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas,
3
- Liberation Mono, monospace;
2
+ --vzcode-font-family: ui-monospace, SFMono-Regular,
3
+ SF Mono, Menlo, Consolas, Liberation Mono, monospace;
4
+ --vz-bg-color: rgb(
5
+ 41,
6
+ 44,
7
+ 52
8
+ ); /* Variable for the background color */
4
9
  }
5
10
 
6
- /* width */
11
+ /* Scrollbar Styles */
7
12
  ::-webkit-scrollbar {
8
13
  width: 10px;
9
14
  height: 10px;
10
15
  }
11
16
 
12
- /* Track */
13
17
  ::-webkit-scrollbar-track {
14
- background: rgb(41, 44, 52);
18
+ background: var(--app-bg-color);
15
19
  }
16
20
 
17
- /* Handle */
18
21
  ::-webkit-scrollbar-thumb {
19
22
  background: rgb(37, 37, 38);
20
23
  }
21
24
 
22
- /* Handle on hover */
23
25
  ::-webkit-scrollbar-thumb:hover {
24
26
  background: #555;
25
27
  }
26
28
 
29
+ /* Layout Styles */
27
30
  html,
28
31
  body,
29
32
  #root,
30
33
  .app {
31
34
  height: 100%;
32
35
  }
33
- .app {
34
- /* TODO use a variable for this */
35
- background-color: rgb(41, 44, 52);
36
36
 
37
+ .app {
38
+ background-color: var(--vz-bg-color);
37
39
  font-family: 'Poppins', sans-serif;
38
40
  display: flex;
39
41
 
40
- .left {
42
+ /* To support Resizer, which is position: absolute */
43
+ position: relative;
44
+
45
+ .left,
46
+ .right {
41
47
  display: flex;
42
48
  }
49
+
43
50
  .right {
44
- display: flex;
45
51
  flex: 1;
46
52
  flex-direction: column;
47
53
  min-width: 0;
@@ -41,14 +41,16 @@ export const themes: Array<ThemeOption> = [
41
41
  ];
42
42
 
43
43
  // Map theme labels to theme values
44
- export const themeOptionsByLabel: Record<ThemeLabel, ThemeOption> =
45
- themes.reduce(
46
- (acc, themeOption: ThemeOption) => {
47
- acc[themeOption.label] = themeOption;
48
- return acc;
49
- },
50
- {} as Record<ThemeLabel, any>,
51
- );
44
+ export const themeOptionsByLabel: Record<
45
+ ThemeLabel,
46
+ ThemeOption
47
+ > = themes.reduce(
48
+ (acc, themeOption: ThemeOption) => {
49
+ acc[themeOption.label] = themeOption;
50
+ return acc;
51
+ },
52
+ {} as Record<ThemeLabel, any>,
53
+ );
52
54
 
53
55
  // The default theme
54
56
  export const defaultTheme: ThemeLabel = 'oneDark';
@@ -13,8 +13,11 @@ export const useDynamicTheme = (
13
13
  isFirstRun.current = false;
14
14
  } else {
15
15
  // When the user changes the theme, update the theme of all editors.
16
- for (const editorCacheValue of Array.from(editorCache.values())) {
17
- const { editor, themeCompartment } = editorCacheValue;
16
+ for (const editorCacheValue of Array.from(
17
+ editorCache.values(),
18
+ )) {
19
+ const { editor, themeCompartment } =
20
+ editorCacheValue;
18
21
  editor.dispatch({
19
22
  effects: themeCompartment.reconfigure([
20
23
  themeOptionsByLabel[theme].value,
@@ -19,21 +19,24 @@ import { useState, useCallback } from 'react';
19
19
  // https://github.com/vizhub-core/vizhub/blob/main/vizhub-v2/packages/neoFrontend/src/pages/VizPage/Body/Editor/FilesSection/useOpenDirectories.js
20
20
  export const useOpenDirectories = () => {
21
21
  // The set of open directories by path/
22
- const [openDirectories, setOpenDirectories] = useState<Set<string>>(
23
- new Set(),
24
- );
22
+ const [openDirectories, setOpenDirectories] = useState<
23
+ Set<string>
24
+ >(new Set());
25
25
 
26
26
  // Whether a directory is open.
27
- const isDirectoryOpen: (path: string) => boolean = useCallback(
28
- (path) => openDirectories.has(path),
29
- [openDirectories],
30
- );
27
+ const isDirectoryOpen: (path: string) => boolean =
28
+ useCallback(
29
+ (path) => openDirectories.has(path),
30
+ [openDirectories],
31
+ );
31
32
 
32
33
  // Toggle whether a directory is open.
33
34
  const toggleDirectory = useCallback(
34
35
  (path) => {
35
36
  const newOpenDirectories = new Set(openDirectories);
36
- newOpenDirectories[isDirectoryOpen(path) ? 'delete' : 'add'](path);
37
+ newOpenDirectories[
38
+ isDirectoryOpen(path) ? 'delete' : 'add'
39
+ ](path);
37
40
  setOpenDirectories(newOpenDirectories);
38
41
  },
39
42
  [openDirectories, isDirectoryOpen],