Skip to content

Commit 991689e

Browse files
committed
feat: show streaming indicators!
1 parent 220c44b commit 991689e

File tree

8 files changed

+653
-452
lines changed

8 files changed

+653
-452
lines changed

src/App.tsx

Lines changed: 31 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ import { GlobalColors } from "./styles/colors";
55
import { GlobalFonts } from "./styles/fonts";
66
import type { ProjectConfig } from "./config";
77
import type { WorkspaceSelection } from "./components/ProjectSidebar";
8-
import type { WorkspaceMetadata } from "./types/workspace";
98
import ProjectSidebar from "./components/ProjectSidebar";
109
import NewWorkspaceModal from "./components/NewWorkspaceModal";
1110
import { AIView } from "./components/AIView";
1211
import { ErrorBoundary } from "./components/ErrorBoundary";
1312
import { TipsCarousel } from "./components/TipsCarousel";
1413
import { usePersistedState } from "./hooks/usePersistedState";
1514
import { matchesKeybind, KEYBINDS } from "./utils/ui/keybinds";
15+
import { useProjectManagement } from "./hooks/useProjectManagement";
16+
import { useWorkspaceManagement } from "./hooks/useWorkspaceManagement";
17+
import { useWorkspaceAggregators } from "./hooks/useWorkspaceAggregators";
1618

