From 482beb43306c1dcf6b48f5ec6e0306ee6c793209 Mon Sep 17 00:00:00 2001 From: Matt Pua Date: Tue, 28 Jul 2026 10:59:18 -0400 Subject: [PATCH 1/6] Fix loop back navigation from channels Generated-By: PostHog Code Task-Id: 3e0a680c-b843-46f6-ba7e-7898c06bcdb8 --- .../canvas/components/WebsiteChannelLoops.tsx | 2 +- .../loops/components/LoopDetailView.tsx | 15 ++++++++++- .../src/features/loops/components/LoopRow.tsx | 6 +++++ .../src/features/loops/loopBackTarget.test.ts | 20 +++++++++++++++ .../ui/src/features/loops/loopBackTarget.ts | 25 +++++++++++++++++++ 5 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 packages/ui/src/features/loops/loopBackTarget.test.ts create mode 100644 packages/ui/src/features/loops/loopBackTarget.ts diff --git a/packages/ui/src/features/canvas/components/WebsiteChannelLoops.tsx b/packages/ui/src/features/canvas/components/WebsiteChannelLoops.tsx index a27711078c..73db51bf30 100644 --- a/packages/ui/src/features/canvas/components/WebsiteChannelLoops.tsx +++ b/packages/ui/src/features/canvas/components/WebsiteChannelLoops.tsx @@ -139,7 +139,7 @@ export function WebsiteChannelLoops({ channelId }: { channelId: string }) { {attachedLoops.map((loop) => ( - + ))} diff --git a/packages/ui/src/features/loops/components/LoopDetailView.tsx b/packages/ui/src/features/loops/components/LoopDetailView.tsx index ff8685713f..a3f8c0e9c0 100644 --- a/packages/ui/src/features/loops/components/LoopDetailView.tsx +++ b/packages/ui/src/features/loops/components/LoopDetailView.tsx @@ -34,6 +34,7 @@ import { track } from "@posthog/ui/shell/analytics"; import { useHostCapabilities } from "@posthog/ui/shell/useHostCapabilities"; import { Flex, Text } from "@radix-ui/themes"; import { useQuery } from "@tanstack/react-query"; +import { useNavigate } from "@tanstack/react-router"; import { useEffect, useRef, useState } from "react"; import { useLoop } from "../hooks/useLoop"; import { @@ -47,6 +48,7 @@ import { buildLoopEnabledToggledProps, buildLoopViewedProps, } from "../loopAnalytics"; +import { useLoopBackTarget } from "../loopBackTarget"; import { describeTrigger, loopFireBlockedMessage, @@ -62,6 +64,8 @@ import { LoopLoadError } from "./LoopFallbacks"; import { LoopRunRow } from "./LoopRunRow"; export function LoopDetailView({ loopId }: { loopId: string }) { + const navigate = useNavigate(); + const backTarget = useLoopBackTarget(); const { data: loop, isLoading, isError } = useLoop(loopId); const updateLoop = useUpdateLoop(loopId); const deleteLoop = useDeleteLoop(); @@ -207,7 +211,16 @@ export function LoopDetailView({ loopId }: { loopId: string }) { diff --git a/packages/ui/src/features/loops/components/LoopRow.tsx b/packages/ui/src/features/loops/components/LoopRow.tsx index af66eeeeec..2b53b2f4a2 100644 --- a/packages/ui/src/features/loops/components/LoopRow.tsx +++ b/packages/ui/src/features/loops/components/LoopRow.tsx @@ -18,14 +18,12 @@ export function LoopRow({ creatorLoading = false, creatorError = false, creatorLookupComplete = true, - channelId, }: { loop: LoopSchemas.Loop; creator?: UserBasic; creatorLoading?: boolean; creatorError?: boolean; creatorLookupComplete?: boolean; - channelId?: string; }) { const description = loop.description.trim(); @@ -57,10 +55,6 @@ export function LoopRow({ ({ - ...previous, - loopBackTarget: channelId ? { channelId } : undefined, - })} className="flex items-center justify-between gap-3 rounded-(--radius-2) border border-border bg-(--color-panel-solid) px-4 py-3.5 no-underline transition-colors duration-150 hover:border-(--gray-6) hover:bg-(--gray-2)" > diff --git a/packages/ui/src/features/loops/loopBackTarget.test.ts b/packages/ui/src/features/loops/loopBackTarget.test.ts deleted file mode 100644 index e23767b237..0000000000 --- a/packages/ui/src/features/loops/loopBackTarget.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { asLoopBackTarget } from "./loopBackTarget"; - -describe("asLoopBackTarget", () => { - it.each([ - [undefined], - [null], - [{}], - [{ channelId: "" }], - [{ channelId: 42 }], - ])("rejects an invalid history target", (target) => { - expect(asLoopBackTarget(target)).toBeNull(); - }); - - it("accepts a channel origin", () => { - expect(asLoopBackTarget({ channelId: "channel-1" })).toEqual({ - channelId: "channel-1", - }); - }); -}); diff --git a/packages/ui/src/features/loops/loopBackTarget.ts b/packages/ui/src/features/loops/loopBackTarget.ts deleted file mode 100644 index 3268220687..0000000000 --- a/packages/ui/src/features/loops/loopBackTarget.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { useLocation } from "@tanstack/react-router"; - -export interface LoopBackTarget { - channelId: string; -} - -declare module "@tanstack/react-router" { - interface HistoryState { - loopBackTarget?: LoopBackTarget; - } -} - -export function asLoopBackTarget(value: unknown): LoopBackTarget | null { - if (!value || typeof value !== "object") return null; - const { channelId } = value as Record; - if (typeof channelId !== "string" || channelId.length === 0) return null; - return { channelId }; -} - -export function useLoopBackTarget(): LoopBackTarget | null { - const target = useLocation({ - select: (location) => location.state.loopBackTarget, - }); - return asLoopBackTarget(target); -} From 65db0dcc6e7ed8be747130bff9082e00d490c6dd Mon Sep 17 00:00:00 2001 From: Matt Pua Date: Tue, 28 Jul 2026 11:11:35 -0400 Subject: [PATCH 3/6] Handle direct loop detail navigation safely Generated-By: PostHog Code Task-Id: f4cc7312-bef8-46af-86a7-84a19118d770 --- .../ui/src/features/loops/components/LoopDetailView.tsx | 6 +++++- packages/ui/src/features/loops/components/LoopRow.tsx | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/features/loops/components/LoopDetailView.tsx b/packages/ui/src/features/loops/components/LoopDetailView.tsx index 51edebd28f..fc9665c972 100644 --- a/packages/ui/src/features/loops/components/LoopDetailView.tsx +++ b/packages/ui/src/features/loops/components/LoopDetailView.tsx @@ -35,6 +35,7 @@ import { import { track } from "@posthog/ui/shell/analytics"; import { useHostCapabilities } from "@posthog/ui/shell/useHostCapabilities"; import { Flex, Text } from "@radix-ui/themes"; +import { useLocation } from "@tanstack/react-router"; import { useQuery } from "@tanstack/react-query"; import { useEffect, useRef, useState } from "react"; import { useLoop } from "../hooks/useLoop"; @@ -64,6 +65,9 @@ import { LoopLoadError } from "./LoopFallbacks"; import { LoopRunRow } from "./LoopRunRow"; export function LoopDetailView({ loopId }: { loopId: string }) { + const hasLoopListOrigin = useLocation({ + select: (location) => location.state.loopListOrigin === true, + }); const { data: loop, isLoading, isError } = useLoop(loopId); const updateLoop = useUpdateLoop(loopId); const deleteLoop = useDeleteLoop(); @@ -210,7 +214,7 @@ export function LoopDetailView({ loopId }: { loopId: string }) { variant="link-muted" size="sm" onClick={() => { - if (canGoBackInHistory()) { + if (hasLoopListOrigin && canGoBackInHistory()) { goBackInHistory(); return; } diff --git a/packages/ui/src/features/loops/components/LoopRow.tsx b/packages/ui/src/features/loops/components/LoopRow.tsx index 2b53b2f4a2..4f2c4f4bab 100644 --- a/packages/ui/src/features/loops/components/LoopRow.tsx +++ b/packages/ui/src/features/loops/components/LoopRow.tsx @@ -12,6 +12,12 @@ import { summarizeNotificationDestinations, } from "../loopDisplay"; +declare module "@tanstack/react-router" { + interface HistoryState { + loopListOrigin?: boolean; + } +} + export function LoopRow({ loop, creator, @@ -55,6 +61,7 @@ export function LoopRow({ From f0cfa2ad56f5b25423b1983231392feea73b53bd Mon Sep 17 00:00:00 2001 From: Shy Alter Date: Tue, 28 Jul 2026 18:16:18 +0200 Subject: [PATCH 4/6] Add Loops to global channel navigation Generated-By: PostHog Code Task-Id: 6620f68f-565d-4c9a-b8eb-5a5a2170879d --- .../features/canvas/components/ChannelNav.tsx | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/features/canvas/components/ChannelNav.tsx b/packages/ui/src/features/canvas/components/ChannelNav.tsx index c95247fa7a..56d9bb1a6d 100644 --- a/packages/ui/src/features/canvas/components/ChannelNav.tsx +++ b/packages/ui/src/features/canvas/components/ChannelNav.tsx @@ -1,5 +1,11 @@ -import { BellIcon, EnvelopeSimple, Lightning } from "@phosphor-icons/react"; +import { + BellIcon, + EnvelopeSimple, + Lightning, + RepeatIcon, +} from "@phosphor-icons/react"; import { cn } from "@posthog/quill"; +import { LOOPS_FLAG } from "@posthog/shared"; import { ANALYTICS_EVENTS, type SidebarNavItem, @@ -10,12 +16,14 @@ import { SHORTCUTS, } from "@posthog/ui/features/command/keyboard-shortcuts"; import { useCommandCenterActiveCount } from "@posthog/ui/features/command-center/useCommandCenterActiveCount"; +import { useFeatureFlag } from "@posthog/ui/features/feature-flags/useFeatureFlag"; import { useInboxAllReports } from "@posthog/ui/features/inbox/hooks/useInboxAllReports"; import { CountBadge } from "@posthog/ui/primitives/CountBadge"; import { Tooltip } from "@posthog/ui/primitives/Tooltip"; import { navigateToActivity, navigateToInbox, + navigateToLoops, navigateToWebsiteCommandCenter, } from "@posthog/ui/router/navigationBridge"; import { useAppView } from "@posthog/ui/router/useAppView"; @@ -64,6 +72,7 @@ function NavIcon({ export function ChannelNav() { const view = useAppView(); + const loopsEnabled = useFeatureFlag(LOOPS_FLAG, import.meta.env.DEV); const { counts } = useInboxAllReports({ ignoreFilters: true, @@ -118,6 +127,19 @@ export function ChannelNav() { /> } /> + {loopsEnabled ? ( + + } + label="Loops" + isActive={view.type === "loops"} + onClick={withTrack("loops", navigateToLoops)} + /> + ) : null} ); } From 7f08e50eb2c315f85cc6ae6cad6a0377c05eacee Mon Sep 17 00:00:00 2001 From: Shy Alter Date: Tue, 28 Jul 2026 18:17:17 +0200 Subject: [PATCH 5/6] Add Configure to global channel navigation Generated-By: PostHog Code Task-Id: 6620f68f-565d-4c9a-b8eb-5a5a2170879d --- packages/ui/src/features/canvas/components/ChannelNav.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/ui/src/features/canvas/components/ChannelNav.tsx b/packages/ui/src/features/canvas/components/ChannelNav.tsx index 56d9bb1a6d..5089bd5fbd 100644 --- a/packages/ui/src/features/canvas/components/ChannelNav.tsx +++ b/packages/ui/src/features/canvas/components/ChannelNav.tsx @@ -3,6 +3,7 @@ import { EnvelopeSimple, Lightning, RepeatIcon, + SlidersHorizontal, } from "@phosphor-icons/react"; import { cn } from "@posthog/quill"; import { LOOPS_FLAG } from "@posthog/shared"; @@ -18,6 +19,7 @@ import { import { useCommandCenterActiveCount } from "@posthog/ui/features/command-center/useCommandCenterActiveCount"; import { useFeatureFlag } from "@posthog/ui/features/feature-flags/useFeatureFlag"; import { useInboxAllReports } from "@posthog/ui/features/inbox/hooks/useInboxAllReports"; +import { openSettings } from "@posthog/ui/features/settings/hooks/useOpenSettings"; import { CountBadge } from "@posthog/ui/primitives/CountBadge"; import { Tooltip } from "@posthog/ui/primitives/Tooltip"; import { @@ -140,6 +142,12 @@ export function ChannelNav() { onClick={withTrack("loops", navigateToLoops)} /> ) : null} + } + label="Configure" + isActive={false} + onClick={withTrack("configure", () => openSettings("agents"))} + /> ); } From c174cc79c56ae708517999b7e9cd53282c26f6bf Mon Sep 17 00:00:00 2001 From: Matt Pua Date: Tue, 28 Jul 2026 12:25:04 -0400 Subject: [PATCH 6/6] Fix loop detail import ordering Generated-By: PostHog Code Task-Id: f4cc7312-bef8-46af-86a7-84a19118d770 --- packages/ui/src/features/loops/components/LoopDetailView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/features/loops/components/LoopDetailView.tsx b/packages/ui/src/features/loops/components/LoopDetailView.tsx index fc9665c972..48abf2c7cc 100644 --- a/packages/ui/src/features/loops/components/LoopDetailView.tsx +++ b/packages/ui/src/features/loops/components/LoopDetailView.tsx @@ -35,8 +35,8 @@ import { import { track } from "@posthog/ui/shell/analytics"; import { useHostCapabilities } from "@posthog/ui/shell/useHostCapabilities"; import { Flex, Text } from "@radix-ui/themes"; -import { useLocation } from "@tanstack/react-router"; import { useQuery } from "@tanstack/react-query"; +import { useLocation } from "@tanstack/react-router"; import { useEffect, useRef, useState } from "react"; import { useLoop } from "../hooks/useLoop"; import {