vzcode 0.17.0 → 0.18.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 (37) hide show
  1. package/README.md +47 -39
  2. package/dist/assets/index-0053b846.css +1 -0
  3. package/dist/assets/{index-ab3ae13e.js → index-24635cd2.js} +37 -37
  4. package/dist/index.html +15 -5
  5. package/package.json +2 -2
  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 +63 -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-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,63 @@
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
+ const localStorageWriteDebounceMS = 800;
17
+
18
+ const localStoragePropertyName = 'vizHubCodeEditorWidth';
19
+ const initialWidthLocalStorage = +localStorage.getItem(
20
+ localStoragePropertyName,
21
+ );
22
+ // const initialWidthPercentage = window.innerWidth / 3;
23
+ const initialWidthDefault = 220;
24
+ const initialWidth =
25
+ initialWidthLocalStorage || initialWidthDefault;
26
+
27
+ const add = (a: number, b: number) => a + b;
28
+
29
+ export const SplitPaneResizeProvider = ({ children }) => {
30
+ const [codeEditorWidth, moveSplitPane] = useReducer(
31
+ add,
32
+ initialWidth,
33
+ );
34
+ const [isDragging, setIsDragging] = useState(false);
35
+
36
+ useEffect(() => {
37
+ if (codeEditorWidth !== initialWidth) {
38
+ const timeout = setTimeout(() => {
39
+ localStorage.setItem(
40
+ localStoragePropertyName,
41
+ '' + codeEditorWidth,
42
+ );
43
+ }, localStorageWriteDebounceMS);
44
+
45
+ return () => {
46
+ clearTimeout(timeout);
47
+ };
48
+ }
49
+ }, [codeEditorWidth]);
50
+
51
+ return (
52
+ <SplitPaneResizeContext.Provider
53
+ value={{
54
+ codeEditorWidth,
55
+ moveSplitPane,
56
+ isDragging,
57
+ setIsDragging,
58
+ }}
59
+ >
60
+ {children}
61
+ </SplitPaneResizeContext.Provider>
62
+ );
63
+ };
@@ -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],