Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Tooltip } from "@components/ui/Tooltip";
import { CHAT_CONTENT_MAX_WIDTH } from "@features/sessions/constants";
import type { IconProps } from "@phosphor-icons/react";
import {
Expand All @@ -22,7 +23,10 @@ import { Badge, Box, Flex, Text } from "@radix-ui/themes";
import type { AcpMessage } from "@shared/types/session-events";
import { openUrlInBrowser } from "@utils/browser";
import { type ComponentType, useMemo } from "react";
import { accumulateSessionResources } from "./accumulateSessionResources";
import {
accumulateSessionResources,
type ResourceProduct,
} from "./accumulateSessionResources";

/**
* Icon per PostHog product. `Record<PostHogProductId, …>` keeps this exhaustive:
Expand All @@ -49,8 +53,11 @@ const PRODUCT_ICON: Record<PostHogProductId, ComponentType<IconProps>> = {
/**
* Docs page on posthog.com per product, so a chip links to the relevant
* product docs. `Partial` on purpose — products without a dedicated docs page
* (e.g. apm, which PostHog folds into LLM analytics / Logs) render as a plain,
* non-clickable badge rather than linking somewhere misleading.
* render as a plain, non-clickable badge rather than linking somewhere
* misleading. Deliberately excluded:
* - `code`: this chip means "the agent read files from your repository", not a
* PostHog data product, so it must not link to the /code marketing page.
* - `apm`: PostHog folds APM into LLM analytics / Logs, no standalone page.
*/
const PRODUCT_DOC_URL: Partial<Record<PostHogProductId, string>> = {
product_analytics: "https://posthog.com/docs/product-analytics",
Expand All @@ -65,20 +72,60 @@ const PRODUCT_DOC_URL: Partial<Record<PostHogProductId, string>> = {
cdp: "https://posthog.com/docs/cdp",
logs: "https://posthog.com/docs/logs",
sql: "https://posthog.com/docs/sql",
code: "https://posthog.com/code",
posthog: "https://posthog.com/docs",
};

/**
* Per-product hover explanation. For products that link to docs the default
* "Open … docs" is enough; the entries here override that for chips whose
* meaning isn't obvious from the label alone.
*/
const PRODUCT_TOOLTIP: Partial<Record<PostHogProductId, string>> = {
code: "PostHog Code read files from your repository this session",
};

interface SessionResourcesBarProps {
events: AcpMessage[];
}

/**
* A single product chip. Clickable chips (those with a docs page) open it on
* click; chips get a tooltip only when it adds something beyond the label —
* a per-product explanation or an "open docs" hint — otherwise they render
* bare (e.g. apm).
*/
function ResourceChip({ id, label }: ResourceProduct) {
const Icon = PRODUCT_ICON[id] ?? SparkleIcon;
const docUrl = PRODUCT_DOC_URL[id];
const tooltip =
PRODUCT_TOOLTIP[id] ?? (docUrl ? `Open ${label} docs` : undefined);

const badge = (
<Badge
size="1"
color="gray"
variant="soft"
className={docUrl ? "cursor-pointer hover:bg-gray-4" : undefined}
onClick={docUrl ? () => void openUrlInBrowser(docUrl) : undefined}
>
<Icon size={12} />
{label}
</Badge>
);

if (!tooltip) return badge;
return <Tooltip content={tooltip}>{badge}</Tooltip>;
}

/**
* Persistent bar above the composer listing the PostHog products the agent has
* touched so far this session — via the MCP `exec` tool, or by reading a file
* from the codebase (the "Code" chip). Each product appears once and is added
* the moment it's first used. Hidden until at least one product has been used.
* Mirrors PlanStatusBar's placement and styling.
* drawn on so far this session — via the MCP `exec` tool, or by reading a file
* from the codebase (the "Code" chip). It's a transparency hint: at a glance
* you can see which parts of PostHog grounded the answer. Each product appears
* once and is added the moment it's first used. Chips that map to a product
* docs page open it on click; others (e.g. "Code") are informational only.
* Hidden until at least one product has been used. Mirrors PlanStatusBar's
* placement and styling.
*/
export function SessionResourcesBar({ events }: SessionResourcesBarProps) {
const products = useMemo(() => accumulateSessionResources(events), [events]);
Expand All @@ -89,31 +136,17 @@ export function SessionResourcesBar({ events }: SessionResourcesBarProps) {
<Box className="mb-3">
<Box className="mx-auto" style={{ maxWidth: CHAT_CONTENT_MAX_WIDTH }}>
<Flex align="center" gap="2" wrap="wrap" className="px-3 pt-2">
<Text color="gray" className="whitespace-nowrap text-[12px]">
PostHog resources used
</Text>
{products.map((product) => {
const Icon = PRODUCT_ICON[product.id] ?? SparkleIcon;
const docUrl = PRODUCT_DOC_URL[product.id];
return (
<Badge
key={product.id}
size="1"
color="gray"
variant="soft"
className={
docUrl ? "cursor-pointer hover:bg-gray-4" : undefined
}
onClick={
docUrl ? () => void openUrlInBrowser(docUrl) : undefined
}
title={docUrl ? `Open ${product.label} docs` : undefined}
>
<Icon size={12} />
{product.label}
</Badge>
);
})}
<Tooltip content="PostHog products the agent drew on while working on this session. Click a product to open its docs.">
<Text
color="gray"
className="cursor-default whitespace-nowrap text-[12px]"
>
PostHog products used
</Text>
</Tooltip>
{products.map((product) => (
<ResourceChip key={product.id} {...product} />
))}
</Flex>
</Box>
</Box>
Expand Down
Loading