vzcode 1.5.0 → 1.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 (36) hide show
  1. package/dist/assets/{index-ClPo_hzs.css → index-BKRpkJER.css} +1 -1
  2. package/dist/assets/index-BwUWRAvf.js +425 -0
  3. package/dist/assets/index-Cq7BgKMs.js +157 -0
  4. package/dist/assets/{worker-DqNV0IYq.js → worker-Cs0ZU3m1.js} +193 -193
  5. package/dist/index.html +2 -2
  6. package/package.json +24 -20
  7. package/src/client/CodeEditor/getOrCreateEditor.ts +6 -0
  8. package/src/client/CodeEditor/index.tsx +19 -7
  9. package/src/client/CodeEditor/json1PresenceDisplay.ts +6 -5
  10. package/src/client/Icons/CssSVG.tsx +16 -0
  11. package/src/client/Icons/FolderSVG.tsx +20 -0
  12. package/src/client/Icons/HtmlSVG.tsx +16 -0
  13. package/src/client/Icons/JavaScriptSVG.tsx +16 -0
  14. package/src/client/Icons/JsonSVG.tsx +16 -0
  15. package/src/client/Icons/MarkdownSVG.tsx +16 -0
  16. package/src/client/Icons/PinSVG.tsx +14 -0
  17. package/src/client/Icons/ReactSVG.tsx +16 -0
  18. package/src/client/Icons/SearchFileSVG.tsx +19 -0
  19. package/src/client/Icons/SearchSVG.tsx +20 -0
  20. package/src/client/Icons/SvelteSVG.tsx +16 -0
  21. package/src/client/Icons/TypeScriptSVG.tsx +16 -0
  22. package/src/client/Icons/index.tsx +12 -0
  23. package/src/client/TabList/style.scss +2 -2
  24. package/src/client/VZCodeContext.tsx +36 -1
  25. package/src/client/VZSidebar/Search.tsx +242 -0
  26. package/src/client/VZSidebar/index.tsx +196 -112
  27. package/src/client/VZSidebar/styles.scss +101 -9
  28. package/src/client/useActions.ts +68 -1
  29. package/src/client/useFileCRUD.ts +1 -1
  30. package/src/client/vzReducer/createInitialState.ts +2 -0
  31. package/src/client/vzReducer/index.ts +60 -3
  32. package/src/client/vzReducer/searchReducer.ts +114 -0
  33. package/src/client/vzReducer/toggleAutoFollowReducer.ts +12 -0
  34. package/src/types.ts +20 -0
  35. package/dist/assets/index-Bx5B6Xgl.js +0 -400
  36. package/dist/assets/index-D6aZiqme.js +0 -156
