diff --git a/apps/code/src/renderer/components/GlobalEventHandlers.tsx b/apps/code/src/renderer/components/GlobalEventHandlers.tsx index e1453521d..f4597643c 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 inboxHidden = useFeatureFlag("posthog-code-inbox-hidden"); + 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: !inboxHidden, + }); 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..3e59f6984 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 inboxHidden = useFeatureFlag("posthog-code-inbox-hidden"); + const shortcutsByCategory = useMemo(() => { + const grouped = getShortcutsByCategory(); + if (inboxHidden) { + grouped.navigation = grouped.navigation.filter((s) => s.id !== "inbox"); + } + return grouped; + }, [inboxHidden]); const categoryOrder: ShortcutCategory[] = [ "general", diff --git a/apps/code/src/renderer/components/MainLayout.tsx b/apps/code/src/renderer/components/MainLayout.tsx index cfd2f4cb5..901b93ba2 100644 --- a/apps/code/src/renderer/components/MainLayout.tsx +++ b/apps/code/src/renderer/components/MainLayout.tsx @@ -51,6 +51,7 @@ export function MainLayout() { } = useShortcutsSheetStore(); const { data: tasks } = useTasks(); const { showPrompt, isChecking, check, dismiss } = useConnectivity(); + const inboxHidden = useFeatureFlag("posthog-code-inbox-hidden"); const billingEnabled = useFeatureFlag(BILLING_FLAG); // Space switcher data @@ -108,7 +109,7 @@ export function MainLayout() { {view.type === "folder-settings" && } - {view.type === "inbox" && } + {view.type === "inbox" && !inboxHidden && } {view.type === "archived" && } diff --git a/apps/code/src/renderer/features/onboarding/components/OnboardingFlow.tsx b/apps/code/src/renderer/features/onboarding/components/OnboardingFlow.tsx index 2cb2ef939..c53b38abb 100644 --- a/apps/code/src/renderer/features/onboarding/components/OnboardingFlow.tsx +++ b/apps/code/src/renderer/features/onboarding/components/OnboardingFlow.tsx @@ -28,6 +28,7 @@ export function OnboardingFlow() { const { currentStep, activeSteps, + isLastStep, direction, next, back, @@ -46,12 +47,10 @@ export function OnboardingFlow() { ); usePrefetchSignalData(); - useHotkeys("right", next, { enableOnFormTags: false }, [next]); - useHotkeys("left", back, { enableOnFormTags: false }, [back]); + const handleNext = isLastStep ? completeOnboarding : next; - const handleComplete = () => { - completeOnboarding(); - }; + useHotkeys("right", handleNext, { enableOnFormTags: false }, [handleNext]); + useHotkeys("left", back, { enableOnFormTags: false }, [back]); const footerRight = ( @@ -75,7 +74,7 @@ export function OnboardingFlow() { size="1" variant="ghost" color="gray" - onClick={handleComplete} + onClick={completeOnboarding} style={{ opacity: 0.5 }} > @@ -100,7 +99,7 @@ export function OnboardingFlow() { transition={{ duration: 0.3 }} style={{ width: "100%", flex: 1, minHeight: 0 }} > - + )} @@ -115,7 +114,7 @@ export function OnboardingFlow() { transition={{ duration: 0.3 }} style={{ width: "100%", flex: 1, minHeight: 0 }} > - + )} @@ -130,7 +129,7 @@ export function OnboardingFlow() { transition={{ duration: 0.3 }} style={{ width: "100%", flex: 1, minHeight: 0 }} > - + )} @@ -146,7 +145,7 @@ export function OnboardingFlow() { style={{ width: "100%", flex: 1, minHeight: 0 }} > - + )} @@ -182,7 +181,7 @@ export function OnboardingFlow() { transition={{ duration: 0.3 }} style={{ width: "100%", flex: 1, minHeight: 0 }} > - + )} diff --git a/apps/code/src/renderer/features/onboarding/components/WelcomeScreen.tsx b/apps/code/src/renderer/features/onboarding/components/WelcomeScreen.tsx index 0438932de..c10177daf 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 inboxHidden = useFeatureFlag("posthog-code-inbox-hidden"); + const features = useMemo( + () => + inboxHidden + ? ALL_FEATURES.filter((f) => f.id !== "signals-inbox") + : ALL_FEATURES, + [inboxHidden], + ); 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 inboxHidden = useFeatureFlag("posthog-code-inbox-hidden"); 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 (inboxHidden) { + steps = steps.filter((s) => s !== "signals"); + } + return steps; + }, [hasCodeAccess, inboxHidden]); 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 b16165af0..29cbc55ed 100644 --- a/apps/code/src/renderer/features/settings/components/SettingsDialog.tsx +++ b/apps/code/src/renderer/features/settings/components/SettingsDialog.tsx @@ -129,15 +129,18 @@ export function SettingsDialog() { const client = useOptionalAuthenticatedClient(); const { data: user } = useCurrentUser({ client }); const { seat, planLabel } = useSeat(); + const inboxHidden = useFeatureFlag("posthog-code-inbox-hidden"); const billingEnabled = useFeatureFlag(BILLING_FLAG); 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" && inboxHidden) return false; + return true; + }), + [billingEnabled, inboxHidden], ); useHotkeys("escape", close, { diff --git a/apps/code/src/renderer/features/sidebar/components/SidebarMenu.tsx b/apps/code/src/renderer/features/sidebar/components/SidebarMenu.tsx index 47222ff98..ce1e2d1e5 100644 --- a/apps/code/src/renderer/features/sidebar/components/SidebarMenu.tsx +++ b/apps/code/src/renderer/features/sidebar/components/SidebarMenu.tsx @@ -13,6 +13,7 @@ import { } from "@features/tasks/hooks/useArchiveTask"; import { useTasks, useUpdateTask } from "@features/tasks/hooks/useTasks"; import { useWorkspaces } from "@features/workspace/hooks/useWorkspace"; +import { useFeatureFlag } from "@hooks/useFeatureFlag"; import { useTaskContextMenu } from "@hooks/useTaskContextMenu"; import { ScrollArea, Separator } from "@posthog/quill"; import { Box, Flex } from "@radix-ui/themes"; @@ -55,10 +56,12 @@ function SidebarMenuComponent() { const sidebarData = useSidebarData({ activeView: view, }); + const inboxHidden = useFeatureFlag("posthog-code-inbox-hidden"); const inboxPollingActive = useRendererWindowFocusStore((s) => s.focused); const { data: inboxProbe } = useInboxReports( { status: INBOX_PIPELINE_STATUS_FILTER }, { + enabled: !inboxHidden, refetchInterval: inboxPollingActive ? INBOX_REFETCH_INTERVAL_MS : false, refetchIntervalInBackground: false, staleTime: inboxPollingActive ? INBOX_REFETCH_INTERVAL_MS : 15_000, @@ -277,13 +280,15 @@ function SidebarMenuComponent() { /> - - - + {!inboxHidden && ( + + + + )}