From cf5d2c72ddbb3c3e1a93b8cef5ca82e0edec14da Mon Sep 17 00:00:00 2001 From: Raquel Smith Date: Fri, 19 Jun 2026 17:51:36 -0700 Subject: [PATCH 1/3] fix: keep new task box fixed and fade suggestions in/out The suggestions below the new-task prompt previously vanished instantly and the input box snapped from 38% to 50% as soon as the user typed. Decouple the box position from editor-empty state so it stays put, and wrap the suggestions in an AnimatePresence fade so they fade out as the user types and fade back in when the prompt is cleared. Generated-By: PostHog Code Task-Id: d2e4e3ed-f0b5-449f-b9ca-4ff7aca329ff --- .../task-detail/components/TaskInput.tsx | 93 ++++++++++--------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/packages/ui/src/features/task-detail/components/TaskInput.tsx b/packages/ui/src/features/task-detail/components/TaskInput.tsx index a508276af..0dc8e32a1 100644 --- a/packages/ui/src/features/task-detail/components/TaskInput.tsx +++ b/packages/ui/src/features/task-detail/components/TaskInput.tsx @@ -10,6 +10,7 @@ import { navigateToInbox } from "@posthog/ui/router/navigationBridge"; import { useAppView } from "@posthog/ui/router/useAppView"; import { Flex, Text, Tooltip } from "@radix-ui/themes"; import { useQuery } from "@tanstack/react-query"; +import { AnimatePresence, motion } from "framer-motion"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useConnectivity } from "../../../hooks/useConnectivity"; import { DotPatternBackground } from "../../../primitives/DotPatternBackground"; @@ -688,12 +689,10 @@ export function TaskInput({ style={{ // Raise the input when the suggestion cards are shown so the longer // list below it isn't squished against the bottom of the viewport. - // Tied to the same condition as the cards (which hide once the - // editor has content) so the input recenters as the user types. - top: - suggestions && suggestions.length > 0 && editorIsEmpty - ? "38%" - : "50%", + // Note: this is NOT tied to `editorIsEmpty` — the input keeps its + // position as the user types so the box doesn't jump down when the + // suggestions fade out (and back in when the prompt is cleared). + top: suggestions && suggestions.length > 0 ? "38%" : "50%", transform: "translate(-50%, -50%)", }} className="absolute left-1/2 z-[1] flex w-[calc(100%-2rem)] max-w-[600px] flex-col gap-2" @@ -942,43 +941,53 @@ export function TaskInput({
{suggestions ? ( - suggestions.length > 0 && - editorIsEmpty && ( -
- + {suggestions.length > 0 && editorIsEmpty && ( + - Suggestions - -
- {suggestions.map((suggestion) => ( - { - // Use pending content (not setContent) so the - // multi-line template — intro + "User input:" fill-in - // lines — keeps its line breaks; focuses at the end. - useDraftStore - .getState() - .actions.setPendingContent(sessionId, { - segments: [ - { type: "text", text: suggestion.prompt }, - ], - }); - // Bug/feature suggestions start in plan mode; the - // analysis ones start in auto mode. - if (isValidConfigValue(modeOption, suggestion.mode)) { - setConfigOption(modeOption.id, suggestion.mode); - } - }} - /> - ))} -
-
- ) + + Suggestions + +
+ {suggestions.map((suggestion) => ( + { + // Use pending content (not setContent) so the + // multi-line template — intro + "User input:" fill-in + // lines — keeps its line breaks; focuses at the end. + useDraftStore + .getState() + .actions.setPendingContent(sessionId, { + segments: [ + { type: "text", text: suggestion.prompt }, + ], + }); + // Bug/feature suggestions start in plan mode; the + // analysis ones start in auto mode. + if ( + isValidConfigValue(modeOption, suggestion.mode) + ) { + setConfigOption(modeOption.id, suggestion.mode); + } + }} + /> + ))} +
+ + )} + ) : ( )} From eab600805fe82f800b555fd324aec385052dd6a8 Mon Sep 17 00:00:00 2001 From: Raquel Smith Date: Fri, 19 Jun 2026 17:52:54 -0700 Subject: [PATCH 2/3] feat: limit channel tasks to 5 in bluebird nav with view more Each channel in the Channels sidebar rendered every filed task, which flooded the nav for busy channels. Cap the list at 5 per channel and add a "View N more" button that reveals the rest on demand. Generated-By: PostHog Code Task-Id: d2e4e3ed-f0b5-449f-b9ca-4ff7aca329ff --- .../canvas/components/ChannelsList.tsx | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/features/canvas/components/ChannelsList.tsx b/packages/ui/src/features/canvas/components/ChannelsList.tsx index 19dde93f1..2657f0e69 100644 --- a/packages/ui/src/features/canvas/components/ChannelsList.tsx +++ b/packages/ui/src/features/canvas/components/ChannelsList.tsx @@ -70,6 +70,10 @@ import { useNavigate, useRouterState } from "@tanstack/react-router"; import { type ReactNode, useEffect, useState } from "react"; import { hostClient } from "../hostClient"; +// Cap how many tasks each channel shows by default; the rest hide behind a +// "View more" button so a busy channel doesn't dominate the sidebar. +const MAX_VISIBLE_TASKS_PER_CHANNEL = 5; + // A canvas's leading icon, chosen from its template so the tree reads at a // glance: bar chart for dashboards, line chart for web-analytics, plain file for // blank canvases. @@ -524,9 +528,17 @@ function ChannelSection({ const [open, setOpen] = useState(isActive); // Lifted so the hover button group stays visible while the menu is open. const [menuOpen, setMenuOpen] = useState(false); + // Only the first few tasks per channel show by default; "View more" reveals + // another batch each click so a busy channel doesn't flood the sidebar. + const [taskLimit, setTaskLimit] = useState(MAX_VISIBLE_TASKS_PER_CHANNEL); useEffect(() => { if (isActive) setOpen(true); }, [isActive]); + // Toggle expansion; collapsing also resets back to the first batch of tasks. + const toggleOpen = () => { + setOpen((o) => !o); + if (open) setTaskLimit(MAX_VISIBLE_TASKS_PER_CHANNEL); + }; // Lazy: a channel's canvases and filed tasks are only fetched once it's // expanded, so the tree doesn't fire one query per channel on mount. @@ -539,6 +551,13 @@ function ChannelSection({ ({ taskId }) => !archivedTaskIds.has(taskId) && tasks?.some((t) => t.id === taskId), ); + const displayedFiledTasks = visibleFiledTasks.slice(0, taskLimit); + const hiddenTaskCount = visibleFiledTasks.length - displayedFiledTasks.length; + // Reveal one more batch, capped at the remaining count. + const nextBatchCount = Math.min( + hiddenTaskCount, + MAX_VISIBLE_TASKS_PER_CHANNEL, + ); return ( @@ -550,7 +569,7 @@ function ChannelSection({ + )} )} From cebb82679cc86cfca70509a90f57cc1e3a55ad81 Mon Sep 17 00:00:00 2001 From: Raquel Smith Date: Fri, 19 Jun 2026 17:54:06 -0700 Subject: [PATCH 3/3] fix: archive from bluebird nav returns to website new task route Archiving the active task from the channels (bluebird) nav navigated to the /code new-task screen. Thread a navigateSpace option through the archive flow so the channels nav lands on the /website new-task route. Generated-By: PostHog Code Task-Id: d2e4e3ed-f0b5-449f-b9ca-4ff7aca329ff --- .../ui/src/features/archive/useArchiveTask.ts | 20 +++++++++++++++---- .../canvas/components/ChannelsList.tsx | 4 +++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/ui/src/features/archive/useArchiveTask.ts b/packages/ui/src/features/archive/useArchiveTask.ts index fabafdcc7..16901cc01 100644 --- a/packages/ui/src/features/archive/useArchiveTask.ts +++ b/packages/ui/src/features/archive/useArchiveTask.ts @@ -74,7 +74,7 @@ function makeCacheWriter( function makeOrchestrationDeps( queryClient: QueryClient, keys: ArchiveCacheKeys, - options?: { skipNavigate?: boolean }, + options?: { skipNavigate?: boolean; navigateSpace?: "code" | "website" }, ): ArchiveOrchestrationDeps { const hostClient = resolveService(HOST_TRPC_CLIENT); return { @@ -91,7 +91,9 @@ function makeOrchestrationDeps( if (options?.skipNavigate) return; const view = getAppViewSnapshot(); if (view.type === "task-detail" && view.taskId === taskId) { - openTaskInput(); + openTaskInput( + options?.navigateSpace ? { space: options.navigateSpace } : undefined, + ); } }, snapshotTerminalStates: (taskId) => @@ -147,7 +149,11 @@ export async function archiveTaskImperative( taskId: string, queryClient: QueryClient, keys: ArchiveCacheKeys, - options?: { skipNavigate?: boolean; optimistic?: boolean }, + options?: { + skipNavigate?: boolean; + optimistic?: boolean; + navigateSpace?: "code" | "website"; + }, ): Promise { await archiveTask( taskId, @@ -173,7 +179,12 @@ export async function archiveTasksImperative( ); } -export function useArchiveTask() { +export function useArchiveTask(options?: { + // Which new-task screen to land on if the archived task is the active view. + // Defaults to Code; the bluebird/channels nav passes "website" so archiving + // from there returns to the website new-task screen instead. + navigateSpace?: "code" | "website"; +}) { const queryClient = useQueryClient(); const keys = useArchiveCacheKeys(); const { restore } = useUnarchiveTask(); @@ -183,6 +194,7 @@ export function useArchiveTask() { // is confirmed, rather than removing it instantly and rolling back on error. await archiveTaskImperative(taskId, queryClient, keys, { optimistic: false, + navigateSpace: options?.navigateSpace, }); const toastId = `archive-undo-${taskId}`; toast.success("Task archived", { diff --git a/packages/ui/src/features/canvas/components/ChannelsList.tsx b/packages/ui/src/features/canvas/components/ChannelsList.tsx index 2657f0e69..e1a5697d4 100644 --- a/packages/ui/src/features/canvas/components/ChannelsList.tsx +++ b/packages/ui/src/features/canvas/components/ChannelsList.tsx @@ -380,7 +380,9 @@ function TaskRow({ const navigate = useNavigate(); const pathname = useRouterState({ select: (s) => s.location.pathname }); const { fileTask, unfileTask } = useChannelTaskMutations(); - const { archiveTask } = useArchiveTask(); + // Archiving from the bluebird/channels nav should return to the website + // new-task screen, not the Code one. + const { archiveTask } = useArchiveTask({ navigateSpace: "website" }); const taskData = useChannelTaskData(task); const workspace = useWorkspace(taskId); const workspaceMode =