vzcode 1.4.0 → 1.6.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 (32) hide show
  1. package/dist/assets/index-B2OgpmfL.js +157 -0
  2. package/dist/assets/index-BwUWRAvf.js +425 -0
  3. package/dist/assets/{index-ClPo_hzs.css → index-DAYbg783.css} +1 -1
  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/json1PresenceDisplay.ts +14 -3
  8. package/src/client/Icons/CssSVG.tsx +16 -0
  9. package/src/client/Icons/FolderSVG.tsx +20 -0
  10. package/src/client/Icons/HtmlSVG.tsx +16 -0
  11. package/src/client/Icons/JavaScriptSVG.tsx +16 -0
  12. package/src/client/Icons/JsonSVG.tsx +16 -0
  13. package/src/client/Icons/MarkdownSVG.tsx +16 -0
  14. package/src/client/Icons/PinSVG.tsx +14 -0
  15. package/src/client/Icons/ReactSVG.tsx +16 -0
  16. package/src/client/Icons/SearchFileSVG.tsx +19 -0
  17. package/src/client/Icons/SearchSVG.tsx +20 -0
  18. package/src/client/Icons/SvelteSVG.tsx +16 -0
  19. package/src/client/Icons/TypeScriptSVG.tsx +16 -0
  20. package/src/client/Icons/index.tsx +11 -0
  21. package/src/client/VZCodeContext.tsx +28 -1
  22. package/src/client/VZSidebar/Search.tsx +242 -0
  23. package/src/client/VZSidebar/index.tsx +164 -111
  24. package/src/client/VZSidebar/styles.scss +101 -10
  25. package/src/client/useActions.ts +61 -1
  26. package/src/client/useFileCRUD.ts +1 -1
  27. package/src/client/vzReducer/createInitialState.ts +2 -0
  28. package/src/client/vzReducer/index.ts +48 -2
  29. package/src/client/vzReducer/searchReducer.ts +114 -0
  30. package/src/types.ts +20 -0
  31. package/dist/assets/index-Bx5B6Xgl.js +0 -400
  32. package/dist/assets/index-wJsFLYuD.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
+ };
@@ -5,10 +5,13 @@ import {
5
5
  FileTreeFile,
6
6
  } from '../../types';
7
7
  import { Tooltip, OverlayTrigger } from '../bootstrap';
8
+ import { Search } from './Search';
8
9
  import { getFileTree } from '../getFileTree';
9
10
  import { sortFileTree } from '../sortFileTree';
10
11
  import { SplitPaneResizeContext } from '../SplitPaneResizeContext';
11
12
  import {
13
+ FolderSVG,
14
+ SearchSVG,
12
15
  BugSVG,
13
16
  GearSVG,
14
17
  NewSVG,
@@ -31,18 +34,24 @@ export const VZSidebar = ({
31
34
  openSettingsTooltipText = 'Open Settings',
32
35
  openKeyboardShortcuts = 'Keyboard Shortcuts',
33
36
  reportBugTooltipText = 'Report Bug',
37
+ searchToolTipText = 'Search',
38
+ filesToolTipText = 'Files',
34
39
  }: {
35
40
  createFileTooltipText?: string;
36
41
  createDirTooltipText?: string;
37
42
  openSettingsTooltipText?: string;
38
43
  reportBugTooltipText?: string;
39
44
  openKeyboardShortcuts?: string;
45
+ searchToolTipText?: string;
46
+ filesToolTipText?: string;
40
47
  }) => {
41
48
  const {
42
49
  files,
43
50
  openTab,
44
51
  setIsSettingsOpen,
45
52
  setIsDocOpen,
53
+ isSearchOpen,
54
+ setIsSearchOpen,
46
55
  handleOpenCreateFileModal,
47
56
  handleOpenCreateDirModal,
48
57
  connected,
@@ -103,126 +112,170 @@ export const VZSidebar = ({
103
112
  onDragLeave={handleDragLeave}
104
113
  onDrop={handleDrop}
105
114
  >
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
- }
115
+ <div
116
+ className="full-box"
117
+ ref={sidebarRef}
118
+ tabIndex={-1}
119
+ >
120
+ <div className="sidebar-section-buttons">
121
+ <OverlayTrigger
122
+ placement="right"
123
+ overlay={
124
+ <Tooltip id="files-tooltip">
125
+ {filesToolTipText}
126
+ </Tooltip>
127
+ }
128
+ >
129
+ <i
130
+ onClick={() => setIsSearchOpen(false)}
131
+ className="icon-button icon-button-dark"
117
132
  >
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
- }
133
+ <FolderSVG />
134
+ </i>
135
+ </OverlayTrigger>
136
+
137
+ <OverlayTrigger
138
+ placement="right"
139
+ overlay={
140
+ <Tooltip id="search-tooltip">
141
+ {searchToolTipText}
142
+ </Tooltip>
143
+ }
144
+ >
145
+ <i
146
+ onClick={() => setIsSearchOpen(true)}
147
+ className="icon-button icon-button-dark"
133
148
  >
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
- }
149
+ <SearchSVG />
150
+ </i>
151
+ </OverlayTrigger>
152
+
153
+ <OverlayTrigger
154
+ placement="right"
155
+ overlay={
156
+ <Tooltip id="open-keyboard-shortcuts">
157
+ {openKeyboardShortcuts}
158
+ </Tooltip>
159
+ }
160
+ >
161
+ <i
162
+ onClick={handleQuestionMarkClick}
163
+ className="icon-button icon-button-dark"
152
164
  >
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
- }
165
+ <QuestionMarkSVG />
166
+ </i>
167
+ </OverlayTrigger>
168
+
169
+ <OverlayTrigger
170
+ placement="right"
171
+ overlay={
172
+ <Tooltip id="report-bug-tooltip">
173
+ {reportBugTooltipText}
174
+ </Tooltip>
175
+ }
176
+ >
177
+ <a
178
+ href="https://github.com/vizhub-core/vzcode/issues/new"
179
+ target="_blank"
180
+ rel="noopener noreferrer"
168
181
  >
169
- <i
170
- onClick={handleOpenCreateFileModal}
171
- className="icon-button icon-button-dark"
172
- >
173
- <NewSVG />
182
+ <i className="icon-button icon-button-dark">
183
+ <BugSVG />
174
184
  </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
- }
185
+ </a>
186
+ </OverlayTrigger>
187
+
188
+ <OverlayTrigger
189
+ placement="right"
190
+ overlay={
191
+ <Tooltip id="open-settings-tooltip">
192
+ {openSettingsTooltipText}
193
+ </Tooltip>
194
+ }
195
+ >
196
+ <i
197
+ onClick={handleSettingsClick}
198
+ className="icon-button icon-button-dark"
185
199
  >
