vzcode 0.48.0 → 0.50.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/dist/assets/index-jYtAJn79.css +1 -0
- package/dist/assets/index-mCJe54sw.js +147 -0
- package/dist/index.html +2 -2
- package/package.json +9 -8
- package/src/client/AIAssist/AIAssistWidget/index.tsx +28 -12
- package/src/client/TabList/index.tsx +15 -13
- package/src/client/VZCodeContext.tsx +19 -1
- package/src/client/VZMiddle.tsx +11 -3
- package/src/client/VZResizer/index.tsx +10 -2
- package/src/client/bootstrap.ts +3 -1
- package/src/client/main.jsx +4 -1
- package/src/client/tabsSearchParameters.test.ts +102 -0
- package/src/client/tabsSearchParameters.ts +73 -0
- package/src/client/vzReducer/createInitialState.ts +11 -4
- package/src/server/generateAIResponse.js +7 -3
- package/dist/assets/index-NqGS0RuC.css +0 -1
- package/dist/assets/index-ohy9XGuy.js +0 -112
package/dist/index.html
CHANGED
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
href="https://unpkg.com/boxicons@2.0.7/css/boxicons.min.css"
|
|
25
25
|
rel="stylesheet"
|
|
26
26
|
/>
|
|
27
|
-
<script type="module" crossorigin src="/assets/index-
|
|
28
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
27
|
+
<script type="module" crossorigin src="/assets/index-mCJe54sw.js"></script>
|
|
28
|
+
<link rel="stylesheet" crossorigin href="/assets/index-jYtAJn79.css">
|
|
29
29
|
</head>
|
|
30
30
|
<body>
|
|
31
31
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vzcode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
4
4
|
"description": "Multiplayer code editor system",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -98,24 +98,25 @@
|
|
|
98
98
|
"ignore": "^5.3.0",
|
|
99
99
|
"json0-ot-diff": "^1.1.2",
|
|
100
100
|
"ngrok": "^4.3.3",
|
|
101
|
-
"open": "^10.0.
|
|
102
|
-
"openai": "^4.24.
|
|
101
|
+
"open": "^10.0.3",
|
|
102
|
+
"openai": "^4.24.3",
|
|
103
103
|
"react": "^18.2.0",
|
|
104
104
|
"react-bootstrap": "^2.9.2",
|
|
105
105
|
"react-dom": "^18.2.0",
|
|
106
|
+
"react-router-dom": "^6.21.2",
|
|
106
107
|
"sharedb": "^4.1.2",
|
|
107
108
|
"sharedb-client-browser": "^4.4.0",
|
|
108
|
-
"vizhub-ui": "^3.
|
|
109
|
+
"vizhub-ui": "^3.17.0",
|
|
109
110
|
"ws": "^8.16.0"
|
|
110
111
|
},
|
|
111
112
|
"devDependencies": {
|
|
112
|
-
"@types/react": "^18.2.
|
|
113
|
+
"@types/react": "^18.2.47",
|
|
113
114
|
"@types/react-dom": "^18.2.18",
|
|
114
115
|
"@vitejs/plugin-react": "^4.2.1",
|
|
115
116
|
"prettier": "^3.1.1",
|
|
116
|
-
"sass": "^1.69.
|
|
117
|
+
"sass": "^1.69.7",
|
|
117
118
|
"typescript": "^5.3.3",
|
|
118
|
-
"vite": "^5.0.
|
|
119
|
-
"vitest": "^1.1.
|
|
119
|
+
"vite": "^5.0.11",
|
|
120
|
+
"vitest": "^1.1.3"
|
|
120
121
|
}
|
|
121
122
|
}
|
|
@@ -4,7 +4,11 @@ import {
|
|
|
4
4
|
ShareDBDoc,
|
|
5
5
|
VZCodeContent,
|
|
6
6
|
} from '../../../types';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
Button,
|
|
9
|
+
OverlayTrigger,
|
|
10
|
+
Tooltip,
|
|
11
|
+
} from '../../bootstrap';
|
|
8
12
|
import { EditorCache } from '../../useEditorCache';
|
|
9
13
|
import { TabState } from '../../vzReducer';
|
|
10
14
|
import {
|
|
@@ -24,6 +28,8 @@ export const AIAssistWidget = ({
|
|
|
24
28
|
tabList,
|
|
25
29
|
aiAssistEndpoint,
|
|
26
30
|
aiAssistOptions,
|
|
31
|
+
aiAssistTooltipText = 'Start AI Assist',
|
|
32
|
+
aiAssistClickOverride,
|
|
27
33
|
}: {
|
|
28
34
|
activeFileId: FileId;
|
|
29
35
|
shareDBDoc: ShareDBDoc<VZCodeContent>;
|
|
@@ -31,6 +37,8 @@ export const AIAssistWidget = ({
|
|
|
31
37
|
tabList: Array<TabState>;
|
|
32
38
|
aiAssistEndpoint: string;
|
|
33
39
|
aiAssistOptions: { [key: string]: string };
|
|
40
|
+
aiAssistTooltipText?: string;
|
|
41
|
+
aiAssistClickOverride?: () => void;
|
|
34
42
|
}) => {
|
|
35
43
|
// The stream ID of the most recent request.
|
|
36
44
|
// * If `null`, no request has been made yet.
|
|
@@ -78,18 +86,26 @@ export const AIAssistWidget = ({
|
|
|
78
86
|
return (
|
|
79
87
|
showWidget && (
|
|
80
88
|
<div className="vz-code-ai-assist-widget">
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
<OverlayTrigger
|
|
90
|
+
placement="top"
|
|
91
|
+
overlay={
|
|
92
|
+
<Tooltip id="ai-assist-widget-tooltip">
|
|
93
|
+
{aiAssistTooltipText}
|
|
94
|
+
</Tooltip>
|
|
95
|
+
}
|
|
84
96
|
>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
97
|
+
<Button
|
|
98
|
+
onClick={aiAssistClickOverride || handleClick}
|
|
99
|
+
>
|
|
100
|
+
{aiStreamId ? (
|
|
101
|
+
enableStopGeneration ? (
|
|
102
|
+
<StopSVG />
|
|
103
|
+
) : null
|
|
104
|
+
) : (
|
|
105
|
+
<SparklesSVG />
|
|
106
|
+
)}
|
|
107
|
+
</Button>
|
|
108
|
+
</OverlayTrigger>
|
|
93
109
|
</div>
|
|
94
110
|
)
|
|
95
111
|
);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useCallback, useEffect } from 'react';
|
|
2
1
|
import type { TabState } from '../vzReducer';
|
|
3
2
|
import type { FileId, Files } from '../../types';
|
|
4
3
|
import { Tab } from './Tab';
|
|
@@ -25,18 +24,21 @@ export const TabList = ({
|
|
|
25
24
|
return (
|
|
26
25
|
<div className="vz-tab-list">
|
|
27
26
|
{files &&
|
|
28
|
-
tabList
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
27
|
+
tabList
|
|
28
|
+
// Handles the case that the tab list references a file that was deleted.
|
|
29
|
+
.filter((tabState) => tabState.fileId in files)
|
|
30
|
+
.map((tabState: TabState) => (
|
|
31
|
+
<Tab
|
|
32
|
+
key={tabState.fileId}
|
|
33
|
+
fileId={tabState.fileId}
|
|
34
|
+
isTransient={tabState.isTransient}
|
|
35
|
+
isActive={tabState.fileId === activeFileId}
|
|
36
|
+
setActiveFileId={setActiveFileId}
|
|
37
|
+
openTab={openTab}
|
|
38
|
+
closeTabs={closeTabs}
|
|
39
|
+
fileName={files[tabState.fileId].name}
|
|
40
|
+
/>
|
|
41
|
+
))}
|
|
40
42
|
</div>
|
|
41
43
|
);
|
|
42
44
|
};
|
|
@@ -24,6 +24,10 @@ import {
|
|
|
24
24
|
EditorCache,
|
|
25
25
|
useEditorCache,
|
|
26
26
|
} from './useEditorCache';
|
|
27
|
+
import {
|
|
28
|
+
useInitialTabs,
|
|
29
|
+
usePersistTabs,
|
|
30
|
+
} from './tabsSearchParameters';
|
|
27
31
|
|
|
28
32
|
// This context centralizes all the "smart" logic
|
|
29
33
|
// to do with the application state. This includes
|
|
@@ -142,11 +146,22 @@ export const VZCodeProvider = ({
|
|
|
142
146
|
typeScriptWorker,
|
|
143
147
|
});
|
|
144
148
|
|
|
149
|
+
// Get the initial open tabs from the URL
|
|
150
|
+
const {
|
|
151
|
+
tabList: initialTabList,
|
|
152
|
+
activeFileId: initialActiveFileId,
|
|
153
|
+
} = useInitialTabs();
|
|
154
|
+
|
|
145
155
|
// Set up the reducer that manages much of the application state.
|
|
146
156
|
// See https://react.dev/reference/react/useReducer
|
|
147
157
|
const [state, dispatch] = useReducer(
|
|
148
158
|
vzReducer,
|
|
149
|
-
{
|
|
159
|
+
{
|
|
160
|
+
defaultTheme,
|
|
161
|
+
initialUsername,
|
|
162
|
+
initialTabList,
|
|
163
|
+
initialActiveFileId,
|
|
164
|
+
},
|
|
150
165
|
createInitialState,
|
|
151
166
|
);
|
|
152
167
|
|
|
@@ -160,6 +175,9 @@ export const VZCodeProvider = ({
|
|
|
160
175
|
username,
|
|
161
176
|
} = state;
|
|
162
177
|
|
|
178
|
+
// Sync tab state to the URL
|
|
179
|
+
usePersistTabs({ activeFileId, tabList });
|
|
180
|
+
|
|
163
181
|
// Functions for dispatching actions to the reducer.
|
|
164
182
|
const {
|
|
165
183
|
setActiveFileId,
|
package/src/client/VZMiddle.tsx
CHANGED
|
@@ -17,10 +17,14 @@ export const VZMiddle = ({
|
|
|
17
17
|
enableAIAssist = true,
|
|
18
18
|
aiAssistEndpoint,
|
|
19
19
|
aiAssistOptions,
|
|
20
|
+
aiAssistTooltipText,
|
|
21
|
+
aiAssistClickOverride,
|
|
20
22
|
}: {
|
|
21
23
|
enableAIAssist?: boolean;
|
|
22
|
-
aiAssistEndpoint
|
|
23
|
-
aiAssistOptions
|
|
24
|
+
aiAssistEndpoint?: string;
|
|
25
|
+
aiAssistOptions?: { [key: string]: string };
|
|
26
|
+
aiAssistTooltipText?: string;
|
|
27
|
+
aiAssistClickOverride?: () => void;
|
|
24
28
|
}) => {
|
|
25
29
|
const { codeEditorWidth } = useContext(
|
|
26
30
|
SplitPaneResizeContext,
|
|
@@ -63,7 +67,9 @@ export const VZMiddle = ({
|
|
|
63
67
|
closeTabs={closeTabs}
|
|
64
68
|
createFile={createFile}
|
|
65
69
|
/>
|
|
66
|
-
{content &&
|
|
70
|
+
{content &&
|
|
71
|
+
activeFileId &&
|
|
72
|
+
shareDBDoc.data.files[activeFileId] ? (
|
|
67
73
|
<CodeEditor
|
|
68
74
|
shareDBDoc={shareDBDoc}
|
|
69
75
|
submitOperation={submitOperation}
|
|
@@ -88,6 +94,8 @@ export const VZMiddle = ({
|
|
|
88
94
|
tabList={tabList}
|
|
89
95
|
aiAssistEndpoint={aiAssistEndpoint}
|
|
90
96
|
aiAssistOptions={aiAssistOptions}
|
|
97
|
+
aiAssistTooltipText={aiAssistTooltipText}
|
|
98
|
+
aiAssistClickOverride={aiAssistClickOverride}
|
|
91
99
|
/>
|
|
92
100
|
) : null}
|
|
93
101
|
<CodeErrorOverlay
|
|
@@ -26,7 +26,13 @@ const resizerInteractionSurfaceWidthWhileDragging = 200;
|
|
|
26
26
|
// This is the part of the resizer that is visible to the user.
|
|
27
27
|
const resizerThumbWidth = 4;
|
|
28
28
|
|
|
29
|
-
export const VZResizer = ({
|
|
29
|
+
export const VZResizer = ({
|
|
30
|
+
side,
|
|
31
|
+
isSidebarVisible = true,
|
|
32
|
+
}: {
|
|
33
|
+
side: Side;
|
|
34
|
+
isSidebarVisible?: boolean;
|
|
35
|
+
}) => {
|
|
30
36
|
const {
|
|
31
37
|
sidebarWidth,
|
|
32
38
|
codeEditorWidth,
|
|
@@ -96,7 +102,9 @@ export const VZResizer = ({ side }: { side: Side }) => {
|
|
|
96
102
|
const left =
|
|
97
103
|
(side === 'left'
|
|
98
104
|
? sidebarWidth
|
|
99
|
-
:
|
|
105
|
+
: isSidebarVisible
|
|
106
|
+
? sidebarWidth + codeEditorWidth
|
|
107
|
+
: codeEditorWidth) -
|
|
100
108
|
resizerWidth / 2;
|
|
101
109
|
|
|
102
110
|
return shouldRenderResizer ? (
|
package/src/client/bootstrap.ts
CHANGED
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
import Form from 'react-bootstrap/cjs/Form.js';
|
|
5
5
|
import Button from 'react-bootstrap/cjs/Button.js';
|
|
6
6
|
import Modal from 'react-bootstrap/cjs/Modal.js';
|
|
7
|
+
import OverlayTrigger from 'react-bootstrap/cjs/OverlayTrigger.js';
|
|
8
|
+
import Tooltip from 'react-bootstrap/cjs/Tooltip.js';
|
|
7
9
|
|
|
8
10
|
// Pull in custom CSS from vizhub-ui.
|
|
9
11
|
import 'vizhub-ui/dist/vizhub-ui.css';
|
|
10
12
|
|
|
11
|
-
export { Form, Button, Modal };
|
|
13
|
+
export { Form, Button, Modal, OverlayTrigger, Tooltip };
|
package/src/client/main.jsx
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import * as ReactDOM from 'react-dom/client';
|
|
3
3
|
import App from './App';
|
|
4
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
4
5
|
|
|
5
6
|
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
6
7
|
// <React.StrictMode>
|
|
7
|
-
<
|
|
8
|
+
<BrowserRouter>
|
|
9
|
+
<App />
|
|
10
|
+
</BrowserRouter>,
|
|
8
11
|
// </React.StrictMode>,
|
|
9
12
|
);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { describe, expect, it, test } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
encodeTabs,
|
|
4
|
+
decodeTabs,
|
|
5
|
+
type EditorTabsState,
|
|
6
|
+
} from './tabsSearchParameters';
|
|
7
|
+
|
|
8
|
+
describe('tabsSearchParameters', () => {
|
|
9
|
+
test.each([
|
|
10
|
+
{
|
|
11
|
+
name: 'empty',
|
|
12
|
+
search: '',
|
|
13
|
+
state: { tabList: [], activeFileId: null },
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'persistent tab',
|
|
17
|
+
search: '123=pa',
|
|
18
|
+
state: {
|
|
19
|
+
tabList: [{ fileId: '123', isTransient: false }],
|
|
20
|
+
activeFileId: '123',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'transient tab',
|
|
25
|
+
search: '456=ta',
|
|
26
|
+
state: {
|
|
27
|
+
tabList: [{ fileId: '456', isTransient: true }],
|
|
28
|
+
activeFileId: '456',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'no active file',
|
|
33
|
+
search: '678=p',
|
|
34
|
+
state: {
|
|
35
|
+
tabList: [{ fileId: '678', isTransient: false }],
|
|
36
|
+
activeFileId: null,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'three tabs',
|
|
41
|
+
search: '111=p&222=ta&333=p',
|
|
42
|
+
state: {
|
|
43
|
+
tabList: [
|
|
44
|
+
{ fileId: '111', isTransient: false },
|
|
45
|
+
{ fileId: '222', isTransient: true },
|
|
46
|
+
{ fileId: '333', isTransient: false },
|
|
47
|
+
],
|
|
48
|
+
activeFileId: '222',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
])('round trip: $name', ({ search, state }) => {
|
|
52
|
+
const decoded = decodeTabs(new URLSearchParams(search));
|
|
53
|
+
expect(decoded).toStrictEqual(state);
|
|
54
|
+
const encoded = encodeTabs(state).toString();
|
|
55
|
+
expect(encoded).toBe(search);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test.each([
|
|
59
|
+
{
|
|
60
|
+
name: 'empty value',
|
|
61
|
+
search: 'abc',
|
|
62
|
+
correctedSearch: 'abc=p',
|
|
63
|
+
state: {
|
|
64
|
+
tabList: [{ fileId: 'abc', isTransient: false }],
|
|
65
|
+
activeFileId: null,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'unknown characters',
|
|
70
|
+
search: '890=xytzaw',
|
|
71
|
+
correctedSearch: '890=ta',
|
|
72
|
+
state: {
|
|
73
|
+
tabList: [{ fileId: '890', isTransient: true }],
|
|
74
|
+
activeFileId: '890',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'wrong order',
|
|
79
|
+
search: '321=ap',
|
|
80
|
+
correctedSearch: '321=pa',
|
|
81
|
+
state: {
|
|
82
|
+
tabList: [{ fileId: '321', isTransient: false }],
|
|
83
|
+
activeFileId: '321',
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
])(
|
|
87
|
+
'decode: $name',
|
|
88
|
+
({ search, correctedSearch, state }) => {
|
|
89
|
+
const { tabList, activeFileId } = decodeTabs(
|
|
90
|
+
new URLSearchParams(),
|
|
91
|
+
);
|
|
92
|
+
expect(tabList).toStrictEqual([]);
|
|
93
|
+
expect(activeFileId).toBe(null);
|
|
94
|
+
const decoded = decodeTabs(
|
|
95
|
+
new URLSearchParams(search),
|
|
96
|
+
);
|
|
97
|
+
expect(decoded).toStrictEqual(state);
|
|
98
|
+
const corrected = encodeTabs(decoded).toString();
|
|
99
|
+
expect(corrected).toBe(correctedSearch);
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { useNavigate } from 'react-router-dom';
|
|
2
|
+
import { TabState } from './vzReducer';
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { FileId } from '../types';
|
|
5
|
+
|
|
6
|
+
export interface EditorTabsState {
|
|
7
|
+
// The ID of the file that is currently active.
|
|
8
|
+
// Invariant: `activeFileId` is always in `tabList`.
|
|
9
|
+
activeFileId: FileId | null;
|
|
10
|
+
|
|
11
|
+
// The list of open tabs.
|
|
12
|
+
tabList: Array<TabState>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const encodeTabs = ({
|
|
16
|
+
tabList,
|
|
17
|
+
activeFileId,
|
|
18
|
+
}: EditorTabsState): URLSearchParams => {
|
|
19
|
+
const searchParams = new URLSearchParams();
|
|
20
|
+
for (const tab of tabList) {
|
|
21
|
+
let tabState = tab.isTransient ? 't' : 'p';
|
|
22
|
+
// mark the active file
|
|
23
|
+
if (tab.fileId === activeFileId) {
|
|
24
|
+
tabState += 'a';
|
|
25
|
+
}
|
|
26
|
+
searchParams.set(tab.fileId, tabState);
|
|
27
|
+
}
|
|
28
|
+
return searchParams;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const decodeTabs = (
|
|
32
|
+
searchParams: URLSearchParams,
|
|
33
|
+
): EditorTabsState => {
|
|
34
|
+
const tabList: TabState[] = [];
|
|
35
|
+
let activeFileId = null;
|
|
36
|
+
for (const [fileId, tabState] of Array.from(
|
|
37
|
+
searchParams.entries(),
|
|
38
|
+
)) {
|
|
39
|
+
const isTransient = tabState.includes('t');
|
|
40
|
+
const isActive = tabState.includes('a');
|
|
41
|
+
if (isActive) {
|
|
42
|
+
activeFileId = fileId;
|
|
43
|
+
}
|
|
44
|
+
tabList.push({ fileId, isTransient });
|
|
45
|
+
}
|
|
46
|
+
return { tabList, activeFileId };
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const useInitialTabs = (): EditorTabsState => {
|
|
50
|
+
return decodeTabs(
|
|
51
|
+
new URLSearchParams(window.location.search),
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const usePersistTabs = ({
|
|
56
|
+
activeFileId,
|
|
57
|
+
tabList,
|
|
58
|
+
}: EditorTabsState) => {
|
|
59
|
+
const navigate = useNavigate();
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
navigate(
|
|
62
|
+
{
|
|
63
|
+
search: encodeTabs({
|
|
64
|
+
activeFileId,
|
|
65
|
+
tabList,
|
|
66
|
+
})
|
|
67
|
+
.toString()
|
|
68
|
+
.replace(/^\?$/, ''),
|
|
69
|
+
},
|
|
70
|
+
{ replace: true },
|
|
71
|
+
);
|
|
72
|
+
}, [activeFileId, tabList]);
|
|
73
|
+
};
|
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
import { VZState } from '.';
|
|
2
|
-
import { Username } from '../../types';
|
|
1
|
+
import { TabState, VZState } from '.';
|
|
2
|
+
import { FileId, Username } from '../../types';
|
|
3
3
|
import { ThemeLabel } from '../themes';
|
|
4
4
|
|
|
5
5
|
export const createInitialState = ({
|
|
6
6
|
defaultTheme,
|
|
7
7
|
initialUsername = 'Anonymous',
|
|
8
|
+
initialTabList = [],
|
|
9
|
+
initialActiveFileId = null,
|
|
8
10
|
}: {
|
|
9
11
|
defaultTheme: ThemeLabel;
|
|
10
12
|
initialUsername?: Username;
|
|
13
|
+
initialTabList?: TabState[];
|
|
14
|
+
initialActiveFileId?: FileId;
|
|
11
15
|
}): VZState => ({
|
|
12
|
-
tabList:
|
|
13
|
-
activeFileId:
|
|
16
|
+
tabList: initialTabList,
|
|
17
|
+
activeFileId:
|
|
18
|
+
initialActiveFileId ??
|
|
19
|
+
initialTabList[0]?.fileId ??
|
|
20
|
+
null,
|
|
14
21
|
theme: defaultTheme,
|
|
15
22
|
isSettingsOpen: false,
|
|
16
23
|
editorWantsFocus: false,
|
|
@@ -65,16 +65,20 @@ export const generateAIResponse = async ({
|
|
|
65
65
|
content: [
|
|
66
66
|
'You are an expert programmer.',
|
|
67
67
|
'Your task is to output ONLY the code that replaces <FILL_ME> correctly.',
|
|
68
|
-
'Do not add any markdown.',
|
|
68
|
+
'Do not add any markdown around.',
|
|
69
69
|
'Do not duplicate the code before or after <FILL_ME>.',
|
|
70
70
|
'Do not make any changes outside of <FILL_ME>.',
|
|
71
|
+
'Do not enclose the output with backticks.',
|
|
72
|
+
'If any additional instructions are required, they will be provided in comments.',
|
|
71
73
|
].join(' '),
|
|
72
74
|
},
|
|
73
75
|
{ role: 'user', content: inputText },
|
|
74
76
|
];
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
if (debug) {
|
|
79
|
+
console.log('[generateAIResponse] messages:');
|
|
80
|
+
console.log(JSON.stringify(messages, null, 2));
|
|
81
|
+
}
|
|
78
82
|
|
|
79
83
|
streams[streamId] = await openai.chat.completions.create({
|
|
80
84
|
// model: 'gpt-3.5-turbo',
|