vzcode 1.31.0 → 1.33.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.
- package/README.md +0 -1
- package/dist/assets/index-DPIrco_g.js +235 -0
- package/dist/assets/index-DnunmWjy.css +1 -0
- package/dist/assets/worker-BrwN8AXx.js +330 -0
- package/dist/assets/worker-CkHcCP1n.js +136 -0
- package/dist/assets/worker-DkYJN5WQ.js +453 -0
- package/dist/index.html +2 -2
- package/package.json +48 -41
- package/src/client/App/index.tsx +3 -0
- package/src/client/CodeEditor/getOrCreateEditor.ts +98 -9
- package/src/client/CodeEditor/index.tsx +56 -24
- package/src/client/CodeEditor/typescriptExtension/worker.ts +32 -0
- package/src/client/Icons/MicSVG.tsx +1 -1
- package/src/client/VZCodeContext.tsx +18 -2
- package/src/client/VZMiddle.tsx +11 -0
- package/src/client/VZSidebar/AIChat.tsx +142 -0
- package/src/client/VZSidebar/Search.tsx +11 -0
- package/src/client/VZSidebar/index.tsx +53 -8
- package/src/client/VZSidebar/styles.scss +178 -1
- package/src/client/featureFlags.ts +2 -0
- package/src/client/useActions.ts +20 -0
- package/src/client/useESLint/index.ts +72 -0
- package/src/client/useESLint/worker.ts +113 -0
- package/src/client/useEditorCache.ts +1 -0
- package/src/client/useFileCRUD.ts +47 -0
- package/src/client/useOpenDirectories.ts +43 -12
- package/src/client/utils/fileExtension.ts +10 -0
- package/src/client/vzReducer/aiChatReducer.ts +31 -0
- package/src/client/vzReducer/createInitialState.ts +2 -0
- package/src/client/vzReducer/index.ts +20 -0
- package/src/server/featureFlags.js +2 -0
- package/dist/assets/index-D01yzCWj.js +0 -234
- package/dist/assets/index-DTtcN2MP.css +0 -1
- package/dist/assets/worker-CaP6zYz7.js +0 -335
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as eslint from 'eslint-linter-browserify';
|
|
2
|
+
import globals from 'globals';
|
|
3
|
+
|
|
4
|
+
// Feature flag to enable or disable JSX linting
|
|
5
|
+
// It's disabled for now since it doesn't work.
|
|
6
|
+
// See issue:
|
|
7
|
+
// https://github.com/vizhub-core/vzcode/issues/921
|
|
8
|
+
const enableJSXLinting = false;
|
|
9
|
+
|
|
10
|
+
const linter = new eslint.Linter();
|
|
11
|
+
|
|
12
|
+
const config = {
|
|
13
|
+
languageOptions: {
|
|
14
|
+
globals: {
|
|
15
|
+
...globals.browser,
|
|
16
|
+
...globals.es2021,
|
|
17
|
+
},
|
|
18
|
+
parserOptions: {
|
|
19
|
+
ecmaVersion: 2022,
|
|
20
|
+
sourceType: 'module',
|
|
21
|
+
ecmaFeatures: {
|
|
22
|
+
// Add this to enable JSX parsing
|
|
23
|
+
jsx: true,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
rules: {
|
|
28
|
+
'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
|
29
|
+
'no-undef': 'error',
|
|
30
|
+
semi: 'off',
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Helper function to convert line/column to character offset
|
|
35
|
+
function getOffset(
|
|
36
|
+
docLines: string[],
|
|
37
|
+
line: number,
|
|
38
|
+
column: number,
|
|
39
|
+
): number {
|
|
40
|
+
let offset = 0;
|
|
41
|
+
for (let i = 0; i < line - 1; i++) {
|
|
42
|
+
offset += (docLines[i] ? docLines[i].length : 0) + 1; // +1 for newline
|
|
43
|
+
}
|
|
44
|
+
return offset + column - 1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
self.onmessage = (event) => {
|
|
48
|
+
const { code, requestId, fileName } = event.data;
|
|
49
|
+
if (typeof code !== 'string') {
|
|
50
|
+
self.postMessage({
|
|
51
|
+
diagnostics: [],
|
|
52
|
+
requestId,
|
|
53
|
+
error: 'Invalid code received',
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Only lint .js files, and .jsx files if enableJSXLinting is true
|
|
59
|
+
if (!fileName) {
|
|
60
|
+
self.postMessage({ diagnostics: [], requestId });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const isJsFile = /\.js$/i.test(fileName);
|
|
65
|
+
const isJsxFile = /\.jsx$/i.test(fileName);
|
|
66
|
+
|
|
67
|
+
if (!isJsFile && (!isJsxFile || !enableJSXLinting)) {
|
|
68
|
+
self.postMessage({ diagnostics: [], requestId });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const messages = linter.verify(code, config as any);
|
|
74
|
+
const docLines = code.split('\n');
|
|
75
|
+
|
|
76
|
+
const diagnostics = messages.map((msg) => {
|
|
77
|
+
const from = getOffset(
|
|
78
|
+
docLines,
|
|
79
|
+
msg.line,
|
|
80
|
+
msg.column,
|
|
81
|
+
);
|
|
82
|
+
// endLine and endColumn may not always exist
|
|
83
|
+
const to =
|
|
84
|
+
msg.endLine && msg.endColumn
|
|
85
|
+
? getOffset(docLines, msg.endLine, msg.endColumn)
|
|
86
|
+
: from +
|
|
87
|
+
(msg.fix?.range[1] - msg.fix?.range[0] || 1);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
from,
|
|
91
|
+
to,
|
|
92
|
+
severity:
|
|
93
|
+
msg.severity === 2
|
|
94
|
+
? 'error'
|
|
95
|
+
: msg.severity === 1
|
|
96
|
+
? 'warning'
|
|
97
|
+
: 'info',
|
|
98
|
+
message: msg.message,
|
|
99
|
+
source: msg.ruleId
|
|
100
|
+
? `eslint(${msg.ruleId})`
|
|
101
|
+
: 'eslint',
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
self.postMessage({ diagnostics, requestId });
|
|
105
|
+
} catch (e: any) {
|
|
106
|
+
console.error('Error linting in ESLint worker:', e);
|
|
107
|
+
self.postMessage({
|
|
108
|
+
error: e.message,
|
|
109
|
+
diagnostics: [],
|
|
110
|
+
requestId,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
};
|
|
@@ -2,6 +2,10 @@ import { useCallback } from 'react';
|
|
|
2
2
|
import { FileTreePath } from '../types';
|
|
3
3
|
import { VizFileId, VizContent } from '@vizhub/viz-types';
|
|
4
4
|
import { randomId } from '../randomId';
|
|
5
|
+
import { EditorCache } from './useEditorCache';
|
|
6
|
+
import { getFileExtension } from './utils/fileExtension';
|
|
7
|
+
import { getLanguageExtension } from './CodeEditor/getOrCreateEditor';
|
|
8
|
+
import { editorCacheKey } from './useEditorCache';
|
|
5
9
|
|
|
6
10
|
// CRUD operations for files and directories
|
|
7
11
|
// CRUD = Create, Read, Update, Delete
|
|
@@ -9,6 +13,8 @@ export const useFileCRUD = ({
|
|
|
9
13
|
submitOperation,
|
|
10
14
|
closeTabs,
|
|
11
15
|
openTab,
|
|
16
|
+
editorCache,
|
|
17
|
+
content,
|
|
12
18
|
}: {
|
|
13
19
|
submitOperation: (
|
|
14
20
|
operation: (document: VizContent) => VizContent,
|
|
@@ -21,6 +27,8 @@ export const useFileCRUD = ({
|
|
|
21
27
|
fileId: VizFileId;
|
|
22
28
|
isTransient: boolean;
|
|
23
29
|
}) => void;
|
|
30
|
+
editorCache: EditorCache;
|
|
31
|
+
content: VizContent;
|
|
24
32
|
}) => {
|
|
25
33
|
// Create a new file
|
|
26
34
|
const createFile = useCallback(
|
|
@@ -78,6 +86,45 @@ export const useFileCRUD = ({
|
|
|
78
86
|
},
|
|
79
87
|
},
|
|
80
88
|
}));
|
|
89
|
+
|
|
90
|
+
const oldName = content.files[fileId].name;
|
|
91
|
+
const oldExtension = getFileExtension(oldName);
|
|
92
|
+
|
|
93
|
+
const newExtension = getFileExtension(newName);
|
|
94
|
+
|
|
95
|
+
const didExtensionChange =
|
|
96
|
+
newExtension !== oldExtension;
|
|
97
|
+
if (didExtensionChange) {
|
|
98
|
+
// Update the language compartment of the corresponding CodeMirror editor
|
|
99
|
+
// with the new language, based on the new extension, only if it changed.
|
|
100
|
+
// Search through all cached editors for this file ID
|
|
101
|
+
for (const [
|
|
102
|
+
cacheKey,
|
|
103
|
+
cachedEditor,
|
|
104
|
+
] of editorCache.entries()) {
|
|
105
|
+
// Check if this cache entry is for the current file
|
|
106
|
+
if (cacheKey.startsWith(fileId + '|')) {
|
|
107
|
+
if (
|
|
108
|
+
cachedEditor &&
|
|
109
|
+
cachedEditor.languageCompartment
|
|
110
|
+
) {
|
|
111
|
+
const newLanguageExtension =
|
|
112
|
+
getLanguageExtension(newExtension);
|
|
113
|
+
|
|
114
|
+
// Reconfigure the language compartment with the new language extension
|
|
115
|
+
cachedEditor.editor.dispatch({
|
|
116
|
+
effects:
|
|
117
|
+
cachedEditor.languageCompartment.reconfigure(
|
|
118
|
+
newLanguageExtension
|
|
119
|
+
? [newLanguageExtension]
|
|
120
|
+
: [],
|
|
121
|
+
),
|
|
122
|
+
});
|
|
123
|
+
// Continue to update all instances of this file in different panes
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
81
128
|
},
|
|
82
129
|
[submitOperation],
|
|
83
130
|
);
|
|
@@ -1,26 +1,36 @@
|
|
|
1
|
-
import { useState, useCallback } from 'react';
|
|
1
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import type { Pane } from '../types';
|
|
3
|
+
import { VizContent } from '@vizhub/viz-types';
|
|
2
4
|
|
|
3
5
|
// TODO bring this feature back
|
|
4
6
|
// When a page is opened with an active file,
|
|
5
7
|
// make sure all the directories leading to that file
|
|
6
8
|
// are opened automatically.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
export const initialOpenDirectories = (
|
|
10
|
+
activeFile: string | null,
|
|
11
|
+
): Set<VZPath> => {
|
|
12
|
+
const openDirectories = new Set<VZPath>();
|
|
13
|
+
if (activeFile) {
|
|
14
|
+
const path = activeFile.split('/');
|
|
15
|
+
for (let i = 1; i < path.length; i++) {
|
|
16
|
+
openDirectories.add(path.slice(0, i).join('/'));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return openDirectories;
|
|
20
|
+
};
|
|
17
21
|
|
|
18
22
|
type VZPath = string;
|
|
19
23
|
|
|
20
24
|
// Inspired by
|
|
21
25
|
// https://github.com/vizhub-core/vizhub/blob/main/vizhub-v2/packages/neoFrontend/src/pages/VizPage/Body/Editor/FilesSection/useOpenDirectories.js
|
|
22
26
|
// TODO move this into reducer
|
|
23
|
-
export const useOpenDirectories = (
|
|
27
|
+
export const useOpenDirectories = ({
|
|
28
|
+
activePane,
|
|
29
|
+
content,
|
|
30
|
+
}: {
|
|
31
|
+
activePane: Pane;
|
|
32
|
+
content: VizContent;
|
|
33
|
+
}): {
|
|
24
34
|
isDirectoryOpen: (path: VZPath) => boolean;
|
|
25
35
|
toggleDirectory: (path: VZPath) => void;
|
|
26
36
|
} => {
|
|
@@ -29,6 +39,27 @@ export const useOpenDirectories = (): {
|
|
|
29
39
|
Set<VZPath>
|
|
30
40
|
>(new Set());
|
|
31
41
|
|
|
42
|
+
// (client-side only) initialize the open directories
|
|
43
|
+
// based on the open files from the URL params.
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (
|
|
46
|
+
activePane.type === 'leafPane' &&
|
|
47
|
+
activePane?.activeFileId &&
|
|
48
|
+
content.files[activePane.activeFileId]
|
|
49
|
+
) {
|
|
50
|
+
const activeFileId = activePane.activeFileId;
|
|
51
|
+
const fileName = content.files[activeFileId].name;
|
|
52
|
+
const dirsToOpen = initialOpenDirectories(fileName);
|
|
53
|
+
setOpenDirectories((prev) => {
|
|
54
|
+
const updated = new Set(prev);
|
|
55
|
+
for (const dir of dirsToOpen) {
|
|
56
|
+
updated.add(dir);
|
|
57
|
+
}
|
|
58
|
+
return updated;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}, [activePane]);
|
|
62
|
+
|
|
32
63
|
// Whether a directory is open.
|
|
33
64
|
const isDirectoryOpen: (path: VZPath) => boolean =
|
|
34
65
|
useCallback(
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility function to extract file extension from a file name.
|
|
3
|
+
* @param fileName - The name of the file (e.g., "index.html", "script.js")
|
|
4
|
+
* @returns The file extension without the dot (e.g., "html", "js") or undefined if no extension
|
|
5
|
+
*/
|
|
6
|
+
export const getFileExtension = (
|
|
7
|
+
fileName: string,
|
|
8
|
+
): string | undefined => {
|
|
9
|
+
return fileName.split('.').pop();
|
|
10
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { VZState, VZAction } from '.';
|
|
2
|
+
|
|
3
|
+
export const setIsAIChatOpenReducer = (
|
|
4
|
+
state: VZState,
|
|
5
|
+
action: VZAction,
|
|
6
|
+
): VZState => {
|
|
7
|
+
if (action.type === 'set_is_ai_chat_open') {
|
|
8
|
+
return {
|
|
9
|
+
...state,
|
|
10
|
+
isAIChatOpen: action.value,
|
|
11
|
+
// When opening AI chat, close search
|
|
12
|
+
isSearchOpen: action.value
|
|
13
|
+
? false
|
|
14
|
+
: state.isSearchOpen,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return state;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const toggleAIChatFocusedReducer = (
|
|
21
|
+
state: VZState,
|
|
22
|
+
action: VZAction,
|
|
23
|
+
): VZState => {
|
|
24
|
+
if (action.type === 'toggle_ai_chat_focused') {
|
|
25
|
+
return {
|
|
26
|
+
...state,
|
|
27
|
+
aiChatFocused: !state.aiChatFocused,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return state;
|
|
31
|
+
};
|
|
@@ -30,6 +30,10 @@ import {
|
|
|
30
30
|
setSearchFocusedIndexReducer,
|
|
31
31
|
toggleSearchFocusedReducer,
|
|
32
32
|
} from './searchReducer';
|
|
33
|
+
import {
|
|
34
|
+
setIsAIChatOpenReducer,
|
|
35
|
+
toggleAIChatFocusedReducer,
|
|
36
|
+
} from './aiChatReducer';
|
|
33
37
|
import { toggleAutoFollowReducer } from './toggleAutoFollowReducer';
|
|
34
38
|
import { updatePresenceIndicatorReducer } from './updatePresenceIndicatorReducer';
|
|
35
39
|
import { splitCurrentPaneReducer } from './splitCurrentPaneReducer';
|
|
@@ -52,6 +56,12 @@ export type VZState = {
|
|
|
52
56
|
// True to show the search instead of files
|
|
53
57
|
isSearchOpen: boolean;
|
|
54
58
|
|
|
59
|
+
// True to show the AI chat instead of files
|
|
60
|
+
isAIChatOpen: boolean;
|
|
61
|
+
|
|
62
|
+
// True if the AI chat input should focus on the next render.
|
|
63
|
+
aiChatFocused: boolean;
|
|
64
|
+
|
|
55
65
|
// True to show the settings modal.
|
|
56
66
|
isSettingsOpen: boolean;
|
|
57
67
|
|
|
@@ -115,6 +125,14 @@ export type VZAction =
|
|
|
115
125
|
// * Sets whether the search tab is open.
|
|
116
126
|
| { type: 'set_is_search_open'; value: boolean }
|
|
117
127
|
|
|
128
|
+
// `set_is_ai_chat_open`
|
|
129
|
+
// * Sets whether the AI chat tab is open.
|
|
130
|
+
| { type: 'set_is_ai_chat_open'; value: boolean }
|
|
131
|
+
|
|
132
|
+
// `toggle_ai_chat_focused`
|
|
133
|
+
// * Toggles focused variable to trigger AI chat input focus
|
|
134
|
+
| { type: 'toggle_ai_chat_focused' }
|
|
135
|
+
|
|
118
136
|
// `set_search`
|
|
119
137
|
// * Sets the current search pattern
|
|
120
138
|
| { type: 'set_search'; value: string }
|
|
@@ -192,6 +210,8 @@ const reducers = [
|
|
|
192
210
|
setSearchFocusedIndexReducer,
|
|
193
211
|
toggleSearchFocusedReducer,
|
|
194
212
|
setIsSearchOpenReducer,
|
|
213
|
+
setIsAIChatOpenReducer,
|
|
214
|
+
toggleAIChatFocusedReducer,
|
|
195
215
|
setIsSettingsOpenReducer,
|
|
196
216
|
setIsDocOpenReducer,
|
|
197
217
|
editorNoLongerWantsFocusReducer,
|