@@ -0,0 +1,242 @@
1
+ import {
2
+ JavaScriptSVG,
3
+ TypeScriptSVG,
4
+ ReactSVG,
5
+ SvelteSVG,
6
+ JsonSVG,
7
+ MarkdownSVG,
8
+ HtmlSVG,
9
+ CssSVG,
10
+ SearchFileSVG,
11
+ DirectoryArrowSVG,
12
+ CloseSVG,
13
+ } from '../Icons';
14
+ import {
15
+ useRef,
16
+ useEffect,
17
+ useContext,
18
+ useState,
19
+ } from 'react';
20
+ import { Form } from '../bootstrap';
21
+ import { VZCodeContext } from '../VZCodeContext';
22
+ import { SearchFile } from '../../types';
23
+ import { EditorView } from 'codemirror';
24
+
25
+ function getExtensionIcon(extension: string) {
26
+ switch (extension) {
27
+ case 'jsx':
28
+ case 'tsx':
29
+ return <ReactSVG />;
30
+ case 'js':
31
+ return <JavaScriptSVG />;
32
+ case 'ts':
33
+ return <TypeScriptSVG />;
34
+ case 'json':
35
+ return <JsonSVG />;
36
+ case 'md':
37
+ return <MarkdownSVG />;
38
+ case 'html':
39
+ return <HtmlSVG />;
40
+ case 'css':
41
+ return <CssSVG />;
42
+ case 'svelte':
43
+ return <SvelteSVG />;
44
+ default:
45
+ return <SearchFileSVG />;
46
+ }
47
+ }
48
+
49
+ function jumpToPattern(
50
+ editor: EditorView,
51
+ pattern: string,
52
+ line: number,
53
+ index: number,
54
+ ) {
55
+ const position: number =
56
+ editor.state.doc.line(line).from + index;
57
+
58
+ editor.dispatch({
59
+ selection: {
60
+ anchor: position,
61
+ head: position + pattern.length,
62
+ },
63
+ scrollIntoView: true,
64
+ effects: EditorView.scrollIntoView(position, {
65
+ y: 'center',
66
+ }),
67
+ });
68
+ }
69
+
70
+ export const Search = () => {
71
+ const inputRef = useRef(null);
72
+ const [isMounted, setIsMounted] = useState(false);
73
+ const [isSearching, setIsSearching] = useState(false);
74
+ const {
75
+ search,
76
+ setSearch,
77
+ setActiveFileId,
78
+ openTab,
79
+ setSearchResults,
80
+ setSearchFileVisibility,
81
+ shareDBDoc,
82
+ editorCache,
83
+ } = useContext(VZCodeContext);
84
+ const { pattern, results } = search;
85
+
86
+ useEffect(() => {
87
+ if (isMounted) {
88
+ // Only conduct a search after fully mounting and entering a new non-empty pattern
89
+ setIsSearching(pattern.trim().length >= 1);
90
+
91
+ // Search about 2 seconds after entering a new pattern
92
+ const delaySearch = setTimeout(() => {
93
+ if (
94
+ pattern.trim().length >= 1 &&
95
+ inputRef.current
96
+ ) {
97
+ setSearchResults(shareDBDoc);
98
+ setIsSearching(false);
99
+ }
100
+ }, 2000);
101
+
102
+ return () => clearTimeout(delaySearch);
103
+ } else {
104
+ setIsMounted(true);
105
+ }
106
+ }, [pattern]);
107
+
108
+ return (
109
+ <div>
110
+ <Form.Group
111
+ className="sidebar-search-form mb-3"
112
+ controlId="searchName"
113
+ >
114
+ <Form.Control
115
+ type="text"
116
+ value={pattern}
117
+ onChange={(e) => setSearch(e.target.value)}
118
+ ref={inputRef}
119
+ spellCheck="false"
120
+ />
121
+ </Form.Group>
122
+ {Object.keys(results).length >= 1 &&
123
+ pattern.trim().length >= 1 ? (
124
+ <div className="search-results">
125
+ {Object.entries(results)
126
+ .filter(
127
+ ([_, file]) => file.visibility !== 'closed',
128
+ )
129
+ .map(([fileId, file]: [string, SearchFile]) => (
130
+ <div
131
+ className="search-result"
132
+ key={file.name}
133
+ >
134
+ <div className="search-file-heading">
135
+ <div className="search-file-title">
136
+ <div
137
+ className="arrow-wrapper"
138
+ style={{
139
+ transform: `rotate(${file.visibility === 'open' ? 90 : 0}deg)`,
140
+ }}
141
+ onClick={() => {
142
+ setSearchFileVisibility(
143
+ shareDBDoc,
144
+ fileId,
145
+ file.visibility === 'open'
146
+ ? 'flattened'
147
+ : 'open',
148
+ );
149
+ }}
150
+ >
151
+ <DirectoryArrowSVG />
152
+ </div>
153
+ <div className="search-file-name">
154
+ {getExtensionIcon(
155
+ file.name.split('.')[1],
156
+ )}
157
+ <h5>{file.name}</h5>
158
+ </div>
159
+ </div>
160
+ <div className="search-file-info">
161
+ <h6 className="search-file-count">
162
+ {file.matches.length}
163
+ </h6>
164
+ <div
165
+ className="search-file-close"
166
+ onClick={() => {
167
+ setSearchFileVisibility(
168
+ shareDBDoc,
169
+ fileId,
170
+ 'closed',
171
+ );
172
+ }}
173
+ >
174
+ <CloseSVG />
175
+ </div>
176
+ </div>
177
+ </div>
178
+ <div className="search-file-lines">
179
+ {file.visibility != 'flattened' &&
180
+ file.matches.map((match) => {
181
+ const before = match.text.substring(
182
+ 0,
183
+ match.index,
184
+ );
185
+ const hit = match.text.substring(
186
+ match.index,
187
+ match.index + pattern.length,
188
+ );
189
+ const after = match.text.substring(
190
+ match.index + pattern.length,
191
+ );
192
+
193
+ return (
194
+ <p
195
+ className="search-line"
196
+ key={
197
+ file.name +
198
+ ' - ' +
199
+ match.line +
200
+ ' - ' +
201
+ match.index
202
+ }
203
+ onClick={() => {
204
+ setActiveFileId(fileId);
205
+ openTab({
206
+ fileId: fileId,
207
+ isTransient: false,
208
+ });
209
+
210
+ if (editorCache.get(fileId)) {
211
+ jumpToPattern(
212
+ editorCache.get(fileId)
213
+ .editor,
214
+ pattern,
215
+ match.line,
216
+ match.index,
217
+ );
218
+ }
219
+ }}
220
+ >
221
+ {before}
222
+ <span className="search-pattern">
223
+ {hit}
224
+ </span>
225
+ {after}
226
+ </p>
227
+ );
228
+ })}
229
+ </div>
230
+ </div>
231
+ ))}
232
+ </div>
233
+ ) : (
234
+ <div className="search-results">
235
+ <h6>
236
+ {isSearching ? 'Searching...' : 'No Results'}
237
+ </h6>
238
+ </div>
239
+ )}
240
+ </div>
241
+ );
242
+ };
@@ -1,19 +1,28 @@
1
- import { useCallback, useContext, useMemo } from 'react';
1
+ import {
2
+ useCallback,
3
+ useContext,
4
+ useMemo,
5
+ useState,
6
+ } from 'react';
2
7
  import {
3
8
  FileId,
4
9
  FileTree,
5
10
  FileTreeFile,
6
11
  } from '../../types';
