@@ -5,14 +5,16 @@ import { GlobalColors } from "./styles/colors";
55import { GlobalFonts } from "./styles/fonts" ;
66import type { ProjectConfig } from "./config" ;
77import type { WorkspaceSelection } from "./components/ProjectSidebar" ;
8- import type { WorkspaceMetadata } from "./types/workspace" ;
98import ProjectSidebar from "./components/ProjectSidebar" ;
109import NewWorkspaceModal from "./components/NewWorkspaceModal" ;
1110import { AIView } from "./components/AIView" ;
1211import { ErrorBoundary } from "./components/ErrorBoundary" ;
1312import { TipsCarousel } from "./components/TipsCarousel" ;
1413import { usePersistedState } from "./hooks/usePersistedState" ;
1514import { 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
1820const globalStyles = css `
@@ -140,10 +142,6 @@ const WelcomeView = styled.div`
140142` ;
141143
142144function 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