vzcode 0.5.0 → 0.7.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 +20 -5
  2. package/dist/assets/index-ccd52a89.css +1 -0
  3. package/dist/assets/index-d2d6b59b.js +89 -0
  4. package/dist/index.html +17 -3
  5. package/package.json +26 -21
  6. package/src/client/{App.jsx → App.tsx} +75 -120
  7. package/src/client/CodeEditor/getOrCreateEditor.ts +113 -0
  8. package/src/client/CodeEditor/index.tsx +50 -0
  9. package/src/client/{json1PresenceDisplay.js → CodeEditor/json1PresenceDisplay.js} +2 -2
  10. package/src/client/CodeEditor/style.scss +11 -0
  11. package/src/client/Settings.tsx +76 -0
  12. package/src/client/Sidebar/DirectoryListing.tsx +77 -0
  13. package/src/client/Sidebar/{File.jsx → FileListing.tsx} +14 -4
  14. package/src/client/Sidebar/Listing.tsx +44 -0
  15. package/src/client/Sidebar/index.tsx +81 -0
  16. package/src/client/Sidebar/styles.scss +84 -0
  17. package/src/client/TabList/index.tsx +82 -0
  18. package/src/client/TabList/style.scss +32 -0
  19. package/src/client/{getFileTree.js → getFileTree.ts} +6 -3
  20. package/src/client/main.jsx +1 -1
  21. package/src/client/{sortFileTree.js → sortFileTree.ts} +7 -3
  22. package/src/client/style.scss +49 -0
  23. package/src/client/themes.ts +31 -0
  24. package/src/client/{Sidebar/useOpenDirectories.js → useOpenDirectories.ts} +18 -14
  25. package/src/client/useTabsState.ts +51 -0
  26. package/src/server/index.js +0 -0
  27. package/src/types.ts +69 -0
  28. package/dist/assets/index-29b6a606.css +0 -1
  29. package/dist/assets/index-523ecad7.js +0 -89
  30. package/src/client/CodeEditor.jsx +0 -31
  31. package/src/client/Sidebar/Directory.jsx +0 -62
  32. package/src/client/Sidebar/FileOrDirectory.jsx +0 -33
  33. package/src/client/Sidebar/index.jsx +0 -64
  34. package/src/client/Sidebar/styles.css +0 -88
  35. package/src/client/getOrCreateEditor.js +0 -84
  36. package/src/client/settings.jsx +0 -141
  37. package/src/client/style.css +0 -120
  38. /package/src/client/{json1PresenceBroadcast.js → CodeEditor/json1PresenceBroadcast.js} +0 -0
