1- import { CloudIcon , PlusIcon , RepeatIcon } from "@phosphor-icons/react" ;
1+ import {
2+ ChatCircleDotsIcon ,
3+ CloudIcon ,
4+ PlusIcon ,
5+ RepeatIcon ,
6+ } from "@phosphor-icons/react" ;
27import type { LoopSchemas } from "@posthog/api-client/loops" ;
38import type { UserBasic } from "@posthog/shared/domain-types" ;
49import { useOrgMembers } from "@posthog/ui/features/canvas/hooks/useOrgMembers" ;
10+ import { StopCloudRunDialog } from "@posthog/ui/features/sessions/components/StopCloudRunDialog" ;
511import { useSetHeaderContent } from "@posthog/ui/hooks/useSetHeaderContent" ;
612import { Button } from "@posthog/ui/primitives/Button" ;
7- import { navigateToNewLoop } from "@posthog/ui/router/navigationBridge" ;
13+ import { toast } from "@posthog/ui/primitives/toast" ;
14+ import {
15+ navigateToNewLoop ,
16+ navigateToTaskDetail ,
17+ } from "@posthog/ui/router/navigationBridge" ;
818import { Flex , Heading , Text } from "@radix-ui/themes" ;
919import { useMemo , useState } from "react" ;
20+ import { useLoopBuilderSessions } from "../hooks/useLoopBuilderSessions" ;
1021import { useLoopLimits , useLoops } from "../hooks/useLoops" ;
22+ import {
23+ type LoopBuilderSession ,
24+ useLoopBuilderSessionStore ,
25+ } from "../loopBuilderSessionStore" ;
1126import { useLoopDraftStore } from "../loopDraftStore" ;
1227import type { LoopTemplate } from "../loopTemplates" ;
1328import { LoopBuilderComposer } from "./LoopBuilderComposer" ;
@@ -23,6 +38,7 @@ function loopLimitReason(max: number): string {
2338}
2439
2540const EMPTY_MEMBERS : UserBasic [ ] = [ ] ;
41+ const EMPTY_BUILDER_SESSIONS : LoopBuilderSession [ ] = [ ] ;
2642
2743const SECTION_PREVIEW_COUNT = 5 ;
2844
@@ -31,6 +47,14 @@ function startBlankLoop(): void {
3147 navigateToNewLoop ( ) ;
3248}
3349
50+ function resumeBuilderSession ( taskId : string ) : void {
51+ navigateToTaskDetail ( taskId ) ;
52+ }
53+
54+ function removeBuilderSession ( taskId : string ) : void {
55+ useLoopBuilderSessionStore . getState ( ) . removeSession ( taskId ) ;
56+ }
57+
3458function startLoopFromTemplate ( template : LoopTemplate ) : void {
3559 useLoopDraftStore
3660 . getState ( )
@@ -60,6 +84,8 @@ export function LoopsListView() {
6084 ) ;
6185 useSetHeaderContent ( headerContent ) ;
6286
87+ const builderSessions = useLoopBuilderSessions ( ) ;
88+
6389 const allLoops = loops ?? [ ] ;
6490 const teamLoops = allLoops . filter ( ( loop ) => loop . visibility === "team" ) ;
6591 const {
@@ -79,8 +105,11 @@ export function LoopsListView() {
79105 membersLoading = { membersLoading }
80106 membersError = { membersError }
81107 membersComplete = { membersComplete }
108+ builderSessions = { builderSessions }
82109 onStartBlank = { startBlankLoop }
83110 onStartFromTemplate = { startLoopFromTemplate }
111+ onResumeBuilderSession = { resumeBuilderSession }
112+ onBuilderSessionStopped = { removeBuilderSession }
84113 />
85114 ) ;
86115}
@@ -94,8 +123,11 @@ interface LoopsListViewPresentationProps {
94123 membersLoading ?: boolean ;
95124 membersError ?: boolean ;
96125 membersComplete ?: boolean ;
126+ builderSessions ?: LoopBuilderSession [ ] ;
97127 onStartBlank : ( ) => void ;
98128 onStartFromTemplate : ( template : LoopTemplate ) => void ;
129+ onResumeBuilderSession ?: ( taskId : string ) => void ;
130+ onBuilderSessionStopped ?: ( taskId : string ) => void ;
99131}
100132
101133export function LoopsListViewPresentation ( {
@@ -107,8 +139,11 @@ export function LoopsListViewPresentation({
107139 membersLoading = false ,
108140 membersError = false ,
109141 membersComplete = true ,
142+ builderSessions = EMPTY_BUILDER_SESSIONS ,
110143 onStartBlank,
111144 onStartFromTemplate,
145+ onResumeBuilderSession,
146+ onBuilderSessionStopped,
112147} : LoopsListViewPresentationProps ) {
113148 const personalLoops = loops . filter ( ( loop ) => loop . visibility === "personal" ) ;
114149 const teamLoops = loops . filter ( ( loop ) => loop . visibility === "team" ) ;
@@ -199,13 +234,79 @@ export function LoopsListViewPresentation({
199234 gap = "2"
200235 className = "mx-auto w-full max-w-5xl px-8 pb-6"
201236 >
237+ { builderSessions . map ( ( session ) => (
238+ < BuilderSessionRow
239+ key = { session . taskId }
240+ session = { session }
241+ onResume = { onResumeBuilderSession }
242+ onStopped = { onBuilderSessionStopped }
243+ />
244+ ) ) }
202245 < LoopBuilderComposer disabledReason = { limitReason } />
203246 </ Flex >
204247 </ div >
205248 </ Flex >
206249 ) ;
207250}
208251
252+ function BuilderSessionRow ( {
253+ session,
254+ onResume,
255+ onStopped,
256+ } : {
257+ session : LoopBuilderSession ;
258+ onResume ?: ( taskId : string ) => void ;
259+ onStopped ?: ( taskId : string ) => void ;
260+ } ) {
261+ const [ confirmStop , setConfirmStop ] = useState ( false ) ;
262+
263+ return (
264+ < Flex
265+ align = "center"
266+ gap = "3"
267+ className = "rounded-(--radius-2) border border-border bg-(--color-panel-solid) px-3 py-2"
268+ >
269+ < ChatCircleDotsIcon size = { 16 } className = "shrink-0 text-(--accent-11)" />
270+ < Flex direction = "column" className = "min-w-0 flex-1" >
271+ < Text className = "font-medium text-[12px] text-gray-10 uppercase tracking-wide" >
272+ Builder in progress
273+ </ Text >
274+ < Text className = "truncate text-[13px] text-gray-12" >
275+ { session . prompt }
276+ </ Text >
277+ </ Flex >
278+ < Button
279+ variant = "soft"
280+ color = "red"
281+ size = "1"
282+ onClick = { ( ) => setConfirmStop ( true ) }
283+ >
284+ Stop
285+ </ Button >
286+ < Button
287+ variant = "soft"
288+ size = "1"
289+ onClick = { ( ) => onResume ?.( session . taskId ) }
290+ >
291+ Resume
292+ </ Button >
293+ { confirmStop ? (
294+ < StopCloudRunDialog
295+ open = { confirmStop }
296+ taskId = { session . taskId }
297+ title = "Stop loop builder"
298+ buttonLabel = "Stop builder"
299+ onOpenChange = { setConfirmStop }
300+ onStopped = { ( ) => {
301+ toast . success ( "Builder stopped" ) ;
302+ onStopped ?.( session . taskId ) ;
303+ } }
304+ />
305+ ) : null }
306+ </ Flex >
307+ ) ;
308+ }
309+
209310function LoopListSection ( {
210311 title,
211312 loops,
0 commit comments