Skip to content

Commit 001ffb8

Browse files
committed
Add view manager and organize view display
1 parent 346be11 commit 001ffb8

File tree

17 files changed

+493
-344
lines changed

17 files changed

+493
-344
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,5 @@ yarn-error.log*
3838
next-env.d.ts
3939

4040
# electron
41-
/out-desktop/
41+
/out-desktop/
42+
certificates

app/page.tsx

Lines changed: 41 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,22 @@
11
"use client";
22

3-
import CodeEditorView, {
4-
CodeEditorViewRef,
5-
} from "@/components/views/code-editor-view";
3+
import { CodeEditorViewRef } from "@/components/views/code-editor-view";
64
import { useMicVAD, utils } from "@/lib/hooks/use-mic-vad";
7-
import { BaseLLM, getModelLLM } from "@/lib/llm/llm";
8-
import { BaseSTT, getModelSTT } from "@/lib/stt/stt";
5+
import { BaseLLM } from "@/lib/llm/llm";
6+
import { BaseSTT } from "@/lib/stt/stt";
97
import { useContext, useEffect, useRef, useState } from "react";
108
import toast from "react-hot-toast";
119
import { CodeEditorAgent } from "@/lib/agent/code-editor-agent";
12-
import { BaseTTS, getModelTTS } from "@/lib/tts/tts";
13-
import AgentChatTerminalView from "@/components/views/agent-chat-terminal-view";
14-
import { AnimatePresence, motion } from "framer-motion";
15-
import { ViewDocument, ViewRef } from "@/lib/types";
10+
import { BaseTTS } from "@/lib/tts/tts";
1611
import EditorToolbar from "@/components/editor-toolbar";
1712
import { EditorContext } from "@/components/providers/editor-context-provider";
13+
import ViewDisplayArea from "@/components/view-display-area";
1814