1719
// Global Styles with nice fonts
1820
const globalStyles = css`
@@ -140,10 +142,6 @@ const WelcomeView = styled.div`
140142
`;
141143

142144
function App() {
143-
const [projects, setProjects] = useState<Map<string, ProjectConfig>>(new Map());
144-
const [workspaceMetadata, setWorkspaceMetadata] = useState<Map<string, WorkspaceMetadata>>(
145-
new Map()
146-
);
147145
const [selectedWorkspace, setSelectedWorkspace] = usePersistedState<WorkspaceSelection | null>(
148146
"selectedWorkspace",
149147
null
@@ -152,79 +150,34 @@ function App() {
152150
const [workspaceModalProject, setWorkspaceModalProject] = useState<string | null>(null);
153151
const [sidebarCollapsed, setSidebarCollapsed] = usePersistedState("sidebarCollapsed", false);
154152

155-
useEffect(() => {
156-
void loadProjects();
157-
void loadWorkspaceMetadata();
158-
}, []);
159-
160-
const loadProjects = async () => {
161-
try {
162-
console.log("Loading projects from config...");
163-
const config = await window.api.config.load();
164-
console.log("Received config:", config);
165-
166-
if (config && Array.isArray(config.projects)) {
167-
console.log("Projects array length:", config.projects.length);
168-
const projectsMap = new Map<string, ProjectConfig>(config.projects);
169-
console.log("Created projects map, size:", projectsMap.size);
170-
setProjects(projectsMap);
171-
} else {
172-
console.log("No projects or invalid format");
173-
setProjects(new Map());
174-
}
175-
} catch (error) {
176-
console.error("Failed to load config:", error);
177-
setProjects(new Map());
178-
}
179-
};
153+
// Use custom hooks for project and workspace management
154+
const { projects, setProjects, addProject, removeProject } = useProjectManagement();
180155

181-
const loadWorkspaceMetadata = async () => {
182-
try {
183-
const metadataList = await window.api.workspace.list();
184-
const metadataMap = new Map();
185-
for (const metadata of metadataList) {
186-
metadataMap.set(metadata.workspacePath, metadata);
187-
}
188-
setWorkspaceMetadata(metadataMap);
189-
} catch (error) {
190-
console.error("Failed to load workspace metadata:", error);
191-
}
192-
};
156+
// Workspace management needs to update projects state when workspace operations complete
157+
const handleProjectsUpdate = useCallback(
158+
(newProjects: Map<string, ProjectConfig>) => {
159+
setProjects(newProjects);
160+
},
161+
[setProjects]
162+
);
193163

194-
const handleAddProject = async () => {
195-
try {
196-
const selectedPath = await window.api.dialog.selectDirectory();
197-
if (selectedPath && !projects.has(selectedPath)) {
198-
const newProjects = new Map(projects);
199-
newProjects.set(selectedPath, { path: selectedPath, workspaces: [] });
200-
setProjects(newProjects);
201-
202-
await window.api.config.save({
203-
projects: Array.from(newProjects.entries()),
204-
});
205-
}
206-
} catch (error) {
207-
console.error("Failed to add project:", error);
208-
}
209-
};
164+
const { workspaceMetadata, createWorkspace, removeWorkspace, renameWorkspace } =
165+
useWorkspaceManagement({
166+
projects,
167+
selectedWorkspace,
168+
onProjectsUpdate: handleProjectsUpdate,
169+
onSelectedWorkspaceUpdate: setSelectedWorkspace,
170+
});
210171

211-
const handleRemoveProject = async (path: string) => {
212-
const newProjects = new Map(projects);
213-
newProjects.delete(path);
214-
setProjects(newProjects);
172+
// Use workspace aggregators hook for message state
173+
const { getWorkspaceState, streamingStates } = useWorkspaceAggregators(workspaceMetadata);
215174

175+
const handleRemoveProject = async (path: string) => {
216176
// Clear selected workspace if it belongs to the removed project
217177
if (selectedWorkspace?.projectPath === path) {
218178
setSelectedWorkspace(null);
219179
}
220-
221-
try {
222-
await window.api.config.save({
223-
projects: Array.from(newProjects.entries()),
224-
});
225-
} catch (error) {
226-
console.error("Failed to save config:", error);
227-
}
180+
await removeProject(path);
228181
};
229182

230183
const handleAddWorkspace = (projectPath: string) => {
@@ -235,94 +188,9 @@ function App() {
235188
const handleCreateWorkspace = async (branchName: string) => {
236189
if (!workspaceModalProject) return;
237190

238-
const result = await window.api.workspace.create(workspaceModalProject, branchName);
239-
if (result.success) {
240-
// Update the project config with the new workspace
241-
const newProjects = new Map(projects);
242-
const projectConfig = newProjects.get(workspaceModalProject);
243-
if (projectConfig) {
244-
projectConfig.workspaces.push({
245-
path: result.metadata.workspacePath,
246-
});
247-
setProjects(newProjects);
248-
249-
await window.api.config.save({
250-
projects: Array.from(newProjects.entries()),
251-
});
252-
253-
// Reload workspace metadata to get the new workspace ID
254-
await loadWorkspaceMetadata();
255-
256-
// Construct WorkspaceSelection from backend metadata + frontend context
257-
setSelectedWorkspace({
258-
projectPath: workspaceModalProject,
259-
projectName: result.metadata.projectName,
260-
workspacePath: result.metadata.workspacePath,
261-
workspaceId: result.metadata.id,
262-
});
263-
}
264-
} else {
265-
throw new Error(result.error);
266-
}
267-
};
268-
269-
const handleRemoveWorkspace = async (
270-
workspaceId: string
271-
): Promise<{ success: boolean; error?: string }> => {
272-
const result = await window.api.workspace.remove(workspaceId);
273-
if (result.success) {
274-
// Reload config since backend has updated it
275-
const config = await window.api.config.load();
276-
const loadedProjects = new Map(config.projects);
277-
setProjects(loadedProjects);
278-
279-
// Reload workspace metadata
280-
await loadWorkspaceMetadata();
281-
282-
// Clear selected workspace if it was removed
283-
if (selectedWorkspace?.workspaceId === workspaceId) {
284-
setSelectedWorkspace(null);
285-
}
286-
return { success: true };
287-
} else {
288-
console.error("Failed to remove workspace:", result.error);
289-
return { success: false, error: result.error };
290-
}
291-
};
292-
293-
const handleRenameWorkspace = async (
294-
workspaceId: string,
295-
newName: string
296-
): Promise<{ success: boolean; error?: string }> => {
297-
const result = await window.api.workspace.rename(workspaceId, newName);
298-
if (result.success) {
299-
// Reload config since backend has updated it
300-
const config = await window.api.config.load();
301-
const loadedProjects = new Map(config.projects);
302-
setProjects(loadedProjects);
303-
304-
// Reload workspace metadata
305-
await loadWorkspaceMetadata();
306-
307-
// Update selected workspace if it was renamed
308-
if (selectedWorkspace?.workspaceId === workspaceId) {
309-
const newWorkspaceId = result.data.newWorkspaceId;
310-
311-
// Get updated workspace metadata from backend
312-
const newMetadata = await window.api.workspace.getInfo(newWorkspaceId);
313-
if (newMetadata) {
314-
setSelectedWorkspace({
315-
projectPath: selectedWorkspace.projectPath,
316-
projectName: newMetadata.projectName,
317-
workspacePath: newMetadata.workspacePath,
318-
workspaceId: newWorkspaceId,
319-
});
320-
}
321-
}
322-
return { success: true };
323-
} else {
324-
console.error("Failed to rename workspace:", result.error);
325-
return { success: false, error: result.error };
191+
const newWorkspace = await createWorkspace(workspaceModalProject, branchName);
192+
if (newWorkspace) {
193+
setSelectedWorkspace(newWorkspace);
326194
}
327195
};
328196

@@ -393,11 +261,12 @@ function App() {
393261
workspaceMetadata={workspaceMetadata}
394262
selectedWorkspace={selectedWorkspace}
395263
onSelectWorkspace={setSelectedWorkspace}
396-
onAddProject={() => void handleAddProject()}
264+
onAddProject={() => void addProject()}
397265
onAddWorkspace={(projectPath) => void handleAddWorkspace(projectPath)}
398266
onRemoveProject={(path) => void handleRemoveProject(path)}
399-
onRemoveWorkspace={handleRemoveWorkspace}
400-
onRenameWorkspace={handleRenameWorkspace}
267+
onRemoveWorkspace={removeWorkspace}
268+
onRenameWorkspace={renameWorkspace}
269+
streamingStates={streamingStates}
401270
collapsed={sidebarCollapsed}
402271
onToggleCollapsed={() => setSidebarCollapsed((prev) => !prev)}
403272
/>
@@ -415,6 +284,7 @@ function App() {
415284
workspaceId={selectedWorkspace.workspaceId}
416285
projectName={selectedWorkspace.projectName}
417286
branch={selectedWorkspace.workspacePath.split("/").pop() ?? ""}
287+
workspaceState={getWorkspaceState(selectedWorkspace.workspaceId)}
418288
/>
419289
</ErrorBoundary>
420290
) : (

0 commit comments

Comments
 (0)