7
12
  import { Tooltip, OverlayTrigger } from '../bootstrap';
13
+ import { Search } from './Search';
8
14
  import { getFileTree } from '../getFileTree';
9
15
  import { sortFileTree } from '../sortFileTree';
10
16
  import { SplitPaneResizeContext } from '../SplitPaneResizeContext';
11
17
  import {
18
+ FolderSVG,
19
+ SearchSVG,
12
20
  BugSVG,
13
21
  GearSVG,
14
22
  NewSVG,
15
23
  FileSVG,
16
24
  QuestionMarkSVG,
25
+ PinSVG,
17
26
  } from '../Icons';
18
27
  import { VZCodeContext } from '../VZCodeContext';
19
28
  import { Listing } from './Listing';
@@ -31,22 +40,30 @@ export const VZSidebar = ({
31
40
  openSettingsTooltipText = 'Open Settings',
32
41
  openKeyboardShortcuts = 'Keyboard Shortcuts',
33
42
  reportBugTooltipText = 'Report Bug',
43
+ searchToolTipText = 'Search',
44
+ filesToolTipText = 'Files',
34
45
  }: {
35
46
  createFileTooltipText?: string;
36
47
  createDirTooltipText?: string;
37
48
  openSettingsTooltipText?: string;
38
49
  reportBugTooltipText?: string;
39
50
  openKeyboardShortcuts?: string;
51
+ searchToolTipText?: string;
52
+ filesToolTipText?: string;
40
53
  }) => {
41
54
  const {
42
55
  files,
43
56
  openTab,
44
57
  setIsSettingsOpen,
45
58
  setIsDocOpen,
59
+ isSearchOpen,
60
+ setIsSearchOpen,
46
61
  handleOpenCreateFileModal,
47
62
  handleOpenCreateDirModal,
48
63
  connected,
49
64
  sidebarRef,
65
+ enableAutoFollow,
66
+ toggleAutoFollow,
50
67
  } = useContext(VZCodeContext);
51
68
 
52
69
  const fileTree = useMemo(
@@ -103,126 +120,193 @@ export const VZSidebar = ({
103
120
  onDragLeave={handleDragLeave}
104
121
  onDrop={handleDrop}
105
122
  >
106
- <div className="files" ref={sidebarRef} tabIndex={-1}>
107
- <div className="full-box">
108
- <div className="sidebar-section-hint">Files</div>
109
- <div className="sidebar-section-buttons">
110
- <OverlayTrigger
111
- placement="right"
112
- overlay={
113
- <Tooltip id="open-keyboard-shortcuts">
114
- {openKeyboardShortcuts}
115
- </Tooltip>
116
- }
123
+ <div
124
+ className="full-box"
125
+ ref={sidebarRef}
126
+ tabIndex={-1}
127
+ >
128
+ <div className="sidebar-section-buttons">
129
+ <OverlayTrigger
130
+ placement="right"
131
+ overlay={
132
+ <Tooltip id="files-tooltip">
133
+ {filesToolTipText}
134
+ </Tooltip>
135
+ }
136
+ >
137
+ <i
138
+ onClick={() => setIsSearchOpen(false)}
139
+ className="icon-button icon-button-dark"
117
140
  >
118
- <i
119
- onClick={handleQuestionMarkClick}
120
- className="icon-button icon-button-dark"
121
- >
122
- <QuestionMarkSVG />
123
- </i>
124
- </OverlayTrigger>
125
-
126
- <OverlayTrigger
127
- placement="right"
128
- overlay={
129
- <Tooltip id="report-bug-tooltip">
130
- {reportBugTooltipText}
131
- </Tooltip>
132
- }
141
+ <FolderSVG />
142
+ </i>
143
+ </OverlayTrigger>
144
+
145
+ <OverlayTrigger
146
+ placement="right"
147
+ overlay={
148
+ <Tooltip id="search-tooltip">
149
+ {searchToolTipText}
150
+ </Tooltip>
151
+ }
152
+ >
153
+ <i
154
+ onClick={() => setIsSearchOpen(true)}
155
+ className="icon-button icon-button-dark"
133
156
  >
134
- <a
135
- href="https://github.com/vizhub-core/vzcode/issues/new"
136
- target="_blank"
137
- rel="noopener noreferrer"
138
- >
139
- <i className="icon-button icon-button-dark">
140
- <BugSVG />
141
- </i>
142
- </a>
143
- </OverlayTrigger>
144
-
145
- <OverlayTrigger
146
- placement="left"
147
- overlay={
148
- <Tooltip id="open-settings-tooltip">
149
- {openSettingsTooltipText}
150
- </Tooltip>
151
- }
157
+ <SearchSVG />
158
+ </i>
159
+ </OverlayTrigger>
160
+
161
+ <OverlayTrigger
162
+ placement="right"
163
+ overlay={
164
+ <Tooltip id="open-keyboard-shortcuts">
165
+ {openKeyboardShortcuts}
166
+ </Tooltip>
167
+ }
168
+ >
169
+ <i
170
+ onClick={handleQuestionMarkClick}
171
+ className="icon-button icon-button-dark"
152
172
  >
153
- <i
154
- onClick={handleSettingsClick}
155
- className="icon-button icon-button-dark"
156
- >
157
- <GearSVG />
158
- </i>
159
- </OverlayTrigger>
160
-
161
- <OverlayTrigger
162
- placement="left"
163
- overlay={
164
- <Tooltip id="create-file-tooltip">
165
- {createFileTooltipText}
166
- </Tooltip>
167
- }
173
+ <QuestionMarkSVG />
174
+ </i>
175
+ </OverlayTrigger>
176
+
177
+ <OverlayTrigger
178
+ placement="right"
179
+ overlay={
180
+ <Tooltip id="report-bug-tooltip">
181
+ {reportBugTooltipText}
182
+ </Tooltip>
183
+ }
184
+ >
185
+ <a
186
+ href="https://github.com/vizhub-core/vzcode/issues/new"
187
+ target="_blank"
188
+ rel="noopener noreferrer"
168
189
  >
169
- <i
170
- onClick={handleOpenCreateFileModal}
171
- className="icon-button icon-button-dark"
172
- >
173
- <NewSVG />
190
+ <i className="icon-button icon-button-dark">
191
+ <BugSVG />
174
192
  </i>
175
- </OverlayTrigger>
176
-
177
- {/*Directory Rename*/}
178
- <OverlayTrigger
179
- placement="left"
180
- overlay={
181
- <Tooltip id="create-dir-tooltip">
182
- {createDirTooltipText}
183
- </Tooltip>
184
- }
193
+ </a>
194
+ </OverlayTrigger>
195
+
196
+ <OverlayTrigger
197
+ placement="right"
198
+ overlay={
199
+ <Tooltip id="open-settings-tooltip">
200
+ {openSettingsTooltipText}
201
+ </Tooltip>
202
+ }
203
+ >
204
+ <i
205
+ onClick={handleSettingsClick}
206
+ className="icon-button icon-button-dark"
185
207
  >
186
- <i
187
- onClick={handleOpenCreateDirModal}
188
- className="icon-button icon-button-dark"
189
- >
190
- <FileSVG />
191
- </i>
192
- </OverlayTrigger>
193
- </div>
208
+ <GearSVG />
209
+ </i>
210
+ </OverlayTrigger>
211
+
212
+ <OverlayTrigger
213
+ placement="right"
214
+ overlay={
215
+ <Tooltip id="create-file-tooltip">
216
+ {createFileTooltipText}
217
+ </Tooltip>
218
+ }
219
+ >
220
+ <i
221
+ onClick={handleOpenCreateFileModal}
222
+ className="icon-button icon-button-dark"
223
+ >
224
+ <NewSVG />
225
+ </i>
226
+ </OverlayTrigger>
227
+
228
+ {/*Directory Rename*/}
229
+ <OverlayTrigger
230
+ placement="right"
231
+ overlay={
232
+ <Tooltip id="create-dir-tooltip">
233
+ {createDirTooltipText}
234
+ </Tooltip>
235
+ }
236
+ >
237
+ <i
238
+ onClick={handleOpenCreateDirModal}
239
+ className="icon-button icon-button-dark"
240
+ >
241
+ <FileSVG />
242
+ </i>
243
+ </OverlayTrigger>
244
+
245
+ {/*Toggle Follow*/}
246
+ <OverlayTrigger
247
+ placement="right"
248
+ overlay={
249
+ <Tooltip id="toggle-auto-follow">
250
+ {enableAutoFollow
251
+ ? 'Disable Auto Follow'
252
+ : 'Enable Auto Follow'}
253
+ </Tooltip>
254
+ }
255
+ >
256
+ <i
257
+ onClick={toggleAutoFollow}
258
+ className={`icon-button icon-button-dark${
259
+ enableAutoFollow
260
+ ? ' vh-color-success-01'
261
+ : ''
262
+ }`}
263
+ >
264
+ <PinSVG />
265
+ </i>
266
+ </OverlayTrigger>
194
267
  </div>
195
- {isDragOver ? (
196
- <div className="empty">
197
- <div className="empty-text">
198
- Drop files here!
268
+
269
+ <div className="files">
270
+ {!isSearchOpen ? (
271
+ <div className="sidebar-files">
272
+ {isDragOver ? (
273
+ <div className="empty">
274
+ <div className="empty-text">
275
+ Drop files here!
276
+ </div>
277
+ </div>
278
+ ) : filesExist ? (
279
+ fileTree.children.map((entity) => {
280
+ const { fileId } = entity as FileTreeFile;
281
+ const { path } = entity as FileTree;
282
+ const key = fileId ? fileId : path;
283
+ return (
284
+ <Listing
285
+ key={key}
286
+ entity={entity}
287
+ handleFileClick={handleFileClick}
288
+ handleFileDoubleClick={
289
+ handleFileDoubleClick
290
+ }
291
+ />
292
+ );
293
+ })
294
+ ) : (
295
+ <div className="empty">
296
+ <div className="empty-text">
297
+ It looks like you don't have any files
298
+ yet! Click the "Create file" button
299
+ above to create your first file.
300
+ </div>
301
+ </div>
302
+ )}
199
303
  </div>
200
- </div>
201
- ) : filesExist ? (
202
- fileTree.children.map((entity) => {
203
- const { fileId } = entity as FileTreeFile;
204
- const { path } = entity as FileTree;
205
- const key = fileId ? fileId : path;
206
- return (
207
- <Listing
208
- key={key}
209
- entity={entity}
210
- handleFileClick={handleFileClick}
211
- handleFileDoubleClick={
212
- handleFileDoubleClick
213
- }
214
- />
215
- );
216
- })
217
- ) : (
218
- <div className="empty">
219
- <div className="empty-text">
220
- It looks like you don't have any files yet!
221
- Click the "Create file" button above to create
222
- your first file.
304
+ ) : (
305
+ <div className="sidebar-search">
306
+ <Search />
223
307
  </div>
224
- </div>
225
- )}
308
+ )}
309
+ </div>
226
310
  </div>
227
311
 
228
312
  {enableConnectionStatus && (