1915
export default function Home() {
20-
const [isCanvasReady, setIsCanvasReady] = useState(false);
21-
22-
// const viewMap = useRef<Map<string, ViewRef | null>>(new Map());
2316
const editorContext = useContext(EditorContext);
2417

25-
const sttModelRef = useRef<BaseSTT | undefined>(undefined);
26-
const llmModelRef = useRef<BaseLLM | undefined>(undefined);
27-
const ttsModelRef = useRef<BaseTTS | undefined>(undefined);
28-
2918
// TODO: Use a timer to stop recorder if no speech is detected for more than 30 seconds
30-
31-
const [isProcessing, setIsProcessing] = useState(false);
19+
const isProcessingRef = useRef(false);
3220
const vad = useMicVAD({
3321
startOnLoad: false,
3422
ortConfig(ort) {
@@ -37,31 +25,48 @@ export default function Home() {
3725
workletURL: "/vad/vad.worklet.bundle.min.js",
3826
modelURL: "/vad/silero_vad.onnx",
3927
onSpeechStart: () => {
40-
if (!isProcessing) {
28+
if (!isProcessingRef.current) {
29+
const sttModel = editorContext?.aiModelConfig.getSTTModel();
30+
const llmModel = editorContext?.aiModelConfig.getLLMModel();
31+
const ttsModel = editorContext?.aiModelConfig.getTTSModel();
32+
let isConfigured = true;
33+
if (!sttModel) {
34+
toast.error("STT model is not configured in settings.");
35+
isConfigured = false;
36+
}
37+
if (!llmModel) {
38+
toast.error("LLM model is not configured in settings.");
39+
isConfigured = false;
40+
}
41+
if (!ttsModel) {
42+
toast.error("TTS model is not configured in settings.");
43+
isConfigured = false;
44+
}
45+
46+
if (!isConfigured) {
47+
return;
48+
}
49+
4150
editorContext?.setEditorStates((prev) => ({
4251
...prev,
4352
isListening: true,
4453
}));
4554
}
4655
},
4756
onSpeechEnd: (audio) => {
48-
if (!isProcessing) {
49-
setIsProcessing(true);
57+
if (!isProcessingRef.current) {
58+
isProcessingRef.current = true;
5059
const wavBuffer = utils.encodeWAV(audio);
5160
const blob = new Blob([wavBuffer], { type: "audio/wav" });
5261
console.log("Speech end\n", blob);
5362

54-
if (!llmModelRef.current) {
55-
toast.error("LLM model not loaded");
56-
return;
57-
}
58-
const agent = new CodeEditorAgent(
59-
sttModelRef.current,
60-
llmModelRef.current,
61-
ttsModelRef.current,
62-
);
63-
const codeEditor = editorContext?.getViewById("1") as CodeEditorViewRef;
64-
const viewDocument = codeEditor?.getViewDocument();
63+
const sttModel = editorContext?.aiModelConfig.getSTTModel() as BaseSTT;
64+
const llmModel = editorContext?.aiModelConfig.getLLMModel() as BaseLLM;
65+
const ttsModel = editorContext?.aiModelConfig.getTTSModel() as BaseTTS;
66+
67+
const agent = new CodeEditorAgent(sttModel, llmModel, ttsModel);
68+
const activeView = editorContext?.viewManager?.getActiveView();
69+
const viewDocument = activeView?.viewDocument;
6570
editorContext?.setEditorStates((prev) => ({
6671
...prev,
6772
isListening: false,
@@ -83,9 +88,7 @@ export default function Home() {
8388
}));
8489

8590
// Apply changes
86-
const codeEditor = editorContext?.getViewById(
87-
"1",
88-
) as CodeEditorViewRef;
91+
const codeEditor = activeView?.viewRef as CodeEditorViewRef;
8992
codeEditor?.applyChanges(changes);
9093

9194
// Play the audio in the blob
@@ -97,7 +100,7 @@ export default function Home() {
97100
...prev,
98101
isSpeaking: false,
99102
}));
100-
setIsProcessing(false);
103+
isProcessingRef.current = false;
101104
};
102105
editorContext?.setEditorStates((prev) => ({
103106
...prev,
@@ -106,68 +109,12 @@ export default function Home() {
106109
audio.play();
107110
return;
108111
}
109-
setIsProcessing(false);
112+
isProcessingRef.current = false;
110113
});
111114
}
112115
},
113116
});
114117

115-
// Load models
116-
useEffect(() => {
117-
if (editorContext?.persistSettings) {
118-
// Load STT
119-
if (
120-
editorContext?.persistSettings.sttAPIKey &&
121-
editorContext?.persistSettings.sttProvider &&
122-
editorContext?.persistSettings.sttModel
123-
) {
124-
const model = getModelSTT(
125-
editorContext?.persistSettings.sttAPIKey,
126-
editorContext?.persistSettings.sttProvider,
127-
editorContext?.persistSettings.sttModel,
128-
);
129-
sttModelRef.current = model;
130-
} else {
131-
toast.error("Please set STT Provider, Model and API key in settings");
132-
}
133-
134-
// Load LLM
135-
if (
136-
editorContext?.persistSettings.llmAPIKey &&
137-
editorContext?.persistSettings.llmProvider &&
138-
editorContext?.persistSettings.llmModel
139-
) {
140-
const model = getModelLLM(
141-
editorContext?.persistSettings.llmAPIKey,
142-
editorContext?.persistSettings.llmProvider,
143-
editorContext?.persistSettings.llmModel,
144-
0.85,
145-
);
146-
llmModelRef.current = model;
147-
} else {
148-
toast.error("Please set LLM Provider, Model and API key in settings");
149-
}
150-
151-
// Load TTS
152-
if (
153-
editorContext?.persistSettings.ttsAPIKey &&
154-
editorContext?.persistSettings.ttsProvider &&
155-
editorContext?.persistSettings.ttsModel &&
156-
editorContext?.persistSettings.ttsVoice
157-
) {
158-
const model = getModelTTS(
159-
editorContext?.persistSettings.ttsAPIKey,
160-
editorContext?.persistSettings.ttsProvider,
161-
editorContext?.persistSettings.ttsModel,
162-
editorContext?.persistSettings.ttsVoice,
163-
);
164-
ttsModelRef.current = model;
165-
} else {
166-
toast.error("Please set TTS Provider, Model and API key in settings");
167-
}
168-
}
169-
}, [editorContext?.persistSettings]);
170-
171118
// Toggle recording
172119
useEffect(() => {
173120
if (editorContext?.editorStates?.isRecording) {
@@ -177,77 +124,10 @@ export default function Home() {
177124
}
178125
}, [editorContext?.editorStates, vad]);
179126

180-
// useEffect(() => {
181-
// const url = "/test.tsx";
182-
// if (url) {
183-
// fetch(url)
184-
// .then((res) => res.text())
185-
// .then((text) => {
186-
// const viewId = "1";
187-
188-
// // Init a new viewDocument
189-
// const viewDocument: ViewDocument = {
190-
// fileContent: text,
191-
// filePath: url,
192-
// };
193-
194-
// // Get the code editor view
195-
// const codeEditor = editorContext?.getViewById(
196-
// viewId,
197-
// ) as CodeEditorViewRef;
198-
199-
// // Set the viewDocument
200-
// codeEditor?.setViewDocument(viewDocument);
201-
// });
202-
// }
203-
// }, []);
204-
205127
return (
206128
<div className="flex h-full w-full flex-col">
207129
<EditorToolbar />
208-
209-
<div className="flex h-full w-full flex-col p-1">
210-
<div className="flex h-full w-full flex-col items-start justify-between space-y-1.5 overflow-hidden rounded-xl bg-default p-2">
211-
<div
212-
className={`min-h-0 w-full flex-grow`}
213-
style={{
214-
cursor:
215-
editorContext?.editorStates?.isDrawing && !isCanvasReady
216-
? "wait"
217-
: "auto",
218-
}}
219-
>
220-
<CodeEditorView
221-
ref={(ref) => {
222-
if (ref) editorContext?.addView("1", ref);
223-
}}
224-
width="100%"
225-
height="100%"
226-
isDrawingMode={editorContext?.editorStates?.isDrawing}
227-
isDownloadClip={editorContext?.editorStates?.isDownloadClip}
228-
isDrawHulls={editorContext?.editorStates?.isDrawHulls}
229-
setIsCanvasReady={setIsCanvasReady}
230-
/>
231-
</div>
232-
<AnimatePresence>
233-
{editorContext?.editorStates?.isOpenChatView && (
234-
<motion.div
235-
className="h-full min-h-[60%] w-full pb-14"
236-
// Enter from bottom and exit to bottom
237-
initial={{ y: "100%" }}
238-
animate={{ y: 0 }}
239-
exit={{ y: "100%" }}
240-
>
241-
<AgentChatTerminalView
242-
ref={(ref) => {
243-
if (ref) editorContext?.addView("2", ref);
244-
}}
245-
/>
246-
</motion.div>
247-
)}
248-
</AnimatePresence>
249-
</div>
250-
</div>
130+
<ViewDisplayArea />
251131
</div>
252132
);
253133
}

components/editor-toolbar.tsx

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,37 @@
11
"use client";
22

33
import { Button, Divider, Tooltip } from "@nextui-org/react";
4-
import { useContext, useEffect, useState } from "react";
4+
import { useContext, useState } from "react";
55
import Icon from "@/components/icon";
66
import SettingModal from "@/components/modals/settings-modal";
77
import { AnimatePresence, motion } from "framer-motion";
88
import { EditorContext } from "./providers/editor-context-provider";
9+
import { getPlatform } from "@/lib/platform-api/platform-checker";
10+
import { PlatformEnum } from "@/lib/platform-api/available-platforms";
11+
import toast from "react-hot-toast";
912

1013
export default function EditorToolbar() {
1114
const editorContext = useContext(EditorContext);
1215

13-
const [isOpen, setIsOpen] = useState(false);
1416
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
1517

16-
useEffect(() => {
17-
setIsOpen(true);
18-
}, []);
18+
function setIsOpen(val: boolean) {
19+
if (editorContext) {
20+
editorContext.setEditorStates((prev) => ({
21+
...prev,
22+
isToolbarOpen: val,
23+
}));
24+
}
25+
}
1926

2027
return (
2128
<div
2229
className={
23-
"fixed bottom-0 left-0 z-10 flex w-full flex-col items-center justify-center space-y-0.5 pb-1"
30+
"fixed bottom-0 left-1/2 z-10 flex w-fit -translate-x-1/2 flex-col items-center justify-center space-y-0.5 pb-1"
2431
}
2532
>
2633
<AnimatePresence>
27-
{isOpen && (
34+
{editorContext?.editorStates.isToolbarOpen && (
2835
<motion.div
2936
initial={{
3037
y: 60,
@@ -65,25 +72,23 @@ export default function EditorToolbar() {
6572
<Icon name="comment" variant="outlined" />
6673
</Button>
6774
</Tooltip>
68-
{/* <Button variant="light"
69-
isIconOnly
70-
className="h-8 w-8 min-w-8 px-1 py-1 text-default-foreground"
71-
>
72-
<IconErase />
73-
</Button> */}
7475

7576
<Divider className="mx-1" orientation="vertical" />
7677
<Tooltip content={"Open Chat View"}>
7778
<Button
78-
variant="light"
79+
variant={
80+
editorContext?.editorStates?.isChatViewOpen
81+
? "solid"
82+
: "light"
83+
}
7984
isIconOnly
8085
className="h-8 w-8 min-w-8 px-1 py-1 text-default-foreground"
8186
onPress={() => {
8287
if (editorContext?.editorStates) {
8388
editorContext?.setEditorStates((prev) => ({
8489
...prev,
85-
isOpenChatView:
86-
!editorContext?.editorStates.isOpenChatView,
90+
isChatViewOpen:
91+
!editorContext?.editorStates.isChatViewOpen,
8792
}));
8893
}
8994
}}
@@ -97,6 +102,13 @@ export default function EditorToolbar() {
97102
isIconOnly
98103
className="h-8 w-8 min-w-8 px-1 py-1 text-default-foreground"
99104
onPress={() => {
105+
if (getPlatform() === PlatformEnum.VSCode) {
106+
toast.error(
107+
"Voice Chat is not supported in VSCode Extension. Please use other versions for Voice Chat.",
108+
);
109+
return;
110+
}
111+
100112
if (editorContext?.editorStates) {
101113
editorContext?.setEditorStates((prev) => ({
102114
...prev,
@@ -164,7 +176,7 @@ export default function EditorToolbar() {
164176
)}
165177
</AnimatePresence>
166178

167-
{isOpen ? (
179+
{editorContext?.editorStates.isToolbarOpen ? (
168180
<Button
169181
isIconOnly
170182
className="h-4 w-10 bg-content2"

0 commit comments

Comments
 (0)