From 77b4320acea1db606f0cdec035199af7d1cdd699 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 22 Apr 2026 14:55:24 +0000 Subject: [PATCH] feat(inbox): gate remaining inbox surfaces behind posthog-code-inbox flag - Onboarding: filter out 'signals' step when flag is off - Settings: hide 'Signals' sidebar entry when flag is off - Welcome screen: hide 'Your signals inbox' feature bullet when flag is off - Keyboard shortcut: disable mod+i inbox hotkey when flag is off - Shortcuts sheet: hide inbox shortcut from list when flag is off --- .../components/GlobalEventHandlers.tsx | 8 +++++- .../components/KeyboardShortcutsSheet.tsx | 10 ++++++- .../onboarding/components/WelcomeScreen.tsx | 26 ++++++++++++++----- .../onboarding/hooks/useOnboardingFlow.ts | 12 ++++++--- .../settings/components/SettingsDialog.tsx | 11 +++++--- 5 files changed, 52 insertions(+), 15 deletions(-) diff --git a/apps/code/src/renderer/components/GlobalEventHandlers.tsx b/apps/code/src/renderer/components/GlobalEventHandlers.tsx index e1453521d..5b0776fbe 100644 --- a/apps/code/src/renderer/components/GlobalEventHandlers.tsx +++ b/apps/code/src/renderer/components/GlobalEventHandlers.tsx @@ -8,6 +8,7 @@ import { useSidebarStore } from "@features/sidebar/stores/sidebarStore"; import { useTasks } from "@features/tasks/hooks/useTasks"; import { useFocusWorkspace } from "@features/workspace/hooks/useFocusWorkspace"; import { useWorkspaces } from "@features/workspace/hooks/useWorkspace"; +import { useFeatureFlag } from "@hooks/useFeatureFlag"; import { SHORTCUTS } from "@renderer/constants/keyboard-shortcuts"; import { useTRPC } from "@renderer/trpc"; import type { Task } from "@shared/types"; @@ -170,10 +171,15 @@ export function GlobalEventHandlers({ setReviewMode(currentTaskId, mode === "closed" ? "split" : "closed"); }, [currentTaskId, getReviewMode, setReviewMode]); + const inboxEnabled = useFeatureFlag("posthog-code-inbox"); + useHotkeys(SHORTCUTS.TOGGLE_LEFT_SIDEBAR, toggleLeftSidebar, globalOptions); useHotkeys(SHORTCUTS.TOGGLE_REVIEW_PANEL, handleToggleReview, globalOptions); useHotkeys(SHORTCUTS.SHORTCUTS_SHEET, onToggleShortcutsSheet, globalOptions); - useHotkeys(SHORTCUTS.INBOX, navigateToInbox, globalOptions); + useHotkeys(SHORTCUTS.INBOX, navigateToInbox, { + ...globalOptions, + enabled: inboxEnabled, + }); useHotkeys(SHORTCUTS.PREV_TASK, handlePrevTask, globalOptions, [ handlePrevTask, ]); diff --git a/apps/code/src/renderer/components/KeyboardShortcutsSheet.tsx b/apps/code/src/renderer/components/KeyboardShortcutsSheet.tsx index 0699109aa..fee44a38e 100644 --- a/apps/code/src/renderer/components/KeyboardShortcutsSheet.tsx +++ b/apps/code/src/renderer/components/KeyboardShortcutsSheet.tsx @@ -1,3 +1,4 @@ +import { useFeatureFlag } from "@hooks/useFeatureFlag"; import { Box, Dialog, Flex, Text } from "@radix-ui/themes"; import { CATEGORY_LABELS, @@ -130,7 +131,14 @@ function ShortcutsHeader() { } export function KeyboardShortcutsList() { - const shortcutsByCategory = useMemo(() => getShortcutsByCategory(), []); + const inboxEnabled = useFeatureFlag("posthog-code-inbox"); + const shortcutsByCategory = useMemo(() => { + const grouped = getShortcutsByCategory(); + if (!inboxEnabled) { + grouped.navigation = grouped.navigation.filter((s) => s.id !== "inbox"); + } + return grouped; + }, [inboxEnabled]); const categoryOrder: ShortcutCategory[] = [ "general", diff --git a/apps/code/src/renderer/features/onboarding/components/WelcomeScreen.tsx b/apps/code/src/renderer/features/onboarding/components/WelcomeScreen.tsx index 0438932de..d939ea12d 100644 --- a/apps/code/src/renderer/features/onboarding/components/WelcomeScreen.tsx +++ b/apps/code/src/renderer/features/onboarding/components/WelcomeScreen.tsx @@ -1,3 +1,4 @@ +import { useFeatureFlag } from "@hooks/useFeatureFlag"; import { ArrowRight, ChartLine, @@ -8,37 +9,42 @@ import { } from "@phosphor-icons/react"; import { Button, Flex, Text } from "@radix-ui/themes"; import explorerHog from "@renderer/assets/images/hedgehogs/explorer-hog.png"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { FeatureListItem } from "./FeatureListItem"; import { OnboardingHogTip } from "./OnboardingHogTip"; import { StepActions } from "./StepActions"; -const FEATURES = [ +const ALL_FEATURES = [ { + id: "signals-inbox", icon: , title: "Your signals inbox", description: "Automatically surfaces the highest-impact work from your product data so you always know what to do next.", }, { + id: "product-data", icon: , title: "Product data as context", description: "Your agents have context from your analytics, session replays and feature flags built in.", }, { + id: "any-model", icon: , title: "Any model, any harness", description: "Bring your own agent framework or use our built-in harnesses. Swap models without changing your workflow.", }, { + id: "ship-work", icon: , title: "Ship work, not messages", description: "Run tasks in parallel across local and cloud environments. Work gets done whether you're watching or not.", }, { + id: "review-ship", icon: , title: "Review and ship with confidence", description: @@ -51,18 +57,26 @@ interface WelcomeScreenProps { } const CYCLE_INTERVAL_MS = 2500; -const CYCLE_START_DELAY_MS = FEATURES.length * 100 + 400; +const CYCLE_START_DELAY_MS = ALL_FEATURES.length * 100 + 400; export function WelcomeScreen({ onNext }: WelcomeScreenProps) { + const inboxEnabled = useFeatureFlag("posthog-code-inbox"); + const features = useMemo( + () => + inboxEnabled + ? ALL_FEATURES + : ALL_FEATURES.filter((f) => f.id !== "signals-inbox"), + [inboxEnabled], + ); const [activeIndex, setActiveIndex] = useState(-1); const timerRef = useRef>(null); const startCycling = useCallback(() => { if (timerRef.current) clearInterval(timerRef.current); timerRef.current = setInterval(() => { - setActiveIndex((prev) => (prev + 1) % FEATURES.length); + setActiveIndex((prev) => (prev + 1) % features.length); }, CYCLE_INTERVAL_MS); - }, []); + }, [features.length]); useEffect(() => { const timeout = setTimeout(() => { @@ -126,7 +140,7 @@ export function WelcomeScreen({ onNext }: WelcomeScreenProps) { - {FEATURES.map((feature, index) => ( + {features.map((feature, index) => ( state.hasCodeAccess); + const inboxEnabled = useFeatureFlag("posthog-code-inbox"); const activeSteps = useMemo(() => { + let steps = ONBOARDING_STEPS as OnboardingStep[]; if (hasCodeAccess === true) { - return ONBOARDING_STEPS.filter((s) => s !== "invite-code"); + steps = steps.filter((s) => s !== "invite-code"); } - return ONBOARDING_STEPS; - }, [hasCodeAccess]); + if (!inboxEnabled) { + steps = steps.filter((s) => s !== "signals"); + } + return steps; + }, [hasCodeAccess, inboxEnabled]); useEffect(() => { if (!activeSteps.includes(currentStep)) { diff --git a/apps/code/src/renderer/features/settings/components/SettingsDialog.tsx b/apps/code/src/renderer/features/settings/components/SettingsDialog.tsx index 10ea83948..1b0cda973 100644 --- a/apps/code/src/renderer/features/settings/components/SettingsDialog.tsx +++ b/apps/code/src/renderer/features/settings/components/SettingsDialog.tsx @@ -129,14 +129,17 @@ export function SettingsDialog() { const { data: user } = useCurrentUser({ client }); const { seat, planLabel } = useSeat(); const billingEnabled = useFeatureFlag("posthog-code-billing"); + const inboxEnabled = useFeatureFlag("posthog-code-inbox"); const logoutMutation = useLogoutMutation(); const sidebarItems = useMemo( () => - billingEnabled - ? SIDEBAR_ITEMS - : SIDEBAR_ITEMS.filter((item) => item.id !== "plan-usage"), - [billingEnabled], + SIDEBAR_ITEMS.filter((item) => { + if (item.id === "plan-usage" && !billingEnabled) return false; + if (item.id === "signals" && !inboxEnabled) return false; + return true; + }), + [billingEnabled, inboxEnabled], ); useHotkeys("escape", close, {