From 8119cfe0d75cfc21b0c2492dbc1bc6cc1c6434ea Mon Sep 17 00:00:00 2001 From: Matt Pua Date: Wed, 22 Jul 2026 10:59:43 -0400 Subject: [PATCH 1/3] feat(loops): edit instructions inline on loop detail Pull instructions out of the config panel into its own box that's editable in place. Click into the textarea to edit, saves on blur via the existing PATCH; Escape reverts. Removes the round-trip through the full edit wizard just to tweak the prompt. Generated-By: PostHog Code Task-Id: 8a18478b-2989-4a7c-93f6-c54d3db955f0 --- .../loops/components/LoopDetailView.tsx | 75 +++++++++++++++++-- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/packages/ui/src/features/loops/components/LoopDetailView.tsx b/packages/ui/src/features/loops/components/LoopDetailView.tsx index f16b8b85bf..e189d83eeb 100644 --- a/packages/ui/src/features/loops/components/LoopDetailView.tsx +++ b/packages/ui/src/features/loops/components/LoopDetailView.tsx @@ -9,8 +9,8 @@ import { navigateToEditLoop, navigateToLoops, } from "@posthog/ui/router/navigationBridge"; -import { AlertDialog, Flex, Text } from "@radix-ui/themes"; -import { useState } from "react"; +import { AlertDialog, Flex, Text, TextArea } from "@radix-ui/themes"; +import { useEffect, useRef, useState } from "react"; import { useLoop } from "../hooks/useLoop"; import { useDeleteLoop, @@ -165,6 +165,8 @@ export function LoopDetailView({ loopId }: { loopId: string }) { + + @@ -278,13 +280,72 @@ function ConfigSummarySection({ loop }: { loop: LoopSchemas.Loop }) { )} + + + ); +} - -
-            {loop.instructions}
-          
-
+function InstructionsSection({ loop }: { loop: LoopSchemas.Loop }) { + const updateLoop = useUpdateLoop(loop.id); + const [draft, setDraft] = useState(loop.instructions); + // Escape reverts and blurs; skip the resulting onBlur save. + const skipCommit = useRef(false); + + // Keep the box in sync when the loop is refetched or updated elsewhere. + useEffect(() => { + setDraft(loop.instructions); + }, [loop.instructions]); + + const commit = () => { + if (skipCommit.current) { + skipCommit.current = false; + return; + } + const trimmed = draft.trim(); + if (!trimmed) { + setDraft(loop.instructions); + return; + } + if (trimmed === loop.instructions.trim() || updateLoop.isPending) return; + updateLoop.mutate( + { instructions: trimmed }, + { + onSuccess: () => toast.success("Instructions updated"), + onError: (error) => { + setDraft(loop.instructions); + toast.error("Failed to update instructions", { + description: error.message, + }); + }, + }, + ); + }; + + return ( + + + + Instructions + + {updateLoop.isPending ? ( + Saving… + ) : null} +