@photon-ai/pho-ui 2.13.0 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/chunks/IconChevronRight-CzoJnYNI.js +11 -0
  2. package/dist/chunks/IconChevronRight-CzoJnYNI.js.map +1 -0
  3. package/dist/chunks/{basic-page-CJwG4I83.js → basic-page-CVZT_zaH.js} +619 -614
  4. package/dist/chunks/basic-page-CVZT_zaH.js.map +1 -0
  5. package/dist/chunks/code-block-CdozzaHM.js +139 -0
  6. package/dist/chunks/code-block-CdozzaHM.js.map +1 -0
  7. package/dist/chunks/combobox-D4LiGJB2.js +136 -0
  8. package/dist/chunks/combobox-D4LiGJB2.js.map +1 -0
  9. package/dist/chunks/command-Ck8IAgV7.js +192 -0
  10. package/dist/chunks/command-Ck8IAgV7.js.map +1 -0
  11. package/dist/chunks/copy-button-B_B15mEi.js +56 -0
  12. package/dist/chunks/copy-button-B_B15mEi.js.map +1 -0
  13. package/dist/chunks/description-list-C-mwe4ki.js +59 -0
  14. package/dist/chunks/description-list-C-mwe4ki.js.map +1 -0
  15. package/dist/chunks/filter-bar-Cjjq38xo.js +259 -0
  16. package/dist/chunks/filter-bar-Cjjq38xo.js.map +1 -0
  17. package/dist/chunks/log-list-0AXnjFrk.js +244 -0
  18. package/dist/chunks/log-list-0AXnjFrk.js.map +1 -0
  19. package/dist/chunks/toggle-o84tawXy.js +33 -0
  20. package/dist/chunks/toggle-o84tawXy.js.map +1 -0
  21. package/dist/chunks/tooltip-BTF7GChl.js +32 -0
  22. package/dist/chunks/tooltip-BTF7GChl.js.map +1 -0
  23. package/dist/chunks/trace-CbH0IohX.js +314 -0
  24. package/dist/chunks/trace-CbH0IohX.js.map +1 -0
  25. package/dist/chunks/use-copy-D6JwKUll.js +25 -0
  26. package/dist/chunks/use-copy-D6JwKUll.js.map +1 -0
  27. package/dist/components/code-block.js +6 -0
  28. package/dist/components/combobox.js +1 -1
  29. package/dist/components/command.js +3 -189
  30. package/dist/components/copy-button.js +6 -0
  31. package/dist/components/description-list.js +5 -0
  32. package/dist/components/filter-bar.js +8 -0
  33. package/dist/components/log-list.js +8 -0
  34. package/dist/components/phone-input.js +1 -1
  35. package/dist/components/toggle.js +3 -30
  36. package/dist/components/tooltip.js +2 -29
  37. package/dist/components/trace.js +9 -0
  38. package/dist/index.js +122 -96
  39. package/dist/primitives.js +2 -2
  40. package/dist/sections/basic-page.js +17 -17
  41. package/dist/src/components/accordion/index.d.ts +1 -1
  42. package/dist/src/components/code-block/code-block.d.ts +43 -0
  43. package/dist/src/components/code-block/index.d.ts +2 -0
  44. package/dist/src/components/code-block/tokenize-json.d.ts +12 -0
  45. package/dist/src/components/combobox/combobox.d.ts +30 -4
  46. package/dist/src/components/combobox/index.d.ts +1 -1
  47. package/dist/src/components/copy-button/copy-button.d.ts +49 -0
  48. package/dist/src/components/copy-button/index.d.ts +1 -0
  49. package/dist/src/components/description-list/description-list.d.ts +60 -0
  50. package/dist/src/components/description-list/index.d.ts +1 -0
  51. package/dist/src/components/filter-bar/filter-bar.d.ts +101 -0
  52. package/dist/src/components/filter-bar/index.d.ts +1 -0
  53. package/dist/src/components/log-list/index.d.ts +1 -0
  54. package/dist/src/components/log-list/log-list.d.ts +136 -0
  55. package/dist/src/components/trace/index.d.ts +2 -0
  56. package/dist/src/components/trace/layout.d.ts +48 -0
  57. package/dist/src/components/trace/trace.d.ts +53 -0
  58. package/dist/src/index.d.ts +6 -0
  59. package/dist/src/sections/basic-page/basic-page.d.ts +34 -24
  60. package/dist/src/utils/index.d.ts +1 -0
  61. package/dist/src/utils/use-copy.d.ts +14 -0
  62. package/dist/utils.js +6 -3
  63. package/package.json +25 -1
  64. package/src/styles/theme.css +30 -0
  65. package/dist/chunks/basic-page-CJwG4I83.js.map +0 -1
  66. package/dist/chunks/combobox-Dj5m_dDd.js +0 -117
  67. package/dist/chunks/combobox-Dj5m_dDd.js.map +0 -1
  68. package/dist/components/command.js.map +0 -1
  69. package/dist/components/toggle.js.map +0 -1
  70. package/dist/components/tooltip.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-list-0AXnjFrk.js","names":[],"sources":["../../src/components/log-list/log-list.tsx"],"sourcesContent":["import {\n useEffect,\n useId,\n useRef,\n type ComponentProps,\n type MouseEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { Badge, type BadgeColor, type BadgeSize } from \"../badge\";\nimport { Button } from \"../button\";\nimport { Skeleton } from \"../skeleton\";\nimport { Spinner } from \"../spinner\";\nimport { StatusView } from \"../status-view\";\nimport { Tooltip } from \"../tooltip\";\nimport { cn } from \"../../utils/cn\";\nimport { useCopy } from \"../../utils/use-copy\";\nimport { useLinkComponent } from \"../../utils/link-provider\";\n\n/**\n * Log List — rows of requests, deliveries, or messages, newest first,\n * each one line: a mono id you can copy, a status, the method, what\n * happened, and how long ago. Reach for it where a `DataTable` would be\n * columns of the same few fields: a log reads left to right as one line,\n * and the page filters it from a `FilterBar` pinned above.\n *\n * Design notes:\n * - The surface is `BasicPage.Card`'s: hairline border, `base` radius,\n * hairline rules between rows. It sits in a `Stack` as any card does.\n * - The row is one link (or one button). It stretches over the row from\n * the label, the way a `DataTable` row's does, so the whole row is one\n * pointer target and one tab stop; the copy control sits above it.\n * - The id truncates in the middle, keeping the prefix that says what it\n * is and the tail that tells it apart. The full id is a tooltip away,\n * and one click copies it.\n * - `LoadMore` is the sentinel at the end: it asks for the next page as\n * it scrolls into view, and offers a button for keyboards and for\n * browsers without an observer.\n */\n\n/** Same chrome as `BasicPage.Card`. */\nconst LOG_LIST_SURFACE =\n \"rounded-base border border-pho-secondary bg-pho-primary divide-y divide-pho-secondary\";\n\n/** Middle truncation: the prefix and this many after it, then the tail. */\nconst ID_HEAD = 12;\nconst ID_TAIL = 5;\n\n/**\n * Keep the start and the end of an id and fold the middle into an\n * ellipsis — `pho_req_01m04axjqrfzxv4xjet40a84gb` reads\n * `pho_req_01m0…a84gb`. Ids that fit are returned whole.\n */\nexport function truncateMiddle(\n value: string,\n head = ID_HEAD,\n tail = ID_TAIL,\n): string {\n if (value.length <= head + tail + 1) return value;\n return `${value.slice(0, head)}…${value.slice(-tail)}`;\n}\n\n/* -------------------------------- status -------------------------------- */\n\nconst SUCCESS_WORDS = new Set([\n \"success\",\n \"succeeded\",\n \"successful\",\n \"delivered\",\n \"ok\",\n \"sent\",\n \"received\",\n \"completed\",\n \"complete\",\n \"done\",\n \"active\",\n]);\nconst ERROR_WORDS = new Set([\n \"failed\",\n \"failure\",\n \"error\",\n \"errored\",\n \"rejected\",\n \"undelivered\",\n \"timeout\",\n \"timed_out\",\n \"timed out\",\n \"canceled\",\n \"cancelled\",\n]);\nconst WARNING_WORDS = new Set([\n \"pending\",\n \"queued\",\n \"retrying\",\n \"processing\",\n \"sending\",\n \"in_progress\",\n \"in progress\",\n \"scheduled\",\n \"waiting\",\n]);\n\n/**\n * The `Badge` color for a log status: an HTTP status code (2xx success,\n * 3xx gray, 4xx warning, 5xx error) or a word (`delivered` / `failed` /\n * `pending`). Anything else is gray.\n */\nexport function logStatusColor(status: number | string): BadgeColor {\n if (typeof status === \"number\") {\n if (status < 300) return \"success\";\n if (status < 400) return \"gray\";\n if (status < 500) return \"warning\";\n return \"error\";\n }\n const word = status.trim().toLowerCase();\n if (/^\\d{3}$/.test(word)) return logStatusColor(Number(word));\n if (SUCCESS_WORDS.has(word)) return \"success\";\n if (ERROR_WORDS.has(word)) return \"error\";\n if (WARNING_WORDS.has(word)) return \"warning\";\n return \"gray\";\n}\n\n/** A status word as a label: `timed_out` reads `Timed out`. */\nexport function logStatusLabel(status: number | string): string {\n if (typeof status === \"number\") return String(status);\n const word = status.trim().replace(/_/g, \" \");\n return word.charAt(0).toUpperCase() + word.slice(1);\n}\n\nexport interface LogStatusProps extends Omit<\n ComponentProps<\"span\">,\n \"children\" | \"color\"\n> {\n /** An HTTP status code or a status word. */\n value: number | string;\n /** Override the derived word. */\n label?: ReactNode;\n /** Badge size. @default \"sm\" */\n size?: BadgeSize;\n}\n\n/** The status slot — a low-contrast `Badge` colored by `logStatusColor`. */\nfunction Status({\n value,\n label,\n size = \"sm\",\n className,\n ...props\n}: LogStatusProps) {\n return (\n <Badge\n color={logStatusColor(value)}\n contrast=\"low\"\n size={size}\n className={cn(\"tabular-nums\", className)}\n {...props}\n >\n {label ?? logStatusLabel(value)}\n </Badge>\n );\n}\n\n/** The method slot — `POST`, `GET` as a quiet mono chip. */\nfunction Method({\n className,\n ...props\n}: ComponentProps<\"span\"> & { ref?: Ref<HTMLSpanElement> }) {\n return (\n <span\n className={cn(\n \"bg-pho-secondary text-pho-secondary inline-flex h-5 min-w-12 shrink-0 items-center justify-center rounded-sm px-1.5 font-mono text-xs font-medium whitespace-nowrap\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/* ---------------------------------- id ---------------------------------- */\n\n/**\n * The id cell: a mono button that copies the full id, with a tooltip\n * holding the whole of it. The cell is a fixed width in characters so\n * ids line up down the list.\n */\nfunction IdCell({ id, cellId }: { id: string; cellId: string }) {\n const { copied, copy } = useCopy();\n return (\n <Tooltip.Root>\n <Tooltip.Trigger\n id={cellId}\n aria-label={copied ? `${id} copied` : `Copy ${id}`}\n data-copied={copied || undefined}\n onClick={() => void copy(id)}\n className={cn(\n \"text-pho-secondary outline-pho-brand hover:text-pho-primary relative z-10 -mx-1 w-[calc(18ch+0.5rem)] shrink-0 cursor-pointer truncate rounded-sm px-1 py-0.5 text-left font-mono text-xs transition-colors duration-100 ease-linear focus-visible:outline-2\",\n \"data-[copied]:text-pho-success\",\n )}\n >\n {copied ? \"Copied\" : truncateMiddle(id)}\n </Tooltip.Trigger>\n <output className=\"sr-only\" aria-live=\"polite\">\n {copied ? \"Copied\" : \"\"}\n </output>\n <Tooltip.Portal>\n <Tooltip.Positioner>\n <Tooltip.Popup className=\"font-mono\">{id}</Tooltip.Popup>\n </Tooltip.Positioner>\n </Tooltip.Portal>\n </Tooltip.Root>\n );\n}\n\n/* ---------------------------------- row --------------------------------- */\n\nexport interface LogListRowProps extends Omit<\n ComponentProps<\"div\">,\n \"children\" | \"title\" | \"onClick\" | \"id\"\n> {\n /** The record's id — mono, middle-truncated, copied on click. */\n id?: string;\n /** The status slot — usually `LogList.Status`. */\n status?: ReactNode;\n /** The method — `POST`, `GET`; rendered in `LogList.Method`. */\n method?: string;\n /** What happened — the route, the event type, the message. */\n label: ReactNode;\n /** The label in mono — for a route or an event type. */\n code?: boolean;\n /**\n * The line under the label, with real content: who received what from\n * whom, the destination that answered. A sentence, with its period.\n */\n description?: ReactNode;\n /** Trailing: how long it took — \"59 ms\". */\n duration?: ReactNode;\n /** Trailing: when — \"2 min ago\". */\n time?: ReactNode;\n /** Trailing content before the duration and time. */\n children?: ReactNode;\n /** The row is a link to its page, through the host's `LinkProvider`. */\n href?: string;\n /** The row is a button. */\n onClick?: (event: MouseEvent<HTMLElement>) => void;\n /**\n * Replace the row's link element — a router's `Link`, the way Base UI\n * components take `render`. The row's classes and handlers merge in.\n */\n render?: useRender.RenderProp;\n /** The row whose page is open beside the list — it keeps the wash. */\n current?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\nfunction Row({\n id,\n status,\n method,\n label,\n code,\n description,\n duration,\n time,\n children,\n href,\n onClick,\n render,\n current,\n className,\n ...props\n}: LogListRowProps) {\n const Link = useLinkComponent();\n const base = useId();\n const interactive = href != null || onClick != null || render != null;\n const cellIds = {\n id: `${base}-id`,\n status: `${base}-status`,\n method: `${base}-method`,\n meta: `${base}-meta`,\n };\n const hasMeta = children != null || duration != null || time != null;\n const describedBy = [\n id != null && cellIds.id,\n status != null && cellIds.status,\n method != null && cellIds.method,\n hasMeta && cellIds.meta,\n ]\n .filter(Boolean)\n .join(\" \");\n const primary = useRender({\n render: render ?? (href != null ? <Link href={href} /> : undefined),\n defaultTagName: href != null ? \"a\" : onClick != null ? \"button\" : \"span\",\n props: {\n className: cn(\n \"text-pho-primary block min-w-0 max-w-full truncate text-left text-base font-medium outline-none\",\n code && \"font-mono text-xs\",\n // The one link, stretched over the row; the ring is the row's.\n interactive &&\n \"cursor-pointer after:absolute after:inset-0 after:content-['']\",\n ),\n onClick,\n type:\n render == null && href == null && onClick != null\n ? \"button\"\n : undefined,\n \"aria-current\": current ? \"page\" : undefined,\n \"aria-describedby\": describedBy || undefined,\n \"data-log-primary\": \"\",\n children: label,\n },\n });\n return (\n <div\n data-log-row=\"\"\n data-current={current || undefined}\n className={cn(\n \"relative flex min-h-12 min-w-0 items-center gap-3 px-5 py-2.5\",\n interactive &&\n \"hover:bg-pho-primary-hover outline-pho-brand transition-colors has-[[data-log-primary]:focus-visible]:outline-2 has-[[data-log-primary]:focus-visible]:-outline-offset-2\",\n current && \"bg-pho-primary-hover\",\n // The wash follows the card's rounding (minus the 1px border).\n \"first:rounded-t-[calc(var(--radius-base)-1px)] last:rounded-b-[calc(var(--radius-base)-1px)]\",\n className,\n )}\n {...props}\n >\n {id != null ? <IdCell id={id} cellId={cellIds.id} /> : null}\n {status != null ? (\n <span id={cellIds.status} className=\"flex shrink-0 items-center\">\n {status}\n </span>\n ) : null}\n {method != null ? <Method id={cellIds.method}>{method}</Method> : null}\n <div data-log-primary-cell=\"\" className=\"min-w-0 flex-1\">\n {primary}\n {description != null ? (\n <p className=\"text-pho-description truncate text-xs\">{description}</p>\n ) : null}\n </div>\n {hasMeta ? (\n <div\n id={cellIds.meta}\n className=\"text-pho-description ml-auto flex shrink-0 items-center gap-3 text-xs whitespace-nowrap tabular-nums\"\n >\n {children}\n {duration != null ? <span>{duration}</span> : null}\n {time != null ? <span>{time}</span> : null}\n </div>\n ) : null}\n </div>\n );\n}\n\n/* -------------------------------- states -------------------------------- */\n\nexport interface LogListEmptyProps extends Omit<\n ComponentProps<\"div\">,\n \"title\" | \"children\"\n> {\n /** One short line naming the state. @default \"Nothing here yet.\" */\n title?: ReactNode;\n /** What fills it, or why it is empty. */\n description?: ReactNode;\n /** An action, when there is one. */\n children?: ReactNode;\n}\n\n/** The list with no rows — `StatusView` at card size. */\nfunction Empty({\n title = \"Nothing here yet.\",\n description,\n children,\n className,\n ...props\n}: LogListEmptyProps) {\n return (\n <div className={cn(\"flex justify-center\", className)} {...props}>\n <StatusView\n size=\"sm\"\n title={title}\n description={description}\n actions={children}\n />\n </div>\n );\n}\n\n/** Bones in the rows' place while the first page is on its way. */\nfunction Loading({ rows = 5 }: { rows?: number }) {\n return (\n <>\n {Array.from({ length: rows }, (_, index) => (\n <div\n key={index}\n aria-hidden\n className=\"flex min-h-12 items-center gap-3 px-5 py-2.5\"\n >\n <Skeleton className=\"h-3.5 w-32\" />\n <Skeleton className=\"h-5 w-12 rounded-full\" />\n <Skeleton className=\"h-3.5 w-48\" />\n <Skeleton className=\"ml-auto h-3.5 w-16\" />\n </div>\n ))}\n </>\n );\n}\n\nexport interface LogListLoadMoreProps extends Omit<\n ComponentProps<\"div\">,\n \"children\"\n> {\n /** Asked for as the sentinel scrolls into view, and on the button. */\n onLoadMore: () => void;\n /** The next page is on its way — the sentinel shows a spinner and waits. */\n loading?: boolean;\n /** Nothing left to load — renders nothing. @default true */\n hasMore?: boolean;\n /** How far ahead of the viewport to ask. @default \"200px\" */\n rootMargin?: string;\n /** The button's words. @default \"Load more\" */\n label?: ReactNode;\n}\n\n/**\n * The sentinel at the end of the list. An `IntersectionObserver` asks for\n * the next page as it comes into view (`rootMargin` ahead of it); while\n * `loading` it shows a spinner and asks for nothing. The button is the\n * same request for keyboards and for a browser without the observer.\n * SSR-safe: the observer is created in an effect.\n */\nfunction LoadMore({\n onLoadMore,\n loading,\n hasMore = true,\n rootMargin = \"200px\",\n label = \"Load more\",\n className,\n ...props\n}: LogListLoadMoreProps) {\n const ref = useRef<HTMLDivElement | null>(null);\n const latest = useRef(onLoadMore);\n latest.current = onLoadMore;\n useEffect(() => {\n const el = ref.current;\n if (!hasMore || loading || el == null) return;\n if (typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((entry) => entry.isIntersecting)) latest.current();\n },\n { rootMargin },\n );\n observer.observe(el);\n return () => observer.disconnect();\n }, [hasMore, loading, rootMargin]);\n if (!hasMore) return null;\n return (\n <div\n ref={ref}\n data-log-load-more=\"\"\n aria-busy={loading || undefined}\n className={cn(\n \"flex min-h-12 items-center justify-center px-5 py-2\",\n className,\n )}\n {...props}\n >\n {loading ? (\n <Spinner aria-label=\"Loading more\" className=\"text-pho-secondary\" />\n ) : (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => onLoadMore()}>\n {label}\n </Button>\n )}\n </div>\n );\n}\n\n/* ---------------------------------- root -------------------------------- */\n\nexport interface LogListRootProps extends ComponentProps<\"div\"> {\n ref?: Ref<HTMLDivElement>;\n}\n\n/** The card the rows stack in. Mark it `aria-busy` while bones show. */\nfunction Root({ className, ...props }: LogListRootProps) {\n return (\n <div\n data-log-list=\"\"\n className={cn(LOG_LIST_SURFACE, \"min-w-0\", className)}\n {...props}\n />\n );\n}\n\n/**\n * Log List — a card of one-line log rows.\n *\n * ```tsx\n * <LogList.Root>\n * <LogList.Row\n * id=\"pho_req_01m04axjqrfzxv4xjet40a84gb\"\n * status={<LogList.Status value={200} />}\n * method=\"POST\"\n * code\n * label=\"/v1/messages\"\n * description=\"+1 332 259 3374 received a message from +1 332 222 1111.\"\n * duration=\"59 ms\"\n * time=\"2 min ago\"\n * href=\"/requests/pho_req_01m04axjqrfzxv4xjet40a84gb\"\n * />\n * <LogList.LoadMore onLoadMore={fetchNextPage} loading={isFetching} hasMore={hasNextPage} />\n * </LogList.Root>\n * ```\n */\nexport const LogList = {\n Root,\n Row,\n Status,\n Method,\n Empty,\n Loading,\n LoadMore,\n};\n"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ import { t as o } from "./cn-ChIgwEl3.js";
3
+ import { t as i } from "./resolve-variant-CcD8iG5l.js";
4
+ import "react";
5
+ import { jsx as r } from "react/jsx-runtime";
6
+ import { Toggle as a } from "@base-ui/react/toggle";
7
+ var n = {
8
+ sm: {
9
+ classes: "h-8 min-w-8 px-2 text-xs [&>svg]:size-4",
10
+ description: "Small — dense toolbars, a range picker"
11
+ },
12
+ md: {
13
+ classes: "h-9 min-w-9 px-2 text-base [&>svg]:size-5",
14
+ description: "Medium — the default"
15
+ },
16
+ lg: {
17
+ classes: "h-10 min-w-10 px-2.5 text-base [&>svg]:size-5",
18
+ description: "Large"
19
+ }
20
+ };
21
+ function l({ className: e, size: s = "md", ...t }) {
22
+ return /* @__PURE__ */ r(a, {
23
+ className: o("squircle outline-pho-brand text-pho-secondary rounded-base inline-flex cursor-pointer items-center justify-center gap-1.5 font-medium whitespace-nowrap transition duration-100 ease-linear focus-visible:outline-2 focus-visible:outline-offset-2", "hover:bg-pho-ghost-hover hover:text-pho-secondary-hover", "data-[pressed]:bg-pho-inverse data-[pressed]:text-pho-inverse", "data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50", i(n, s), e),
24
+ ...t
25
+ });
26
+ }
27
+ l.displayName = "Toggle";
28
+ export {
29
+ l as n,
30
+ n as t
31
+ };
32
+
33
+ //# sourceMappingURL=toggle-o84tawXy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggle-o84tawXy.js","names":[],"sources":["../../src/components/toggle/toggle.tsx"],"sourcesContent":["import { type ComponentProps } from \"react\";\nimport { Toggle as BaseToggle } from \"@base-ui/react/toggle\";\nimport { cn } from \"../../utils/cn\";\nimport { resolveVariant, type VariantMap } from \"../../utils/resolve-variant\";\n\n/**\n * Size axis — three heights. An icon-only toggle is square (the padding\n * plus the icon add up to the height); one carrying a short label — a\n * range picker's `7d` — grows to fit it. `md` is default.\n */\nexport const TOGGLE_SIZES = {\n sm: {\n classes: \"h-8 min-w-8 px-2 text-xs [&>svg]:size-4\",\n description: \"Small — dense toolbars, a range picker\",\n },\n md: {\n classes: \"h-9 min-w-9 px-2 text-base [&>svg]:size-5\",\n description: \"Medium — the default\",\n },\n lg: {\n classes: \"h-10 min-w-10 px-2.5 text-base [&>svg]:size-5\",\n description: \"Large\",\n },\n} satisfies VariantMap;\n\nexport type ToggleSize = keyof typeof TOGGLE_SIZES;\n\nexport interface ToggleProps extends Omit<\n ComponentProps<typeof BaseToggle>,\n \"className\"\n> {\n className?: string;\n /** Size variant. @default \"md\" */\n size?: ToggleSize;\n}\n\n/**\n * A two-state button (pressed / not pressed) built on Base UI `Toggle` — the\n * canonical Bold/Italic toolbar control. Controlled via `pressed` /\n * `onPressedChange` or uncontrolled via `defaultPressed`. For a set of related\n * toggles, use `ToggleGroup`.\n */\nexport function Toggle({ className, size = \"md\", ...props }: ToggleProps) {\n return (\n <BaseToggle\n className={cn(\n \"squircle outline-pho-brand text-pho-secondary rounded-base inline-flex cursor-pointer items-center justify-center gap-1.5 font-medium whitespace-nowrap transition duration-100 ease-linear focus-visible:outline-2 focus-visible:outline-offset-2\",\n // Ghost control at rest — hover is the alpha wash (Button `tertiary`\n // idiom), not the card-surface hover token.\n \"hover:bg-pho-ghost-hover hover:text-pho-secondary-hover\",\n // Pressed = the site's inverted chip (Tab active): dark chip in light\n // mode, light chip in dark mode — visible on any surface.\n \"data-[pressed]:bg-pho-inverse data-[pressed]:text-pho-inverse\",\n \"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50\",\n resolveVariant(TOGGLE_SIZES, size),\n className,\n )}\n {...props}\n />\n );\n}\n\nToggle.displayName = \"Toggle\";\n"],"mappings":";;;;;;AAUA,IAAa,IAAe;AAAA,EAC1B,IAAI;AAAA,IACF,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,IAAI;AAAA,IACF,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,IAAI;AAAA,IACF,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AACF;AAmBA,SAAgB,EAAO,EAAE,WAAA,GAAW,MAAA,IAAO,MAAM,GAAG,EAAA,GAAsB;AACxE,SACE,gBAAA,EAAC,GAAD;AAAA,IACE,WAAW,EACT,sPAGA,2DAGA,iEACA,iEACA,EAAe,GAAc,CAAI,GACjC,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,EAAO,cAAc"}
@@ -0,0 +1,32 @@
1
+ "use client";
2
+ import { t as a } from "./cn-ChIgwEl3.js";
3
+ import "react";
4
+ import { jsx as i } from "react/jsx-runtime";
5
+ import { Tooltip as r } from "@base-ui/react/tooltip";
6
+ var n = r.Provider, s = r.Root, p = r.Trigger, d = r.Portal;
7
+ function g({ className: o, sideOffset: t = 6, ...e }) {
8
+ return /* @__PURE__ */ i(r.Positioner, {
9
+ sideOffset: t,
10
+ className: a("z-50", o),
11
+ ...e
12
+ });
13
+ }
14
+ function l({ className: o, ...t }) {
15
+ return /* @__PURE__ */ i(r.Popup, {
16
+ className: a("bg-pho-primary text-pho-secondary shadow-pho-lg ring-pho-secondary rounded-base origin-[var(--transform-origin)] px-2.5 py-1.5 text-xs font-medium ring-1 transition-[transform,opacity] duration-150 ease-out ring-inset", "data-[starting-style]:scale-95 data-[starting-style]:opacity-0", "data-[ending-style]:scale-95 data-[ending-style]:opacity-0", o),
17
+ ...t
18
+ });
19
+ }
20
+ var f = {
21
+ Provider: n,
22
+ Root: s,
23
+ Trigger: p,
24
+ Portal: d,
25
+ Positioner: g,
26
+ Popup: l
27
+ };
28
+ export {
29
+ f as t
30
+ };
31
+
32
+ //# sourceMappingURL=tooltip-BTF7GChl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip-BTF7GChl.js","names":[],"sources":["../../src/components/tooltip/tooltip.tsx"],"sourcesContent":["import { type ComponentProps, type ElementType } from \"react\";\nimport { Tooltip as BaseTooltip } from \"@base-ui/react/tooltip\";\nimport { cn } from \"../../utils/cn\";\n\n/** Override Base UI's `string | (state) => string` className with plain string. */\ntype StyledProps<T extends ElementType> = Omit<\n ComponentProps<T>,\n \"className\"\n> & {\n className?: string;\n};\n\nconst Provider = BaseTooltip.Provider;\nconst Root = BaseTooltip.Root;\nconst Trigger = BaseTooltip.Trigger;\nconst Portal = BaseTooltip.Portal;\n\nfunction Positioner({\n className,\n sideOffset = 6,\n ...props\n}: StyledProps<typeof BaseTooltip.Positioner>) {\n return (\n <BaseTooltip.Positioner\n sideOffset={sideOffset}\n className={cn(\"z-50\", className)}\n {...props}\n />\n );\n}\n\nfunction Popup({ className, ...props }: StyledProps<typeof BaseTooltip.Popup>) {\n return (\n <BaseTooltip.Popup\n className={cn(\n // Floating layer: bg-pho-primary surface, pho-secondary hairline ring, keeps shadow elevation.\n \"bg-pho-primary text-pho-secondary shadow-pho-lg ring-pho-secondary rounded-base origin-[var(--transform-origin)] px-2.5 py-1.5 text-xs font-medium ring-1 transition-[transform,opacity] duration-150 ease-out ring-inset\",\n \"data-[starting-style]:scale-95 data-[starting-style]:opacity-0\",\n \"data-[ending-style]:scale-95 data-[ending-style]:opacity-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Tooltip built on Base UI `Tooltip` — a brief, hover/focus-triggered label for\n * an icon or control. Wrap your app (or a subtree) in `Tooltip.Provider` to\n * share open/close timing. The trigger must be a focusable element. Never put\n * essential or interactive content in a tooltip; use `Popover` for that.\n */\nexport const Tooltip = { Provider, Root, Trigger, Portal, Positioner, Popup };\n"],"mappings":";;;;;AAYA,IAAM,IAAW,EAAY,UACvB,IAAO,EAAY,MACnB,IAAU,EAAY,SACtB,IAAS,EAAY;AAE3B,SAAS,EAAW,EAClB,WAAA,GACA,YAAA,IAAa,GACb,GAAG,EAAA,GAC0C;AAC7C,SACE,gBAAA,EAAC,EAAY,YAAb;AAAA,IACc,YAAA;AAAA,IACZ,WAAW,EAAG,QAAQ,CAAS;AAAA,IAC/B,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,SAAS,EAAM,EAAE,WAAA,GAAW,GAAG,EAAA,GAAgD;AAC7E,SACE,gBAAA,EAAC,EAAY,OAAb;AAAA,IACE,WAAW,EAET,6NACA,kEACA,8DACA,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AAQA,IAAa,IAAU;AAAA,EAAE,UAAA;AAAA,EAAU,MAAA;AAAA,EAAM,SAAA;AAAA,EAAS,QAAA;AAAA,EAAQ,YAAA;AAAA,EAAY,OAAA;AAAM"}
@@ -0,0 +1,314 @@
1
+ "use client";
2
+ import { t as f } from "./cn-ChIgwEl3.js";
3
+ import { t as S } from "./IconChevronRight-CzoJnYNI.js";
4
+ import { useMemo as $, useState as I } from "react";
5
+ import { jsx as n, jsxs as l } from "react/jsx-runtime";
6
+ function j(e, r = 6) {
7
+ if (e <= 0) return 1;
8
+ const i = e / r, c = 10 ** Math.floor(Math.log10(i));
9
+ for (const u of [
10
+ 1,
11
+ 2,
12
+ 5,
13
+ 10
14
+ ]) if (u * c >= i) return u * c;
15
+ return 10 * c;
16
+ }
17
+ function E(e) {
18
+ if (e.length === 0) return {
19
+ rows: [],
20
+ startMs: 0,
21
+ endMs: 0,
22
+ durationMs: 0,
23
+ ticks: [0]
24
+ };
25
+ const r = new Map(e.map((t) => [t.id, t])), i = /* @__PURE__ */ new Map();
26
+ for (const t of e) {
27
+ const m = t.parentId != null && r.has(t.parentId) ? t.parentId : null, b = i.get(m) ?? [];
28
+ b.push(t), i.set(m, b);
29
+ }
30
+ for (const t of i.values()) t.sort((m, b) => m.startMs - b.startMs);
31
+ const c = Math.min(...e.map((t) => t.startMs)), u = Math.max(...e.map((t) => t.startMs + Math.max(t.durationMs, 0))), d = Math.max(u - c, 0), a = d > 0 ? 1 / d : 0, x = [], p = (t, m) => {
32
+ const b = i.get(t.id) ?? [], y = {
33
+ span: t,
34
+ depth: m,
35
+ left: (t.startMs - c) * a,
36
+ width: Math.max(t.durationMs, 0) * a,
37
+ children: b.map((g) => g.id),
38
+ descendants: 0
39
+ };
40
+ x.push(y);
41
+ let N = 0;
42
+ for (const g of b) N += 1 + p(g, m + 1);
43
+ return y.descendants = N, N;
44
+ };
45
+ for (const t of i.get(null) ?? []) p(t, 0);
46
+ const o = j(d), v = [];
47
+ for (let t = 0; t <= d; t += o) v.push(t);
48
+ return {
49
+ rows: x,
50
+ startMs: c,
51
+ endMs: u,
52
+ durationMs: d,
53
+ ticks: v
54
+ };
55
+ }
56
+ function k(e) {
57
+ if (!Number.isFinite(e)) return "";
58
+ const r = Math.abs(e);
59
+ return r < 10 ? `${e.toFixed(2)} ms` : r < 100 ? `${e.toFixed(1)} ms` : r < 1e3 ? `${Math.round(e)} ms` : r < 6e4 ? `${(e / 1e3).toFixed(2)} s` : `${(e / 6e4).toFixed(1)} min`;
60
+ }
61
+ function L(e) {
62
+ return e >= 1e3 ? `${Number((e / 1e3).toFixed(2))} s` : `${Number(e.toFixed(2))} ms`;
63
+ }
64
+ function w(e) {
65
+ return `${Number((e * 100).toFixed(4))}%`;
66
+ }
67
+ var C = "rounded-base border border-pho-secondary bg-pho-primary overflow-hidden", F = "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)]";
68
+ function T(e) {
69
+ return e.width > 0.5 ? "inside" : e.left + e.width > 0.8 ? "before" : "after";
70
+ }
71
+ function z({ row: e, collapsed: r, selected: i, onToggle: c, onSelect: u }) {
72
+ const { span: d } = e, a = d.status === "error", x = e.children.length > 0, p = T(e), o = k(d.durationMs), v = [d.service, d.kind].filter(Boolean).join(", ");
73
+ return /* @__PURE__ */ l("div", {
74
+ "data-trace-row": "",
75
+ "data-depth": e.depth,
76
+ "data-selected": i || void 0,
77
+ className: f(F, "relative min-h-9 items-center transition-colors", "has-[[data-trace-select]:focus-visible]:outline-pho-brand has-[[data-trace-select]:focus-visible]:outline-2 has-[[data-trace-select]:focus-visible]:-outline-offset-2", u != null && "hover:bg-pho-primary-hover", i && "bg-pho-primary-hover"),
78
+ children: [/* @__PURE__ */ l("div", {
79
+ className: "flex min-w-0 items-center gap-1.5 py-1.5 pr-3",
80
+ style: { paddingLeft: `${0.75 + e.depth}rem` },
81
+ children: [
82
+ x ? /* @__PURE__ */ n("button", {
83
+ type: "button",
84
+ "aria-expanded": !r,
85
+ "aria-label": `${r ? "Expand" : "Collapse"} ${d.name}`,
86
+ onClick: c,
87
+ className: "text-pho-secondary outline-pho-brand hover:bg-pho-ghost-hover hover:text-pho-secondary-hover relative z-10 flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-sm transition-[color,background-color,transform] duration-100 ease-linear focus-visible:outline-2",
88
+ children: /* @__PURE__ */ n(S, {
89
+ "aria-hidden": !0,
90
+ className: f("size-4 transition-transform duration-150 ease-out", !r && "rotate-90")
91
+ })
92
+ }) : /* @__PURE__ */ n("span", {
93
+ "aria-hidden": !0,
94
+ className: "size-5 shrink-0"
95
+ }),
96
+ /* @__PURE__ */ n("span", {
97
+ "aria-hidden": !0,
98
+ className: f("size-1.5 shrink-0 rounded-full", a ? "bg-pho-error-ink" : "bg-pho-inverse")
99
+ }),
100
+ /* @__PURE__ */ l(R, {
101
+ selected: i,
102
+ onSelect: u,
103
+ children: [
104
+ /* @__PURE__ */ n("span", {
105
+ className: "text-pho-primary truncate text-base",
106
+ children: d.name
107
+ }),
108
+ v ? /* @__PURE__ */ n("span", {
109
+ className: "text-pho-description shrink-[3] truncate text-xs",
110
+ children: v
111
+ }) : null,
112
+ r && e.descendants > 0 ? /* @__PURE__ */ l("span", {
113
+ className: "text-pho-description shrink-0 text-xs tabular-nums",
114
+ children: [
115
+ "(",
116
+ e.descendants,
117
+ ")"
118
+ ]
119
+ }) : null,
120
+ a ? /* @__PURE__ */ n("span", {
121
+ className: "sr-only",
122
+ children: "Error."
123
+ }) : null
124
+ ]
125
+ })
126
+ ]
127
+ }), /* @__PURE__ */ l("div", {
128
+ className: "border-pho-secondary relative h-9 border-l",
129
+ children: [
130
+ /* @__PURE__ */ n("div", {
131
+ "aria-hidden": !0,
132
+ "data-trace-bar": "",
133
+ className: f("absolute top-1/2 h-4 -translate-y-1/2 rounded-sm", a ? "bg-pho-error-ink" : "bg-pho-inverse"),
134
+ style: {
135
+ left: w(e.left),
136
+ width: w(e.width),
137
+ minWidth: "2px"
138
+ },
139
+ children: p === "inside" ? /* @__PURE__ */ n("span", {
140
+ className: f("absolute inset-y-0 right-1.5 flex items-center text-xs whitespace-nowrap tabular-nums", a ? "text-pho-on-brand" : "text-pho-inverse"),
141
+ children: o
142
+ }) : null
143
+ }),
144
+ p !== "inside" ? /* @__PURE__ */ n("span", {
145
+ "aria-hidden": !0,
146
+ className: "text-pho-description absolute top-1/2 -translate-y-1/2 text-xs whitespace-nowrap tabular-nums",
147
+ style: p === "after" ? { left: `calc(${w(e.left + e.width)} + 0.375rem)` } : { right: `calc(${w(1 - e.left)} + 0.375rem)` },
148
+ children: o
149
+ }) : null,
150
+ /* @__PURE__ */ n("span", {
151
+ className: "sr-only",
152
+ children: o
153
+ })
154
+ ]
155
+ })]
156
+ });
157
+ }
158
+ function R({ selected: e, onSelect: r, children: i }) {
159
+ const c = "flex min-w-0 flex-1 items-baseline gap-2 text-left";
160
+ return r == null ? /* @__PURE__ */ n("span", {
161
+ className: c,
162
+ children: i
163
+ }) : /* @__PURE__ */ n("button", {
164
+ type: "button",
165
+ "data-trace-select": "",
166
+ "aria-current": e ? "true" : void 0,
167
+ onClick: r,
168
+ className: f(c, "cursor-pointer outline-none after:absolute after:inset-0 after:content-['']"),
169
+ children: i
170
+ });
171
+ }
172
+ function _({ spans: e, selectedId: r, onSelect: i, defaultCollapsed: c, collapsed: u, onCollapsedChange: d, title: a, className: x, ...p }) {
173
+ const o = $(() => E(e), [e]), [v, t] = I(() => c ?? []), m = new Set(u ?? v), b = (s) => {
174
+ const h = new Set(m);
175
+ h.has(s) ? h.delete(s) : h.add(s);
176
+ const M = Array.from(h);
177
+ u == null && t(M), d?.(M);
178
+ }, y = /* @__PURE__ */ new Set(), N = /* @__PURE__ */ new Map();
179
+ for (const s of o.rows) for (const h of s.children) N.set(h, s.span.id);
180
+ for (const s of o.rows) {
181
+ let h = N.get(s.span.id) ?? null;
182
+ for (; h != null; ) {
183
+ if (m.has(h)) {
184
+ y.add(s.span.id);
185
+ break;
186
+ }
187
+ h = N.get(h) ?? null;
188
+ }
189
+ }
190
+ const g = o.rows.length;
191
+ return /* @__PURE__ */ l("div", {
192
+ "data-trace": "",
193
+ className: f(C, "min-w-0", x),
194
+ ...p,
195
+ children: [
196
+ a != null ? /* @__PURE__ */ l("div", {
197
+ className: "border-pho-secondary flex min-h-10 items-center justify-between gap-3 border-b px-3",
198
+ children: [/* @__PURE__ */ n("div", {
199
+ className: "text-pho-secondary min-w-0 truncate text-xs font-medium",
200
+ children: a
201
+ }), /* @__PURE__ */ l("div", {
202
+ className: "text-pho-description shrink-0 text-xs tabular-nums",
203
+ children: [
204
+ g,
205
+ " ",
206
+ g === 1 ? "span" : "spans",
207
+ ".",
208
+ " ",
209
+ k(o.durationMs),
210
+ "."
211
+ ]
212
+ })]
213
+ }) : null,
214
+ /* @__PURE__ */ l("div", {
215
+ "aria-hidden": !0,
216
+ className: f(F, "border-pho-secondary h-7 border-b"),
217
+ children: [/* @__PURE__ */ n("div", {}), /* @__PURE__ */ n("div", {
218
+ className: "border-pho-secondary relative h-full border-l",
219
+ children: o.ticks.map((s) => {
220
+ const h = o.durationMs > 0 ? s / o.durationMs : 0, M = h > 0.85;
221
+ return /* @__PURE__ */ l("span", {
222
+ className: "text-pho-description absolute top-0 h-full text-[10px] tabular-nums",
223
+ style: { left: w(h) },
224
+ children: [/* @__PURE__ */ n("span", { className: "bg-pho-secondary absolute bottom-0 left-0 h-1.5 w-px" }), /* @__PURE__ */ n("span", {
225
+ className: f("absolute bottom-0 pb-1.5 whitespace-nowrap", M ? "right-1" : "left-1"),
226
+ children: s === 0 ? "0" : L(s)
227
+ })]
228
+ }, s);
229
+ })
230
+ })]
231
+ }),
232
+ /* @__PURE__ */ n("ol", {
233
+ "aria-label": typeof a == "string" ? a : "Spans",
234
+ className: "m-0 list-none p-0",
235
+ children: o.rows.map((s) => y.has(s.span.id) ? null : /* @__PURE__ */ n("li", {
236
+ className: "m-0",
237
+ children: /* @__PURE__ */ n(z, {
238
+ row: s,
239
+ collapsed: m.has(s.span.id),
240
+ selected: r != null && r === s.span.id,
241
+ onToggle: () => b(s.span.id),
242
+ onSelect: i ? () => i(s.span.id) : void 0
243
+ })
244
+ }, s.span.id))
245
+ })
246
+ ]
247
+ });
248
+ }
249
+ var A = {
250
+ debug: "text-pho-description",
251
+ info: "text-pho-secondary",
252
+ warn: "text-pho-warning",
253
+ error: "text-pho-error"
254
+ };
255
+ function U({ logs: e, spans: r, title: i = "Logs", className: c, ...u }) {
256
+ const d = $(() => new Map((r ?? []).map((a) => [a.id, a.name])), [r]);
257
+ return e == null || e.length === 0 ? null : /* @__PURE__ */ l("section", {
258
+ "data-trace-logs": "",
259
+ className: f(C, "min-w-0", c),
260
+ ...u,
261
+ children: [/* @__PURE__ */ l("div", {
262
+ className: "border-pho-secondary flex min-h-10 items-center justify-between gap-3 border-b px-3",
263
+ children: [/* @__PURE__ */ n("div", {
264
+ className: "text-pho-secondary min-w-0 truncate text-xs font-medium",
265
+ children: i
266
+ }), /* @__PURE__ */ l("div", {
267
+ className: "text-pho-description shrink-0 text-xs tabular-nums",
268
+ children: [
269
+ e.length,
270
+ " ",
271
+ e.length === 1 ? "line" : "lines"
272
+ ]
273
+ })]
274
+ }), /* @__PURE__ */ n("ol", {
275
+ className: "divide-pho-secondary m-0 list-none divide-y p-0 font-mono text-xs",
276
+ children: e.map((a, x) => {
277
+ const p = a.level ?? "info", o = a.spanId ? d.get(a.spanId) : void 0;
278
+ return /* @__PURE__ */ l("li", {
279
+ className: "grid grid-cols-[5rem_3.5rem_minmax(0,1fr)] items-baseline gap-3 px-3 py-1.5",
280
+ children: [
281
+ /* @__PURE__ */ l("span", {
282
+ className: "text-pho-description tabular-nums",
283
+ children: ["+", k(a.offsetMs)]
284
+ }),
285
+ /* @__PURE__ */ n("span", {
286
+ className: f("truncate", A[p]),
287
+ children: p.charAt(0).toUpperCase() + p.slice(1)
288
+ }),
289
+ /* @__PURE__ */ l("span", {
290
+ className: "text-pho-primary [overflow-wrap:anywhere] whitespace-pre-wrap",
291
+ children: [a.message, o ? /* @__PURE__ */ l("span", {
292
+ className: "text-pho-description",
293
+ children: [" ", o]
294
+ }) : null]
295
+ })
296
+ ]
297
+ }, a.id ?? x);
298
+ })
299
+ })]
300
+ });
301
+ }
302
+ var V = {
303
+ Root: _,
304
+ Logs: U
305
+ };
306
+ export {
307
+ j as a,
308
+ E as i,
309
+ k as n,
310
+ L as r,
311
+ V as t
312
+ };
313
+
314
+ //# sourceMappingURL=trace-CbH0IohX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-CbH0IohX.js","names":[],"sources":["../../src/components/trace/layout.ts","../../src/components/trace/trace.tsx"],"sourcesContent":["/**\n * The waterfall's arithmetic, apart from the drawing: spans in, rows out\n * — each with its depth and its bar's place along the trace's length —\n * plus the tick marks for the time axis.\n */\n\nexport interface TraceSpan {\n id: string;\n /** The span this one runs inside. Absent (or unknown) means a root. */\n parentId?: string | null;\n name: string;\n /** Start, in ms, on any shared clock — only the differences matter. */\n startMs: number;\n durationMs: number;\n status?: \"ok\" | \"error\" | \"unset\";\n /** A hint after the name — the service, the host. */\n service?: string;\n /** A hint after the service — client, server, db. */\n kind?: string;\n}\n\nexport interface TraceRow {\n span: TraceSpan;\n depth: number;\n /** The bar's start, as a fraction of the trace's length. */\n left: number;\n /** The bar's length, as a fraction of the trace's length. */\n width: number;\n children: string[];\n /** Every span beneath this one, at any depth. */\n descendants: number;\n}\n\nexport interface TraceLayout {\n /** Every span, root first, each parent before its children. */\n rows: TraceRow[];\n startMs: number;\n endMs: number;\n durationMs: number;\n /** Tick marks along the axis, in ms from the start. */\n ticks: number[];\n}\n\n/** A tick step from the 1 / 2 / 5 ladder so the axis has at most `max` ticks. */\nexport function niceStep(durationMs: number, max = 6): number {\n if (durationMs <= 0) return 1;\n const rough = durationMs / max;\n const power = 10 ** Math.floor(Math.log10(rough));\n for (const unit of [1, 2, 5, 10]) {\n if (unit * power >= rough) return unit * power;\n }\n return 10 * power;\n}\n\nexport function layoutTrace(spans: readonly TraceSpan[]): TraceLayout {\n if (spans.length === 0) {\n return { rows: [], startMs: 0, endMs: 0, durationMs: 0, ticks: [0] };\n }\n const byId = new Map(spans.map((span) => [span.id, span]));\n const children = new Map<string | null, TraceSpan[]>();\n for (const span of spans) {\n const parent =\n span.parentId != null && byId.has(span.parentId) ? span.parentId : null;\n const list = children.get(parent) ?? [];\n list.push(span);\n children.set(parent, list);\n }\n for (const list of children.values()) {\n list.sort((a, b) => a.startMs - b.startMs);\n }\n const startMs = Math.min(...spans.map((span) => span.startMs));\n const endMs = Math.max(\n ...spans.map((span) => span.startMs + Math.max(span.durationMs, 0)),\n );\n const durationMs = Math.max(endMs - startMs, 0);\n const scale = durationMs > 0 ? 1 / durationMs : 0;\n const rows: TraceRow[] = [];\n const visit = (span: TraceSpan, depth: number): number => {\n const kids = children.get(span.id) ?? [];\n const row: TraceRow = {\n span,\n depth,\n left: (span.startMs - startMs) * scale,\n width: Math.max(span.durationMs, 0) * scale,\n children: kids.map((kid) => kid.id),\n descendants: 0,\n };\n rows.push(row);\n let count = 0;\n for (const kid of kids) count += 1 + visit(kid, depth + 1);\n row.descendants = count;\n return count;\n };\n for (const root of children.get(null) ?? []) visit(root, 0);\n const step = niceStep(durationMs);\n const ticks: number[] = [];\n for (let at = 0; at <= durationMs; at += step) ticks.push(at);\n return { rows, startMs, endMs, durationMs, ticks };\n}\n\n/** \"0.42 ms\", \"59.2 ms\", \"1.25 s\" — the precision follows the size. */\nexport function formatDuration(ms: number): string {\n if (!Number.isFinite(ms)) return \"\";\n const abs = Math.abs(ms);\n if (abs < 10) return `${ms.toFixed(2)} ms`;\n if (abs < 100) return `${ms.toFixed(1)} ms`;\n if (abs < 1000) return `${Math.round(ms)} ms`;\n if (abs < 60_000) return `${(ms / 1000).toFixed(2)} s`;\n return `${(ms / 60_000).toFixed(1)} min`;\n}\n\n/** An axis tick's label: whole numbers where the step allows, \"2 s\" past a second. */\nexport function formatTick(ms: number): string {\n if (ms >= 1000) return `${Number((ms / 1000).toFixed(2))} s`;\n return `${Number(ms.toFixed(2))} ms`;\n}\n\n/** A fraction as a CSS percentage, rounded so floats never leak into styles. */\nexport function percent(fraction: number): string {\n return `${Number((fraction * 100).toFixed(4))}%`;\n}\n","import {\n useMemo,\n useState,\n type ComponentProps,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport { IconChevronRight } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils/cn\";\nimport {\n formatDuration,\n formatTick,\n layoutTrace,\n percent,\n type TraceRow,\n type TraceSpan,\n} from \"./layout\";\n\n/**\n * Trace — one request's spans as a waterfall: each row a span, nested\n * under its parent by indentation, its bar placed along the trace's\n * length from where it started to where it ended. Pure layout from\n * `{ id, parentId, name, startMs, durationMs }`: no fetching, no clock.\n *\n * Design notes:\n * - Two columns, half and half: the names on the left, the bars on the\n * right under a time axis. Bars are the inverse chip surface; an error\n * span's bar and dot are the error ink.\n * - A parent folds its children on its chevron; the row itself selects\n * (`selectedId` / `onSelect`) and the selected row keeps the wash.\n * - `Trace.Logs` lists the log lines that belong to the trace, each at\n * its offset from the start. With no lines it renders nothing.\n */\n\nconst TRACE_SURFACE =\n \"rounded-base border border-pho-secondary bg-pho-primary overflow-hidden\";\n\n/** The two columns every row and the axis share. */\nconst COLUMNS = \"grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)]\";\n\n/** Where a bar's duration label goes, given the bar's place. */\nfunction labelPlacement(row: TraceRow): \"inside\" | \"before\" | \"after\" {\n if (row.width > 0.5) return \"inside\";\n if (row.left + row.width > 0.8) return \"before\";\n return \"after\";\n}\n\nfunction SpanRow({\n row,\n collapsed,\n selected,\n onToggle,\n onSelect,\n}: {\n row: TraceRow;\n collapsed: boolean;\n selected: boolean;\n onToggle: () => void;\n onSelect?: () => void;\n}) {\n const { span } = row;\n const error = span.status === \"error\";\n const hasChildren = row.children.length > 0;\n const placement = labelPlacement(row);\n const label = formatDuration(span.durationMs);\n const hint = [span.service, span.kind].filter(Boolean).join(\", \");\n return (\n <div\n data-trace-row=\"\"\n data-depth={row.depth}\n data-selected={selected || undefined}\n className={cn(\n COLUMNS,\n \"relative min-h-9 items-center transition-colors\",\n \"has-[[data-trace-select]:focus-visible]:outline-pho-brand has-[[data-trace-select]:focus-visible]:outline-2 has-[[data-trace-select]:focus-visible]:-outline-offset-2\",\n onSelect != null && \"hover:bg-pho-primary-hover\",\n selected && \"bg-pho-primary-hover\",\n )}\n >\n <div\n className=\"flex min-w-0 items-center gap-1.5 py-1.5 pr-3\"\n style={{ paddingLeft: `${0.75 + row.depth}rem` }}\n >\n {hasChildren ? (\n <button\n type=\"button\"\n aria-expanded={!collapsed}\n aria-label={`${collapsed ? \"Expand\" : \"Collapse\"} ${span.name}`}\n onClick={onToggle}\n className=\"text-pho-secondary outline-pho-brand hover:bg-pho-ghost-hover hover:text-pho-secondary-hover relative z-10 flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-sm transition-[color,background-color,transform] duration-100 ease-linear focus-visible:outline-2\"\n >\n <IconChevronRight\n aria-hidden\n className={cn(\n \"size-4 transition-transform duration-150 ease-out\",\n !collapsed && \"rotate-90\",\n )}\n />\n </button>\n ) : (\n <span aria-hidden className=\"size-5 shrink-0\" />\n )}\n <span\n aria-hidden\n className={cn(\n \"size-1.5 shrink-0 rounded-full\",\n error ? \"bg-pho-error-ink\" : \"bg-pho-inverse\",\n )}\n />\n <SelectTarget selected={selected} onSelect={onSelect}>\n <span className=\"text-pho-primary truncate text-base\">\n {span.name}\n </span>\n {hint ? (\n <span className=\"text-pho-description shrink-[3] truncate text-xs\">\n {hint}\n </span>\n ) : null}\n {collapsed && row.descendants > 0 ? (\n <span className=\"text-pho-description shrink-0 text-xs tabular-nums\">\n ({row.descendants})\n </span>\n ) : null}\n {error ? <span className=\"sr-only\">Error.</span> : null}\n </SelectTarget>\n </div>\n <div className=\"border-pho-secondary relative h-9 border-l\">\n <div\n aria-hidden\n data-trace-bar=\"\"\n className={cn(\n \"absolute top-1/2 h-4 -translate-y-1/2 rounded-sm\",\n error ? \"bg-pho-error-ink\" : \"bg-pho-inverse\",\n )}\n style={{\n left: percent(row.left),\n width: percent(row.width),\n minWidth: \"2px\",\n }}\n >\n {placement === \"inside\" ? (\n <span\n className={cn(\n \"absolute inset-y-0 right-1.5 flex items-center text-xs whitespace-nowrap tabular-nums\",\n error ? \"text-pho-on-brand\" : \"text-pho-inverse\",\n )}\n >\n {label}\n </span>\n ) : null}\n </div>\n {placement !== \"inside\" ? (\n <span\n aria-hidden\n className=\"text-pho-description absolute top-1/2 -translate-y-1/2 text-xs whitespace-nowrap tabular-nums\"\n style={\n placement === \"after\"\n ? {\n left: `calc(${percent(row.left + row.width)} + 0.375rem)`,\n }\n : { right: `calc(${percent(1 - row.left)} + 0.375rem)` }\n }\n >\n {label}\n </span>\n ) : null}\n <span className=\"sr-only\">{label}</span>\n </div>\n </div>\n );\n}\n\n/** The row's selectable part — a button when the trace takes a selection. */\nfunction SelectTarget({\n selected,\n onSelect,\n children,\n}: {\n selected: boolean;\n onSelect?: () => void;\n children: ReactNode;\n}) {\n const className = \"flex min-w-0 flex-1 items-baseline gap-2 text-left\";\n if (onSelect == null) {\n return <span className={className}>{children}</span>;\n }\n return (\n <button\n type=\"button\"\n data-trace-select=\"\"\n aria-current={selected ? \"true\" : undefined}\n onClick={onSelect}\n // The one target, stretched over the row; the ring is the row's.\n className={cn(\n className,\n \"cursor-pointer outline-none after:absolute after:inset-0 after:content-['']\",\n )}\n >\n {children}\n </button>\n );\n}\n\nexport interface TraceRootProps extends Omit<\n ComponentProps<\"div\">,\n \"children\" | \"onSelect\" | \"title\"\n> {\n spans: readonly TraceSpan[];\n /** The span whose details are open — it keeps the wash. */\n selectedId?: string | null;\n /** Rows select on click when given. */\n onSelect?: (id: string) => void;\n /** Spans folded at first (uncontrolled). */\n defaultCollapsed?: readonly string[];\n /** Spans folded (controlled). */\n collapsed?: readonly string[];\n onCollapsedChange?: (ids: string[]) => void;\n /** A title row above the axis — \"Waterfall\". Omit for none. */\n title?: ReactNode;\n ref?: Ref<HTMLDivElement>;\n}\n\nfunction Root({\n spans,\n selectedId,\n onSelect,\n defaultCollapsed,\n collapsed: collapsedProp,\n onCollapsedChange,\n title,\n className,\n ...props\n}: TraceRootProps) {\n const layout = useMemo(() => layoutTrace(spans), [spans]);\n const [ownCollapsed, setOwnCollapsed] = useState<readonly string[]>(\n () => defaultCollapsed ?? [],\n );\n const collapsed = new Set(collapsedProp ?? ownCollapsed);\n const toggle = (id: string) => {\n const next = new Set(collapsed);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n const list = Array.from(next);\n if (collapsedProp == null) setOwnCollapsed(list);\n onCollapsedChange?.(list);\n };\n // A row shows while none of its ancestors is folded.\n const hidden = new Set<string>();\n const parents = new Map<string, string | null>();\n for (const row of layout.rows) {\n for (const child of row.children) parents.set(child, row.span.id);\n }\n for (const row of layout.rows) {\n let parent = parents.get(row.span.id) ?? null;\n while (parent != null) {\n if (collapsed.has(parent)) {\n hidden.add(row.span.id);\n break;\n }\n parent = parents.get(parent) ?? null;\n }\n }\n const count = layout.rows.length;\n return (\n <div\n data-trace=\"\"\n className={cn(TRACE_SURFACE, \"min-w-0\", className)}\n {...props}\n >\n {title != null ? (\n <div className=\"border-pho-secondary flex min-h-10 items-center justify-between gap-3 border-b px-3\">\n <div className=\"text-pho-secondary min-w-0 truncate text-xs font-medium\">\n {title}\n </div>\n <div className=\"text-pho-description shrink-0 text-xs tabular-nums\">\n {count} {count === 1 ? \"span\" : \"spans\"}.{\" \"}\n {formatDuration(layout.durationMs)}.\n </div>\n </div>\n ) : null}\n <div\n aria-hidden\n className={cn(COLUMNS, \"border-pho-secondary h-7 border-b\")}\n >\n <div />\n <div className=\"border-pho-secondary relative h-full border-l\">\n {layout.ticks.map((tick) => {\n const at = layout.durationMs > 0 ? tick / layout.durationMs : 0;\n // A tick near the end reads to the left of its mark, so the\n // label never runs past the axis's edge.\n const late = at > 0.85;\n return (\n <span\n key={tick}\n className=\"text-pho-description absolute top-0 h-full text-[10px] tabular-nums\"\n style={{ left: percent(at) }}\n >\n <span className=\"bg-pho-secondary absolute bottom-0 left-0 h-1.5 w-px\" />\n <span\n className={cn(\n \"absolute bottom-0 pb-1.5 whitespace-nowrap\",\n late ? \"right-1\" : \"left-1\",\n )}\n >\n {tick === 0 ? \"0\" : formatTick(tick)}\n </span>\n </span>\n );\n })}\n </div>\n </div>\n <ol\n aria-label={typeof title === \"string\" ? title : \"Spans\"}\n className=\"m-0 list-none p-0\"\n >\n {layout.rows.map((row) =>\n hidden.has(row.span.id) ? null : (\n <li key={row.span.id} className=\"m-0\">\n <SpanRow\n row={row}\n collapsed={collapsed.has(row.span.id)}\n selected={selectedId != null && selectedId === row.span.id}\n onToggle={() => toggle(row.span.id)}\n onSelect={onSelect ? () => onSelect(row.span.id) : undefined}\n />\n </li>\n ),\n )}\n </ol>\n </div>\n );\n}\n\n/* --------------------------------- logs --------------------------------- */\n\nexport type TraceLogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport interface TraceLog {\n id?: string;\n /** When, in ms after the trace's start. */\n offsetMs: number;\n level?: TraceLogLevel;\n message: string;\n /** The span it was written in, when known — its name shows after. */\n spanId?: string;\n}\n\nconst LEVEL_INK: Record<TraceLogLevel, string> = {\n debug: \"text-pho-description\",\n info: \"text-pho-secondary\",\n warn: \"text-pho-warning\",\n error: \"text-pho-error\",\n};\n\nexport interface TraceLogsProps extends Omit<\n ComponentProps<\"section\">,\n \"children\" | \"title\"\n> {\n logs?: readonly TraceLog[] | null;\n /** The spans, to name the one each line was written in. */\n spans?: readonly TraceSpan[];\n /** @default \"Logs\" */\n title?: ReactNode;\n ref?: Ref<HTMLElement>;\n}\n\n/**\n * The log lines that belong to the trace, each at its offset from the\n * start, in the mono face. Renders nothing when there are none — a\n * trace with no logs shows no empty block.\n */\nfunction Logs({\n logs,\n spans,\n title = \"Logs\",\n className,\n ...props\n}: TraceLogsProps) {\n const names = useMemo(\n () => new Map((spans ?? []).map((span) => [span.id, span.name])),\n [spans],\n );\n if (logs == null || logs.length === 0) return null;\n return (\n <section\n data-trace-logs=\"\"\n className={cn(TRACE_SURFACE, \"min-w-0\", className)}\n {...props}\n >\n <div className=\"border-pho-secondary flex min-h-10 items-center justify-between gap-3 border-b px-3\">\n <div className=\"text-pho-secondary min-w-0 truncate text-xs font-medium\">\n {title}\n </div>\n <div className=\"text-pho-description shrink-0 text-xs tabular-nums\">\n {logs.length} {logs.length === 1 ? \"line\" : \"lines\"}\n </div>\n </div>\n <ol className=\"divide-pho-secondary m-0 list-none divide-y p-0 font-mono text-xs\">\n {logs.map((log, index) => {\n const level = log.level ?? \"info\";\n const spanName = log.spanId ? names.get(log.spanId) : undefined;\n return (\n <li\n key={log.id ?? index}\n className=\"grid grid-cols-[5rem_3.5rem_minmax(0,1fr)] items-baseline gap-3 px-3 py-1.5\"\n >\n <span className=\"text-pho-description tabular-nums\">\n +{formatDuration(log.offsetMs)}\n </span>\n <span className={cn(\"truncate\", LEVEL_INK[level])}>\n {level.charAt(0).toUpperCase() + level.slice(1)}\n </span>\n <span className=\"text-pho-primary [overflow-wrap:anywhere] whitespace-pre-wrap\">\n {log.message}\n {spanName ? (\n <span className=\"text-pho-description\"> {spanName}</span>\n ) : null}\n </span>\n </li>\n );\n })}\n </ol>\n </section>\n );\n}\n\n/**\n * ```tsx\n * <Trace.Root spans={spans} selectedId={id} onSelect={setId} title=\"Waterfall\" />\n * <Trace.Logs logs={logs} spans={spans} />\n * ```\n */\nexport const Trace = { Root, Logs };\n"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ "use client";
2
+ import { useCallback as a, useEffect as n, useRef as i, useState as o } from "react";
3
+ var s = 1500;
4
+ function f(t = s) {
5
+ const [u, r] = o(!1), e = i(null);
6
+ return n(() => () => {
7
+ e.current && clearTimeout(e.current);
8
+ }, []), {
9
+ copied: u,
10
+ copy: a(async (c) => {
11
+ try {
12
+ await navigator.clipboard.writeText(c);
13
+ } catch {
14
+ return !1;
15
+ }
16
+ return r(!0), e.current && clearTimeout(e.current), e.current = setTimeout(() => r(!1), t), !0;
17
+ }, [t])
18
+ };
19
+ }
20
+ export {
21
+ f as n,
22
+ s as t
23
+ };
24
+
25
+ //# sourceMappingURL=use-copy-D6JwKUll.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-copy-D6JwKUll.js","names":[],"sources":["../../src/utils/use-copy.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\n\n/** How long a copy control reads \"Copied\" before it settles back. */\nexport const COPY_FEEDBACK_MS = 1500;\n\n/**\n * Copy text to the clipboard and remember, briefly, that it happened —\n * the state behind every copy affordance in Pho (the id in a log row,\n * the value in a description list, a code panel's button). `copy`\n * resolves to whether the write landed; `copied` holds for\n * `COPY_FEEDBACK_MS` so the control can swap its glyph and its live\n * region can announce. SSR-safe: the clipboard is only touched on call.\n */\nexport function useCopy(resetMs = COPY_FEEDBACK_MS) {\n const [copied, setCopied] = useState(false);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(\n () => () => {\n if (timer.current) clearTimeout(timer.current);\n },\n [],\n );\n\n const copy = useCallback(\n async (text: string) => {\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n return false;\n }\n setCopied(true);\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopied(false), resetMs);\n return true;\n },\n [resetMs],\n );\n\n return { copied, copy };\n}\n"],"mappings":";;AAGA,IAAa,IAAmB;AAUhC,SAAgB,EAAQ,IAAU,GAAkB;AAClD,QAAM,CAAC,GAAQ,CAAA,IAAa,EAAS,EAAK,GACpC,IAAQ,EAA6C,IAAI;AAE/D,SAAA,EAAA,MAAA,MACc;AACV,IAAI,EAAM,WAAS,aAAa,EAAM,OAAO;AAAA,EAC/C,GACA,CAAC,CACH,GAiBO;AAAA,IAAE,QAAA;AAAA,IAAQ,MAfJ,EACX,OAAO,MAAiB;AACtB,UAAI;AACF,cAAM,UAAU,UAAU,UAAU,CAAI;AAAA,MAC1C,QAAQ;AACN,eAAO;AAAA,MACT;AACA,aAAA,EAAU,EAAI,GACV,EAAM,WAAS,aAAa,EAAM,OAAO,GAC7C,EAAM,UAAU,WAAA,MAAiB,EAAU,EAAK,GAAG,CAAO,GACnD;AAAA,IACT,GACA,CAAC,CAAO,CAGO;AAAA,EAAK;AACxB"}
@@ -0,0 +1,6 @@
1
+ "use client";
2
+ import { n as e, t } from "../chunks/code-block-CdozzaHM.js";
3
+ export {
4
+ t as CodeBlock,
5
+ e as tokenizeJson
6
+ };
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { t } from "../chunks/combobox-Dj5m_dDd.js";
2
+ import { t } from "../chunks/combobox-D4LiGJB2.js";
3
3
  export {
4
4
  t as Combobox
5
5
  };