186
- <i
187
- onClick={handleOpenCreateDirModal}
188
- className="icon-button icon-button-dark"
189
- >
190
- <FileSVG />
191
- </i>
192
- </OverlayTrigger>
193
- </div>
200
+ <GearSVG />
201
+ </i>
202
+ </OverlayTrigger>
203
+
204
+ <OverlayTrigger
205
+ placement="right"
206
+ overlay={
207
+ <Tooltip id="create-file-tooltip">
208
+ {createFileTooltipText}
209
+ </Tooltip>
210
+ }
211
+ >
212
+ <i
213
+ onClick={handleOpenCreateFileModal}
214
+ className="icon-button icon-button-dark"
215
+ >
216
+ <NewSVG />
217
+ </i>
218
+ </OverlayTrigger>
219
+
220
+ {/*Directory Rename*/}
221
+ <OverlayTrigger
222
+ placement="right"
223
+ overlay={
224
+ <Tooltip id="create-dir-tooltip">
225
+ {createDirTooltipText}
226
+ </Tooltip>
227
+ }
228
+ >
229
+ <i
230
+ onClick={handleOpenCreateDirModal}
231
+ className="icon-button icon-button-dark"
232
+ >
233
+ <FileSVG />
234
+ </i>
235
+ </OverlayTrigger>
194
236
  </div>
195
- {isDragOver ? (
196
- <div className="empty">
197
- <div className="empty-text">
198
- Drop files here!
237
+
238
+ <div className="files">
239
+ {!isSearchOpen ? (
240
+ <div className="sidebar-files">
241
+ {isDragOver ? (
242
+ <div className="empty">
243
+ <div className="empty-text">
244
+ Drop files here!
245
+ </div>
246
+ </div>
247
+ ) : filesExist ? (
248
+ fileTree.children.map((entity) => {
249
+ const { fileId } = entity as FileTreeFile;
250
+ const { path } = entity as FileTree;
251
+ const key = fileId ? fileId : path;
252
+ return (
253
+ <Listing
254
+ key={key}
255
+ entity={entity}
256
+ handleFileClick={handleFileClick}
257
+ handleFileDoubleClick={
258
+ handleFileDoubleClick
259
+ }
260
+ />
261
+ );
262
+ })
263
+ ) : (
264
+ <div className="empty">
265
+ <div className="empty-text">
266
+ It looks like you don't have any files
267
+ yet! Click the "Create file" button
268
+ above to create your first file.
269
+ </div>
270
+ </div>
271
+ )}
199
272
  </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.
273
+ ) : (
274
+ <div className="sidebar-search">
275
+ <Search />
223
276
  </div>
224
- </div>
225
- )}
277
+ )}
278
+ </div>
226
279
  </div>
227
280
 
228
281
  {enableConnectionStatus && (