From 1559125bc152b9437a7f71c8a161e3f2f187bcaa Mon Sep 17 00:00:00 2001 From: Adam Leith Date: Wed, 8 Jul 2026 10:34:56 +0100 Subject: [PATCH 1/6] fix: clearer status on channel task cards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework the task card status row in the channel feed: - Once a PR exists, its state (Merged / PR ready / Draft / Closed) is the sole top-line status, replacing the run badge — so a shipped task never reads "Ready + Merged" or a stale "In progress + PR ready". - Merged PRs show a purple badge and lift the card to a purple border + tint, matching the sidebar's merge accent. - Fall back to the neutral "PR ready" when a PR URL exists but its GitHub state hasn't resolved yet, so the badge and the "PR" link never disagree. - "Ready" instead of "Completed" for finished runs — the work is ready to look at, not necessarily shipped. - Drop the redundant "Local" pill; environment shows in the meta row ("· Local" / "· Cloud"). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../canvas/components/ChannelFeedView.tsx | 146 ++++++++++++++++-- 1 file changed, 130 insertions(+), 16 deletions(-) diff --git a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx index 9f73742883..0318d0fce7 100644 --- a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx +++ b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx @@ -24,6 +24,7 @@ import { ChatMessageScrollerItem, ChatMessageScrollerProvider, ChatMessageScrollerViewport, + cn, Spinner, Tooltip, TooltipContent, @@ -41,8 +42,12 @@ import { userDisplayName } from "@posthog/ui/features/canvas/utils/userDisplay"; import { xmlToPlainText } from "@posthog/ui/features/message-editor/content"; import { extractChannelContext } from "@posthog/ui/features/sessions/components/session-update/channelContext"; import { getOriginProductMeta } from "@posthog/ui/features/sidebar/components/items/TaskIcon"; +import { + type SidebarPrState, + useTaskPrStatus, +} from "@posthog/ui/features/sidebar/useTaskPrStatus"; import { Text } from "@radix-ui/themes"; -import { useMemo } from "react"; +import { type ReactNode, useMemo } from "react"; // Feed rows poll their reply counts slower than the open thread panel — the // shared query key means an open panel naturally speeds the row up too. @@ -52,11 +57,27 @@ const STATUS_LABELS: Record = { not_started: "Not started", queued: "Queued", in_progress: "In progress", - completed: "Completed", + // "Ready", not "Completed": the agent has finished its work and the task is + // ready to look at, but the change itself isn't necessarily shipped/done. + completed: "Ready", failed: "Failed", cancelled: "Cancelled", }; +// Once a PR exists its GitHub state is the truest top-line status — more +// accurate than the run status, which routinely lingers on "in_progress" +// (or a stale cloud status) after the agent opens the PR. Mirrors the PR +// states the sidebar's TaskIcon already renders. +const PR_STATE_LABELS: Record< + Exclude, + { label: string; variant: "success" | "info" | "default" | "destructive" } +> = { + merged: { label: "Merged", variant: "success" }, + open: { label: "PR ready", variant: "info" }, + draft: { label: "Draft PR", variant: "default" }, + closed: { label: "Closed", variant: "destructive" }, +}; + function statusBadge(status: TaskRunStatus) { const variant = status === "completed" @@ -74,30 +95,112 @@ function statusBadge(status: TaskRunStatus) { ); } +interface TaskStatusDisplay { + // The run/environment badge ("Local", "Completed", "In progress", …). + base: ReactNode; + // The PR's GitHub state, shown alongside the run badge when a PR exists. + prState: Exclude | null; + // Whether the PR has merged — the card lifts this to a purple border + tint. + isMerged: boolean; +} + // Live status for the card, derived the same way the sidebar's TaskIcon does // (via useChannelTaskData: local session + workspace + cloud run). The raw // `latest_run.status` alone is wrong for local runs — the backend row often // stays "queued" while the agent runs on the creator's machine — so it is // only trusted for cloud runs and terminal states (which imply a sync). -function TaskStatusBadge({ task }: { task: Task }) { +// +// Once a PR exists its state ("PR ready", "Merged", …) is the sole top-line +// status — it replaces the run badge rather than sitting next to it, so a +// shipped task never reads "Ready + Merged" or a stale "In progress + PR +// ready". A failed/cancelled run suppresses the PR badge instead — that is a +// deliberate end state we should not soften with a PR. +function useTaskStatusDisplay(task: Task): TaskStatusDisplay { const data = useChannelTaskData(task); - if (data?.needsPermission) - return Needs input; - if (data?.isGenerating) { - return ( + const { prState } = useTaskPrStatus({ + id: task.id, + cloudPrUrl: data?.cloudPrUrl ?? null, + taskRunEnvironment: data?.taskRunEnvironment ?? null, + }); + const status = data?.taskRunStatus ?? task.latest_run?.status; + const environment = data?.taskRunEnvironment ?? task.latest_run?.environment; + // `prState` is resolved async from git/`gh` and is routinely null for cloud + // tasks (the details fetch hasn't landed, or there's no cached row). But the + // PR URL itself is a hard signal a PR exists — the card's "PR" link keys off + // exactly this. Fall back to it so the badge and the link never disagree; a + // known URL with no resolved state is shown as the neutral "open" ("PR + // ready"), never something stronger like "merged". + const hasPrUrl = + typeof (data?.cloudPrUrl ?? task.latest_run?.output?.pr_url) === "string"; + const effectivePrState: Exclude | null = + prState ?? (hasPrUrl ? "open" : null); + const showPrState = + !!effectivePrState && status !== "failed" && status !== "cancelled"; + + let base: ReactNode; + if (data?.needsPermission) { + // Live, actionable states still win over the PR badge — the agent is + // waiting on the user right now, which matters more than a PR existing. + base = Needs input; + } else if (data?.isGenerating) { + base = ( In progress ); + } else if (showPrState) { + // Otherwise the PR badge is the whole story once a PR exists; skip the run + // badge so we never show "Ready + Merged" or a stale "In progress". + base = null; + } else if (!status) { + base = Draft; + } else if (environment === "cloud" || isTerminalStatus(status)) { + base = statusBadge(status); + } else { + // Local, non-terminal: the run status is unreliable (the backend row stays + // "queued" while the agent runs on the creator's machine), and the + // environment already shows in the card's meta row ("· Local"), so we + // render no status badge here rather than a redundant "Local" pill. + base = null; } - const status = data?.taskRunStatus ?? task.latest_run?.status; - const environment = data?.taskRunEnvironment ?? task.latest_run?.environment; - if (!status) return Draft; - if (environment === "cloud" || isTerminalStatus(status)) { - return statusBadge(status); + + return { + base, + prState: showPrState ? effectivePrState : null, + isMerged: showPrState && effectivePrState === "merged", + }; +} + +// The merged badge borrows the purple GitHub-merge accent (matching the +// sidebar's TaskIcon merge glyph). Quill has no purple variant, so we tint a +// neutral badge with the Radix purple scale — allowed inline because the +// values are CSS variables, not hardcoded colors. +function PrStateBadge({ prState }: { prState: Exclude }) { + const { label, variant } = PR_STATE_LABELS[prState]; + if (prState === "merged") { + return ( + + {label} + + ); } - return Local; + return {label}; +} + +function TaskStatusBadge({ display }: { display: TaskStatusDisplay }) { + return ( +
+ {display.base} + {display.prState && } +
+ ); } // The prompt as the user typed it: drop the channel CONTEXT.md block the saga @@ -130,28 +233,39 @@ function TaskCardOrigin({ task }: { task: Task }) { // The task the message kicked off, as a card everyone in the channel sees: // origin + status up top, bold title, then run metadata. function TaskCard({ task, onOpen }: { task: Task; onOpen: () => void }) { + const statusDisplay = useTaskStatusDisplay(task); const prUrl = typeof task.latest_run?.output?.pr_url === "string" ? task.latest_run.output.pr_url : undefined; // The repository renders separately with its icon; `meta` is the plain-text // remainder of the row. + const environment = task.latest_run?.environment; const meta = [ task.slug || null, task.latest_run?.stage ?? null, - task.latest_run?.environment === "cloud" ? "Cloud" : null, + environment === "cloud" + ? "Cloud" + : environment === "local" + ? "Local" + : null, ].filter(Boolean) as string[]; return (
- +
{/* Same live status icon as the code side nav, so the card and the From fc120246d8b11bd03972073b4997b5708ed91ac7 Mon Sep 17 00:00:00 2001 From: Adam Leith Date: Wed, 8 Jul 2026 12:36:38 +0100 Subject: [PATCH 2/6] styling --- .../canvas/components/ChannelFeedView.tsx | 149 +++++++++--------- .../canvas/components/WebsiteChannelHome.tsx | 2 +- .../message-editor/components/PromptInput.tsx | 2 +- .../components/message-editor.css | 2 +- pnpm-lock.yaml | 48 ++++-- pnpm-workspace.yaml | 2 +- 6 files changed, 114 insertions(+), 91 deletions(-) diff --git a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx index 0318d0fce7..bcd539445b 100644 --- a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx +++ b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx @@ -10,14 +10,8 @@ import { AvatarFallback, AvatarGroup, Badge, - Button, - ButtonGroup, Card, CardContent, - ChatMessage, - ChatMessageAvatar, - ChatMessageContent, - ChatMessageHeader, ChatMessageScroller, ChatMessageScrollerButton, ChatMessageScrollerContent, @@ -26,10 +20,18 @@ import { ChatMessageScrollerViewport, cn, Spinner, - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, + ThreadItem, + ThreadItemAction, + ThreadItemActions, + ThreadItemAuthor, + ThreadItemBody, + ThreadItemContent, + ThreadItemGutter, + ThreadItemHeader, + ThreadItemReplies, + ThreadItemRepliesLabel, + ThreadItemRepliesMeta, + ThreadItemTimestamp, } from "@posthog/quill"; import { formatRelativeTimeShort } from "@posthog/shared"; import type { Task, TaskRunStatus } from "@posthog/shared/domain-types"; @@ -255,9 +257,9 @@ function TaskCard({ task, onOpen }: { task: Task; onOpen: () => void }) { + {authors.map((author, index) => ( @@ -340,13 +338,13 @@ function RepliesRow({ ))} - + {messages.length} {messages.length === 1 ? "reply" : "replies"} - - + + Last reply {formatRelativeTimeShort(last.created_at)} - - + + ); } @@ -363,11 +361,8 @@ function FeedItem({ const isAgent = !task.created_by || task.origin_product !== "user_created"; return ( - - {/* Quill's avatar slot bottom-aligns for bubble chats; a Slack feed - anchors it beside the name row. The slot draws its own circle, so - drop its background and let the inner Avatar render. */} - + + {isAgent && !task.created_by ? ( @@ -377,64 +372,49 @@ function FeedItem({ )} - - - - + + + + + {task.created_by ? userDisplayName(task.created_by) : "Agent"} - + {isAgent && Agent} - + {formatRelativeTimeShort(task.created_at)} - - + + - + {prompt} - + onOpenTask(task)} /> onOpenThread(task)} /> - + - {/* Hover toolbar, storybook-style: floats top-right of the row. */} -
- - - - onOpenThread(task)} - > - - - } - /> - Reply in thread - - - onOpenTask(task)} - > - - - } - /> - Open task - - - -
-
+ {/* Actions anchor to the row's top-right corner; a top tooltip there + overhangs the panel edge and gets clipped by the scroll container, so + open tooltips toward the content instead. */} + + onOpenThread(task)} + > + + + onOpenTask(task)} + > + + + + ); } @@ -470,9 +450,22 @@ export function ChannelFeedView({ - + {/* Horizontal padding is load-bearing: ThreadItem's actions float at + the row's top-right corner (absolute, past the row edge). Without a + gutter they hug the scroll container and get clipped. */} + {tasks.map((task) => ( - + -
+
( =20'} peerDependencies: '@base-ui/react': ^1.4.0 @@ -18850,7 +18850,7 @@ snapshots: react-dom: 19.2.6(react@19.2.6) simple-statistics: 7.8.9 - '@posthog/quill@0.3.0-beta.21(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.2.2)': + '@posthog/quill@0.3.0-beta.22(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.2.2)': dependencies: '@base-ui/react': 1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) class-variance-authority: 0.7.1 @@ -18862,7 +18862,7 @@ snapshots: tailwind-merge: 2.6.1 tailwindcss: 4.2.2 - '@posthog/quill@0.3.0-beta.21(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.1)': + '@posthog/quill@0.3.0-beta.22(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.1)': dependencies: '@base-ui/react': 1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) class-variance-authority: 0.7.1 @@ -21406,7 +21406,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vitest: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@22.20.0)(@vitest/ui@4.1.8)(jsdom@26.1.0)(msw@2.12.8(@types/node@22.20.0)(typescript@5.9.3))(vite@7.3.5(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)) + vitest: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@24.12.0)(@vitest/ui@4.1.8)(jsdom@26.1.0)(msw@2.12.8(@types/node@24.12.0)(typescript@5.9.3))(rolldown-vite@7.3.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)) '@vitest/utils@3.2.4': dependencies: @@ -29149,6 +29149,36 @@ snapshots: transitivePeerDependencies: - msw + vitest@4.1.8(@opentelemetry/api@1.9.1)(@types/node@24.12.0)(@vitest/ui@4.1.8)(jsdom@26.1.0)(msw@2.12.8(@types/node@24.12.0)(typescript@5.9.3))(rolldown-vite@7.3.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.8 + '@vitest/mocker': 4.1.8(msw@2.12.8(@types/node@24.12.0)(typescript@5.9.3))(rolldown-vite@7.3.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.8 + '@vitest/runner': 4.1.8 + '@vitest/snapshot': 4.1.8 + '@vitest/spy': 4.1.8 + '@vitest/utils': 4.1.8 + es-module-lexer: 2.0.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 4.1.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.1.0 + vite: rolldown-vite@7.3.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.1 + '@types/node': 24.12.0 + '@vitest/ui': 4.1.8(vitest@4.1.8) + jsdom: 26.1.0 + transitivePeerDependencies: + - msw + vitest@4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.2.0)(@vitest/ui@4.1.8)(jsdom@26.1.0)(msw@2.12.8(@types/node@25.2.0)(typescript@5.9.3))(vite@7.3.5(@types/node@25.2.0)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.8 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 45a646a55a..d0e75b2c93 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -8,7 +8,7 @@ catalog: '@hono/trpc-server': ^0.3.4 '@parcel/watcher': ^2.5.6 '@phosphor-icons/react': ^2.1.10 - '@posthog/quill': 0.3.0-beta.21 + '@posthog/quill': 0.3.0-beta.22 '@radix-ui/themes': ^3.2.1 '@tanstack/react-query': ^5.100.14 '@tanstack/react-router': ^1.170.10 From 04b8b76ca3cff4fa753f8a1570780435077cd857 Mon Sep 17 00:00:00 2001 From: Adam Leith Date: Wed, 8 Jul 2026 13:15:31 +0100 Subject: [PATCH 3/6] feat(channel): quill ThreadItem feed with day separators, smoother scroll - Rebuild feed rows on quill's ThreadItem primitives (gutter avatar, header, body, hover action bar) instead of hand-rolled ChatMessage markup. - Group rows by day with a ChatMarker separator: "Today" / "Yesterday", then a weekday + ordinal ("Monday 5th"), adding month/year further back. - Cut scroll jank: memoize rows so task-list polls don't re-render the whole feed, gate each row's 15s reply-teaser poll behind an in-view observer, and tune contain-intrinsic-size to the real ~13rem row height. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../canvas/components/ChannelFeedView.tsx | 133 ++++++++++++++---- 1 file changed, 109 insertions(+), 24 deletions(-) diff --git a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx index bcd539445b..978952fdac 100644 --- a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx +++ b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx @@ -12,6 +12,8 @@ import { Badge, Card, CardContent, + ChatMarker, + ChatMarkerContent, ChatMessageScroller, ChatMessageScrollerButton, ChatMessageScrollerContent, @@ -48,8 +50,9 @@ import { type SidebarPrState, useTaskPrStatus, } from "@posthog/ui/features/sidebar/useTaskPrStatus"; +import { useInView } from "@posthog/ui/primitives/hooks/useInView"; import { Text } from "@radix-ui/themes"; -import { type ReactNode, useMemo } from "react"; +import { Fragment, memo, type ReactNode, useMemo } from "react"; // Feed rows poll their reply counts slower than the open thread panel — the // shared query key means an open panel naturally speeds the row up too. @@ -97,6 +100,39 @@ function statusBadge(status: TaskRunStatus) { ); } +// Local calendar-day identity, so tasks created on the same day share a heading +// regardless of time. Uses local getters (not the UTC ISO) so the split lands +// on the viewer's midnight. +function dayKey(iso: string): string { + const d = new Date(iso); + return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`; +} + +function ordinal(n: number): string { + const suffix = ["th", "st", "nd", "rd"]; + const rem = n % 100; + return `${n}${suffix[(rem - 20) % 10] ?? suffix[rem] ?? suffix[0]}`; +} + +// The day-separator label: "Today" / "Yesterday" for the recent days, then a +// weekday + ordinal ("Monday 5th") within the week, adding the month (and the +// year when it differs) further back so older separators stay unambiguous. +function dayLabel(iso: string, now: Date): string { + const date = new Date(iso); + const startOfDay = (d: Date) => + new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); + const days = Math.round((startOfDay(now) - startOfDay(date)) / 86_400_000); + if (days <= 0) return "Today"; + if (days === 1) return "Yesterday"; + const weekday = date.toLocaleDateString(undefined, { weekday: "long" }); + const day = ordinal(date.getDate()); + if (days < 7) return `${weekday} ${day}`; + const month = date.toLocaleDateString(undefined, { month: "long" }); + const year = + date.getFullYear() === now.getFullYear() ? "" : `, ${date.getFullYear()}`; + return `${weekday}, ${month} ${day}${year}`; +} + interface TaskStatusDisplay { // The run/environment badge ("Local", "Completed", "In progress", …). base: ReactNode; @@ -331,7 +367,7 @@ function RepliesRow({ return ( - + {authors.map((author, index) => ( {getUserInitials(author)} @@ -348,12 +384,14 @@ function RepliesRow({ ); } -function FeedItem({ +const FeedItem = memo(function FeedItem({ task, + inView, onOpenTask, onOpenThread, }: { task: Task; + inView: boolean; onOpenTask: (task: Task) => void; onOpenThread: (task: Task) => void; }) { @@ -361,7 +399,7 @@ function FeedItem({ const isAgent = !task.created_by || task.origin_product !== "user_created"; return ( - + @@ -392,7 +430,15 @@ function FeedItem({ onOpenTask(task)} /> - onOpenThread(task)} /> + {/* Off-screen rows drop the reply teaser so a long feed isn't running a + 15s poll timer per row; the wide inView margin mounts it well before + the row scrolls into view, so nothing pops in. */} + {inView && ( + onOpenThread(task)} + /> + )} {/* Actions anchor to the row's top-right corner; a top tooltip there @@ -416,6 +462,40 @@ function FeedItem({ ); +}); + +// One feed row: owns the scroller item (the `content-visibility` boundary, so +// its box is always laid out and safe to observe) and reports whether it is +// near the viewport, letting `FeedItem` shed off-screen polling. +function FeedRow({ + task, + onOpenTask, + onOpenThread, +}: { + task: Task; + onOpenTask: (task: Task) => void; + onOpenThread: (task: Task) => void; +}) { + const [ref, inView] = useInView({ rootMargin: "1200px 0px" }); + return ( + + + + ); } // The Slack-style channel feed: every task kicked off in the channel, oldest @@ -446,6 +526,8 @@ export function ChannelFeedView({ return
{emptyState}
; } + const now = new Date(); + return ( @@ -454,25 +536,28 @@ export function ChannelFeedView({ the row's top-right corner (absolute, past the row edge). Without a gutter they hug the scroll container and get clipped. */} - {tasks.map((task) => ( - - - - ))} + {tasks.map((task, index) => { + const previous = tasks[index - 1]; + const showDayMarker = + !previous || + dayKey(previous.created_at) !== dayKey(task.created_at); + return ( + + {showDayMarker && ( + + + {dayLabel(task.created_at, now)} + + + )} + + + ); + })} From cae6aa497722f1b98d446ad1fcb024feeeeb892f Mon Sep 17 00:00:00 2001 From: Adam Leith Date: Wed, 8 Jul 2026 13:39:29 +0100 Subject: [PATCH 4/6] Update packages/ui/src/features/canvas/components/ChannelFeedView.tsx Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- packages/ui/src/features/canvas/components/ChannelFeedView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx index 978952fdac..072972b1d3 100644 --- a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx +++ b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx @@ -77,7 +77,7 @@ const PR_STATE_LABELS: Record< Exclude, { label: string; variant: "success" | "info" | "default" | "destructive" } > = { - merged: { label: "Merged", variant: "success" }, + merged: { label: "Merged" }, open: { label: "PR ready", variant: "info" }, draft: { label: "Draft PR", variant: "default" }, closed: { label: "Closed", variant: "destructive" }, From bf328702d79c586accce3d946ceaac524088e44f Mon Sep 17 00:00:00 2001 From: Adam Leith Date: Wed, 8 Jul 2026 13:35:53 +0100 Subject: [PATCH 5/6] fix thread action tooltip pos --- .../ui/src/features/canvas/components/ChannelFeedView.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx index 072972b1d3..647fef44d6 100644 --- a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx +++ b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx @@ -447,16 +447,11 @@ const FeedItem = memo(function FeedItem({ onOpenThread(task)} > - onOpenTask(task)} - > + onOpenTask(task)}> From 10c2d78652bd2e55f9e8b52af4c17a6bdae9dd0f Mon Sep 17 00:00:00 2001 From: Adam Leith Date: Wed, 8 Jul 2026 13:50:18 +0100 Subject: [PATCH 6/6] fix --- packages/ui/src/features/canvas/components/ChannelFeedView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx index 647fef44d6..c8c5ed6870 100644 --- a/packages/ui/src/features/canvas/components/ChannelFeedView.tsx +++ b/packages/ui/src/features/canvas/components/ChannelFeedView.tsx @@ -77,7 +77,7 @@ const PR_STATE_LABELS: Record< Exclude, { label: string; variant: "success" | "info" | "default" | "destructive" } > = { - merged: { label: "Merged" }, + merged: { label: "Merged", variant: "default" }, open: { label: "PR ready", variant: "info" }, draft: { label: "Draft PR", variant: "default" }, closed: { label: "Closed", variant: "destructive" },