@@ -53,6 +53,7 @@ import {
5353import { ProjectLogo } from "@/views/Workspace/ProjectLogo" ;
5454import { CreateFeatureDialog } from "./CreateFeatureDialog" ;
5555import { SessionDropdownMenuItems } from "@/components/shared/SessionMenuItems" ;
56+ import { NewTerminalSplitButton } from "@/components/ui/new-terminal-button" ;
5657import type { Feature , WorkspaceData , WorkspaceProject } from "@/views/Workspace/types" ;
5758
5859const MIN_WIDTH = 180 ;
@@ -810,6 +811,109 @@ function ProjectSessionsGroup({
810811 }
811812 } ;
812813
814+ const handleNewTerminal = async ( command ?: string ) => {
815+ let savedWorkspace : WorkspaceData | null = null ;
816+
817+ setWorkspace ( ( currentWorkspace ) => {
818+ if ( ! currentWorkspace ) return currentWorkspace ;
819+
820+ const currentProject = currentWorkspace . projects . find ( ( p ) => p . id === project . id ) ;
821+ if ( ! currentProject ) return currentWorkspace ;
822+
823+ const targetFeature = currentProject . features . find ( ( f ) => ! f . archived ) ;
824+
825+ if ( ! targetFeature ) {
826+ savedWorkspace = {
827+ ...currentWorkspace ,
828+ active_project_id : project . id ,
829+ } ;
830+ return savedWorkspace ;
831+ }
832+
833+ const panelId = targetFeature . panels [ 0 ] ?. id ;
834+ const title = command === "claude" ? "Claude Code" : command === "codex" ? "Codex" : "Terminal" ;
835+
836+ if ( ! panelId ) {
837+ const newPanelId = crypto . randomUUID ( ) ;
838+ const ptySessionId = crypto . randomUUID ( ) ;
839+ const ptyId = crypto . randomUUID ( ) ;
840+
841+ const newPanel = {
842+ id : newPanelId ,
843+ sessions : [ { id : ptySessionId , pty_id : ptyId , title, command } ] ,
844+ active_session_id : ptySessionId ,
845+ is_shared : false ,
846+ cwd : project . path ,
847+ } ;
848+
849+ const newProjects = currentWorkspace . projects . map ( ( p ) => {
850+ if ( p . id !== project . id ) return p ;
851+ return {
852+ ...p ,
853+ features : p . features . map ( ( f ) => {
854+ if ( f . id !== targetFeature . id ) return f ;
855+ return {
856+ ...f ,
857+ panels : [ ...f . panels , newPanel ] ,
858+ layout : { type : "panel" as const , panelId : newPanelId } ,
859+ } ;
860+ } ) ,
861+ active_feature_id : targetFeature . id ,
862+ view_mode : "features" as const ,
863+ } ;
864+ } ) ;
865+
866+ savedWorkspace = {
867+ ...currentWorkspace ,
868+ projects : newProjects ,
869+ active_project_id : project . id ,
870+ } ;
871+ return savedWorkspace ;
872+ } else {
873+ const ptySessionId = crypto . randomUUID ( ) ;
874+ const ptyId = crypto . randomUUID ( ) ;
875+
876+ const newProjects = currentWorkspace . projects . map ( ( p ) => {
877+ if ( p . id !== project . id ) return p ;
878+ return {
879+ ...p ,
880+ features : p . features . map ( ( f ) => {
881+ if ( f . id !== targetFeature . id ) return f ;
882+ return {
883+ ...f ,
884+ panels : f . panels . map ( ( panel ) => {
885+ if ( panel . id !== panelId ) return panel ;
886+ return {
887+ ...panel ,
888+ sessions : [
889+ ...( panel . sessions || [ ] ) ,
890+ { id : ptySessionId , pty_id : ptyId , title, command } ,
891+ ] ,
892+ active_session_id : ptySessionId ,
893+ } ;
894+ } ) ,
895+ } ;
896+ } ) ,
897+ active_feature_id : targetFeature . id ,
898+ view_mode : "features" as const ,
899+ } ;
900+ } ) ;
901+
902+ savedWorkspace = {
903+ ...currentWorkspace ,
904+ projects : newProjects ,
905+ active_project_id : project . id ,
906+ } ;
907+ return savedWorkspace ;
908+ }
909+ } ) ;
910+
911+ if ( savedWorkspace ) {
912+ await invoke ( "workspace_save" , { data : savedWorkspace } ) ;
913+ navigate ( { type : "workspace" , projectId : project . id , mode : "features" } ) ;
914+ }
915+ } ;
916+
813917 const projectDisplayName = project . name
814918 . split ( / [ - _ ] / )
815919 . map ( ( w ) => w . charAt ( 0 ) . toUpperCase ( ) + w . slice ( 1 ) )
@@ -819,7 +923,7 @@ function ProjectSessionsGroup({
819923 < div className = "px-2" >
820924 { /* Project Header */ }
821925 < div
822- className = { `flex items-center gap-1 px-2 py-1.5 rounded-lg cursor-pointer transition-colors ${
926+ className = { `group flex items-center gap-1 px-2 py-1.5 rounded-lg cursor-pointer transition-colors ${
823927 isActiveProject
824928 ? "bg-primary/10 text-primary"
825929 : "text-ink hover:bg-card-alt"
@@ -853,6 +957,13 @@ function ProjectSessionsGroup({
853957 < span className = "text-xs text-muted-foreground" >
854958 { isLoading ? "..." : filteredSessions . length }
855959 </ span >
960+
961+ { /* New Terminal Button */ }
962+ < NewTerminalSplitButton
963+ variant = "icon"
964+ onSelect = { handleNewTerminal }
965+ className = "opacity-0 group-hover:opacity-100"
966+ />
856967 </ div >
857968
858969 { /* Sessions List */ }
0 commit comments