From 9aec46742cb5a0e2f1588bf9fc8b87d3fbab6b1b Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Tue, 26 May 2026 15:52:20 +0200 Subject: [PATCH 01/13] feat(code): Restore signals onboarding inside the Inbox view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #2208 dropped the signals step from app onboarding to improve conversion, but the setup UX it removed never landed anywhere else — users with no GitHub or no signal sources just saw a stripped-down WelcomePane that bounced them into a modal. Replaces that empty state with an inline InboxSetupPane that mirrors the deleted SignalsStep (hero copy, detective hog tip, animated entrance) and embeds SignalSourcesSettings so users get GitHub connect, source toggles, PR auto-start threshold, and Slack notifications without leaving the Inbox. Warming-up state and the configuration dialog (still opened from the toolbar and auto-open effect) are unchanged. Generated-By: PostHog Code Task-Id: 3f6eaf93-f574-44d9-aa91-bc4a7b8a68b9 --- .../inbox/components/InboxEmptyStates.tsx | 64 +------------------ .../inbox/components/InboxSetupPane.tsx | 48 ++++++++++++++ .../inbox/components/InboxSignalsTab.tsx | 32 +++++++--- 3 files changed, 71 insertions(+), 73 deletions(-) create mode 100644 apps/code/src/renderer/features/inbox/components/InboxSetupPane.tsx diff --git a/apps/code/src/renderer/features/inbox/components/InboxEmptyStates.tsx b/apps/code/src/renderer/features/inbox/components/InboxEmptyStates.tsx index 66d1d572f..b4685a7e7 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxEmptyStates.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxEmptyStates.tsx @@ -1,10 +1,9 @@ import { AnimatedEllipsis } from "@features/inbox/components/utils/AnimatedEllipsis"; import { SOURCE_PRODUCT_META } from "@features/inbox/components/utils/source-product-icons"; -import { ArrowDownIcon, CheckCircleIcon } from "@phosphor-icons/react"; +import { CheckCircleIcon } from "@phosphor-icons/react"; import { Box, Button, Flex, Text, Tooltip } from "@radix-ui/themes"; import builderHog from "@renderer/assets/images/hedgehogs/builder-hog-03.png"; import explorerHog from "@renderer/assets/images/hedgehogs/explorer-hog.png"; -import graphsHog from "@renderer/assets/images/hedgehogs/graphs-hog.png"; import mailHog from "@renderer/assets/images/mail-hog.png"; import { ANALYTICS_EVENTS } from "@shared/types/analytics"; import { track } from "@utils/analytics"; @@ -12,67 +11,6 @@ import { useState } from "react"; // ── Full-width empty states ───────────────────────────────────────────────── -export function WelcomePane({ onEnableInbox }: { onEnableInbox: () => void }) { - return ( - - - - - - Welcome to your Inbox - - - - - - Background analysis of your data — while you sleep. - -
- Session recordings watched automatically. Issues, tickets, and evals - analyzed around the clock. -
- - - - - - Ready-to-run fixes for real user problems. - -
- Each report includes evidence and impact numbers — just execute the - prompt in your agent. -
-
- - -
-
- ); -} - export function WarmingUpPane({ onConfigureSources, enabledProducts, diff --git a/apps/code/src/renderer/features/inbox/components/InboxSetupPane.tsx b/apps/code/src/renderer/features/inbox/components/InboxSetupPane.tsx new file mode 100644 index 000000000..5cc9dec64 --- /dev/null +++ b/apps/code/src/renderer/features/inbox/components/InboxSetupPane.tsx @@ -0,0 +1,48 @@ +import { OnboardingHogTip } from "@features/onboarding/components/OnboardingHogTip"; +import { SignalSourcesSettings } from "@features/settings/components/sections/SignalSourcesSettings"; +import { Flex, Text } from "@radix-ui/themes"; +import detectiveHog from "@renderer/assets/images/hedgehogs/detective-hog.png"; +import { motion } from "framer-motion"; + +export function InboxSetupPane() { + return ( + + + + + + Set up your Signals Inbox + + + Connect GitHub and pick which sources to monitor. PostHog Code + will analyze activity around the clock and surface ready-to-run + fixes — with autonomous PRs at the priority threshold you choose. + + + + + + + + + + + + ); +} diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index 480377488..1593302fa 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -4,8 +4,8 @@ import { SelectReportPane, SkeletonBackdrop, WarmingUpPane, - WelcomePane, } from "@features/inbox/components/InboxEmptyStates"; +import { InboxSetupPane } from "@features/inbox/components/InboxSetupPane"; import { InboxSourcesDialog } from "@features/inbox/components/InboxSourcesDialog"; import { inboxBulkSnoozeDisabledReason, @@ -830,8 +830,24 @@ export function InboxSignalsTab() { )} + ) : !hasSignalSources || !hasGithubIntegration ? ( + /* ── Inline setup pane for users still configuring sources / GitHub ── */ + + setSourcesDialogOpen(true)} + /> + + + + ) : ( - /* ── Full-width empty state with skeleton backdrop ──────── */ + /* ── Full-width warming-up state with skeleton backdrop ──────── */ - {!hasSignalSources || !hasGithubIntegration ? ( - setSourcesDialogOpen(true)} /> - ) : ( - setSourcesDialogOpen(true)} - enabledProducts={enabledProducts} - /> - )} + setSourcesDialogOpen(true)} + enabledProducts={enabledProducts} + /> From 3fdc40a2d537ccb8ab50aa6720f538f0453e3159 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Tue, 26 May 2026 16:19:41 +0200 Subject: [PATCH 02/13] fix(code): Gate Inbox onboarding on no-reports + no-sources only Previously the inline setup pane only appeared in the existing empty-state branch, so a user with discovered/suggested tasks (but no real signal reports and no sources configured) was pushed into the two-pane RecommendedSetupTasks view and never saw the onboarding. Lifts the onboarding to a top-level branch ahead of two-pane and changes the condition to `!hasReports && !hasSignalSources`. Suggested tasks alone no longer skip a source-less user past setup. The GitHub-only-missing case now falls through to two-pane or warming-up, where the existing InboxSourcesDialog auto-open continues to prompt when totalCount > 0. Generated-By: PostHog Code Task-Id: 3f6eaf93-f574-44d9-aa91-bc4a7b8a68b9 --- .../inbox/components/InboxSignalsTab.tsx | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index 1593302fa..41c2b3e57 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -475,11 +475,15 @@ export function InboxSignalsTab() { sourceProductFilter.length > 0 || suggestedReviewerFilter.length > 0 || statusFilter.length < 5; + // Onboarding wins over two-pane even if the user has suggested setup tasks — + // discovered tasks alone shouldn't push a source-less user past the inline setup. + const showInboxOnboarding = !hasReports && !hasSignalSources; const shouldShowTwoPane = - hasReports || - !!searchQuery.trim() || - hasActiveFilters || - hasDiscoveredTasks; + !showInboxOnboarding && + (hasReports || + !!searchQuery.trim() || + hasActiveFilters || + hasDiscoveredTasks); // Sticky: once we enter two-pane mode, stay there even if a refetch // momentarily empties the list (e.g. when sort order changes). @@ -686,7 +690,23 @@ export function InboxSignalsTab() { return ( <> - {showTwoPaneLayout ? ( + {showInboxOnboarding ? ( + /* ── Inline setup pane for users with no sources configured ── */ + + setSourcesDialogOpen(true)} + /> + + + + + ) : showTwoPaneLayout ? ( {/* ── Left pane: report list ───────────────────────────────── */} - ) : !hasSignalSources || !hasGithubIntegration ? ( - /* ── Inline setup pane for users still configuring sources / GitHub ── */ - - setSourcesDialogOpen(true)} - /> - - - - ) : ( /* ── Full-width warming-up state with skeleton backdrop ──────── */ From 4f5e4c77e00928a0ea013793eeb5055aa4936166 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Tue, 26 May 2026 16:24:34 +0200 Subject: [PATCH 03/13] fix(code): Hide "Configure sources" toolbar button during Inbox onboarding The inline setup pane already is the configuration UI, so the toolbar shortcut that opens InboxSourcesDialog over the top of it was a duplicate, redundant path. Drop the onConfigureSources prop on the toolbar in the onboarding branch. Generated-By: PostHog Code Task-Id: 3f6eaf93-f574-44d9-aa91-bc4a7b8a68b9 --- .../renderer/features/inbox/components/InboxSignalsTab.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index 41c2b3e57..5f30838e4 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -691,7 +691,9 @@ export function InboxSignalsTab() { return ( <> {showInboxOnboarding ? ( - /* ── Inline setup pane for users with no sources configured ── */ + /* ── Inline setup pane for users with no sources configured ── + No "Configure sources" toolbar shortcut — the pane below is the + configuration UI, so a duplicate button would be confusing. */ setSourcesDialogOpen(true)} /> From 65bacbc860cc1e5a93baf04b5c0252edd25213f6 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Tue, 26 May 2026 16:49:04 +0200 Subject: [PATCH 04/13] feat(code): Sticky Inbox onboarding + revisit "Configure sources" tooltip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three changes on top of the inline Inbox onboarding: - Bigger subtext: bump the InboxSetupPane description from text-sm to text-base leading-relaxed so the explanation reads as a clear hero paragraph rather than fine print. - Hide search bar in onboarding: add a hideSearch prop to SignalsToolbar and pass it in the onboarding render. The search row makes no sense before any reports exist. - Sticky onboarding within a visit + revisit teaching tooltip: onboarding no longer flips off the instant a source is toggled on. An onboardingStickyRef latches true on first entry and unwinds naturally when InboxView unmounts on navigation away. A new persisted store (inboxOnboardingStore) tracks hasSeenOnboarding and hasDismissedConfigTooltip in localStorage. On the next inbox visit after onboarding, the Configure sources toolbar button shows a persistent Radix tooltip "All of your Inbox configuration is here" (side=bottom, align=end). Opening the InboxSourcesDialog by any path — manual click or the existing totalCount>0 auto-open — dismisses the tooltip permanently. Generated-By: PostHog Code Task-Id: 3f6eaf93-f574-44d9-aa91-bc4a7b8a68b9 --- .../components/TokenSpendAnalysisBanner.tsx | 145 +++++-------- .../features/billing/types/spend-analysis.ts | 11 +- .../billing/utils/spendAnalysisFormat.ts | 24 +++ .../billing/utils/spendAnalysisPrompt.test.ts | 194 ++++++++++++++++++ .../billing/utils/spendAnalysisPrompt.ts | 122 +++++++++++ .../inbox/components/InboxSetupPane.tsx | 4 +- .../inbox/components/InboxSignalsTab.tsx | 47 ++++- .../components/detail/ReportDetailPane.tsx | 48 +++-- .../inbox/components/list/SignalsToolbar.tsx | 101 +++++---- .../inbox/stores/inboxOnboardingStore.ts | 27 +++ .../sidebar/components/items/HomeItem.tsx | 16 +- apps/code/src/shared/types/analytics.ts | 21 ++ 12 files changed, 603 insertions(+), 157 deletions(-) create mode 100644 apps/code/src/renderer/features/billing/utils/spendAnalysisFormat.ts create mode 100644 apps/code/src/renderer/features/billing/utils/spendAnalysisPrompt.test.ts create mode 100644 apps/code/src/renderer/features/billing/utils/spendAnalysisPrompt.ts create mode 100644 apps/code/src/renderer/features/inbox/stores/inboxOnboardingStore.ts diff --git a/apps/code/src/renderer/features/billing/components/TokenSpendAnalysisBanner.tsx b/apps/code/src/renderer/features/billing/components/TokenSpendAnalysisBanner.tsx index bbcb37b34..66c5c5e08 100644 --- a/apps/code/src/renderer/features/billing/components/TokenSpendAnalysisBanner.tsx +++ b/apps/code/src/renderer/features/billing/components/TokenSpendAnalysisBanner.tsx @@ -4,59 +4,32 @@ import type { SpendAnalysisProductRow, SpendAnalysisResponse, SpendAnalysisToolRow, - SpendAnalysisTraceRow, } from "@features/billing/types/spend-analysis"; +import { + formatTokens, + formatUsd, + formatWindow, +} from "@features/billing/utils/spendAnalysisFormat"; +import { buildAnalysisPrompt } from "@features/billing/utils/spendAnalysisPrompt"; +import { useSettingsDialogStore } from "@features/settings/stores/settingsDialogStore"; import { ArrowSquareOut, ChartLine, Lightning, + Sparkle, WarningCircle, } from "@phosphor-icons/react"; import { Button, Callout, Flex, Spinner, Table, Text } from "@radix-ui/themes"; +import { ANALYTICS_EVENTS } from "@shared/types/analytics"; +import { useNavigationStore } from "@stores/navigationStore"; +import { track } from "@utils/analytics"; const DOCS_URL = "https://posthog.com/docs/llm-analytics"; -const SKILL_URL = - "https://github.com/PostHog/posthog/blob/master/products/llm_analytics/skills/exploring-llm-costs/SKILL.md"; - -function formatUsd(amount: number): string { - if (amount === 0) return "$0"; - if (amount < 0.01) return "<$0.01"; - if (amount < 100) return `$${amount.toFixed(2)}`; - return `$${Math.round(amount).toLocaleString()}`; -} - -function formatTokens(n: number): string { - if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; - if (n >= 1_000) return `${(n / 1_000).toFixed(0)}k`; - return n.toString(); -} - -function formatTrace(traceId: string | null): string { - if (!traceId) return "(no trace id)"; - if (traceId.length <= 14) return traceId; - return `${traceId.slice(0, 8)}…${traceId.slice(-4)}`; -} - -function formatWindow(fromIso: string, toIso: string): string { - const fromMs = new Date(fromIso).getTime(); - const toMs = new Date(toIso).getTime(); - const days = Math.max(1, Math.round((toMs - fromMs) / (1000 * 60 * 60 * 24))); - return `${days} days`; -} - -function formatDate(iso: string | null): string { - if (!iso) return "—"; - return new Date(iso).toLocaleDateString(undefined, { - month: "short", - day: "numeric", - }); -} function generateSuggestions(data: SpendAnalysisResponse): string[] { const suggestions: string[] = []; const { summary } = data; const toolItems = data.by_tool.items; - const traceItems = data.top_traces.items; if (summary.total_cost_usd === 0) { return ["No LLM spend in the selected window."]; @@ -88,19 +61,9 @@ function generateSuggestions(data: SpendAnalysisResponse): string[] { } } - if (traceItems.length > 0 && codeTotal > 0) { - const topTrace = traceItems[0]; - const share = topTrace.cost_usd / codeTotal; - if (share > 0.15) { - suggestions.push( - `Your top session cost ${formatUsd(topTrace.cost_usd)} — ${Math.round(share * 100)}% of PostHog Code spend in one trace. Long sessions compound context cost.`, - ); - } - } - if (suggestions.length === 0) { suggestions.push( - "Your spend is fairly evenly distributed across tools and sessions — no single hotspot stands out.", + "Your spend is fairly evenly distributed across tools — no single hotspot stands out.", ); } @@ -218,30 +181,6 @@ function ModelTable({ rows }: { rows: SpendAnalysisModelRow[] }) { ); } -function TraceTable({ rows }: { rows: SpendAnalysisTraceRow[] }) { - if (rows.length === 0) return null; - return ( - - {rows.map((r) => ( - - - - {formatTrace(r.trace_id)} - - - {r.generation_count.toLocaleString()} - {formatDate(r.started_at)} - {formatUsd(r.cost_usd)} - - ))} - - ); -} - function SectionTable({ title, headers, @@ -279,9 +218,39 @@ function SectionTable({ ); } -function FooterLinks() { +function FooterLinks({ data }: { data: SpendAnalysisResponse }) { + const navigateToTaskInput = useNavigationStore( + (state) => state.navigateToTaskInput, + ); + const closeSettings = useSettingsDialogStore((state) => state.close); + + const handleAnalyseClick = (): void => { + track(ANALYTICS_EVENTS.SPEND_ANALYSIS_TASK_OPENED, { + total_cost_usd: data.summary.total_cost_usd, + scoped_cost_usd: data.summary.scoped_cost_usd, + scoped_event_count: data.summary.scoped_event_count, + window_days: Math.max( + 1, + Math.round( + (new Date(data.summary.date_to).getTime() - + new Date(data.summary.date_from).getTime()) / + (1000 * 60 * 60 * 24), + ), + ), + tool_row_count: Math.min(data.by_tool.items.length, 10), + model_row_count: data.by_model.items.length, + }); + // This banner lives inside the Settings dialog (modal). `navigateToTaskInput` + // changes the underlying view but the dialog stays mounted on top, so the user + // doesn't see the prefilled task input. Close the dialog first. + closeSettings(); + navigateToTaskInput({ + initialPrompt: buildAnalysisPrompt(data), + }); + }; + return ( - + Use{" "} {" "} in your own project for the full slice-and-dice experience. - - Want an agent to run this kind of analysis on demand? Drop the{" "} - - exploring-llm-costs - {" "} - skill into your agent. - + ); } @@ -346,7 +312,6 @@ export function TokenSpendAnalysisBanner() { - ))} - + ); } @@ -406,7 +371,7 @@ export function TokenSpendAnalysisBanner() { Analyse your token usage with PostHog LLM analytics - See where your spend goes — by tool, by model, by trace — over the + See where your spend goes — by product, tool, and model — over the last 30 days, and get tips on where to optimise. - + + + {isMac ? "⌘↵" : "Ctrl+↵"} to send + + + + + diff --git a/apps/code/src/renderer/features/inbox/components/list/SignalsToolbar.tsx b/apps/code/src/renderer/features/inbox/components/list/SignalsToolbar.tsx index 1304ea04d..6f70e0939 100644 --- a/apps/code/src/renderer/features/inbox/components/list/SignalsToolbar.tsx +++ b/apps/code/src/renderer/features/inbox/components/list/SignalsToolbar.tsx @@ -47,6 +47,7 @@ interface SignalsToolbarProps { pipelinePausedUntil?: string | null; searchDisabledReason?: string | null; hideFilters?: boolean; + hideSearch?: boolean; reports?: SignalReport[]; /** Pre-computed effective bulk selection (store ids or virtual open-report fallback). */ effectiveBulkIds?: string[]; @@ -54,6 +55,12 @@ interface SignalsToolbarProps { onToggleSelectAll?: (checked: boolean) => void; /** Called when the "Configure sources" button is clicked. */ onConfigureSources?: () => void; + /** + * When true, displays a persistent tooltip on the "Configure sources" + * button — used after first onboarding to teach the user where the rest + * of the configuration lives. The parent controls dismissal. + */ + configSourcesTooltipOpen?: boolean; /** * Opens the dismiss flow: exactly one report selected (snooze or permanent suppress, with a reason). * With 2+ reports selected, use the Snooze and Suppress toolbar actions instead. @@ -260,10 +267,12 @@ export function SignalsToolbar({ pipelinePausedUntil, searchDisabledReason, hideFilters, + hideSearch, reports = [], effectiveBulkIds = [], onToggleSelectAll, onConfigureSources, + configSourcesTooltipOpen, onOpenDismissDialog, isDismissMutationPending = false, onReportAction, @@ -498,45 +507,63 @@ export function SignalsToolbar({ ) : null} - {onConfigureSources ? ( - - ) : null} + {onConfigureSources + ? (() => { + const configureSourcesButton = ( + + ); + return configSourcesTooltipOpen ? ( + + {configureSourcesButton} + + ) : ( + configureSourcesButton + ); + })() + : null} - - - {!hideFilters && ( - - - - - )} - + {!hideSearch && ( + + + {!hideFilters && ( + + + + + )} + + )} void; + dismissConfigTooltip: () => void; +} + +export const useInboxOnboardingStore = create()( + persist( + (set) => ({ + hasSeenOnboarding: false, + hasDismissedConfigTooltip: false, + markOnboardingSeen: () => set({ hasSeenOnboarding: true }), + dismissConfigTooltip: () => set({ hasDismissedConfigTooltip: true }), + }), + { name: "inbox-onboarding-storage" }, + ), +); diff --git a/apps/code/src/renderer/features/sidebar/components/items/HomeItem.tsx b/apps/code/src/renderer/features/sidebar/components/items/HomeItem.tsx index 7ca165a1c..648ce78d3 100644 --- a/apps/code/src/renderer/features/sidebar/components/items/HomeItem.tsx +++ b/apps/code/src/renderer/features/sidebar/components/items/HomeItem.tsx @@ -2,6 +2,8 @@ import { Tooltip } from "@components/ui/Tooltip"; import { EnvelopeSimple, Plus } from "@phosphor-icons/react"; import { Badge, type ButtonProps } from "@posthog/quill"; import { SHORTCUTS } from "@renderer/constants/keyboard-shortcuts"; +import { useDraftStore } from "@renderer/features/message-editor/stores/draftStore"; +import { isContentEmpty } from "@renderer/features/message-editor/utils/content"; import { SidebarItem } from "../SidebarItem"; import { SidebarKbdHint } from "./SidebarKbdHint"; @@ -12,6 +14,9 @@ interface NewTaskItemProps { } export function NewTaskItem({ isActive, onClick }: NewTaskItemProps) { + const hasDraft = useDraftStore( + (s) => !isContentEmpty(s.drafts["task-input"]), + ); return ( } + endContent={ + <> + {hasDraft ? ( + + Draft + + ) : null} + + + } /> ); } diff --git a/apps/code/src/shared/types/analytics.ts b/apps/code/src/shared/types/analytics.ts index 61f56c0cf..95588623e 100644 --- a/apps/code/src/shared/types/analytics.ts +++ b/apps/code/src/shared/types/analytics.ts @@ -535,6 +535,21 @@ export interface InboxReportScrolledProperties { time_since_open_ms: number; } +export interface SpendAnalysisTaskOpenedProperties { + /** Total LLM spend in USD across all products for the analysed window. */ + total_cost_usd: number; + /** PostHog Code spend in USD for the analysed window (subset of total). */ + scoped_cost_usd: number; + /** Number of `$ai_generation` events in the analysed window. */ + scoped_event_count: number; + /** Length of the analysed window in days. */ + window_days: number; + /** Number of tool rows the receiving agent will see (capped at 10 in the prompt). */ + tool_row_count: number; + /** Number of model rows the receiving agent will see. */ + model_row_count: number; +} + export interface InboxReportActionProperties { report_id: string; report_title: string | null; @@ -682,6 +697,9 @@ export const ANALYTICS_EVENTS = { INBOX_REPORT_ACTION: "Inbox report action", INBOX_REPORT_SCROLLED: "Inbox report scrolled", + // Spend analysis events + SPEND_ANALYSIS_TASK_OPENED: "Spend analysis task opened", + // Prompt history events PROMPT_HISTORY_OPENED: "Prompt history opened", PROMPT_HISTORY_SELECTED: "Prompt history selected", @@ -794,6 +812,9 @@ export type EventPropertyMap = { [ANALYTICS_EVENTS.INBOX_REPORT_ACTION]: InboxReportActionProperties; [ANALYTICS_EVENTS.INBOX_REPORT_SCROLLED]: InboxReportScrolledProperties; + // Spend analysis events + [ANALYTICS_EVENTS.SPEND_ANALYSIS_TASK_OPENED]: SpendAnalysisTaskOpenedProperties; + // Prompt history events [ANALYTICS_EVENTS.PROMPT_HISTORY_OPENED]: PromptHistoryOpenedProperties; [ANALYTICS_EVENTS.PROMPT_HISTORY_SELECTED]: PromptHistorySelectedProperties; From aa8fb2e1db320dfc30e41a8d95c10052eddaf71a Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Tue, 26 May 2026 17:07:17 +0200 Subject: [PATCH 05/13] fix(code): Suppress the entire toolbar during Inbox onboarding The report counter, search, and bulk-actions row are all meaningless before any source is configured. Render only the InboxSetupPane inside a ScrollArea during onboarding. Drop the now-unused hideSearch prop on SignalsToolbar. Generated-By: PostHog Code Task-Id: 3f6eaf93-f574-44d9-aa91-bc4a7b8a68b9 --- .../inbox/components/InboxSignalsTab.tsx | 21 ++----- .../inbox/components/list/SignalsToolbar.tsx | 58 +++++++++---------- 2 files changed, 32 insertions(+), 47 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index 5a2f46e06..9009c917b 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -730,22 +730,11 @@ export function InboxSignalsTab() { <> {showInboxOnboarding ? ( /* ── Inline setup pane for users with no sources configured ── - No "Configure sources" toolbar shortcut nor search bar — the pane - below is the configuration UI and there's nothing to search yet. */ - - - - - - + The toolbar (report counter, search, bulk actions) is suppressed + entirely — none of it is meaningful before any source is configured. */ + + + ) : showTwoPaneLayout ? ( {/* ── Left pane: report list ───────────────────────────────── */} diff --git a/apps/code/src/renderer/features/inbox/components/list/SignalsToolbar.tsx b/apps/code/src/renderer/features/inbox/components/list/SignalsToolbar.tsx index 6f70e0939..3c8263d54 100644 --- a/apps/code/src/renderer/features/inbox/components/list/SignalsToolbar.tsx +++ b/apps/code/src/renderer/features/inbox/components/list/SignalsToolbar.tsx @@ -47,7 +47,6 @@ interface SignalsToolbarProps { pipelinePausedUntil?: string | null; searchDisabledReason?: string | null; hideFilters?: boolean; - hideSearch?: boolean; reports?: SignalReport[]; /** Pre-computed effective bulk selection (store ids or virtual open-report fallback). */ effectiveBulkIds?: string[]; @@ -267,7 +266,6 @@ export function SignalsToolbar({ pipelinePausedUntil, searchDisabledReason, hideFilters, - hideSearch, reports = [], effectiveBulkIds = [], onToggleSelectAll, @@ -535,35 +533,33 @@ export function SignalsToolbar({ : null} - {!hideSearch && ( - - - {!hideFilters && ( - - - - - )} - - )} + + + {!hideFilters && ( + + + + + )} + Date: Tue, 26 May 2026 17:38:44 +0200 Subject: [PATCH 06/13] Tweak copy in Inbox onboarding and Signal sources settings --- .../features/inbox/components/InboxSetupPane.tsx | 15 ++++----------- .../sections/SignalSlackNotificationsSettings.tsx | 1 + .../components/sections/SignalSourcesSettings.tsx | 7 ++++--- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/InboxSetupPane.tsx b/apps/code/src/renderer/features/inbox/components/InboxSetupPane.tsx index 9429ae530..ed6e76cf9 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSetupPane.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSetupPane.tsx @@ -9,7 +9,7 @@ export function InboxSetupPane() { - - - Set up your Signals Inbox - - - Connect GitHub and pick which sources to monitor. PostHog Code - will analyze activity around the clock and surface ready-to-run - fixes — with autonomous PRs at the priority threshold you choose. - - + + Set up self-driving for your product + { void slackConnect.connect(); }} + className="w-fit" > {slackConnect.isConnecting ? "Waiting for Slack…" diff --git a/apps/code/src/renderer/features/settings/components/sections/SignalSourcesSettings.tsx b/apps/code/src/renderer/features/settings/components/sections/SignalSourcesSettings.tsx index 77b3e4614..09552a568 100644 --- a/apps/code/src/renderer/features/settings/components/sections/SignalSourcesSettings.tsx +++ b/apps/code/src/renderer/features/settings/components/sections/SignalSourcesSettings.tsx @@ -59,9 +59,10 @@ export function SignalSourcesSettings({ return ( - - Automatically analyze your product data and surface actionable insights. - Choose which sources to enable for this project. + + Connect GitHub and pick which sources to monitor. PostHog Code will + analyze activity around the clock and surface ready-to-merge fixes and + improvements. From 45ed7cb234b3a929da0c6b03ce43c72af7adea16 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Tue, 26 May 2026 18:31:48 +0200 Subject: [PATCH 07/13] revert(code): Drop unrelated Discuss popover change from this PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TextField→TextArea + Cmd/Ctrl+Enter change in ReportDetailPane was unrelated to the Inbox onboarding restoration and got bundled into an earlier commit on this branch by mistake. Reverting it back to main. Generated-By: PostHog Code Task-Id: 3f6eaf93-f574-44d9-aa91-bc4a7b8a68b9 --- .../components/detail/ReportDetailPane.tsx | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx b/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx index 81ff7aad4..39eeb2270 100644 --- a/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx +++ b/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx @@ -50,7 +50,6 @@ import type { Task, } from "@shared/types"; import type { InboxReportActionProperties } from "@shared/types/analytics"; -import { useNavigationStore } from "@stores/navigationStore"; import { useQuery } from "@tanstack/react-query"; import { isMac } from "@utils/platform"; import { @@ -63,6 +62,7 @@ import { useState, } from "react"; import { toast } from "sonner"; +import { useCreatePrReport } from "../../hooks/useCreatePrReport"; import { useDiscussReport } from "../../hooks/useDiscussReport"; import { ReportImplementationPrLink } from "../utils/ReportImplementationPrLink"; import { SignalReportActionabilityBadge } from "../utils/SignalReportActionabilityBadge"; @@ -265,7 +265,6 @@ export function ReportDetailPane({ ); // ── Task creation ─────────────────────────────────────────────────────── - const { navigateToTaskInput } = useNavigationStore(); const { data: reportRepository } = useReportRepository(report.id); const trpcReact = useTRPC(); const { data: mostRecentRepo } = useQuery( @@ -360,22 +359,20 @@ export function ReportDetailPane({ [fireDetailAction], ); - const handleCreateImplementationTask = useCallback(() => { - if (!canCreateImplementationPr) return; + const { createPrReport, isCreatingPr } = useCreatePrReport({ + reportId: report.id, + reportTitle: report.title, + cloudRepository: effectiveCloudRepository, + }); + + const handleCreateImplementationTask = useCallback(async () => { + if (!canCreateImplementationPr || isCreatingPr) return; fireDetailAction("create_pr"); - navigateToTaskInput({ - initialPrompt: `Act on this signal report. Investigate the root cause, implement the fix, and open a PR if appropriate.\n\n${report.summary ?? ""}`, - initialCloudRepository: effectiveCloudRepository ?? undefined, - reportAssociation: { - reportId: report.id, - title: report.title ?? "Untitled signal", - }, - }); + await createPrReport(); }, [ canCreateImplementationPr, - navigateToTaskInput, - effectiveCloudRepository, - report, + isCreatingPr, + createPrReport, fireDetailAction, ]); @@ -614,9 +611,10 @@ export function ReportDetailPane({ size="1" variant="solid" className="gap-1 text-[12px]" + disabled={isCreatingPr} onClick={handleCreateImplementationTask} > - + {isCreatingPr ? : } Create PR From c40ada8024bfe0f0109556e491d84d30f7957266 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Tue, 26 May 2026 19:09:38 +0200 Subject: [PATCH 08/13] feat(code): Auto-open sources dialog when leaving Inbox onboarding Drop the sticky-within-visit + persistent tooltip behavior. The full-page InboxSetupPane shows only while !hasReports && !hasSignalSources; the instant that condition turns false (user enables their first source), the InboxSourcesDialog modal pops automatically so the user can keep configuring with the inbox visible underneath. Removes the now-unused inboxOnboardingStore and the configSourcesTooltipOpen plumbing on SignalsToolbar. Generated-By: PostHog Code Task-Id: 3f6eaf93-f574-44d9-aa91-bc4a7b8a68b9 --- .../inbox/components/InboxSignalsTab.tsx | 47 ++++--------------- .../inbox/components/list/SignalsToolbar.tsx | 43 ++++------------- .../inbox/stores/inboxOnboardingStore.ts | 27 ----------- 3 files changed, 19 insertions(+), 98 deletions(-) delete mode 100644 apps/code/src/renderer/features/inbox/stores/inboxOnboardingStore.ts diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index 9009c917b..7d7baa820 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -21,7 +21,6 @@ import { } from "@features/inbox/hooks/useInboxReports"; import { useSeedSuggestedReviewerFilter } from "@features/inbox/hooks/useSeedSuggestedReviewerFilter"; import { useSignalSourceConfigs } from "@features/inbox/hooks/useSignalSourceConfigs"; -import { useInboxOnboardingStore } from "@features/inbox/stores/inboxOnboardingStore"; import { useInboxReportSelectionStore } from "@features/inbox/stores/inboxReportSelectionStore"; import { useInboxSignalsFilterStore } from "@features/inbox/stores/inboxSignalsFilterStore"; import { useInboxSignalsSidebarStore } from "@features/inbox/stores/inboxSignalsSidebarStore"; @@ -478,15 +477,7 @@ export function InboxSignalsTab() { statusFilter.length < 5; // Onboarding wins over two-pane even if the user has suggested setup tasks — // discovered tasks alone shouldn't push a source-less user past the inline setup. - const onboardingShouldShow = !hasReports && !hasSignalSources; - // Sticky within an inbox visit: once we've entered onboarding, keep showing - // it even after the user toggles a source on. The whole visit is the unit; - // the ref resets naturally when InboxView unmounts on navigation away. - const onboardingStickyRef = useRef(false); - if (onboardingShouldShow) { - onboardingStickyRef.current = true; - } - const showInboxOnboarding = onboardingStickyRef.current; + const showInboxOnboarding = !hasReports && !hasSignalSources; const shouldShowTwoPane = !showInboxOnboarding && (hasReports || @@ -502,34 +493,16 @@ export function InboxSignalsTab() { } const showTwoPaneLayout = hasMountedTwoPaneRef.current; - // ── Onboarding-seen + config-tooltip persistence ─────────────────────── - const hasSeenOnboarding = useInboxOnboardingStore((s) => s.hasSeenOnboarding); - const hasDismissedConfigTooltip = useInboxOnboardingStore( - (s) => s.hasDismissedConfigTooltip, - ); - const markOnboardingSeen = useInboxOnboardingStore( - (s) => s.markOnboardingSeen, - ); - const dismissConfigTooltip = useInboxOnboardingStore( - (s) => s.dismissConfigTooltip, - ); - + // When the user transitions out of the full-page onboarding within a single + // inbox visit (i.e. they just enabled their first source), pop the sources + // dialog so they can keep configuring with the inbox visible underneath. + const wasInOnboardingRef = useRef(showInboxOnboarding); useEffect(() => { - if (showInboxOnboarding && !hasSeenOnboarding) { - markOnboardingSeen(); + if (wasInOnboardingRef.current && !showInboxOnboarding) { + setSourcesDialogOpen(true); } - }, [showInboxOnboarding, hasSeenOnboarding, markOnboardingSeen]); - - useEffect(() => { - if (sourcesDialogOpen && !hasDismissedConfigTooltip) { - dismissConfigTooltip(); - } - }, [sourcesDialogOpen, hasDismissedConfigTooltip, dismissConfigTooltip]); - - // Tooltip rides on the Configure sources toolbar button, but only outside - // the onboarding view (which doesn't render that button at all). - const showConfigSourcesTooltip = - hasSeenOnboarding && !hasDismissedConfigTooltip && !showInboxOnboarding; + wasInOnboardingRef.current = showInboxOnboarding; + }, [showInboxOnboarding, setSourcesDialogOpen]); // ── Inbox viewed analytics — fire once per visit when data settles ───── const inboxViewedFiredRef = useRef(false); @@ -806,7 +779,6 @@ export function InboxSignalsTab() { effectiveBulkIds={selectedReportIds} onToggleSelectAll={handleToggleSelectAll} onConfigureSources={() => setSourcesDialogOpen(true)} - configSourcesTooltipOpen={showConfigSourcesTooltip} onOpenDismissDialog={openDismissDialogFromToolbar} isDismissMutationPending={dismissMutationPending} onReportAction={tracker.signalAction} @@ -892,7 +864,6 @@ export function InboxSignalsTab() { searchDisabledReason={searchDisabledReason} hideFilters onConfigureSources={() => setSourcesDialogOpen(true)} - configSourcesTooltipOpen={showConfigSourcesTooltip} /> diff --git a/apps/code/src/renderer/features/inbox/components/list/SignalsToolbar.tsx b/apps/code/src/renderer/features/inbox/components/list/SignalsToolbar.tsx index 3c8263d54..1304ea04d 100644 --- a/apps/code/src/renderer/features/inbox/components/list/SignalsToolbar.tsx +++ b/apps/code/src/renderer/features/inbox/components/list/SignalsToolbar.tsx @@ -54,12 +54,6 @@ interface SignalsToolbarProps { onToggleSelectAll?: (checked: boolean) => void; /** Called when the "Configure sources" button is clicked. */ onConfigureSources?: () => void; - /** - * When true, displays a persistent tooltip on the "Configure sources" - * button — used after first onboarding to teach the user where the rest - * of the configuration lives. The parent controls dismissal. - */ - configSourcesTooltipOpen?: boolean; /** * Opens the dismiss flow: exactly one report selected (snooze or permanent suppress, with a reason). * With 2+ reports selected, use the Snooze and Suppress toolbar actions instead. @@ -270,7 +264,6 @@ export function SignalsToolbar({ effectiveBulkIds = [], onToggleSelectAll, onConfigureSources, - configSourcesTooltipOpen, onOpenDismissDialog, isDismissMutationPending = false, onReportAction, @@ -505,32 +498,16 @@ export function SignalsToolbar({ ) : null} - {onConfigureSources - ? (() => { - const configureSourcesButton = ( - - ); - return configSourcesTooltipOpen ? ( - - {configureSourcesButton} - - ) : ( - configureSourcesButton - ); - })() - : null} + {onConfigureSources ? ( + + ) : null} diff --git a/apps/code/src/renderer/features/inbox/stores/inboxOnboardingStore.ts b/apps/code/src/renderer/features/inbox/stores/inboxOnboardingStore.ts deleted file mode 100644 index 8b1b59546..000000000 --- a/apps/code/src/renderer/features/inbox/stores/inboxOnboardingStore.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { create } from "zustand"; -import { persist } from "zustand/middleware"; - -interface InboxOnboardingState { - /** Latched once the inline InboxSetupPane has rendered at least once. */ - hasSeenOnboarding: boolean; - /** - * Latched once the user has opened the Configure sources dialog — auto or - * manually. Permanently suppresses the "All of your Inbox configuration is - * here" tooltip on the toolbar. - */ - hasDismissedConfigTooltip: boolean; - markOnboardingSeen: () => void; - dismissConfigTooltip: () => void; -} - -export const useInboxOnboardingStore = create()( - persist( - (set) => ({ - hasSeenOnboarding: false, - hasDismissedConfigTooltip: false, - markOnboardingSeen: () => set({ hasSeenOnboarding: true }), - dismissConfigTooltip: () => set({ hasDismissedConfigTooltip: true }), - }), - { name: "inbox-onboarding-storage" }, - ), -); From 7a5dbc55d80440274c771b59d7518f17e76e4e58 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Tue, 26 May 2026 20:08:27 +0200 Subject: [PATCH 09/13] feat(code): Restore sticky Inbox onboarding with "Proceed to Inbox" escape hatch Once a user enters the Inbox onboarding view in a session, keep them there until they either navigate away (natural unmount reset) or click the new "Proceed to Inbox" button in the bottom-right of the setup pane. Replaces the auto-pop-the-sources-dialog behavior. Generated-By: PostHog Code Task-Id: 3f6eaf93-f574-44d9-aa91-bc4a7b8a68b9 --- .../inbox/components/InboxSignalsTab.tsx | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index 7d7baa820..3bda045de 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -42,7 +42,8 @@ import { useIntegrations, useRepositoryIntegration, } from "@hooks/useIntegrations"; -import { Box, Flex, ScrollArea } from "@radix-ui/themes"; +import { ArrowRightIcon } from "@phosphor-icons/react"; +import { Box, Button, Flex, ScrollArea } from "@radix-ui/themes"; import { isDismissalReasonSnooze } from "@shared/dismissalReasons"; import type { SignalReport, SignalReportsQueryParams } from "@shared/types"; import { ANALYTICS_EVENTS } from "@shared/types/analytics"; @@ -477,7 +478,17 @@ export function InboxSignalsTab() { statusFilter.length < 5; // Onboarding wins over two-pane even if the user has suggested setup tasks — // discovered tasks alone shouldn't push a source-less user past the inline setup. - const showInboxOnboarding = !hasReports && !hasSignalSources; + const onboardingShouldShow = !hasReports && !hasSignalSources; + // Sticky within an inbox visit: once we've entered onboarding, keep showing + // it even after the user toggles a source on, until either they explicitly + // click "Proceed to Inbox" or navigate away (unmount resets the ref). + const enteredOnboardingRef = useRef(false); + if (onboardingShouldShow) { + enteredOnboardingRef.current = true; + } + const [userExitedOnboarding, setUserExitedOnboarding] = useState(false); + const showInboxOnboarding = + enteredOnboardingRef.current && !userExitedOnboarding; const shouldShowTwoPane = !showInboxOnboarding && (hasReports || @@ -493,17 +504,6 @@ export function InboxSignalsTab() { } const showTwoPaneLayout = hasMountedTwoPaneRef.current; - // When the user transitions out of the full-page onboarding within a single - // inbox visit (i.e. they just enabled their first source), pop the sources - // dialog so they can keep configuring with the inbox visible underneath. - const wasInOnboardingRef = useRef(showInboxOnboarding); - useEffect(() => { - if (wasInOnboardingRef.current && !showInboxOnboarding) { - setSourcesDialogOpen(true); - } - wasInOnboardingRef.current = showInboxOnboarding; - }, [showInboxOnboarding, setSourcesDialogOpen]); - // ── Inbox viewed analytics — fire once per visit when data settles ───── const inboxViewedFiredRef = useRef(false); useEffect(() => { @@ -704,10 +704,24 @@ export function InboxSignalsTab() { {showInboxOnboarding ? ( /* ── Inline setup pane for users with no sources configured ── The toolbar (report counter, search, bulk actions) is suppressed - entirely — none of it is meaningful before any source is configured. */ - - - + entirely — none of it is meaningful before any source is configured. + Sticky within the visit: stays until the user clicks "Proceed to + Inbox" or navigates away. */ + + + + + + + + ) : showTwoPaneLayout ? ( {/* ── Left pane: report list ───────────────────────────────── */} From e67590e4a563a7ffb5ca02d938153ba608f6fca1 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Tue, 26 May 2026 20:12:05 +0200 Subject: [PATCH 10/13] refactor(code): Move "Proceed to Inbox" into the setup pane column, gate on source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The escape-hatch button now lives at the bottom of the InboxSetupPane flex column (right-aligned) instead of an absolute-positioned overlay, and it's disabled until at least one signal source is enabled — with a "Disabled because Enable at least one source first" tooltip via the shared Button's disabledReason mechanism. Generated-By: PostHog Code Task-Id: 3f6eaf93-f574-44d9-aa91-bc4a7b8a68b9 --- .../inbox/components/InboxSetupPane.tsx | 27 ++++++++++++++++++- .../inbox/components/InboxSignalsTab.tsx | 26 ++++++------------ 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/InboxSetupPane.tsx b/apps/code/src/renderer/features/inbox/components/InboxSetupPane.tsx index ed6e76cf9..11acbc550 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSetupPane.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSetupPane.tsx @@ -1,10 +1,20 @@ +import { Button } from "@components/ui/Button"; import { OnboardingHogTip } from "@features/onboarding/components/OnboardingHogTip"; import { SignalSourcesSettings } from "@features/settings/components/sections/SignalSourcesSettings"; +import { ArrowRightIcon } from "@phosphor-icons/react"; import { Flex, Text } from "@radix-ui/themes"; import detectiveHog from "@renderer/assets/images/hedgehogs/detective-hog.png"; import { motion } from "framer-motion"; -export function InboxSetupPane() { +interface InboxSetupPaneProps { + hasSignalSources: boolean; + onProceedToInbox: () => void; +} + +export function InboxSetupPane({ + hasSignalSources, + onProceedToInbox, +}: InboxSetupPaneProps) { return ( + + + + ); diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index 3bda045de..66487632c 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -42,8 +42,7 @@ import { useIntegrations, useRepositoryIntegration, } from "@hooks/useIntegrations"; -import { ArrowRightIcon } from "@phosphor-icons/react"; -import { Box, Button, Flex, ScrollArea } from "@radix-ui/themes"; +import { Box, Flex, ScrollArea } from "@radix-ui/themes"; import { isDismissalReasonSnooze } from "@shared/dismissalReasons"; import type { SignalReport, SignalReportsQueryParams } from "@shared/types"; import { ANALYTICS_EVENTS } from "@shared/types/analytics"; @@ -706,22 +705,13 @@ export function InboxSignalsTab() { The toolbar (report counter, search, bulk actions) is suppressed entirely — none of it is meaningful before any source is configured. Sticky within the visit: stays until the user clicks "Proceed to - Inbox" or navigates away. */ - - - - - - - - + Inbox" inside the pane or navigates away. */ + + setUserExitedOnboarding(true)} + /> + ) : showTwoPaneLayout ? ( {/* ── Left pane: report list ───────────────────────────────── */} From 667013e4d1a9d7765116af7a644f525a2749ce64 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Tue, 26 May 2026 20:13:14 +0200 Subject: [PATCH 11/13] style(code): Use plain // comments in Inbox onboarding render branches Generated-By: PostHog Code Task-Id: 3f6eaf93-f574-44d9-aa91-bc4a7b8a68b9 --- .../features/inbox/components/InboxSignalsTab.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index 66487632c..57f2e8094 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -701,11 +701,11 @@ export function InboxSignalsTab() { return ( <> {showInboxOnboarding ? ( - /* ── Inline setup pane for users with no sources configured ── - The toolbar (report counter, search, bulk actions) is suppressed - entirely — none of it is meaningful before any source is configured. - Sticky within the visit: stays until the user clicks "Proceed to - Inbox" inside the pane or navigates away. */ + // Inline setup pane for users with no sources configured. + // The toolbar (report counter, search, bulk actions) is suppressed + // entirely — none of it is meaningful before any source is configured. + // Sticky within the visit: stays until the user clicks "Proceed to + // Inbox" inside the pane or navigates away. ) : ( - /* ── Full-width warming-up state with skeleton backdrop ──────── */ + // Full-width warming-up state with skeleton backdrop Date: Tue, 26 May 2026 20:15:28 +0200 Subject: [PATCH 12/13] Use Quill buttons --- .../inbox/components/DataSourceSetup.tsx | 59 +++++++++++++++---- .../inbox/components/InboxSetupPane.tsx | 44 +++++++------- .../inbox/components/SignalSourceToggles.tsx | 19 +++--- .../sections/GitHubIntegrationSection.tsx | 35 ++++++----- .../SignalSlackNotificationsSettings.tsx | 21 +++---- 5 files changed, 109 insertions(+), 69 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/DataSourceSetup.tsx b/apps/code/src/renderer/features/inbox/components/DataSourceSetup.tsx index 406781f68..3f7dfeb9e 100644 --- a/apps/code/src/renderer/features/inbox/components/DataSourceSetup.tsx +++ b/apps/code/src/renderer/features/inbox/components/DataSourceSetup.tsx @@ -9,7 +9,8 @@ import { useGithubRepositories, useRepositoryIntegration, } from "@hooks/useIntegrations"; -import { Box, Button, Flex, Text, TextField } from "@radix-ui/themes"; +import { Button } from "@posthog/quill"; +import { Box, Flex, Text, TextField } from "@radix-ui/themes"; import { trpcClient } from "@renderer/trpc"; import { useCallback, useEffect, useRef, useState } from "react"; import { toast } from "sonner"; @@ -185,11 +186,18 @@ function GitHubSetup({ onComplete, onCancel }: SetupFormProps) { {statusMessage} - + {hasSignalSources ? ( + + ) : ( + + + + + + )} diff --git a/apps/code/src/renderer/features/inbox/components/SignalSourceToggles.tsx b/apps/code/src/renderer/features/inbox/components/SignalSourceToggles.tsx index f727f2308..f295ed602 100644 --- a/apps/code/src/renderer/features/inbox/components/SignalSourceToggles.tsx +++ b/apps/code/src/renderer/features/inbox/components/SignalSourceToggles.tsx @@ -11,15 +11,8 @@ import { TicketIcon, VideoIcon, } from "@phosphor-icons/react"; -import { - Box, - Button, - Flex, - Spinner, - Switch, - Text, - Tooltip, -} from "@radix-ui/themes"; +import { Button } from "@posthog/quill"; +import { Box, Flex, Spinner, Switch, Text, Tooltip } from "@radix-ui/themes"; import type { SignalSourceConfig } from "@renderer/api/posthogClient"; import { memo, useCallback } from "react"; @@ -140,7 +133,9 @@ const SignalSourceToggleCard = memo(function SignalSourceToggleCard({ ) : requiresSetup ? ( - ) : ( - )} ); diff --git a/apps/code/src/renderer/features/settings/components/sections/SignalSlackNotificationsSettings.tsx b/apps/code/src/renderer/features/settings/components/sections/SignalSlackNotificationsSettings.tsx index b18563f26..7b0c6f297 100644 --- a/apps/code/src/renderer/features/settings/components/sections/SignalSlackNotificationsSettings.tsx +++ b/apps/code/src/renderer/features/settings/components/sections/SignalSlackNotificationsSettings.tsx @@ -7,6 +7,7 @@ import { SettingsOptionSelect } from "@features/settings/components/SettingsOpti import { useDebouncedValue } from "@hooks/useDebouncedValue"; import { CaretDown, Hash, Lock } from "@phosphor-icons/react"; import { + Button, Combobox, ComboboxContent, ComboboxEmpty, @@ -14,9 +15,8 @@ import { ComboboxItem, ComboboxList, ComboboxTrigger, - Button as QuillButton, } from "@posthog/quill"; -import { Button, Callout, Flex, Text } from "@radix-ui/themes"; +import { Callout, Flex, Text } from "@radix-ui/themes"; import type { SignalReportPriority, SlackChannelOption } from "@shared/types"; import { useMemo, useRef, useState } from "react"; @@ -175,8 +175,9 @@ export function SignalSlackNotificationsSettings({ } /> {channelComboboxModal ? ( From 97816ecb03d4c2455d0de49e2d075a999ebb8253 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Tue, 26 May 2026 20:22:47 +0200 Subject: [PATCH 13/13] Do en-dash --- .../settings/components/sections/SignalSourcesSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/code/src/renderer/features/settings/components/sections/SignalSourcesSettings.tsx b/apps/code/src/renderer/features/settings/components/sections/SignalSourcesSettings.tsx index 09552a568..b5034eab8 100644 --- a/apps/code/src/renderer/features/settings/components/sections/SignalSourcesSettings.tsx +++ b/apps/code/src/renderer/features/settings/components/sections/SignalSourcesSettings.tsx @@ -19,7 +19,7 @@ const PRIORITY_OPTIONS: { value: SignalReportPriority; label: string }[] = [ const NEVER_VALUE = "__never__"; const USER_PRIORITY_OPTIONS: { value: string; label: string }[] = [ - { value: NEVER_VALUE, label: "Never — opt out of auto-assigned tasks" }, + { value: NEVER_VALUE, label: "Never – opt out of auto-assigned tasks" }, ...PRIORITY_OPTIONS, ];