@@ -1,31 +0,0 @@
1
- import { useRef, useLayoutEffect } from 'react';
2
- import { getOrCreateEditor } from './getOrCreateEditor';
3
-
4
- export const CodeEditor = ({
5
- activeFileId,
6
- shareDBDoc,
7
- localPresence,
8
- docPresence,
9
- theme,
10
- }) => {
11
- const ref = useRef();
12
-
13
- // useEffect was buggy in that sometimes ref.current was undefined.
14
- // useLayoutEffect seems to solve that issue.
15
- useLayoutEffect(() => {
16
- const editor = getOrCreateEditor({
17
- fileId: activeFileId,
18
- shareDBDoc,
19
- localPresence,
20
- docPresence,
21
- theme,
22
- });
23
- ref.current.appendChild(editor.dom);
24
-
25
- return () => {
26
- ref.current.removeChild(editor.dom);
27
- };
28
- }, [shareDBDoc, activeFileId]);
29
-
30
- return <div className="editor" ref={ref} />;
31
- };
@@ -1,62 +0,0 @@
1
- import { useCallback } from 'react';
2
- import { Item } from './Item';
3
- import { FileOrDirectory } from './FileOrDirectory';
4
- import { DirectoryArrowSVG } from './DirectoryArrowSVG';
5
-
6
- export const Directory = ({
7
- name,
8
- path,
9
- children,
10
- handleRenameFileClick,
11
- handleDeleteFileClick,
12
- handleFileClick,
13
- openDirectories,
14
- toggleDirectory,
15
- }) => {
16
- const handleClick = useCallback(() => {
17
- toggleDirectory(path);
18
- }, [toggleDirectory]);
19
-
20
- const handleDeleteClick = useCallback(() => {
21
- console.log('TODO handleDeleteDirectoryClick');
22
- }, []);
23
-
24
- const handleRenameClick = useCallback(() => {
25
- console.log('TODO handleRenameDirectoryClick');
26
- }, []);
27
-
28
- const isOpen = openDirectories[path];
29
- return (
30
- <>
31
- <Item
32
- name={name}
33
- handleClick={handleClick}
34
- handleDeleteClick={handleDeleteClick}
35
- handleRenameClick={handleRenameClick}
36
- >
37
- <div
38
- className="arrow-wrapper"
39
- style={{ transform: `rotate(${isOpen ? 90 : 0}deg)` }}
40
- >
41
- <DirectoryArrowSVG />
42
- </div>
43
- {name}
44
- </Item>
45
- {children && isOpen ? (
46
- <div className="indentation">
47
- {children.map((entity) => (
48
- <FileOrDirectory
49
- entity={entity}
50
- key={entity.fileId || entity.path}
51
- handleRenameFileClick={handleRenameFileClick}
52
- handleDeleteFileClick={handleDeleteFileClick}
53
- handleFileClick={handleFileClick}
54
- openDirectories={openDirectories}
55
- toggleDirectory={toggleDirectory}
56
- />
57
- ))}
58
- </div>
59
- ) : null}
60
- </>
61
- );
62
- };
@@ -1,33 +0,0 @@
1
- import { File } from './File';
2
- import { Directory } from './Directory';
3
-
4
- export const FileOrDirectory = ({
5
- entity,
6
- handleRenameFileClick,
7
- handleDeleteFileClick,
8
- handleFileClick,
9
- openDirectories,
10
- toggleDirectory,
11
- }) => {
12
- const { name, path, fileId, file, children } = entity;
13
- return file ? (
14
- <File
15
- fileId={fileId}
16
- name={name}
17
- handleRenameFileClick={handleRenameFileClick}
18
- handleDeleteFileClick={handleDeleteFileClick}
19
- handleFileClick={handleFileClick}
20
- />
21
- ) : (
22
- <Directory
23
- name={name}
24
- path={path}
25
- children={children}
26
- handleRenameFileClick={handleRenameFileClick}
27
- handleDeleteFileClick={handleDeleteFileClick}
28
- handleFileClick={handleFileClick}
29
- openDirectories={openDirectories}
30
- toggleDirectory={toggleDirectory}
31
- />
32
- );
33
- };
@@ -1,64 +0,0 @@
1
- import { useMemo } from 'react';
2
- import { getFileTree } from '../getFileTree';
3
- import { sortFileTree } from '../sortFileTree';
4
- import { disableSettings } from '../featureFlags';
5
- import { FileOrDirectory } from './FileOrDirectory';
6
- import { useOpenDirectories } from './useOpenDirectories';
7
-
8
- import './styles.css';
9
-
10
- export const Sidebar = ({
11
- createFile,
12
- data,
13
- handleRenameFileClick,
14
- handleDeleteFileClick,
15
- handleFileClick,
16
- setSettings,
17
- settings,
18
- }) => {
19
- const fileTree = useMemo(
20
- () => (data ? sortFileTree(getFileTree(data)) : null),
21
- [data]
22
- );
23
-
24
- const { openDirectories, toggleDirectory } = useOpenDirectories();
25
-
26
- return (
27
- <div className="vz-sidebar">
28
- <div className="files">
29
- <div className="full-box">
30
- <div>
31
- <a className="link-name" href="#">
32
- Files
33
- </a>
34
- </div>
35
- <div>
36
- <i
37
- className="bx bxs-file-plus new-btn"
38
- style={{ color: '#dbdde1' }}
39
- onClick={createFile}
40
- ></i>
41
- </div>
42
- </div>
43
- </div>
44
- {fileTree
45
- ? fileTree.children.map((entity) => (
46
- <FileOrDirectory
47
- entity={entity}
48
- key={entity.fileId || entity.path}
49
- handleRenameFileClick={handleRenameFileClick}
50
- handleDeleteFileClick={handleDeleteFileClick}
51
- handleFileClick={handleFileClick}
52
- openDirectories={openDirectories}
53
- toggleDirectory={toggleDirectory}
54
- />
55
- ))
56
- : null}
57
- {disableSettings ? null : (
58
- <div className="settings" onClick={() => setSettings(!settings)}>
59
- Settings
60
- </div>
61
- )}
62
- </div>
63
- );
64
- };
@@ -1,88 +0,0 @@
1
- .vz-sidebar {
2
- /* TODO remove position: fixed, overhaul CSS to use Flexbox for entire app */
3
-
4
- position: fixed;
5
- top: 0;
6
- left: 0;
7
- height: 100%;
8
- width: 200px;
9
- background: rgb(54, 65, 89);
10
- }
11
-
12
- .vz-sidebar .full-box {
13
- padding: 7px;
14
- display: flex;
15
- flex-direction: row;
16
- align-items: center;
17
- justify-content: space-between;
18
- }
19
-
20
- .vz-sidebar .file-or-directory {
21
- display: flex;
22
- flex-direction: row;
23
- align-items: center;
24
- justify-content: space-between;
25
- cursor: pointer;
26
- margin: 0 10px;
27
- color: rgb(219, 221, 225);
28
- border-radius: 5px;
29
- }
30
-
31
- .vz-sidebar .link-name {
32
- margin-top: 30px;
33
- margin-left: 10px;
34
- color: rgb(154, 160, 172);
35
- font-size: 12px;
36
- text-decoration: none;
37
- }
38
-
39
- .vz-sidebar .new-btn {
40
- justify-content: right;
41
- margin-right: 10px;
42
- cursor: pointer;
43
- }
44
-
45
- .vz-sidebar .utils {
46
- display: flex;
47
- }
48
-
49
- .vz-sidebar .utilities {
50
- margin-right: 3px;
51
- }
52
-
53
- .vz-sidebar .settings {
54
- position: absolute;
55
- bottom: 0;
56
- height: 30px;
57
- width: 180px;
58
- z-index: 100;
59
- color: rgb(219, 221, 225);
60
- cursor: pointer;
61
- display: flex;
62
- align-items: center;
63
- justify-content: center;
64
- padding: 14px 0;
65
- margin: 10px;
66
- border-radius: 5px;
67
- }
68
-
69
- .vz-sidebar .file-or-directory:hover,
70
- .vz-sidebar .settings:hover {
71
- background-color: rgb(74, 84, 105);
72
- }
73
-
74
- .vz-sidebar .name {
75
- display: flex;
76
- text-overflow: ellipsis;
77
- white-space: nowrap;
78
- overflow: hidden;
79
- }
80
-
81
- /* Responsible for indenting directories */
82
- .vz-sidebar .indentation {
83
- margin-left: 20px;
84
- }
85
-
86
- .vz-sidebar .arrow-wrapper {
87
- margin-right: 4px;
88
- }
@@ -1,84 +0,0 @@
1
- import { EditorView, basicSetup } from 'codemirror';
2
- import { EditorState } from '@codemirror/state';
3
- import { javascript } from '@codemirror/lang-javascript';
4
- import { markdown } from '@codemirror/lang-markdown';
5
- import { html } from '@codemirror/lang-html';
6
- import { css } from '@codemirror/lang-css';
7
- import { oneDark } from '@codemirror/theme-one-dark';
8
- import { json1Sync } from 'codemirror-ot';
9
- import { json1Presence, textUnicode } from '../ot';
10
- import { json1PresenceBroadcast } from './json1PresenceBroadcast';
11
- import { json1PresenceDisplay } from './json1PresenceDisplay';
12
-
13
- // Singleton cache of CodeMirror instances
14
- // These are created, but never destroyed.
15
- // TODO consider invalidating this cache at some point.
16
- // * Keys are file ids
17
- // * Values are CodeMirror instances
18
- const editorCache = new Map();
19
-
20
- // Gets or creates a CodeMirror editor for the given file id.
21
- export const getOrCreateEditor = ({
22
- fileId,
23
- shareDBDoc,
24
- localPresence,
25
- docPresence,
26
- }) => {
27
- const data = shareDBDoc.data;
28
-
29
- const fileExtension = data[fileId].name.split('.').pop();
30
-
31
- // The path for this file in the ShareDB document.
32
- const path = [fileId, 'text'];
33
-
34
- const extensions = [
35
- // This plugin implements multiplayer editing,
36
- // real-time synchronozation of changes across clients.
37
- // Does not deal with showing others' cursors.
38
- json1Sync({
39
- shareDBDoc,
40
- path,
41
- json1: json1Presence,
42
- textUnicode,
43
- }),
44
-
45
- // Deals with broadcasting changes in cursor location and selection.
46
- json1PresenceBroadcast({ path, localPresence }),
47
-
48
- // Deals with receiving the broadcas from other clients and displaying them.
49
- json1PresenceDisplay({ path, docPresence }),
50
-
51
- basicSetup,
52
- oneDark,
53
- ];
54
-
55
- if (
56
- fileExtension === 'js' ||
57
- fileExtension === 'json' ||
58
- fileExtension == 'jsx' ||
59
- fileExtension == 'ts'
60
- ) {
61
- extensions.push(javascript());
62
- } else if (fileExtension === 'html') {
63
- extensions.push(html());
64
- } else if (fileExtension === 'css') {
65
- extensions.push(css());
66
- } else if (fileExtension === 'md') {
67
- extensions.push(markdown());
68
- }
69
-
70
- let editor = editorCache.get(fileId);
71
- if (!editor) {
72
- // Cache miss --> mint a new editor.
73
- editor = new EditorView({
74
- state: EditorState.create({
75
- doc: data[fileId].text,
76
- extensions,
77
- }),
78
- });
79
-
80
- // Populate the cache.
81
- editorCache.set(fileId, editor);
82
- }
83
- return editor;
84
- };
@@ -1,141 +0,0 @@
1
- import { useCallback } from 'react';
2
- import Modal from 'react-bootstrap/Modal';
3
- import Form from 'react-bootstrap/Form';
4
- import Button from 'react-bootstrap/Button';
5
- import Select from 'react-select';
6
-
7
- // TODO document where this list came from
8
- // TODO research which themes are available for CodeMirror 6
9
- const themes = [
10
- { value: 'default', label: 'default' },
11
- { value: '3024-day', label: '3024-day' },
12
- { value: '3024-night', label: '3024-night' },
13
- { value: 'abbott', label: 'abbott' },
14
- { value: 'abcdef', label: 'abcdef' },
15
- { value: 'ambiance', label: 'ambiance' },
16
- { value: 'ayu-dark', label: 'ayu-dark' },
17
- { value: 'ayu-mirage', label: 'ayu-mirage' },
18
- { value: 'base16-dark', label: 'base16-dark' },
19
- { value: 'base16-light', label: 'base16-light' },
20
- { value: 'bespin', label: 'bespin' },
21
- { value: 'blackboard', label: 'blackboard' },
22
- { value: 'cobalt', label: 'cobalt' },
23
- { value: 'colorforth', label: 'colorforth' },
24
- { value: 'darcula', label: 'darcula' },
25
- { value: 'duotone-dark', label: 'duotone-dark' },
26
- { value: 'duotone-light', label: 'duotone-light' },
27
- { value: 'eclipse', label: 'eclipse' },
28
- { value: 'elegant', label: 'elegant' },
29
- { value: 'erlang-dark', label: 'erlang-dark' },
30
- { value: 'gruvbox-dark', label: 'gruvbox-dark' },
31
- { value: 'hopscotch', label: 'hopscotch' },
32
- { value: 'icecoder', label: 'icecoder' },
33
- { value: 'idea', label: 'idea' },
34
- { value: 'isotope', label: 'isotope' },
35
- { value: 'juejin', label: 'juejin' },
36
- { value: 'lesser-dark', label: 'lesser-dark' },
37
- { value: 'liquibyte', label: 'liquibyte' },
38
- { value: 'lucario', label: 'lucario' },
39
- { value: 'material', label: 'material' },
40
- { value: 'material-darker', label: 'material-darker' },
41
- { value: 'material-palenight', label: 'material-palenight' },
42
- { value: 'material-ocean', label: 'material-ocean' },
43
- { value: 'mbo', label: 'mbo' },
44
- { value: 'mdn-like', label: 'mdn-like' },
45
- { value: 'midnight', label: 'midnight' },
46
- { value: 'monokai', label: 'monokai' },
47
- { value: 'moxer', label: 'moxer' },
48
- { value: 'neat', label: 'neat' },
49
- { value: 'neo', label: 'neo' },
50
- { value: 'night', label: 'night' },
51
- { value: 'nord', label: 'nord' },
52
- { value: 'oceanic-next', label: 'oceanic-next' },
53
- { value: 'OneDark', label: 'One Dark' },
54
- { value: 'panda-syntax', label: 'panda-syntax' },
55
- { value: 'paraiso-dark', label: 'paraiso-dark' },
56
- { value: 'paraiso-light', label: 'paraiso-light' },
57
- { value: 'pastel-on-dark', label: 'pastel-on-dark' },
58
- { value: 'railscasts', label: 'railscasts' },
59
- { value: 'rubyblue', label: 'rubyblue' },
60
- { value: 'seti', label: 'seti' },
61
- { value: 'shadowfox', label: 'shadowfox' },
62
- { value: 'solarized', label: 'solarized' },
63
- { value: 'ssms', label: 'ssms' },
64
- { value: 'the-matrix', label: 'the-matrix' },
65
- { value: 'tomorrow-night-bright', label: 'tomorrow-night-bright' },
66
- { value: 'tomorrow-night-eighties', label: 'tomorrow-night-eighties' },
67
- { value: 'ttcn', label: 'ttcn' },
68
- { value: 'twilight', label: 'twilight' },
69
- { value: 'vibrant-ink', label: 'vibrant-ink' },
70
- { value: 'xq-dark', label: 'xq-dark' },
71
- { value: 'xq-light', label: 'xq-light' },
72
- { value: 'yeti', label: 'yeti' },
73
- { value: 'yonce', label: 'yonce' },
74
- { value: 'zenburn', label: 'zenburn' },
75
- ];
76
-
77
- const saveTimes = [
78
- { value: 1, label: '1 second' },
79
- { value: 5, label: '5 seconds' },
80
- { value: 10, label: '10 seconds' },
81
- { value: 30, label: '30 seconds' },
82
- ];
83
-
84
- export const Settings = ({ show, onClose, setTheme }) => {
85
- const handleChange = useCallback((selectedOption) => {
86
- setTheme(selectedOption.value);
87
- }, []);
88
-
89
- const handleSaveTimeChange = useCallback((selectedOption) => {
90
- const time = [{ value: selectedOption.value }];
91
- fetch('/saveTime', {
92
- method: 'POST',
93
- headers: {
94
- 'Content-Type': 'application/json',
95
- },
96
- body: JSON.stringify(time),
97
- });
98
- }, []);
99
-
100
- return show ? (
101
- <Modal show={show} onHide={onClose} animation={false}>
102
- <Modal.Header closeButton>
103
- <Modal.Title>Settings</Modal.Title>
104
- </Modal.Header>
105
- <Modal.Body>
106
- <Form.Group className="mb-3" controlId="formFork">
107
- <Form.Label>Username</Form.Label>
108
- <Form.Control type="text" placeholder="Enter username" />
109
- <Form.Text className="text-muted">
110
- Enter a username to be displayed on your cursor
111
- </Form.Text>
112
- </Form.Group>
113
- <Form.Group className="mb-3" controlId="formFork">
114
- <Form.Label>Theme</Form.Label>
115
- <Select
116
- classvalue="themes"
117
- options={themes}
118
- onChange={handleChange}
119
- />
120
- <Form.Text className="text-muted">
121
- Select a color theme for the editor
122
- </Form.Text>
123
- </Form.Group>
124
- <Form.Group className="mb-3" controlId="formFork">
125
- <Form.Label>Auto-Save Time</Form.Label>
126
- <Select
127
- classvalue="themes"
128
- options={saveTimes}
129
- onChange={handleSaveTimeChange}
130
- />
131
- <Form.Text className="text-muted">Select an auto save time</Form.Text>
132
- </Form.Group>
133
- </Modal.Body>
134
- <Modal.Footer>
135
- <Button variant="primary" onClick={onClose}>
136
- Done
137
- </Button>
138
- </Modal.Footer>
139
- </Modal>
140
- ) : null;
141
- };
@@ -1,120 +0,0 @@
1
- /* Inspired by: https://www.codinglabweb.com/2021/06/dropdown-sidebar-menu-html-css.html */
2
- /* Google Fonts Import Link */
3
- @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
4
- @import url('https://unpkg.com/boxicons@2.0.7/css/boxicons.min.css');
5
- @import url('https://unpkg.com/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css');
6
-
7
- * {
8
- margin: 0;
9
- padding: 0;
10
- box-sizing: border-box;
11
- }
12
-
13
- /* width */
14
- ::-webkit-scrollbar {
15
- width: 10px;
16
- height: 20px;
17
- }
18
-
19
- /* Track */
20
- ::-webkit-scrollbar-track {
21
- background: rgb(41, 44, 52);
22
- }
23
-
24
- /* Handle */
25
- ::-webkit-scrollbar-thumb {
26
- background: rgb(37, 37, 38);
27
- }
28
-
29
- /* Handle on hover */
30
- ::-webkit-scrollbar-thumb:hover {
31
- background: #555;
32
- }
33
-
34
- :root {
35
- --main-font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas,
36
- Liberation Mono, monospace;
37
- }
38
-
39
- body {
40
- background: rgb(41, 44, 52);
41
- font-family: 'Poppins', sans-serif;
42
- overflow-x: hidden;
43
- }
44
-
45
- .tab {
46
- width: max-content;
47
- height: 30px;
48
- background: rgb(45, 45, 45);
49
- text-align: center;
50
- color: #fff;
51
- font-family: var(--main-font-family);
52
- justify-content: center;
53
- align-items: center;
54
- display: flex;
55
- padding-left: 10px;
56
- padding-right: 10px;
57
- border-top-left-radius: 10px;
58
- border-top-right-radius: 10px;
59
- margin-top: 5px;
60
- margin-left: 2px;
61
- margin-right: 2px;
62
- cursor: pointer;
63
- }
64
-
65
- .tab.active {
66
- background-color: rgb(41, 44, 52);
67
- }
68
-
69
- .tab-close {
70
- display: flex;
71
- cursor: pointer;
72
- margin-left: 5px;
73
- justify-content: center;
74
- align-items: center;
75
- }
76
-
77
- .tab-close:hover {
78
- border-radius: 30%;
79
- background-color: rgb(37, 37, 38);
80
- }
81
-
82
- .editor {
83
- position: relative;
84
- margin-left: 200px;
85
- width: 100%;
86
- height: 100%;
87
- top: 30px;
88
- bottom: 0;
89
- background-color: rgb(41, 44, 52);
90
- color: #fff;
91
- font-family: var(--main-font-family);
92
- }
93
-
94
- .tab-list {
95
- position: fixed;
96
- display: flex;
97
- flex-direction: row;
98
- top: 0;
99
- left: 200px;
100
- height: 35px;
101
- width: 100%;
102
- background: rgb(37, 37, 38);
103
- z-index: 100;
104
- transition: all 0.5s ease;
105
- }
106
-
107
- .bottom-bar {
108
- position: absolute;
109
- bottom: 0;
110
- left: 0;
111
- height: 20px;
112
- width: 100%;
113
- background: rgb(45, 45, 45);
114
- z-index: 100;
115
- transition: all 0.5s ease;
116
- }
117
-
118
- .themes {
119
- color: rgb(37, 37, 38);
120
- }