@photon-ai/pho-ui 2.6.0 → 2.7.1

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 (78) hide show
  1. package/dist/chunks/IconChevronDown-CZilB71T.js +11 -0
  2. package/dist/chunks/IconChevronDown-CZilB71T.js.map +1 -0
  3. package/dist/chunks/IconSearch-BKAHkLyX.js +14 -0
  4. package/dist/chunks/IconSearch-BKAHkLyX.js.map +1 -0
  5. package/dist/chunks/animated-height-CCO-Ocoz.js +48 -0
  6. package/dist/chunks/animated-height-CCO-Ocoz.js.map +1 -0
  7. package/dist/chunks/basic-page-BF4jnCa1.js +1373 -0
  8. package/dist/chunks/basic-page-BF4jnCa1.js.map +1 -0
  9. package/dist/chunks/{button-DNNs5RsT.js → button-CdqS_t5a.js} +62 -65
  10. package/dist/chunks/button-CdqS_t5a.js.map +1 -0
  11. package/dist/chunks/checkbox-Cdm08HWG.js +129 -0
  12. package/dist/chunks/checkbox-Cdm08HWG.js.map +1 -0
  13. package/dist/chunks/{choice-card-CunOnY9l.js → choice-card-BVNP1NB6.js} +2 -2
  14. package/dist/chunks/{choice-card-CunOnY9l.js.map → choice-card-BVNP1NB6.js.map} +1 -1
  15. package/dist/chunks/data-table--f6am1O3.js +279 -0
  16. package/dist/chunks/data-table--f6am1O3.js.map +1 -0
  17. package/dist/chunks/{dialog-8SRNi_rv.js → dialog-BxKACOj5.js} +4 -4
  18. package/dist/chunks/{dialog-8SRNi_rv.js.map → dialog-BxKACOj5.js.map} +1 -1
  19. package/dist/chunks/{error-Crs-wqMC.js → error-D0yoFxCv.js} +2 -2
  20. package/dist/chunks/{error-Crs-wqMC.js.map → error-D0yoFxCv.js.map} +1 -1
  21. package/dist/chunks/is-react-component-DSnjtBmo.js +10 -0
  22. package/dist/chunks/is-react-component-DSnjtBmo.js.map +1 -0
  23. package/dist/chunks/link-provider-DXzDQ08N.js +24 -0
  24. package/dist/chunks/link-provider-DXzDQ08N.js.map +1 -0
  25. package/dist/chunks/{scroll-area-Bu7egArU.js → scroll-area-BO-9PMM7.js} +27 -26
  26. package/dist/chunks/scroll-area-BO-9PMM7.js.map +1 -0
  27. package/dist/chunks/sidebar-DVzZsCts.js +223 -0
  28. package/dist/chunks/sidebar-DVzZsCts.js.map +1 -0
  29. package/dist/chunks/skeleton-DwjSl4vC.js +18 -0
  30. package/dist/chunks/skeleton-DwjSl4vC.js.map +1 -0
  31. package/dist/chunks/{toast-DoXaRPIZ.js → toast-iSf6NuAS.js} +43 -45
  32. package/dist/chunks/toast-iSf6NuAS.js.map +1 -0
  33. package/dist/chunks/use-press-pulse-IbNstUT0.js +58 -0
  34. package/dist/chunks/use-press-pulse-IbNstUT0.js.map +1 -0
  35. package/dist/components/button.js +1 -1
  36. package/dist/components/checkbox.js +4 -126
  37. package/dist/components/command.js +54 -57
  38. package/dist/components/command.js.map +1 -1
  39. package/dist/components/data-table.js +11 -0
  40. package/dist/components/dialog.js +1 -1
  41. package/dist/components/error.js +1 -1
  42. package/dist/components/phone-input.js +608 -615
  43. package/dist/components/phone-input.js.map +1 -1
  44. package/dist/components/radio-group.js +2 -2
  45. package/dist/components/scroll-area.js +1 -1
  46. package/dist/components/select.js +35 -39
  47. package/dist/components/select.js.map +1 -1
  48. package/dist/components/skeleton.js +2 -15
  49. package/dist/components/toast.js +1 -1
  50. package/dist/index.js +94 -73
  51. package/dist/motion.js +89 -0
  52. package/dist/motion.js.map +1 -0
  53. package/dist/primitives.js +2 -2
  54. package/dist/sections/basic-page.js +16 -14
  55. package/dist/sections/sidebar.js +1 -1
  56. package/dist/src/components/data-table/data-table.d.ts +227 -0
  57. package/dist/src/components/data-table/index.d.ts +2 -0
  58. package/dist/src/index.d.ts +1 -0
  59. package/dist/src/motion/index.d.ts +149 -0
  60. package/dist/src/sections/basic-page/basic-page.d.ts +47 -7
  61. package/dist/src/sections/basic-page/index.d.ts +1 -0
  62. package/dist/utils.js +13 -12
  63. package/package.json +10 -1
  64. package/dist/chunks/animated-height-C2eQstGy.js +0 -51
  65. package/dist/chunks/animated-height-C2eQstGy.js.map +0 -1
  66. package/dist/chunks/basic-page-DsI1xo6p.js +0 -1295
  67. package/dist/chunks/basic-page-DsI1xo6p.js.map +0 -1
  68. package/dist/chunks/button-DNNs5RsT.js.map +0 -1
  69. package/dist/chunks/link-provider-9TFNyDqO.js +0 -28
  70. package/dist/chunks/link-provider-9TFNyDqO.js.map +0 -1
  71. package/dist/chunks/scroll-area-Bu7egArU.js.map +0 -1
  72. package/dist/chunks/sidebar-DvB-G0BP.js +0 -231
  73. package/dist/chunks/sidebar-DvB-G0BP.js.map +0 -1
  74. package/dist/chunks/toast-DoXaRPIZ.js.map +0 -1
  75. package/dist/chunks/use-press-pulse-8iorQPsh.js +0 -62
  76. package/dist/chunks/use-press-pulse-8iorQPsh.js.map +0 -1
  77. package/dist/components/checkbox.js.map +0 -1
  78. package/dist/components/skeleton.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"error-Crs-wqMC.js","names":[],"sources":["../../../../node_modules/.pnpm/@tabler+icons-react@3.46.0_react@19.2.8/node_modules/@tabler/icons-react/dist/esm/icons/IconAlertTriangle.mjs","../../src/components/error/problem.ts","../../src/components/error/error.tsx"],"sourcesContent":["/**\n * @license @tabler/icons-react v3.46.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M12 9v4\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M10.363 3.591l-8.106 13.534a1.914 1.914 0 0 0 1.636 2.871h16.214a1.914 1.914 0 0 0 1.636 -2.87l-8.106 -13.536a1.914 1.914 0 0 0 -3.274 0\", \"key\": \"svg-1\" }], [\"path\", { \"d\": \"M12 16h.01\", \"key\": \"svg-2\" }]];\nconst IconAlertTriangle = createReactComponent(\"outline\", \"alert-triangle\", \"AlertTriangle\", __iconNode);\n\nexport { __iconNode, IconAlertTriangle as default };\n//# sourceMappingURL=IconAlertTriangle.mjs.map\n","/**\n * Client-side mirror of the `@photon-hq/error` response contract — the\n * RFC 9457 Problem Details body every Photon service returns for 4xx / 5xx.\n * Machine logic branches on `code`; `title` / `detail` / `remediation` are\n * the human-readable copy, already reviewed server-side and safe to render.\n *\n * Fetch layers may also hand us a thin status-only shape when the response\n * was not ok but the body was not a contract problem — `title` + `status`\n * still count so the UI can disclose the HTTP code we already know.\n */\n\nexport interface ProblemRemediation {\n /** What the user can do about it — a full sentence. */\n action?: string;\n /** Docs URL explaining the problem. */\n documentation?: string;\n}\n\nexport interface Problem {\n /** Stable machine-readable key (`RATE_LIMITED`) — the field to branch on. */\n code?: string;\n /** Human summary of the problem type (\"Too Many Requests\"). */\n title: string;\n /** HTTP status (4xx / 5xx). */\n status: number;\n /** Problem type URI (`urn:photon:problem:*` or an https URL). */\n type?: string;\n /** Occurrence-specific human sentence. */\n detail?: string;\n /** Correlation id for support (`X-Request-ID`). */\n requestId?: string;\n remediation?: ProblemRemediation;\n /** RFC 9457 extensions (`issues`, `actualVersion`, …). */\n [extension: string]: unknown;\n}\n\n/** Structural check: `title` + a 4xx/5xx status. `code` is optional. */\nexport function isProblem(value: unknown): value is Problem {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.title === \"string\" &&\n typeof v.status === \"number\" &&\n Number.isInteger(v.status) &&\n v.status >= 400 &&\n v.status <= 599 &&\n (v.code === undefined || typeof v.code === \"string\")\n );\n}\n\n/**\n * Find the problem a caught value carries — the value itself, one typed onto\n * an error as a `problem` field (dashboard's `ApiError` shape), or one\n * wrapped in an `Error` `cause` chain (how fetch layers usually rethrow a\n * parsed body). Returns `null` when there is none.\n */\nexport function parseProblem(error: unknown): Problem | null {\n let current: unknown = error;\n for (\n let depth = 0;\n depth < 8 && typeof current === \"object\" && current !== null;\n depth++\n ) {\n if (isProblem(current)) return current;\n const carried = (current as { problem?: unknown }).problem;\n if (isProblem(carried)) return carried;\n current = (current as { cause?: unknown }).cause;\n }\n return null;\n}\n\nexport interface ErrorDescription {\n /** Short name of the failure — the problem `title`, or a generic fallback. */\n title: string;\n /** Sentence(s) to show — `detail` and `remediation.action` joined. */\n description?: string;\n /** Docs URL from `remediation.documentation`. */\n documentation?: string;\n /** Correlation id for support. */\n requestId?: string;\n /** The matched problem, when the input carried one. */\n problem?: Problem;\n}\n\n/**\n * Anything that isn't a problem renders as this — raw `Error` messages are\n * not contract copy and never shown (the same fail-closed stance as the\n * server side of `@photon-hq/error`). Always a full sentence (trailing\n * period).\n */\nexport const GENERIC_ERROR_TITLE = \"Something went wrong.\";\n\n/** Standard reason phrases for statuses we commonly surface without a body. */\nconst HTTP_REASON_PHRASE: Record<number, string> = {\n 400: \"Bad Request\",\n 401: \"Unauthorized\",\n 403: \"Forbidden\",\n 404: \"Not Found\",\n 408: \"Request Timeout\",\n 409: \"Conflict\",\n 410: \"Gone\",\n 413: \"Payload Too Large\",\n 415: \"Unsupported Media Type\",\n 422: \"Unprocessable Entity\",\n 429: \"Too Many Requests\",\n 500: \"Internal Server Error\",\n 501: \"Not Implemented\",\n 502: \"Bad Gateway\",\n 503: \"Service Unavailable\",\n 504: \"Gateway Timeout\",\n};\n\n/** One-sentence explanations when the server gave a status but no problem copy. */\nconst HTTP_STATUS_EXPLANATION: Record<number, string> = {\n 400: \"The request was malformed or incomplete.\",\n 401: \"You need to sign in again.\",\n 403: \"You don't have permission to do that.\",\n 404: \"The requested resource was not found.\",\n 408: \"The request timed out before the server responded.\",\n 409: \"The request conflicts with the current state.\",\n 410: \"That resource is no longer available.\",\n 413: \"The request was too large for the server to accept.\",\n 415: \"That media type isn't supported.\",\n 422: \"The request was understood but couldn't be processed.\",\n 429: \"Request rate limit exceeded. Wait a moment before trying again.\",\n 500: \"The server hit an unexpected error.\",\n 501: \"The server doesn't support that operation.\",\n 502: \"The gateway got an invalid response from an upstream server.\",\n 503: \"The service is temporarily unavailable.\",\n 504: \"The gateway timed out waiting for an upstream server.\",\n};\n\nfunction hasContractCode(problem: Problem): boolean {\n return typeof problem.code === \"string\" && problem.code !== \"\";\n}\n\n/** Reason phrase for a status — map first, then a non-generic `problem.title`. */\nexport function httpReasonPhrase(\n status: number,\n fallbackTitle?: string,\n): string | undefined {\n if (HTTP_REASON_PHRASE[status]) return HTTP_REASON_PHRASE[status];\n if (\n fallbackTitle &&\n fallbackTitle !== GENERIC_ERROR_TITLE &&\n fallbackTitle !== \"Request failed\"\n ) {\n return fallbackTitle.replace(/\\.$/, \"\");\n }\n return undefined;\n}\n\n/** Human sentence for a bare HTTP status, when the body carried no copy. */\nexport function httpStatusExplanation(status: number): string {\n return (\n HTTP_STATUS_EXPLANATION[status] ??\n \"The request failed with an unexpected status.\"\n );\n}\n\n/**\n * Sentence-case the first character. Server `detail` / `title` sometimes\n * arrive fully lowercase; UI copy always leads with a capital.\n */\nexport function capitalizeFirst(text: string): string {\n if (!text) return text;\n const first = text.charAt(0);\n const upper = first.toUpperCase();\n if (first === upper) return text;\n return upper + text.slice(1);\n}\n\n/**\n * Turn a caught value into displayable copy. `Error` and `ErrorView` call\n * this internally; use it directly for other surfaces (a toast, a banner).\n *\n * Status-only failures (HTTP not ok, but no contract `code` / copy) still\n * keep `problem` so the details panel can show the status. Title becomes the\n * reason phrase (e.g. \"Bad Gateway\"); description is a short explanation —\n * not just the numeric code.\n */\nexport function describeError(error: unknown): ErrorDescription {\n const problem = parseProblem(error);\n if (problem == null) return { title: GENERIC_ERROR_TITLE };\n\n const description =\n [problem.detail, problem.remediation?.action]\n .filter((part): part is string => typeof part === \"string\" && part !== \"\")\n .join(\" \") || undefined;\n\n if (!hasContractCode(problem) && description == null) {\n const reason = httpReasonPhrase(problem.status, problem.title);\n return {\n title: capitalizeFirst(reason ?? GENERIC_ERROR_TITLE),\n description: httpStatusExplanation(problem.status),\n requestId: problem.requestId,\n problem,\n };\n }\n\n return {\n title: capitalizeFirst(problem.title || GENERIC_ERROR_TITLE),\n description: description ? capitalizeFirst(description) : undefined,\n documentation: problem.remediation?.documentation,\n requestId: problem.requestId,\n problem,\n };\n}\n","import {\n useEffect,\n useRef,\n useState,\n type ComponentProps,\n type ReactNode,\n} from \"react\";\nimport {\n IconAlertCircleFilled,\n IconAlertTriangle,\n IconInfoCircle,\n} from \"@tabler/icons-react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button, ButtonLink } from \"../button/button\";\nimport { Popover } from \"../popover/popover\";\nimport { StatusView, type StatusViewSize } from \"../status-view/status-view\";\nimport {\n capitalizeFirst,\n describeError,\n httpReasonPhrase,\n type ErrorDescription,\n} from \"./problem\";\n\n/** Capitalize string copy; leave custom ReactNode overrides alone. */\nfunction displayCopy(value: ReactNode): ReactNode {\n return typeof value === \"string\" ? capitalizeFirst(value) : value;\n}\n\n/**\n * Design notes:\n * - ONE input: `error` is the caught value, whatever it is — a Problem\n * Details body from `@photon-hq/error`, an `Error` carrying one as\n * `cause`, or something unknown. The component derives its copy via\n * `describeError`; unknowns fall back to a generic line rather than\n * leaking raw messages.\n * - TWO presentations, one per altitude: `Error` is the inline line for\n * forms, cards, and action rows; `ErrorView` is the region / page state\n * built on StatusView (same `sm` / `base` size axis).\n * - Both render `null` when there is nothing to show, so they can sit\n * unconditionally in JSX (`<Error error={mutation.error} />`).\n * - Client-side *validation* is not this component's job — that belongs to\n * `Field.Error`, wired to control validity.\n */\n\nconst DOC_LINK_LABEL = \"Learn more\";\nconst RETRY_LABEL = \"Try again\";\n\ninterface ErrorBaseProps {\n /** The caught value — a problem body, an `Error` wrapping one, or unknown. */\n error?: unknown;\n /** Override the derived message. */\n children?: ReactNode;\n}\n\nexport interface ErrorProps\n extends ErrorBaseProps, Omit<ComponentProps<\"div\">, keyof ErrorBaseProps> {}\n\n/**\n * Error — one line of red ink naming a server failure (4xx / 5xx), placed\n * inline: under a form, in a card, beside the action that failed. Give it the\n * caught value and it renders the problem's `detail` (falling back to `title`,\n * then to a generic line), plus a docs link when the problem carries one.\n * Renders nothing while `error` is nullish, so it can stay mounted.\n *\n * For a region or page that failed to load, use {@link ErrorView}.\n */\nexport function Error({ error, children, className, ...props }: ErrorProps) {\n if (error == null && children == null) return null;\n const copy = describeError(error);\n return (\n <div\n role=\"alert\"\n className={cn(\n \"text-pho-error flex min-w-0 items-start gap-1.5 text-base\",\n className,\n )}\n {...props}\n >\n <span aria-hidden className=\"flex h-[1lh] shrink-0 items-center\">\n <IconAlertCircleFilled className=\"size-4\" />\n </span>\n <span className=\"min-w-0\">\n {displayCopy(children ?? copy.description ?? copy.title)}\n {copy.documentation != null && (\n <>\n {\" \"}\n <a\n href={copy.documentation}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"font-medium underline underline-offset-2\"\n >\n {DOC_LINK_LABEL}\n </a>\n </>\n )}\n {/* Trailing whitespace collapses when the details render null. */}{\" \"}\n <ErrorDetails copy={copy} />\n </span>\n </div>\n );\n}\n\nError.displayName = \"Error\";\n\n/**\n * Hover / focus disclosure on the inline line — the request's fuller story:\n * problem `title` (with the same alert icon + `text-pho-error` ink as the\n * inline line), the untruncated description, then `status` + `code` and the\n * copyable request id grouped below for a support handoff. Renders only when\n * the caught value actually carried a problem (or at least a request id); a\n * generic unknown has nothing more to disclose. Built on `Popover` rather\n * than `Tooltip` because the panel holds an interactive control (the copy\n * button) — `openOnHover` keeps the tooltip-like gesture.\n */\nfunction ErrorDetails({ copy }: { copy: ErrorDescription }) {\n const { problem, requestId } = copy;\n if (problem == null && requestId == null) return null;\n\n // Contract problems show `status CODE`; status-only shows `status Reason`.\n let statusLine: string | null = null;\n if (problem != null) {\n if (problem.code) {\n statusLine = `${problem.status} ${problem.code}`;\n } else {\n const reason = httpReasonPhrase(problem.status, problem.title);\n statusLine = reason\n ? `${problem.status} ${reason}`\n : String(problem.status);\n }\n }\n\n return (\n <Popover.Root>\n <Popover.Trigger\n openOnHover\n delay={150}\n aria-label=\"Error details\"\n className=\"outline-pho-brand inline-flex size-4 shrink-0 translate-y-[3px] cursor-help items-center justify-center rounded-full opacity-70 transition-opacity hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-2\"\n >\n <IconInfoCircle className=\"size-3.5\" />\n </Popover.Trigger>\n <Popover.Portal>\n <Popover.Positioner align=\"start\">\n <Popover.Popup className=\"text-pho-error\">\n <Popover.Title className=\"text-pho-error flex items-start gap-1.5\">\n <span aria-hidden className=\"flex h-[1lh] shrink-0 items-center\">\n <IconAlertCircleFilled className=\"size-4\" />\n </span>\n {copy.title}\n </Popover.Title>\n {copy.description != null && (\n <Popover.Description className=\"text-pho-error\">\n {copy.description}\n </Popover.Description>\n )}\n {(statusLine != null || requestId != null) && (\n <div className=\"text-pho-error mt-1 font-mono text-[10px] leading-snug\">\n {statusLine != null && <p>{statusLine}</p>}\n {requestId != null && (\n <RequestId requestId={requestId} className=\"mt-0\" />\n )}\n </div>\n )}\n </Popover.Popup>\n </Popover.Positioner>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\n/** Request-id footnote — click copies the id for support handoff. */\nfunction RequestId({\n requestId,\n className,\n}: {\n requestId: string;\n className?: string;\n}) {\n const [copied, setCopied] = useState(false);\n const copiedTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(\n () => () => {\n if (copiedTimer.current) clearTimeout(copiedTimer.current);\n },\n [],\n );\n\n return (\n <span\n className={cn(\"mt-1 block font-mono text-[10px] leading-snug\", className)}\n >\n Request ID:{\" \"}\n <button\n type=\"button\"\n className=\"outline-pho-brand inline cursor-pointer underline-offset-2 select-all hover:underline focus-visible:underline focus-visible:outline-2 focus-visible:-outline-offset-1\"\n aria-label={\n copied ? \"Request ID copied\" : `Copy request ID ${requestId}`\n }\n onClick={async () => {\n try {\n await navigator.clipboard.writeText(requestId);\n } catch {\n return;\n }\n setCopied(true);\n if (copiedTimer.current) clearTimeout(copiedTimer.current);\n copiedTimer.current = setTimeout(() => setCopied(false), 1500);\n }}\n >\n {requestId}\n </button>\n <output className=\"sr-only\" aria-live=\"polite\">\n {copied ? \"Copied\" : \"\"}\n </output>\n </span>\n );\n}\n\ninterface ErrorViewBaseProps {\n /** The caught value — a problem body, an `Error` wrapping one, or unknown. */\n error?: unknown;\n /** Override the derived title. */\n title?: ReactNode;\n /** Override the derived description. */\n description?: ReactNode;\n /** Override the warning glyph. */\n icon?: ReactNode;\n /** Offer a retry — renders a \"Try again\" button. */\n onRetry?: () => void;\n /** Replace the built-in action row entirely. */\n actions?: ReactNode;\n /** Size variant — StatusView's axis. @default \"base\" */\n size?: StatusViewSize;\n}\n\nexport interface ErrorViewProps\n extends\n ErrorViewBaseProps,\n Omit<ComponentProps<\"div\">, keyof ErrorViewBaseProps | \"children\"> {}\n\n/**\n * ErrorView — the failed state of a region or page, built on StatusView:\n * warning icon over the problem's `title`, its `detail` and remediation\n * below, an action row (`onRetry` and the problem's docs link), and the\n * request id as a footnote for support. Announces via `role=\"alert\"`.\n *\n * Use `size=\"sm\"` inside cards and panels, the default `base` for whole\n * pages. Renders nothing while `error` is nullish (unless given a static\n * `title`), so it can sit unconditionally next to a query.\n */\nexport function ErrorView({\n error,\n title,\n description,\n icon,\n onRetry,\n actions,\n size = \"base\",\n ...props\n}: ErrorViewProps) {\n if (error == null && title == null) return null;\n const copy = describeError(error);\n\n const resolvedDescription = displayCopy(description ?? copy.description);\n const body =\n resolvedDescription != null || copy.requestId != null ? (\n <>\n {resolvedDescription}\n {copy.requestId != null && <RequestId requestId={copy.requestId} />}\n </>\n ) : undefined;\n\n const resolvedActions =\n actions ??\n (onRetry != null || copy.documentation != null ? (\n <>\n {onRetry != null && (\n <Button size=\"sm\" onClick={onRetry}>\n {RETRY_LABEL}\n </Button>\n )}\n {copy.documentation != null && (\n <ButtonLink\n size=\"sm\"\n variant=\"outlined\"\n href={copy.documentation}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {DOC_LINK_LABEL}\n </ButtonLink>\n )}\n </>\n ) : undefined);\n\n return (\n <StatusView\n role=\"alert\"\n size={size}\n icon={icon ?? <IconAlertTriangle />}\n title={displayCopy(title ?? copy.title)}\n description={body}\n actions={resolvedActions}\n {...props}\n />\n );\n}\n\nErrorView.displayName = \"ErrorView\";\n"],"x_google_ignoreList":[0],"mappings":";;;;;;;;;;AASA,IAAM,IAAa;AAAA,EAAC,CAAC,QAAQ;AAAA,IAAE,GAAK;AAAA,IAAW,KAAO;AAAA,EAAQ,CAAC;AAAA,EAAG,CAAC,QAAQ;AAAA,IAAE,GAAK;AAAA,IAA4I,KAAO;AAAA,EAAQ,CAAC;AAAA,EAAG,CAAC,QAAQ;AAAA,IAAE,GAAK;AAAA,IAAc,KAAO;AAAA,EAAQ,CAAC;AAAC,GAC1R,IAAoB,EAAqB,WAAW,kBAAkB,iBAAiB,CAAU;AC2BvG,SAAgB,EAAU,GAAkC;AAC1D,MAAI,OAAO,KAAU,YAAY,MAAU,KAAM,QAAO;AACxD,QAAM,IAAI;AACV,SACE,OAAO,EAAE,SAAU,YACnB,OAAO,EAAE,UAAW,YACpB,OAAO,UAAU,EAAE,MAAM,KACzB,EAAE,UAAU,OACZ,EAAE,UAAU,QACX,EAAE,SAAS,UAAa,OAAO,EAAE,QAAS;AAE/C;AAQA,SAAgB,EAAa,GAAgC;AAC3D,MAAI,IAAmB;AACvB,WACM,IAAQ,GACZ,IAAQ,KAAK,OAAO,KAAY,YAAY,MAAY,MACxD,KACA;AACA,QAAI,EAAU,CAAO,EAAG,QAAO;AAC/B,UAAM,IAAW,EAAkC;AACnD,QAAI,EAAU,CAAO,EAAG,QAAO;AAC/B,IAAA,IAAW,EAAgC;AAAA,EAC7C;AACA,SAAO;AACT;AAqBA,IAAa,IAAsB,yBAG7B,IAA6C;AAAA,EACjD,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP,GAGM,IAAkD;AAAA,EACtD,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAEA,SAAS,EAAgB,GAA2B;AAClD,SAAO,OAAO,EAAQ,QAAS,YAAY,EAAQ,SAAS;AAC9D;AAGA,SAAgB,EACd,GACA,GACoB;AACpB,MAAI,EAAmB,CAAA,EAAS,QAAO,EAAmB,CAAA;AAC1D,MACE,KACA,MAAA,2BACA,MAAkB,iBAElB,QAAO,EAAc,QAAQ,OAAO,EAAE;AAG1C;AAGA,SAAgB,EAAsB,GAAwB;AAC5D,SACE,EAAwB,CAAA,KACxB;AAEJ;AAMA,SAAgB,EAAgB,GAAsB;AACpD,MAAI,CAAC,EAAM,QAAO;AAClB,QAAM,IAAQ,EAAK,OAAO,CAAC,GACrB,IAAQ,EAAM,YAAY;AAChC,SAAI,MAAU,IAAc,IACrB,IAAQ,EAAK,MAAM,CAAC;AAC7B;AAWA,SAAgB,EAAc,GAAkC;AAC9D,QAAM,IAAU,EAAa,CAAK;AAClC,MAAI,KAAW,KAAM,QAAO,EAAE,OAAO,EAAoB;AAEzD,QAAM,IACJ,CAAC,EAAQ,QAAQ,EAAQ,aAAa,MAAM,EACzC,OAAA,CAAQ,MAAyB,OAAO,KAAS,YAAY,MAAS,EAAE,EACxE,KAAK,GAAG,KAAK;AAElB,SAAI,CAAC,EAAgB,CAAO,KAAK,KAAe,OAEvC;AAAA,IACL,OAAO,EAFM,EAAiB,EAAQ,QAAQ,EAAQ,KAE/B,KAAA,uBAA6B;AAAA,IACpD,aAAa,EAAsB,EAAQ,MAAM;AAAA,IACjD,WAAW,EAAQ;AAAA,IACnB,SAAA;AAAA,EACF,IAGK;AAAA,IACL,OAAO,EAAgB,EAAQ,SAAA,uBAA4B;AAAA,IAC3D,aAAa,IAAc,EAAgB,CAAW,IAAI;AAAA,IAC1D,eAAe,EAAQ,aAAa;AAAA,IACpC,WAAW,EAAQ;AAAA,IACnB,SAAA;AAAA,EACF;AACF;ACvLA,SAAS,EAAY,GAA6B;AAChD,SAAO,OAAO,KAAU,WAAW,EAAgB,CAAK,IAAI;AAC9D;AAkBA,IAAM,IAAiB,cACjB,IAAc;AAqBpB,SAAgB,EAAM,EAAE,OAAA,GAAO,UAAA,GAAU,WAAA,GAAW,GAAG,EAAA,GAAqB;AAC1E,MAAI,KAAS,QAAQ,KAAY,KAAM,QAAO;AAC9C,QAAM,IAAO,EAAc,CAAK;AAChC,SACE,gBAAA,EAAC,OAAD;AAAA,IACE,MAAK;AAAA,IACL,WAAW,EACT,6DACA,CACF;AAAA,IACA,GAAI;AAAA,IANN,UAAA,CAQE,gBAAA,EAAC,QAAD;AAAA,MAAM,eAAA;AAAA,MAAY,WAAU;AAAA,MAC1B,UAAA,gBAAA,EAAC,GAAD,EAAuB,WAAU,SAAU,CAAA;AAAA,IACvC,CAAA,GACN,gBAAA,EAAC,QAAD;AAAA,MAAM,WAAU;AAAA,MAAhB,UAAA;AAAA,QACG,EAAY,KAAY,EAAK,eAAe,EAAK,KAAK;AAAA,QACtD,EAAK,iBAAiB,QACrB,gBAAA,EAAA,GAAA,EAAA,UAAA,CACG,KACD,gBAAA,EAAC,KAAD;AAAA,UACE,MAAM,EAAK;AAAA,UACX,QAAO;AAAA,UACP,KAAI;AAAA,UACJ,WAAU;AAAA,UAET,UAAA;AAAA,QACA,CAAA,CACH,EAAA,CAAA;AAAA,QAEgE;AAAA,QACpE,gBAAA,EAAC,GAAD,EAAoB,MAAA,EAAO,CAAA;AAAA,MACvB;AAAA,IACH,CAAA,CAAA;AAAA;AAET;AAEA,EAAM,cAAc;AAYpB,SAAS,EAAa,EAAE,MAAA,EAAA,GAAoC;AAC1D,QAAM,EAAE,SAAA,GAAS,WAAA,EAAA,IAAc;AAC/B,MAAI,KAAW,QAAQ,KAAa,KAAM,QAAO;AAGjD,MAAI,IAA4B;AAChC,MAAI,KAAW;AACb,QAAI,EAAQ,KACV,CAAA,IAAa,GAAG,EAAQ,MAAA,IAAU,EAAQ,IAAA;AAAA,SACrC;AACL,YAAM,IAAS,EAAiB,EAAQ,QAAQ,EAAQ,KAAK;AAC7D,MAAA,IAAa,IACT,GAAG,EAAQ,MAAA,IAAU,CAAA,KACrB,OAAO,EAAQ,MAAM;AAAA,IAC3B;AAGF,SACE,gBAAA,EAAC,EAAQ,MAAT,EAAA,UAAA,CACE,gBAAA,EAAC,EAAQ,SAAT;AAAA,IACE,aAAA;AAAA,IACA,OAAO;AAAA,IACP,cAAW;AAAA,IACX,WAAU;AAAA,IAEV,UAAA,gBAAA,EAAC,GAAD,EAAgB,WAAU,WAAY,CAAA;AAAA,EACvB,CAAA,GACjB,gBAAA,EAAC,EAAQ,QAAT,EAAA,UACE,gBAAA,EAAC,EAAQ,YAAT;AAAA,IAAoB,OAAM;AAAA,IACxB,UAAA,gBAAA,EAAC,EAAQ,OAAT;AAAA,MAAe,WAAU;AAAA,MAAzB,UAAA;AAAA,QACE,gBAAA,EAAC,EAAQ,OAAT;AAAA,UAAe,WAAU;AAAA,UAAzB,UAAA,CACE,gBAAA,EAAC,QAAD;AAAA,YAAM,eAAA;AAAA,YAAY,WAAU;AAAA,YAC1B,UAAA,gBAAA,EAAC,GAAD,EAAuB,WAAU,SAAU,CAAA;AAAA,UACvC,CAAA,GACL,EAAK,KACO;AAAA;QACd,EAAK,eAAe,QACnB,gBAAA,EAAC,EAAQ,aAAT;AAAA,UAAqB,WAAU;AAAA,UAC5B,UAAA,EAAK;AAAA,QACa,CAAA;AAAA,SAErB,KAAc,QAAQ,KAAa,SACnC,gBAAA,EAAC,OAAD;AAAA,UAAK,WAAU;AAAA,UAAf,UAAA,CACG,KAAc,QAAQ,gBAAA,EAAC,KAAD,EAAA,UAAI,EAAc,CAAA,GACxC,KAAa,QACZ,gBAAA,EAAC,GAAD;AAAA,YAAsB,WAAA;AAAA,YAAW,WAAU;AAAA,UAAQ,CAAA,CAElD;AAAA;MAEM;AAAA;EACG,CAAA,EACN,CAAA,CACJ,EAAA,CAAA;AAElB;AAGA,SAAS,EAAU,EACjB,WAAA,GACA,WAAA,EAAA,GAIC;AACD,QAAM,CAAC,GAAQ,CAAA,IAAa,EAAS,EAAK,GACpC,IAAc,EAA6C,IAAI;AAErE,SAAA,EAAA,MAAA,MACc;AACV,IAAI,EAAY,WAAS,aAAa,EAAY,OAAO;AAAA,EAC3D,GACA,CAAC,CACH,GAGE,gBAAA,EAAC,QAAD;AAAA,IACE,WAAW,EAAG,iDAAiD,CAAS;AAAA,IAD1E,UAAA;AAAA,MAEC;AAAA,MACa;AAAA,MACZ,gBAAA,EAAC,UAAD;AAAA,QACE,MAAK;AAAA,QACL,WAAU;AAAA,QACV,cACE,IAAS,sBAAsB,mBAAmB,CAAA;AAAA,QAEpD,SAAS,YAAY;AACnB,cAAI;AACF,kBAAM,UAAU,UAAU,UAAU,CAAS;AAAA,UAC/C,QAAQ;AACN;AAAA,UACF;AACA,UAAA,EAAU,EAAI,GACV,EAAY,WAAS,aAAa,EAAY,OAAO,GACzD,EAAY,UAAU,WAAA,MAAiB,EAAU,EAAK,GAAG,IAAI;AAAA,QAC/D;AAAA,QAEC,UAAA;AAAA,MACK,CAAA;AAAA,MACR,gBAAA,EAAC,UAAD;AAAA,QAAQ,WAAU;AAAA,QAAU,aAAU;AAAA,QACnC,UAAA,IAAS,WAAW;AAAA,MACf,CAAA;AAAA,IACJ;AAAA;AAEV;AAkCA,SAAgB,EAAU,EACxB,OAAA,GACA,OAAA,GACA,aAAA,GACA,MAAA,GACA,SAAA,GACA,SAAA,GACA,MAAA,IAAO,QACP,GAAG,EAAA,GACc;AACjB,MAAI,KAAS,QAAQ,KAAS,KAAM,QAAO;AAC3C,QAAM,IAAO,EAAc,CAAK,GAE1B,IAAsB,EAAY,KAAe,EAAK,WAAW,GACjE,IACJ,KAAuB,QAAQ,EAAK,aAAa,OAC/C,gBAAA,EAAA,GAAA,EAAA,UAAA,CACG,GACA,EAAK,aAAa,QAAQ,gBAAA,EAAC,GAAD,EAAW,WAAW,EAAK,UAAY,CAAA,CAClE,EAAA,CAAA,IACA,QAEA,IACJ,MACC,KAAW,QAAQ,EAAK,iBAAiB,OACxC,gBAAA,EAAA,GAAA,EAAA,UAAA,CACG,KAAW,QACV,gBAAA,EAAC,GAAD;AAAA,IAAQ,MAAK;AAAA,IAAK,SAAS;AAAA,IACxB,UAAA;AAAA,EACK,CAAA,GAET,EAAK,iBAAiB,QACrB,gBAAA,EAAC,GAAD;AAAA,IACE,MAAK;AAAA,IACL,SAAQ;AAAA,IACR,MAAM,EAAK;AAAA,IACX,QAAO;AAAA,IACP,KAAI;AAAA,IAEH,UAAA;AAAA,EACS,CAAA,CAEd,EAAA,CAAA,IACA;AAEN,SACE,gBAAA,EAAC,GAAD;AAAA,IACE,MAAK;AAAA,IACC,MAAA;AAAA,IACN,MAAM,KAAQ,gBAAA,EAAC,GAAD,CAAoB,CAAA;AAAA,IAClC,OAAO,EAAY,KAAS,EAAK,KAAK;AAAA,IACtC,aAAa;AAAA,IACb,SAAS;AAAA,IACT,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,EAAU,cAAc"}
1
+ {"version":3,"file":"error-D0yoFxCv.js","names":[],"sources":["../../../../node_modules/.pnpm/@tabler+icons-react@3.46.0_react@19.2.8/node_modules/@tabler/icons-react/dist/esm/icons/IconAlertTriangle.mjs","../../src/components/error/problem.ts","../../src/components/error/error.tsx"],"sourcesContent":["/**\n * @license @tabler/icons-react v3.46.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M12 9v4\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M10.363 3.591l-8.106 13.534a1.914 1.914 0 0 0 1.636 2.871h16.214a1.914 1.914 0 0 0 1.636 -2.87l-8.106 -13.536a1.914 1.914 0 0 0 -3.274 0\", \"key\": \"svg-1\" }], [\"path\", { \"d\": \"M12 16h.01\", \"key\": \"svg-2\" }]];\nconst IconAlertTriangle = createReactComponent(\"outline\", \"alert-triangle\", \"AlertTriangle\", __iconNode);\n\nexport { __iconNode, IconAlertTriangle as default };\n//# sourceMappingURL=IconAlertTriangle.mjs.map\n","/**\n * Client-side mirror of the `@photon-hq/error` response contract — the\n * RFC 9457 Problem Details body every Photon service returns for 4xx / 5xx.\n * Machine logic branches on `code`; `title` / `detail` / `remediation` are\n * the human-readable copy, already reviewed server-side and safe to render.\n *\n * Fetch layers may also hand us a thin status-only shape when the response\n * was not ok but the body was not a contract problem — `title` + `status`\n * still count so the UI can disclose the HTTP code we already know.\n */\n\nexport interface ProblemRemediation {\n /** What the user can do about it — a full sentence. */\n action?: string;\n /** Docs URL explaining the problem. */\n documentation?: string;\n}\n\nexport interface Problem {\n /** Stable machine-readable key (`RATE_LIMITED`) — the field to branch on. */\n code?: string;\n /** Human summary of the problem type (\"Too Many Requests\"). */\n title: string;\n /** HTTP status (4xx / 5xx). */\n status: number;\n /** Problem type URI (`urn:photon:problem:*` or an https URL). */\n type?: string;\n /** Occurrence-specific human sentence. */\n detail?: string;\n /** Correlation id for support (`X-Request-ID`). */\n requestId?: string;\n remediation?: ProblemRemediation;\n /** RFC 9457 extensions (`issues`, `actualVersion`, …). */\n [extension: string]: unknown;\n}\n\n/** Structural check: `title` + a 4xx/5xx status. `code` is optional. */\nexport function isProblem(value: unknown): value is Problem {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.title === \"string\" &&\n typeof v.status === \"number\" &&\n Number.isInteger(v.status) &&\n v.status >= 400 &&\n v.status <= 599 &&\n (v.code === undefined || typeof v.code === \"string\")\n );\n}\n\n/**\n * Find the problem a caught value carries — the value itself, one typed onto\n * an error as a `problem` field (dashboard's `ApiError` shape), or one\n * wrapped in an `Error` `cause` chain (how fetch layers usually rethrow a\n * parsed body). Returns `null` when there is none.\n */\nexport function parseProblem(error: unknown): Problem | null {\n let current: unknown = error;\n for (\n let depth = 0;\n depth < 8 && typeof current === \"object\" && current !== null;\n depth++\n ) {\n if (isProblem(current)) return current;\n const carried = (current as { problem?: unknown }).problem;\n if (isProblem(carried)) return carried;\n current = (current as { cause?: unknown }).cause;\n }\n return null;\n}\n\nexport interface ErrorDescription {\n /** Short name of the failure — the problem `title`, or a generic fallback. */\n title: string;\n /** Sentence(s) to show — `detail` and `remediation.action` joined. */\n description?: string;\n /** Docs URL from `remediation.documentation`. */\n documentation?: string;\n /** Correlation id for support. */\n requestId?: string;\n /** The matched problem, when the input carried one. */\n problem?: Problem;\n}\n\n/**\n * Anything that isn't a problem renders as this — raw `Error` messages are\n * not contract copy and never shown (the same fail-closed stance as the\n * server side of `@photon-hq/error`). Always a full sentence (trailing\n * period).\n */\nexport const GENERIC_ERROR_TITLE = \"Something went wrong.\";\n\n/** Standard reason phrases for statuses we commonly surface without a body. */\nconst HTTP_REASON_PHRASE: Record<number, string> = {\n 400: \"Bad Request\",\n 401: \"Unauthorized\",\n 403: \"Forbidden\",\n 404: \"Not Found\",\n 408: \"Request Timeout\",\n 409: \"Conflict\",\n 410: \"Gone\",\n 413: \"Payload Too Large\",\n 415: \"Unsupported Media Type\",\n 422: \"Unprocessable Entity\",\n 429: \"Too Many Requests\",\n 500: \"Internal Server Error\",\n 501: \"Not Implemented\",\n 502: \"Bad Gateway\",\n 503: \"Service Unavailable\",\n 504: \"Gateway Timeout\",\n};\n\n/** One-sentence explanations when the server gave a status but no problem copy. */\nconst HTTP_STATUS_EXPLANATION: Record<number, string> = {\n 400: \"The request was malformed or incomplete.\",\n 401: \"You need to sign in again.\",\n 403: \"You don't have permission to do that.\",\n 404: \"The requested resource was not found.\",\n 408: \"The request timed out before the server responded.\",\n 409: \"The request conflicts with the current state.\",\n 410: \"That resource is no longer available.\",\n 413: \"The request was too large for the server to accept.\",\n 415: \"That media type isn't supported.\",\n 422: \"The request was understood but couldn't be processed.\",\n 429: \"Request rate limit exceeded. Wait a moment before trying again.\",\n 500: \"The server hit an unexpected error.\",\n 501: \"The server doesn't support that operation.\",\n 502: \"The gateway got an invalid response from an upstream server.\",\n 503: \"The service is temporarily unavailable.\",\n 504: \"The gateway timed out waiting for an upstream server.\",\n};\n\nfunction hasContractCode(problem: Problem): boolean {\n return typeof problem.code === \"string\" && problem.code !== \"\";\n}\n\n/** Reason phrase for a status — map first, then a non-generic `problem.title`. */\nexport function httpReasonPhrase(\n status: number,\n fallbackTitle?: string,\n): string | undefined {\n if (HTTP_REASON_PHRASE[status]) return HTTP_REASON_PHRASE[status];\n if (\n fallbackTitle &&\n fallbackTitle !== GENERIC_ERROR_TITLE &&\n fallbackTitle !== \"Request failed\"\n ) {\n return fallbackTitle.replace(/\\.$/, \"\");\n }\n return undefined;\n}\n\n/** Human sentence for a bare HTTP status, when the body carried no copy. */\nexport function httpStatusExplanation(status: number): string {\n return (\n HTTP_STATUS_EXPLANATION[status] ??\n \"The request failed with an unexpected status.\"\n );\n}\n\n/**\n * Sentence-case the first character. Server `detail` / `title` sometimes\n * arrive fully lowercase; UI copy always leads with a capital.\n */\nexport function capitalizeFirst(text: string): string {\n if (!text) return text;\n const first = text.charAt(0);\n const upper = first.toUpperCase();\n if (first === upper) return text;\n return upper + text.slice(1);\n}\n\n/**\n * Turn a caught value into displayable copy. `Error` and `ErrorView` call\n * this internally; use it directly for other surfaces (a toast, a banner).\n *\n * Status-only failures (HTTP not ok, but no contract `code` / copy) still\n * keep `problem` so the details panel can show the status. Title becomes the\n * reason phrase (e.g. \"Bad Gateway\"); description is a short explanation —\n * not just the numeric code.\n */\nexport function describeError(error: unknown): ErrorDescription {\n const problem = parseProblem(error);\n if (problem == null) return { title: GENERIC_ERROR_TITLE };\n\n const description =\n [problem.detail, problem.remediation?.action]\n .filter((part): part is string => typeof part === \"string\" && part !== \"\")\n .join(\" \") || undefined;\n\n if (!hasContractCode(problem) && description == null) {\n const reason = httpReasonPhrase(problem.status, problem.title);\n return {\n title: capitalizeFirst(reason ?? GENERIC_ERROR_TITLE),\n description: httpStatusExplanation(problem.status),\n requestId: problem.requestId,\n problem,\n };\n }\n\n return {\n title: capitalizeFirst(problem.title || GENERIC_ERROR_TITLE),\n description: description ? capitalizeFirst(description) : undefined,\n documentation: problem.remediation?.documentation,\n requestId: problem.requestId,\n problem,\n };\n}\n","import {\n useEffect,\n useRef,\n useState,\n type ComponentProps,\n type ReactNode,\n} from \"react\";\nimport {\n IconAlertCircleFilled,\n IconAlertTriangle,\n IconInfoCircle,\n} from \"@tabler/icons-react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button, ButtonLink } from \"../button/button\";\nimport { Popover } from \"../popover/popover\";\nimport { StatusView, type StatusViewSize } from \"../status-view/status-view\";\nimport {\n capitalizeFirst,\n describeError,\n httpReasonPhrase,\n type ErrorDescription,\n} from \"./problem\";\n\n/** Capitalize string copy; leave custom ReactNode overrides alone. */\nfunction displayCopy(value: ReactNode): ReactNode {\n return typeof value === \"string\" ? capitalizeFirst(value) : value;\n}\n\n/**\n * Design notes:\n * - ONE input: `error` is the caught value, whatever it is — a Problem\n * Details body from `@photon-hq/error`, an `Error` carrying one as\n * `cause`, or something unknown. The component derives its copy via\n * `describeError`; unknowns fall back to a generic line rather than\n * leaking raw messages.\n * - TWO presentations, one per altitude: `Error` is the inline line for\n * forms, cards, and action rows; `ErrorView` is the region / page state\n * built on StatusView (same `sm` / `base` size axis).\n * - Both render `null` when there is nothing to show, so they can sit\n * unconditionally in JSX (`<Error error={mutation.error} />`).\n * - Client-side *validation* is not this component's job — that belongs to\n * `Field.Error`, wired to control validity.\n */\n\nconst DOC_LINK_LABEL = \"Learn more\";\nconst RETRY_LABEL = \"Try again\";\n\ninterface ErrorBaseProps {\n /** The caught value — a problem body, an `Error` wrapping one, or unknown. */\n error?: unknown;\n /** Override the derived message. */\n children?: ReactNode;\n}\n\nexport interface ErrorProps\n extends ErrorBaseProps, Omit<ComponentProps<\"div\">, keyof ErrorBaseProps> {}\n\n/**\n * Error — one line of red ink naming a server failure (4xx / 5xx), placed\n * inline: under a form, in a card, beside the action that failed. Give it the\n * caught value and it renders the problem's `detail` (falling back to `title`,\n * then to a generic line), plus a docs link when the problem carries one.\n * Renders nothing while `error` is nullish, so it can stay mounted.\n *\n * For a region or page that failed to load, use {@link ErrorView}.\n */\nexport function Error({ error, children, className, ...props }: ErrorProps) {\n if (error == null && children == null) return null;\n const copy = describeError(error);\n return (\n <div\n role=\"alert\"\n className={cn(\n \"text-pho-error flex min-w-0 items-start gap-1.5 text-base\",\n className,\n )}\n {...props}\n >\n <span aria-hidden className=\"flex h-[1lh] shrink-0 items-center\">\n <IconAlertCircleFilled className=\"size-4\" />\n </span>\n <span className=\"min-w-0\">\n {displayCopy(children ?? copy.description ?? copy.title)}\n {copy.documentation != null && (\n <>\n {\" \"}\n <a\n href={copy.documentation}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"font-medium underline underline-offset-2\"\n >\n {DOC_LINK_LABEL}\n </a>\n </>\n )}\n {/* Trailing whitespace collapses when the details render null. */}{\" \"}\n <ErrorDetails copy={copy} />\n </span>\n </div>\n );\n}\n\nError.displayName = \"Error\";\n\n/**\n * Hover / focus disclosure on the inline line — the request's fuller story:\n * problem `title` (with the same alert icon + `text-pho-error` ink as the\n * inline line), the untruncated description, then `status` + `code` and the\n * copyable request id grouped below for a support handoff. Renders only when\n * the caught value actually carried a problem (or at least a request id); a\n * generic unknown has nothing more to disclose. Built on `Popover` rather\n * than `Tooltip` because the panel holds an interactive control (the copy\n * button) — `openOnHover` keeps the tooltip-like gesture.\n */\nfunction ErrorDetails({ copy }: { copy: ErrorDescription }) {\n const { problem, requestId } = copy;\n if (problem == null && requestId == null) return null;\n\n // Contract problems show `status CODE`; status-only shows `status Reason`.\n let statusLine: string | null = null;\n if (problem != null) {\n if (problem.code) {\n statusLine = `${problem.status} ${problem.code}`;\n } else {\n const reason = httpReasonPhrase(problem.status, problem.title);\n statusLine = reason\n ? `${problem.status} ${reason}`\n : String(problem.status);\n }\n }\n\n return (\n <Popover.Root>\n <Popover.Trigger\n openOnHover\n delay={150}\n aria-label=\"Error details\"\n className=\"outline-pho-brand inline-flex size-4 shrink-0 translate-y-[3px] cursor-help items-center justify-center rounded-full opacity-70 transition-opacity hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-2\"\n >\n <IconInfoCircle className=\"size-3.5\" />\n </Popover.Trigger>\n <Popover.Portal>\n <Popover.Positioner align=\"start\">\n <Popover.Popup className=\"text-pho-error\">\n <Popover.Title className=\"text-pho-error flex items-start gap-1.5\">\n <span aria-hidden className=\"flex h-[1lh] shrink-0 items-center\">\n <IconAlertCircleFilled className=\"size-4\" />\n </span>\n {copy.title}\n </Popover.Title>\n {copy.description != null && (\n <Popover.Description className=\"text-pho-error\">\n {copy.description}\n </Popover.Description>\n )}\n {(statusLine != null || requestId != null) && (\n <div className=\"text-pho-error mt-1 font-mono text-[10px] leading-snug\">\n {statusLine != null && <p>{statusLine}</p>}\n {requestId != null && (\n <RequestId requestId={requestId} className=\"mt-0\" />\n )}\n </div>\n )}\n </Popover.Popup>\n </Popover.Positioner>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\n/** Request-id footnote — click copies the id for support handoff. */\nfunction RequestId({\n requestId,\n className,\n}: {\n requestId: string;\n className?: string;\n}) {\n const [copied, setCopied] = useState(false);\n const copiedTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(\n () => () => {\n if (copiedTimer.current) clearTimeout(copiedTimer.current);\n },\n [],\n );\n\n return (\n <span\n className={cn(\"mt-1 block font-mono text-[10px] leading-snug\", className)}\n >\n Request ID:{\" \"}\n <button\n type=\"button\"\n className=\"outline-pho-brand inline cursor-pointer underline-offset-2 select-all hover:underline focus-visible:underline focus-visible:outline-2 focus-visible:-outline-offset-1\"\n aria-label={\n copied ? \"Request ID copied\" : `Copy request ID ${requestId}`\n }\n onClick={async () => {\n try {\n await navigator.clipboard.writeText(requestId);\n } catch {\n return;\n }\n setCopied(true);\n if (copiedTimer.current) clearTimeout(copiedTimer.current);\n copiedTimer.current = setTimeout(() => setCopied(false), 1500);\n }}\n >\n {requestId}\n </button>\n <output className=\"sr-only\" aria-live=\"polite\">\n {copied ? \"Copied\" : \"\"}\n </output>\n </span>\n );\n}\n\ninterface ErrorViewBaseProps {\n /** The caught value — a problem body, an `Error` wrapping one, or unknown. */\n error?: unknown;\n /** Override the derived title. */\n title?: ReactNode;\n /** Override the derived description. */\n description?: ReactNode;\n /** Override the warning glyph. */\n icon?: ReactNode;\n /** Offer a retry — renders a \"Try again\" button. */\n onRetry?: () => void;\n /** Replace the built-in action row entirely. */\n actions?: ReactNode;\n /** Size variant — StatusView's axis. @default \"base\" */\n size?: StatusViewSize;\n}\n\nexport interface ErrorViewProps\n extends\n ErrorViewBaseProps,\n Omit<ComponentProps<\"div\">, keyof ErrorViewBaseProps | \"children\"> {}\n\n/**\n * ErrorView — the failed state of a region or page, built on StatusView:\n * warning icon over the problem's `title`, its `detail` and remediation\n * below, an action row (`onRetry` and the problem's docs link), and the\n * request id as a footnote for support. Announces via `role=\"alert\"`.\n *\n * Use `size=\"sm\"` inside cards and panels, the default `base` for whole\n * pages. Renders nothing while `error` is nullish (unless given a static\n * `title`), so it can sit unconditionally next to a query.\n */\nexport function ErrorView({\n error,\n title,\n description,\n icon,\n onRetry,\n actions,\n size = \"base\",\n ...props\n}: ErrorViewProps) {\n if (error == null && title == null) return null;\n const copy = describeError(error);\n\n const resolvedDescription = displayCopy(description ?? copy.description);\n const body =\n resolvedDescription != null || copy.requestId != null ? (\n <>\n {resolvedDescription}\n {copy.requestId != null && <RequestId requestId={copy.requestId} />}\n </>\n ) : undefined;\n\n const resolvedActions =\n actions ??\n (onRetry != null || copy.documentation != null ? (\n <>\n {onRetry != null && (\n <Button size=\"sm\" onClick={onRetry}>\n {RETRY_LABEL}\n </Button>\n )}\n {copy.documentation != null && (\n <ButtonLink\n size=\"sm\"\n variant=\"outlined\"\n href={copy.documentation}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {DOC_LINK_LABEL}\n </ButtonLink>\n )}\n </>\n ) : undefined);\n\n return (\n <StatusView\n role=\"alert\"\n size={size}\n icon={icon ?? <IconAlertTriangle />}\n title={displayCopy(title ?? copy.title)}\n description={body}\n actions={resolvedActions}\n {...props}\n />\n );\n}\n\nErrorView.displayName = \"ErrorView\";\n"],"x_google_ignoreList":[0],"mappings":";;;;;;;;;;AASA,IAAM,IAAa;AAAA,EAAC,CAAC,QAAQ;AAAA,IAAE,GAAK;AAAA,IAAW,KAAO;AAAA,EAAQ,CAAC;AAAA,EAAG,CAAC,QAAQ;AAAA,IAAE,GAAK;AAAA,IAA4I,KAAO;AAAA,EAAQ,CAAC;AAAA,EAAG,CAAC,QAAQ;AAAA,IAAE,GAAK;AAAA,IAAc,KAAO;AAAA,EAAQ,CAAC;AAAC,GAC1R,IAAoB,EAAqB,WAAW,kBAAkB,iBAAiB,CAAU;AC2BvG,SAAgB,EAAU,GAAkC;AAC1D,MAAI,OAAO,KAAU,YAAY,MAAU,KAAM,QAAO;AACxD,QAAM,IAAI;AACV,SACE,OAAO,EAAE,SAAU,YACnB,OAAO,EAAE,UAAW,YACpB,OAAO,UAAU,EAAE,MAAM,KACzB,EAAE,UAAU,OACZ,EAAE,UAAU,QACX,EAAE,SAAS,UAAa,OAAO,EAAE,QAAS;AAE/C;AAQA,SAAgB,EAAa,GAAgC;AAC3D,MAAI,IAAmB;AACvB,WACM,IAAQ,GACZ,IAAQ,KAAK,OAAO,KAAY,YAAY,MAAY,MACxD,KACA;AACA,QAAI,EAAU,CAAO,EAAG,QAAO;AAC/B,UAAM,IAAW,EAAkC;AACnD,QAAI,EAAU,CAAO,EAAG,QAAO;AAC/B,IAAA,IAAW,EAAgC;AAAA,EAC7C;AACA,SAAO;AACT;AAqBA,IAAa,IAAsB,yBAG7B,IAA6C;AAAA,EACjD,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP,GAGM,IAAkD;AAAA,EACtD,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAEA,SAAS,EAAgB,GAA2B;AAClD,SAAO,OAAO,EAAQ,QAAS,YAAY,EAAQ,SAAS;AAC9D;AAGA,SAAgB,EACd,GACA,GACoB;AACpB,MAAI,EAAmB,CAAA,EAAS,QAAO,EAAmB,CAAA;AAC1D,MACE,KACA,MAAA,2BACA,MAAkB,iBAElB,QAAO,EAAc,QAAQ,OAAO,EAAE;AAG1C;AAGA,SAAgB,EAAsB,GAAwB;AAC5D,SACE,EAAwB,CAAA,KACxB;AAEJ;AAMA,SAAgB,EAAgB,GAAsB;AACpD,MAAI,CAAC,EAAM,QAAO;AAClB,QAAM,IAAQ,EAAK,OAAO,CAAC,GACrB,IAAQ,EAAM,YAAY;AAChC,SAAI,MAAU,IAAc,IACrB,IAAQ,EAAK,MAAM,CAAC;AAC7B;AAWA,SAAgB,EAAc,GAAkC;AAC9D,QAAM,IAAU,EAAa,CAAK;AAClC,MAAI,KAAW,KAAM,QAAO,EAAE,OAAO,EAAoB;AAEzD,QAAM,IACJ,CAAC,EAAQ,QAAQ,EAAQ,aAAa,MAAM,EACzC,OAAA,CAAQ,MAAyB,OAAO,KAAS,YAAY,MAAS,EAAE,EACxE,KAAK,GAAG,KAAK;AAElB,SAAI,CAAC,EAAgB,CAAO,KAAK,KAAe,OAEvC;AAAA,IACL,OAAO,EAFM,EAAiB,EAAQ,QAAQ,EAAQ,KAE/B,KAAA,uBAA6B;AAAA,IACpD,aAAa,EAAsB,EAAQ,MAAM;AAAA,IACjD,WAAW,EAAQ;AAAA,IACnB,SAAA;AAAA,EACF,IAGK;AAAA,IACL,OAAO,EAAgB,EAAQ,SAAA,uBAA4B;AAAA,IAC3D,aAAa,IAAc,EAAgB,CAAW,IAAI;AAAA,IAC1D,eAAe,EAAQ,aAAa;AAAA,IACpC,WAAW,EAAQ;AAAA,IACnB,SAAA;AAAA,EACF;AACF;ACvLA,SAAS,EAAY,GAA6B;AAChD,SAAO,OAAO,KAAU,WAAW,EAAgB,CAAK,IAAI;AAC9D;AAkBA,IAAM,IAAiB,cACjB,IAAc;AAqBpB,SAAgB,EAAM,EAAE,OAAA,GAAO,UAAA,GAAU,WAAA,GAAW,GAAG,EAAA,GAAqB;AAC1E,MAAI,KAAS,QAAQ,KAAY,KAAM,QAAO;AAC9C,QAAM,IAAO,EAAc,CAAK;AAChC,SACE,gBAAA,EAAC,OAAD;AAAA,IACE,MAAK;AAAA,IACL,WAAW,EACT,6DACA,CACF;AAAA,IACA,GAAI;AAAA,IANN,UAAA,CAQE,gBAAA,EAAC,QAAD;AAAA,MAAM,eAAA;AAAA,MAAY,WAAU;AAAA,MAC1B,UAAA,gBAAA,EAAC,GAAD,EAAuB,WAAU,SAAU,CAAA;AAAA,IACvC,CAAA,GACN,gBAAA,EAAC,QAAD;AAAA,MAAM,WAAU;AAAA,MAAhB,UAAA;AAAA,QACG,EAAY,KAAY,EAAK,eAAe,EAAK,KAAK;AAAA,QACtD,EAAK,iBAAiB,QACrB,gBAAA,EAAA,GAAA,EAAA,UAAA,CACG,KACD,gBAAA,EAAC,KAAD;AAAA,UACE,MAAM,EAAK;AAAA,UACX,QAAO;AAAA,UACP,KAAI;AAAA,UACJ,WAAU;AAAA,UAET,UAAA;AAAA,QACA,CAAA,CACH,EAAA,CAAA;AAAA,QAEgE;AAAA,QACpE,gBAAA,EAAC,GAAD,EAAoB,MAAA,EAAO,CAAA;AAAA,MACvB;AAAA,IACH,CAAA,CAAA;AAAA;AAET;AAEA,EAAM,cAAc;AAYpB,SAAS,EAAa,EAAE,MAAA,EAAA,GAAoC;AAC1D,QAAM,EAAE,SAAA,GAAS,WAAA,EAAA,IAAc;AAC/B,MAAI,KAAW,QAAQ,KAAa,KAAM,QAAO;AAGjD,MAAI,IAA4B;AAChC,MAAI,KAAW;AACb,QAAI,EAAQ,KACV,CAAA,IAAa,GAAG,EAAQ,MAAA,IAAU,EAAQ,IAAA;AAAA,SACrC;AACL,YAAM,IAAS,EAAiB,EAAQ,QAAQ,EAAQ,KAAK;AAC7D,MAAA,IAAa,IACT,GAAG,EAAQ,MAAA,IAAU,CAAA,KACrB,OAAO,EAAQ,MAAM;AAAA,IAC3B;AAGF,SACE,gBAAA,EAAC,EAAQ,MAAT,EAAA,UAAA,CACE,gBAAA,EAAC,EAAQ,SAAT;AAAA,IACE,aAAA;AAAA,IACA,OAAO;AAAA,IACP,cAAW;AAAA,IACX,WAAU;AAAA,IAEV,UAAA,gBAAA,EAAC,GAAD,EAAgB,WAAU,WAAY,CAAA;AAAA,EACvB,CAAA,GACjB,gBAAA,EAAC,EAAQ,QAAT,EAAA,UACE,gBAAA,EAAC,EAAQ,YAAT;AAAA,IAAoB,OAAM;AAAA,IACxB,UAAA,gBAAA,EAAC,EAAQ,OAAT;AAAA,MAAe,WAAU;AAAA,MAAzB,UAAA;AAAA,QACE,gBAAA,EAAC,EAAQ,OAAT;AAAA,UAAe,WAAU;AAAA,UAAzB,UAAA,CACE,gBAAA,EAAC,QAAD;AAAA,YAAM,eAAA;AAAA,YAAY,WAAU;AAAA,YAC1B,UAAA,gBAAA,EAAC,GAAD,EAAuB,WAAU,SAAU,CAAA;AAAA,UACvC,CAAA,GACL,EAAK,KACO;AAAA;QACd,EAAK,eAAe,QACnB,gBAAA,EAAC,EAAQ,aAAT;AAAA,UAAqB,WAAU;AAAA,UAC5B,UAAA,EAAK;AAAA,QACa,CAAA;AAAA,SAErB,KAAc,QAAQ,KAAa,SACnC,gBAAA,EAAC,OAAD;AAAA,UAAK,WAAU;AAAA,UAAf,UAAA,CACG,KAAc,QAAQ,gBAAA,EAAC,KAAD,EAAA,UAAI,EAAc,CAAA,GACxC,KAAa,QACZ,gBAAA,EAAC,GAAD;AAAA,YAAsB,WAAA;AAAA,YAAW,WAAU;AAAA,UAAQ,CAAA,CAElD;AAAA;MAEM;AAAA;EACG,CAAA,EACN,CAAA,CACJ,EAAA,CAAA;AAElB;AAGA,SAAS,EAAU,EACjB,WAAA,GACA,WAAA,EAAA,GAIC;AACD,QAAM,CAAC,GAAQ,CAAA,IAAa,EAAS,EAAK,GACpC,IAAc,EAA6C,IAAI;AAErE,SAAA,EAAA,MAAA,MACc;AACV,IAAI,EAAY,WAAS,aAAa,EAAY,OAAO;AAAA,EAC3D,GACA,CAAC,CACH,GAGE,gBAAA,EAAC,QAAD;AAAA,IACE,WAAW,EAAG,iDAAiD,CAAS;AAAA,IAD1E,UAAA;AAAA,MAEC;AAAA,MACa;AAAA,MACZ,gBAAA,EAAC,UAAD;AAAA,QACE,MAAK;AAAA,QACL,WAAU;AAAA,QACV,cACE,IAAS,sBAAsB,mBAAmB,CAAA;AAAA,QAEpD,SAAS,YAAY;AACnB,cAAI;AACF,kBAAM,UAAU,UAAU,UAAU,CAAS;AAAA,UAC/C,QAAQ;AACN;AAAA,UACF;AACA,UAAA,EAAU,EAAI,GACV,EAAY,WAAS,aAAa,EAAY,OAAO,GACzD,EAAY,UAAU,WAAA,MAAiB,EAAU,EAAK,GAAG,IAAI;AAAA,QAC/D;AAAA,QAEC,UAAA;AAAA,MACK,CAAA;AAAA,MACR,gBAAA,EAAC,UAAD;AAAA,QAAQ,WAAU;AAAA,QAAU,aAAU;AAAA,QACnC,UAAA,IAAS,WAAW;AAAA,MACf,CAAA;AAAA,IACJ;AAAA;AAEV;AAkCA,SAAgB,EAAU,EACxB,OAAA,GACA,OAAA,GACA,aAAA,GACA,MAAA,GACA,SAAA,GACA,SAAA,GACA,MAAA,IAAO,QACP,GAAG,EAAA,GACc;AACjB,MAAI,KAAS,QAAQ,KAAS,KAAM,QAAO;AAC3C,QAAM,IAAO,EAAc,CAAK,GAE1B,IAAsB,EAAY,KAAe,EAAK,WAAW,GACjE,IACJ,KAAuB,QAAQ,EAAK,aAAa,OAC/C,gBAAA,EAAA,GAAA,EAAA,UAAA,CACG,GACA,EAAK,aAAa,QAAQ,gBAAA,EAAC,GAAD,EAAW,WAAW,EAAK,UAAY,CAAA,CAClE,EAAA,CAAA,IACA,QAEA,IACJ,MACC,KAAW,QAAQ,EAAK,iBAAiB,OACxC,gBAAA,EAAA,GAAA,EAAA,UAAA,CACG,KAAW,QACV,gBAAA,EAAC,GAAD;AAAA,IAAQ,MAAK;AAAA,IAAK,SAAS;AAAA,IACxB,UAAA;AAAA,EACK,CAAA,GAET,EAAK,iBAAiB,QACrB,gBAAA,EAAC,GAAD;AAAA,IACE,MAAK;AAAA,IACL,SAAQ;AAAA,IACR,MAAM,EAAK;AAAA,IACX,QAAO;AAAA,IACP,KAAI;AAAA,IAEH,UAAA;AAAA,EACS,CAAA,CAEd,EAAA,CAAA,IACA;AAEN,SACE,gBAAA,EAAC,GAAD;AAAA,IACE,MAAK;AAAA,IACC,MAAA;AAAA,IACN,MAAM,KAAQ,gBAAA,EAAC,GAAD,CAAoB,CAAA;AAAA,IAClC,OAAO,EAAY,KAAS,EAAK,KAAK;AAAA,IACtC,aAAa;AAAA,IACb,SAAS;AAAA,IACT,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,EAAU,cAAc"}
@@ -0,0 +1,10 @@
1
+ "use client";
2
+ import { isValidElement as n } from "react";
3
+ function e(t) {
4
+ return typeof t == "function" ? !0 : typeof t == "object" && t !== null && "$$typeof" in t && !n(t);
5
+ }
6
+ export {
7
+ e as t
8
+ };
9
+
10
+ //# sourceMappingURL=is-react-component-DSnjtBmo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-react-component-DSnjtBmo.js","names":[],"sources":["../../src/utils/is-react-component.ts"],"sourcesContent":["import { isValidElement, type ComponentType } from \"react\";\n\n/**\n * Distinguish an icon passed as a *component* (e.g. a Tabler icon or any\n * `FC`/forwardRef) from one passed as an already-rendered *element*. Lets icon\n * props accept either form: `prefix={Plus}` or `prefix={<Plus />}`.\n */\nexport function isReactComponent(\n value: unknown,\n): value is ComponentType<{ className?: string }> {\n if (typeof value === \"function\") return true;\n // forwardRef / memo components are objects carrying `$$typeof` but, unlike a\n // rendered element, are not valid React elements.\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"$$typeof\" in value &&\n !isValidElement(value)\n );\n}\n"],"mappings":";;AAOA,SAAgB,EACd,GACgD;AAChD,SAAI,OAAO,KAAU,aAAmB,KAItC,OAAO,KAAU,YACjB,MAAU,QACV,cAAc,KACd,CAAC,EAAe,CAAK;AAEzB"}
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ import { createContext as o, createElement as a, forwardRef as i, useContext as u } from "react";
3
+ import { jsx as f } from "react/jsx-runtime";
4
+ var n = i((e, t) => a("a", {
5
+ ...e,
6
+ ref: t
7
+ }));
8
+ n.displayName = "DefaultLink";
9
+ var r = o(n);
10
+ function l({ component: e, children: t }) {
11
+ return /* @__PURE__ */ f(r.Provider, {
12
+ value: e,
13
+ children: t
14
+ });
15
+ }
16
+ function c() {
17
+ return u(r);
18
+ }
19
+ export {
20
+ c as n,
21
+ l as t
22
+ };
23
+
24
+ //# sourceMappingURL=link-provider-DXzDQ08N.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-provider-DXzDQ08N.js","names":[],"sources":["../../src/utils/link-provider.tsx"],"sourcesContent":["import {\n createContext,\n createElement,\n forwardRef,\n useContext,\n type AnchorHTMLAttributes,\n type ComponentType,\n type ReactNode,\n} from \"react\";\n\n/**\n * The shape a host app's link component must satisfy. Routers (TanStack Router,\n * React Router, Next.js) expose a `Link` that takes `href` + anchor props; this\n * lets Pho components render real client-side links without depending on any\n * specific router.\n */\nexport type LinkComponent = ComponentType<\n AnchorHTMLAttributes<HTMLAnchorElement> & { href: string }\n>;\n\n// Forwards its ref so wrappers (e.g. Motion) can reach the underlying anchor.\nconst DefaultLink: LinkComponent = forwardRef<\n HTMLAnchorElement,\n AnchorHTMLAttributes<HTMLAnchorElement> & { href: string }\n>((props, ref) => createElement(\"a\", { ...props, ref }));\nDefaultLink.displayName = \"DefaultLink\";\n\nconst LinkContext = createContext<LinkComponent>(DefaultLink);\n\n/** Inject the host router's link component for all Pho links beneath it. */\nexport function LinkProvider({\n component,\n children,\n}: {\n component: LinkComponent;\n children: ReactNode;\n}) {\n return (\n <LinkContext.Provider value={component}>{children}</LinkContext.Provider>\n );\n}\n\n/** Read the active link component (falls back to a plain `<a>`). */\nexport function useLinkComponent(): LinkComponent {\n return useContext(LinkContext);\n}\n"],"mappings":";;;AAqBA,IAAM,IAA6B,EAAA,CAGhC,GAAO,MAAQ,EAAc,KAAK;AAAA,EAAE,GAAG;AAAA,EAAO,KAAA;AAAI,CAAC,CAAC;AACvD,EAAY,cAAc;AAE1B,IAAM,IAAc,EAA6B,CAAW;AAG5D,SAAgB,EAAa,EAC3B,WAAA,GACA,UAAA,EAAA,GAIC;AACD,SACE,gBAAA,EAAC,EAAY,UAAb;AAAA,IAAsB,OAAO;AAAA,IAAY,UAAA;AAAA,EAA+B,CAAA;AAE5E;AAGA,SAAgB,IAAkC;AAChD,SAAO,EAAW,CAAW;AAC/B"}
@@ -1,11 +1,12 @@
1
1
  "use client";
2
2
  import { t as d } from "./cn-ChIgwEl3.js";
3
+ import "../motion.js";
3
4
  import { createContext as S, useContext as A, useLayoutEffect as k, useRef as m } from "react";
4
5
  import { jsx as l, jsxs as x } from "react/jsx-runtime";
5
6
  import { motion as C } from "motion/react";
6
7
  import { ScrollArea as c } from "@base-ui/react/scroll-area";
7
- var L = "pointer-events-none absolute inset-x-0 z-10 h-8", $ = 8, q = 1;
8
- function N(t, e) {
8
+ var L = "pointer-events-none absolute inset-x-0 z-10 h-8", $ = 8, N = 1;
9
+ function _(t, e) {
9
10
  const a = 100 / (t + 1);
10
11
  return Array.from({ length: t }, (r, o) => ({
11
12
  blurPx: o / (t - 1) * e,
@@ -17,13 +18,13 @@ function N(t, e) {
17
18
  ]
18
19
  })).filter((r) => r.blurPx > 0);
19
20
  }
20
- var _ = N($, 1);
21
+ var P = _($, N);
21
22
  function T(t, e) {
22
23
  const a = t === "top" ? "to top" : "to bottom", [r, o, i, n] = e;
23
24
  return `linear-gradient(${a}, transparent ${r}%, black ${o}%, black ${i}%, transparent ${n}%)`;
24
25
  }
25
- var z = "var(--pho-surface, var(--background-color-pho-primary))";
26
- function F(t, e) {
26
+ var U = "var(--pho-surface, var(--background-color-pho-primary))";
27
+ function z(t, e) {
27
28
  return `linear-gradient(${t === "top" ? "to top" : "to bottom"}, color-mix(in oklab, ${e} 20%, transparent) 0%, ${e} 100%)`;
28
29
  }
29
30
  function h(t, e) {
@@ -38,13 +39,13 @@ function h(t, e) {
38
39
  r.style.backdropFilter = o, r.style.setProperty("-webkit-backdrop-filter", o);
39
40
  }
40
41
  }
41
- function g({ edge: t, edgeRef: e, surface: a = z, className: r }) {
42
+ function g({ edge: t, edgeRef: e, surface: a = U, className: r }) {
42
43
  return /* @__PURE__ */ x("div", {
43
44
  ref: e,
44
45
  "aria-hidden": !0,
45
46
  "data-edge": t,
46
47
  className: d(L, r ?? (t === "top" ? "top-0" : "bottom-0")),
47
- children: [_.map(({ blurPx: o, window: i }, n) => {
48
+ children: [P.map(({ blurPx: o, window: i }, n) => {
48
49
  const s = T(t, i);
49
50
  return /* @__PURE__ */ l("div", {
50
51
  "data-blur-px": o,
@@ -57,12 +58,12 @@ function g({ edge: t, edgeRef: e, surface: a = z, className: r }) {
57
58
  }), /* @__PURE__ */ l("div", {
58
59
  "data-scroll-fade": "wash",
59
60
  className: "absolute inset-0 opacity-0",
60
- style: { background: F(t, a) }
61
+ style: { background: z(t, a) }
61
62
  })]
62
63
  });
63
64
  }
64
65
  var E = S(null);
65
- function P() {
66
+ function B() {
66
67
  const t = A(E);
67
68
  if (t == null) throw new Error("ScrollArea parts must be used inside ScrollArea.Root");
68
69
  return t;
@@ -70,10 +71,10 @@ function P() {
70
71
  function y(t) {
71
72
  return t <= 0 ? 0 : t >= 1 ? 1 : t;
72
73
  }
73
- function U(t, e) {
74
+ function F(t, e) {
74
75
  typeof t == "function" ? t(e) : t && (t.current = e);
75
76
  }
76
- function B({ inset: t, insetTop: e, insetBottom: a, style: r, ref: o, ...i }) {
77
+ function H({ inset: t, insetTop: e, insetBottom: a, style: r, ref: o, ...i }) {
77
78
  return /* @__PURE__ */ l(c.Content, {
78
79
  ref: o,
79
80
  style: {
@@ -84,8 +85,8 @@ function B({ inset: t, insetTop: e, insetBottom: a, style: r, ref: o, ...i }) {
84
85
  ...i
85
86
  });
86
87
  }
87
- var H = c.Corner;
88
- function M({ className: t, children: e, ...a }) {
88
+ var M = c.Corner;
89
+ function O({ className: t, children: e, ...a }) {
89
90
  const r = m(null), o = m(null);
90
91
  return /* @__PURE__ */ l(c.Root, {
91
92
  className: d("relative overflow-hidden", t),
@@ -109,14 +110,14 @@ function M({ className: t, children: e, ...a }) {
109
110
  })
110
111
  });
111
112
  }
112
- function O({ className: t, ref: e, ...a }) {
113
- const { topEl: r, bottomEl: o } = P(), i = m(null);
113
+ function X({ className: t, ref: e, ...a }) {
114
+ const { topEl: r, bottomEl: o } = B(), i = m(null);
114
115
  return k(() => {
115
116
  const n = i.current;
116
117
  if (!n) return;
117
118
  const s = () => {
118
- const v = r.current?.offsetHeight || o.current?.offsetHeight || 32, p = n.scrollHeight - n.clientHeight, w = p <= 0 ? 0 : y(n.scrollTop / v), R = p <= 0 ? 0 : y((p - n.scrollTop) / v);
119
- h(r.current, w), h(o.current, R);
119
+ const v = r.current?.offsetHeight || o.current?.offsetHeight || 32, p = n.scrollHeight - n.clientHeight, R = p <= 0 ? 0 : y(n.scrollTop / v), w = p <= 0 ? 0 : y((p - n.scrollTop) / v);
120
+ h(r.current, R), h(o.current, w);
120
121
  };
121
122
  let u = 0;
122
123
  const b = () => {
@@ -134,7 +135,7 @@ function O({ className: t, ref: e, ...a }) {
134
135
  className: d("outline-pho-brand h-full min-h-0 w-full overscroll-contain rounded-[inherit] focus-visible:outline-2 focus-visible:-outline-offset-1", t),
135
136
  ...a,
136
137
  ref: (n) => {
137
- i.current = n, U(e, n);
138
+ i.current = n, F(e, n);
138
139
  }
139
140
  });
140
141
  }
@@ -150,17 +151,17 @@ function D({ className: t, ...e }) {
150
151
  ...e
151
152
  });
152
153
  }
153
- var W = {
154
- Root: M,
155
- Viewport: O,
156
- Content: B,
154
+ var K = {
155
+ Root: O,
156
+ Viewport: X,
157
+ Content: H,
157
158
  Scrollbar: j,
158
159
  Thumb: D,
159
- Corner: H
160
+ Corner: M
160
161
  };
161
162
  export {
162
- q as n,
163
- W as t
163
+ N as n,
164
+ K as t
164
165
  };
165
166
 
166
- //# sourceMappingURL=scroll-area-Bu7egArU.js.map
167
+ //# sourceMappingURL=scroll-area-BO-9PMM7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-area-BO-9PMM7.js","names":[],"sources":["../../src/components/scroll-area/scroll-area-edges.tsx","../../src/components/scroll-area/scroll-area.tsx"],"sourcesContent":["import { type Ref } from \"react\";\nimport { BLUR_PX } from \"../../motion\";\nimport { cn } from \"../../utils/cn\";\n\n/** Overlay strip height — a short ramp, not a banner. `h-8` = 32px. */\nexport const EDGE_CLASS = \"pointer-events-none absolute inset-x-0 z-10 h-8\";\nexport const EDGE_REVEAL_PX = 32;\n\nconst BLUR_LAYER_COUNT = 8;\n\n/**\n * The system's one blur strength. The edge fade peaks here, and a motion\n * that softens (a scope swap, a bar title) borrows the same value, so\n * every soft focus in the product is the same soft focus.\n */\nexport const BLUR_MAX_PX = BLUR_PX;\n\nfunction buildBlurLayers(count: number, maxPx: number) {\n const segment = 100 / (count + 1);\n return Array.from({ length: count }, (_, i) => ({\n blurPx: (i / (count - 1)) * maxPx,\n window: [\n i * segment,\n (i + 1) * segment,\n (i + 2) * segment,\n (i + 3) * segment,\n ] as const,\n })).filter((layer) => layer.blurPx > 0);\n}\n\nconst BLUR_LAYERS = buildBlurLayers(BLUR_LAYER_COUNT, BLUR_MAX_PX);\n\nfunction edgeMask(edge: \"top\" | \"bottom\", window: readonly number[]) {\n const toEdge = edge === \"top\" ? \"to top\" : \"to bottom\";\n const [a, b, c, d] = window;\n return `linear-gradient(${toEdge}, transparent ${a}%, black ${b}%, black ${c}%, transparent ${d}%)`;\n}\n\n// The wash fades into the block it sits on: every `bg-pho-*` surface\n// registers itself as `--pho-surface` (see the stylesheet), so the nearest\n// surface ancestor's background carries down here — live across themes.\n// The primary surface is the fallback for hosts outside any pho surface.\nconst DEFAULT_SURFACE =\n \"var(--pho-surface, var(--background-color-pho-primary))\";\n\nfunction washGradient(edge: \"top\" | \"bottom\", surface: string) {\n const to = edge === \"top\" ? \"to top\" : \"to bottom\";\n return `linear-gradient(${to}, color-mix(in oklab, ${surface} 20%, transparent) 0%, ${surface} 100%)`;\n}\n\nexport function paintEdge(node: HTMLDivElement | null, progress: number) {\n if (!node) return;\n for (const child of node.children) {\n const el = child as HTMLElement;\n if (el.dataset.scrollFade === \"wash\") {\n el.style.opacity = String(progress);\n continue;\n }\n const blur = `blur(${Number(el.dataset.blurPx) * progress}px)`;\n el.style.backdropFilter = blur;\n el.style.setProperty(\"-webkit-backdrop-filter\", blur);\n }\n}\n\nexport function Edge({\n edge,\n edgeRef,\n surface = DEFAULT_SURFACE,\n className,\n}: {\n edge: \"top\" | \"bottom\";\n edgeRef: Ref<HTMLDivElement>;\n /** The surface the wash fades into — the card by default, or a page. */\n surface?: string;\n /** Position override — the strip sits at the named edge unless placed. */\n className?: string;\n}) {\n return (\n <div\n ref={edgeRef}\n aria-hidden\n data-edge={edge}\n className={cn(\n EDGE_CLASS,\n className ?? (edge === \"top\" ? \"top-0\" : \"bottom-0\"),\n )}\n >\n {BLUR_LAYERS.map(({ blurPx, window }, index) => {\n const mask = edgeMask(edge, window);\n return (\n <div\n key={index}\n data-blur-px={blurPx}\n className=\"absolute inset-0\"\n style={{\n maskImage: mask,\n WebkitMaskImage: mask,\n }}\n />\n );\n })}\n <div\n data-scroll-fade=\"wash\"\n className=\"absolute inset-0 opacity-0\"\n style={{ background: washGradient(edge, surface) }}\n />\n </div>\n );\n}\n","import {\n createContext,\n useContext,\n useLayoutEffect,\n useRef,\n type ComponentProps,\n type ElementType,\n type Ref,\n type RefObject,\n} from \"react\";\nimport { ScrollArea as BaseScrollArea } from \"@base-ui/react/scroll-area\";\nimport { motion } from \"motion/react\";\nimport { cn } from \"../../utils/cn\";\nimport { Edge, EDGE_REVEAL_PX, paintEdge } from \"./scroll-area-edges\";\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 ScrollAreaContext = createContext<{\n topEl: RefObject<HTMLDivElement | null>;\n bottomEl: RefObject<HTMLDivElement | null>;\n} | null>(null);\n\nfunction useScrollAreaContext() {\n const ctx = useContext(ScrollAreaContext);\n if (ctx == null) {\n throw new Error(\"ScrollArea parts must be used inside ScrollArea.Root\");\n }\n return ctx;\n}\n\nfunction clamp01(n: number) {\n if (n <= 0) return 0;\n if (n >= 1) return 1;\n return n;\n}\n\nfunction assignRef<T>(ref: Ref<T> | undefined, node: T | null) {\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n // React 18 types `RefObject.current` as readonly; the write is legal.\n (ref as { current: T | null }).current = node;\n }\n}\n\n/**\n * The scrolled content. `inset` is a resting inset in the scroll axis —\n * like a scroll view's safe-area padding: the content stands clear of the\n * edges at rest but scrolls past them to the true edge, and the scrollbar\n * and edge fade stay on `Root`'s edges. `insetTop` / `insetBottom` set the\n * ends separately (they win over `inset`).\n */\nfunction Content({\n inset,\n insetTop,\n insetBottom,\n style,\n ref,\n ...props\n}: ComponentProps<typeof BaseScrollArea.Content> & {\n inset?: number | string;\n insetTop?: number | string;\n insetBottom?: number | string;\n ref?: Ref<HTMLDivElement>;\n}) {\n return (\n <BaseScrollArea.Content\n ref={ref}\n style={{\n paddingTop: insetTop ?? inset,\n paddingBottom: insetBottom ?? inset,\n ...style,\n }}\n {...props}\n />\n );\n}\n\nconst Corner = BaseScrollArea.Corner;\n\nfunction Root({\n className,\n children,\n ...props\n}: StyledProps<typeof BaseScrollArea.Root>) {\n const topEl = useRef<HTMLDivElement | null>(null);\n const bottomEl = useRef<HTMLDivElement | null>(null);\n\n return (\n <BaseScrollArea.Root\n className={cn(\"relative overflow-hidden\", className)}\n {...props}\n >\n <ScrollAreaContext.Provider value={{ topEl, bottomEl }}>\n {children}\n <Edge edge=\"top\" edgeRef={topEl} />\n <Edge edge=\"bottom\" edgeRef={bottomEl} />\n </ScrollAreaContext.Provider>\n </BaseScrollArea.Root>\n );\n}\n\nfunction Viewport({\n className,\n ref,\n ...props\n}: StyledProps<typeof BaseScrollArea.Viewport>) {\n const { topEl, bottomEl } = useScrollAreaContext();\n const viewportRef = useRef<HTMLElement | null>(null);\n\n useLayoutEffect(() => {\n const viewport = viewportRef.current;\n if (!viewport) return;\n\n const paint = () => {\n const reveal =\n topEl.current?.offsetHeight ||\n bottomEl.current?.offsetHeight ||\n EDGE_REVEAL_PX;\n const maxScroll = viewport.scrollHeight - viewport.clientHeight;\n const top = maxScroll <= 0 ? 0 : clamp01(viewport.scrollTop / reveal);\n const bottom =\n maxScroll <= 0 ? 0 : clamp01((maxScroll - viewport.scrollTop) / reveal);\n paintEdge(topEl.current, top);\n paintEdge(bottomEl.current, bottom);\n };\n\n let frame = 0;\n const onScroll = () => {\n if (frame) return;\n frame = requestAnimationFrame(() => {\n frame = 0;\n paint();\n });\n };\n\n paint();\n viewport.addEventListener(\"scroll\", onScroll, { passive: true });\n let observer: ResizeObserver | null = null;\n if (typeof ResizeObserver !== \"undefined\") {\n observer = new ResizeObserver(paint);\n observer.observe(viewport);\n if (viewport.firstElementChild) {\n observer.observe(viewport.firstElementChild);\n }\n }\n return () => {\n viewport.removeEventListener(\"scroll\", onScroll);\n if (frame) cancelAnimationFrame(frame);\n observer?.disconnect();\n };\n }, [topEl, bottomEl]);\n\n return (\n <BaseScrollArea.Viewport\n // A motion element with layoutScroll: any layout-animated content\n // inside (a shared-element morph, a FLIP row) projects against the\n // viewport's scroll offset instead of reading a scroll as a layout\n // change.\n render={<motion.div layoutScroll />}\n className={cn(\n \"outline-pho-brand h-full min-h-0 w-full overscroll-contain rounded-[inherit] focus-visible:outline-2 focus-visible:-outline-offset-1\",\n className,\n )}\n {...props}\n ref={(node) => {\n viewportRef.current = node;\n assignRef(ref, node);\n }}\n />\n );\n}\n\nfunction Scrollbar({\n className,\n ...props\n}: StyledProps<typeof BaseScrollArea.Scrollbar>) {\n return (\n <BaseScrollArea.Scrollbar\n className={cn(\n \"relative z-20 flex touch-none p-0.5 opacity-0 transition-opacity delay-300 select-none\",\n \"data-[hovering]:opacity-100 data-[hovering]:delay-0 data-[scrolling]:opacity-100 data-[scrolling]:delay-0 data-[scrolling]:duration-0\",\n \"data-[orientation=horizontal]:h-2.5 data-[orientation=horizontal]:flex-col data-[orientation=vertical]:w-2.5\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Thumb({\n className,\n ...props\n}: StyledProps<typeof BaseScrollArea.Thumb>) {\n return (\n <BaseScrollArea.Thumb\n className={cn(\n \"flex-1 rounded-full bg-[var(--border-color-pho-primary)]\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Scroll area built on Base UI `ScrollArea` — a capped region with overlay\n * scrollbars and progressive edge fade. Approaching rows blur (0 → 1px) and\n * wash into the surface; strength tracks scroll distance over one strip\n * height. Give `Root` a fixed size; put content in `Viewport` → `Content`\n * and add a `Scrollbar` → `Thumb` per axis.\n */\nexport const ScrollArea = {\n Root,\n Viewport,\n Content,\n Scrollbar,\n Thumb,\n Corner,\n};\n"],"mappings":"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"}
@@ -0,0 +1,223 @@
1
+ "use client";
2
+ import { t as o } from "./cn-ChIgwEl3.js";
3
+ import { t as I } from "./is-react-component-DSnjtBmo.js";
4
+ import { n as R } from "./link-provider-DXzDQ08N.js";
5
+ import { SPRING_PRESS as S } from "../motion.js";
6
+ import { a as E, u as _ } from "./button-CdqS_t5a.js";
7
+ import { t as A } from "./createReactComponent-BqCmnqfX.js";
8
+ import { t as w } from "./IconArrowUpRight-DGgKzQwV.js";
9
+ import { n as u } from "./avatar-BkmGDIqf.js";
10
+ import { t as C } from "./IconSearch-BKAHkLyX.js";
11
+ import { t as m } from "./scroll-area-BO-9PMM7.js";
12
+ import { t as T } from "./kbd-DNI10uy7.js";
13
+ import { createContext as k, isValidElement as B, useContext as D, useMemo as M } from "react";
14
+ import { Fragment as O, jsx as r, jsxs as i } from "react/jsx-runtime";
15
+ import { motion as f } from "motion/react";
16
+ var z = [
17
+ ["path", {
18
+ d: "M4 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0",
19
+ key: "svg-0"
20
+ }],
21
+ ["path", {
22
+ d: "M11 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0",
23
+ key: "svg-1"
24
+ }],
25
+ ["path", {
26
+ d: "M18 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0",
27
+ key: "svg-2"
28
+ }]
29
+ ], F = A("outline", "dots", "Dots", z), h = {
30
+ initial: !1,
31
+ whileTap: { scale: 0.97 },
32
+ transition: S
33
+ }, g = "border-pho-secondary bg-pho-page sticky top-0 flex h-dvh w-64 shrink-0 flex-col border-r", b = {
34
+ base: "px-4 pt-5 pb-4",
35
+ sm: "px-3 pt-4 pb-3"
36
+ }, fe = o(g, b.base), K = {
37
+ base: "mb-6 px-2",
38
+ sm: "mb-3 px-0"
39
+ }, L = "outline-pho-brand flex min-h-9 w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left text-base transition-colors focus-visible:outline-2 focus-visible:-outline-offset-1", V = "bg-pho-primary text-pho-primary ring-pho-secondary font-medium ring-1 ring-inset *:data-icon:text-pho-primary", P = o("text-pho-secondary *:data-icon:text-pho-secondary", "hover:bg-pho-primary hover:text-pho-primary hover:*:data-icon:text-pho-primary"), y = k("base");
40
+ function j({ className: t, inset: e = "base", ...a }) {
41
+ return /* @__PURE__ */ r(y.Provider, {
42
+ value: e,
43
+ children: /* @__PURE__ */ r("aside", {
44
+ className: o(g, b[e], t),
45
+ ...a,
46
+ "data-inset": e
47
+ })
48
+ });
49
+ }
50
+ function G({ className: t, ...e }) {
51
+ const a = D(y);
52
+ return /* @__PURE__ */ r("div", {
53
+ className: o("flex items-center gap-2.5 [&:has(+nav)]:mb-0", K[a], t),
54
+ ...e
55
+ });
56
+ }
57
+ function U({ label: t = "Search", kbd: e = "⌘K", className: a, ...n }) {
58
+ return /* @__PURE__ */ r(E, {
59
+ variant: "outlined",
60
+ shape: "square",
61
+ size: "md",
62
+ "aria-keyshortcuts": e === "⌘K" ? "Meta+K Control+K" : void 0,
63
+ prefix: /* @__PURE__ */ r(C, { "aria-hidden": !0 }),
64
+ suffix: /* @__PURE__ */ r(T, {
65
+ keys: e,
66
+ className: "pointer-events-none"
67
+ }),
68
+ className: o("hover:ring-pho-secondary min-h-9 w-full justify-start gap-2 rounded-sm px-2 py-1.5 has-[[data-icon=prefix]]:pl-2 has-[[data-icon=suffix]]:pr-2 [&>[data-text]]:min-w-0 [&>[data-text]]:flex-1 [&>[data-text]]:truncate [&>[data-text]]:px-0 [&>[data-text]]:text-left", a),
69
+ ...n,
70
+ children: t
71
+ });
72
+ }
73
+ function H({ scroll: t = !0, inset: e, insetTop: a = e ?? 24, insetBottom: n = e ?? 16, className: s, style: l, children: p, ...c }) {
74
+ return t ? /* @__PURE__ */ r("nav", {
75
+ className: o("flex min-h-0 flex-1 flex-col", s),
76
+ style: l,
77
+ ...c,
78
+ children: /* @__PURE__ */ i(m.Root, {
79
+ className: "min-h-0 flex-1",
80
+ children: [/* @__PURE__ */ r(m.Viewport, { children: /* @__PURE__ */ r(m.Content, {
81
+ inset: e,
82
+ insetTop: a,
83
+ insetBottom: n,
84
+ className: "flex flex-col gap-6",
85
+ children: p
86
+ }) }), /* @__PURE__ */ r(m.Scrollbar, { children: /* @__PURE__ */ r(m.Thumb, {}) })]
87
+ })
88
+ }) : /* @__PURE__ */ r("nav", {
89
+ className: o("flex flex-col gap-6", s),
90
+ style: {
91
+ paddingTop: a ?? e,
92
+ paddingBottom: n ?? e,
93
+ ...l
94
+ },
95
+ ...c,
96
+ children: p
97
+ });
98
+ }
99
+ function q({ className: t, ...e }) {
100
+ return /* @__PURE__ */ r("div", {
101
+ className: t,
102
+ ...e
103
+ });
104
+ }
105
+ function W({ className: t, ...e }) {
106
+ return /* @__PURE__ */ r("div", {
107
+ className: o("text-pho-description mb-2 px-2 text-xs font-normal", t),
108
+ ...e
109
+ });
110
+ }
111
+ function $({ className: t, columns: e, ...a }) {
112
+ return /* @__PURE__ */ r("ul", {
113
+ className: o("m-0 list-none p-0 *:m-0", e ? "grid grid-cols-2 gap-x-2 gap-y-0.5" : "flex flex-col gap-0.5", t),
114
+ ...a
115
+ });
116
+ }
117
+ function J({ className: t, ...e }) {
118
+ return /* @__PURE__ */ r("div", {
119
+ className: o("mt-4 flex flex-col gap-0.5 [nav~&]:mt-0", t),
120
+ ...e
121
+ });
122
+ }
123
+ function d(t) {
124
+ if (t == null || t === !1) return null;
125
+ let e = null;
126
+ return I(t) ? e = /* @__PURE__ */ r(t, {}) : B(t) && (e = t), e == null ? null : /* @__PURE__ */ r("span", {
127
+ "data-icon": !0,
128
+ className: "pointer-events-none inline-flex shrink-0 items-center [&>svg]:size-4 [&>svg]:shrink-0",
129
+ children: e
130
+ });
131
+ }
132
+ function Q({ icon: t, active: e, className: a, children: n, href: s, external: l, ...p }) {
133
+ const c = R(), N = M(() => f.create(c), [c]), x = o(L, e ? V : P, a), v = /* @__PURE__ */ r("span", {
134
+ className: "min-w-0 flex-1 truncate",
135
+ children: n
136
+ });
137
+ return s !== void 0 ? /* @__PURE__ */ r("li", { children: /* @__PURE__ */ i(N, {
138
+ href: s,
139
+ "aria-current": e ? "page" : void 0,
140
+ ...l && {
141
+ target: "_blank",
142
+ rel: "noopener noreferrer"
143
+ },
144
+ ...p,
145
+ className: x,
146
+ ...h,
147
+ children: [
148
+ d(t),
149
+ v,
150
+ l && /* @__PURE__ */ i(O, { children: [d(/* @__PURE__ */ r(w, { "aria-hidden": !0 })), /* @__PURE__ */ r("span", {
151
+ className: "sr-only",
152
+ children: "(opens in a new tab)"
153
+ })] })
154
+ ]
155
+ }) }) : /* @__PURE__ */ r("li", { children: /* @__PURE__ */ i(f.button, {
156
+ type: "button",
157
+ "aria-current": e ? "page" : void 0,
158
+ ...p,
159
+ className: x,
160
+ ...h,
161
+ children: [d(t), v]
162
+ }) });
163
+ }
164
+ var X = "group outline-pho-brand flex w-full cursor-pointer items-center gap-2 rounded-base p-2 text-left focus-visible:outline-2 focus-visible:outline-offset-2";
165
+ function Y(t) {
166
+ const e = t.split(/\s+/).filter(Boolean);
167
+ return ((e[0]?.[0] ?? t[0] ?? "?") + (e.length >= 2 ? e[e.length - 1]?.[0] ?? "" : "")).toUpperCase();
168
+ }
169
+ function Z({ name: t, avatarSrc: e, initials: a, className: n, ...s }) {
170
+ return /* @__PURE__ */ i(f.button, {
171
+ type: "button",
172
+ className: o(X, n),
173
+ ...s,
174
+ ...h,
175
+ children: [
176
+ /* @__PURE__ */ i(u.Root, {
177
+ size: "xs",
178
+ children: [e && /* @__PURE__ */ r(u.Image, {
179
+ src: e,
180
+ alt: ""
181
+ }), /* @__PURE__ */ r(u.Fallback, { children: a ?? Y(t) })]
182
+ }),
183
+ /* @__PURE__ */ r("span", {
184
+ className: "text-pho-primary min-w-0 flex-1 truncate text-base",
185
+ children: t
186
+ }),
187
+ /* @__PURE__ */ r("span", {
188
+ "aria-hidden": !0,
189
+ className: _({
190
+ variant: "outlined",
191
+ size: "sm",
192
+ shape: "circle",
193
+ className: "group-hover:bg-pho-primary-hover group-hover:text-pho-secondary-hover group-data-[popup-open]:bg-pho-primary-hover group-data-[popup-open]:text-pho-secondary-hover p-1"
194
+ }),
195
+ children: /* @__PURE__ */ r(F, { className: "size-4" })
196
+ })
197
+ ]
198
+ });
199
+ }
200
+ var he = {
201
+ Root: j,
202
+ Header: G,
203
+ Search: U,
204
+ Nav: H,
205
+ Group: q,
206
+ GroupLabel: W,
207
+ List: $,
208
+ Item: Q,
209
+ Footer: J,
210
+ AccountRow: Z
211
+ };
212
+ export {
213
+ g as a,
214
+ he as c,
215
+ P as i,
216
+ L as n,
217
+ fe as o,
218
+ V as r,
219
+ b as s,
220
+ K as t
221
+ };
222
+
223
+ //# sourceMappingURL=sidebar-DVzZsCts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebar-DVzZsCts.js","names":[],"sources":["../../../../node_modules/.pnpm/@tabler+icons-react@3.46.0_react@19.2.8/node_modules/@tabler/icons-react/dist/esm/icons/IconDots.mjs","../../src/sections/sidebar/sidebar.tsx"],"sourcesContent":["/**\n * @license @tabler/icons-react v3.46.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M4 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M11 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0\", \"key\": \"svg-1\" }], [\"path\", { \"d\": \"M18 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0\", \"key\": \"svg-2\" }]];\nconst IconDots = createReactComponent(\"outline\", \"dots\", \"Dots\", __iconNode);\n\nexport { __iconNode, IconDots as default };\n//# sourceMappingURL=IconDots.mjs.map\n","import {\n createContext,\n isValidElement,\n useContext,\n useMemo,\n type AnchorHTMLAttributes,\n type ButtonHTMLAttributes,\n type ComponentProps,\n type ComponentType,\n type FC,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport { IconArrowUpRight, IconDots } from \"@tabler/icons-react\";\nimport { SPRING_PRESS } from \"../../motion\";\nimport { motion } from \"motion/react\";\nimport { IconSearch } from \"@tabler/icons-react\";\nimport { Button } from \"../../components/button\";\nimport { Kbd } from \"../../components/kbd\";\nimport { ScrollArea } from \"../../components/scroll-area\";\nimport { cn } from \"../../utils/cn\";\nimport { isReactComponent } from \"../../utils/is-react-component\";\nimport { useLinkComponent } from \"../../utils/link-provider\";\nimport { Avatar } from \"../../components/avatar\";\nimport { buttonVariants } from \"../../components/button\";\n\n/** Same press compression as `Button` — transform-only, every interactive row. */\nconst SIDEBAR_PRESS_SCALE = 0.97;\n/** Framer source transition shared with `Button`: `spring-physics 200 30 0.1`. */\nconst SIDEBAR_PRESS_SPRING = SPRING_PRESS;\nconst SIDEBAR_PRESS_MOTION = {\n initial: false as const,\n whileTap: { scale: SIDEBAR_PRESS_SCALE },\n transition: SIDEBAR_PRESS_SPRING,\n};\n\n/**\n * Sidebar — Pho's first SECTION: a composed app rail, not a micro component.\n * It encodes the navigation rail used across Photon surfaces (the Pho docs\n * site, the dashboard): a sticky full-height column with a brand header, a\n * scrollable stack of titled nav groups, and a pinned footer that usually\n * carries the account row.\n *\n * Sections stay presentational — no router, no data fetching, no theme\n * plumbing. Links render through the host's `LinkProvider` (same contract as\n * `ButtonLink`), state arrives via props (`active`), and interactive footers\n * compose with other Pho parts (e.g. wrap `Sidebar.AccountRow` in a\n * `Menu.Trigger render={...}` to open an account menu).\n *\n * `Root` takes `inset` (`base` | `sm`, default `base`) for the rail's outer\n * padding and header spacing — prefer that over host-app class overrides.\n */\n\n/* ------------------------------- class map ------------------------------- */\n\n/** The rail itself — sticky, full-height, hairline right edge (no inset). */\nexport const SIDEBAR_ROOT =\n \"border-pho-secondary bg-pho-page sticky top-0 flex h-dvh w-64 shrink-0 flex-col border-r\";\n\n/** Outer inset on `Root` — `base` is the docs/default rail; `sm` is tighter. */\nexport const SIDEBAR_ROOT_INSET = {\n base: \"px-4 pt-5 pb-4\",\n sm: \"px-3 pt-4 pb-3\",\n} as const;\n\n/** `SIDEBAR_ROOT` with the default `base` inset — one string for docs chrome. */\nexport const SIDEBAR_ROOT_BASE = cn(SIDEBAR_ROOT, SIDEBAR_ROOT_INSET.base);\n\n/** Header spacing paired with `Root`'s `inset`. */\nexport const SIDEBAR_HEADER_INSET = {\n base: \"mb-6 px-2\",\n sm: \"mb-3 px-0\",\n} as const;\n\nexport type SidebarInset = keyof typeof SIDEBAR_ROOT_INSET;\n\n/** A nav row at rest and on hover. Active/inactive inks split below. */\nexport const SIDEBAR_ITEM =\n \"outline-pho-brand flex min-h-9 w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left text-base transition-colors focus-visible:outline-2 focus-visible:-outline-offset-1\";\n\n/**\n * Selected row — lifted onto the primary surface with the site hairline.\n * The icon carries the same ink as the label so the pair reads as one unit.\n */\nexport const SIDEBAR_ITEM_ACTIVE =\n \"bg-pho-primary text-pho-primary ring-pho-secondary font-medium ring-1 ring-inset *:data-icon:text-pho-primary\";\n\n/**\n * Resting row — secondary ink (75%). Hover lifts onto the primary surface\n * (same plane as the active chip, without the ring) so the wash stays quiet\n * on `pho-page`. Icon tracks the label's ink at every state.\n */\nexport const SIDEBAR_ITEM_INACTIVE = cn(\n \"text-pho-secondary *:data-icon:text-pho-secondary\",\n \"hover:bg-pho-primary hover:text-pho-primary hover:*:data-icon:text-pho-primary\",\n);\n\nconst SidebarInsetContext = createContext<SidebarInset>(\"base\");\n\n/* --------------------------------- frame --------------------------------- */\n\ninterface RootProps extends ComponentProps<\"aside\"> {\n /** Outer padding + header spacing. Defaults to `base`. */\n inset?: SidebarInset;\n ref?: Ref<HTMLElement>;\n}\n\n/** The rail — a sticky, full-height `<aside>` with the hairline right edge. */\nfunction Root({ className, inset = \"base\", ...props }: RootProps) {\n return (\n <SidebarInsetContext.Provider value={inset}>\n <aside\n className={cn(SIDEBAR_ROOT, SIDEBAR_ROOT_INSET[inset], className)}\n {...props}\n data-inset={inset}\n />\n </SidebarInsetContext.Provider>\n );\n}\n\n/** Brand slot at the top of the rail — logo, wordmark, workspace switcher. */\nfunction Header({ className, ...props }: ComponentProps<\"div\">) {\n const inset = useContext(SidebarInsetContext);\n return (\n <div\n className={cn(\n \"flex items-center gap-2.5 [&:has(+nav)]:mb-0\",\n SIDEBAR_HEADER_INSET[inset],\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * The rail's search row — an outlined button dressed to `Item` metrics so\n * its label lines up with the rows below, the shortcut at the trailing\n * edge. The press is the Button's own; wire `onClick` (and a global ⌘K)\n * to a `Command.Dialog`.\n */\nfunction Search({\n label = \"Search\",\n kbd = \"⌘K\",\n className,\n ...props\n}: Omit<ComponentProps<typeof Button>, \"children\" | \"variant\" | \"size\"> & {\n label?: ReactNode;\n kbd?: string;\n}) {\n return (\n <Button\n variant=\"outlined\"\n shape=\"square\"\n size=\"md\"\n aria-keyshortcuts={kbd === \"⌘K\" ? \"Meta+K Control+K\" : undefined}\n prefix={<IconSearch aria-hidden />}\n suffix={<Kbd keys={kbd} className=\"pointer-events-none\" />}\n className={cn(\n \"hover:ring-pho-secondary min-h-9 w-full justify-start gap-2 rounded-sm px-2 py-1.5 has-[[data-icon=prefix]]:pl-2 has-[[data-icon=suffix]]:pr-2 [&>[data-text]]:min-w-0 [&>[data-text]]:flex-1 [&>[data-text]]:truncate [&>[data-text]]:px-0 [&>[data-text]]:text-left\",\n className,\n )}\n {...props}\n >\n {label}\n </Button>\n );\n}\n\n/**\n * The scrollable middle — a stack of `Sidebar.Group`s in a `ScrollArea`\n * (overlay scrollbar, edge fade). Pass `scroll={false}` where a parent\n * scrolls the whole rail as one column (a narrow-viewport drawer) — the\n * nav is then a plain stack at its natural height.\n *\n * The nav owns its breathing room toward its neighbors as a resting inset\n * (a scroll view's safe-area padding): 24px above, 16px below by default —\n * rows stand clear of the ends at rest and scroll through the gap, under\n * the edge fade, instead of clipping at a dead margin. `inset` /\n * `insetTop` / `insetBottom` override it; the same values become plain\n * padding when `scroll={false}`, so both modes keep one geometry. A\n * `Header` right before the nav and a `Footer` after it drop their own\n * margins — the gap lives here, not on the neighbors.\n */\nfunction Nav({\n scroll = true,\n inset,\n insetTop = inset ?? 24,\n insetBottom = inset ?? 16,\n className,\n style,\n children,\n ...props\n}: ComponentProps<\"nav\"> & {\n scroll?: boolean;\n inset?: number | string;\n insetTop?: number | string;\n insetBottom?: number | string;\n}) {\n if (!scroll) {\n return (\n <nav\n className={cn(\"flex flex-col gap-6\", className)}\n style={{\n paddingTop: insetTop ?? inset,\n paddingBottom: insetBottom ?? inset,\n ...style,\n }}\n {...props}\n >\n {children}\n </nav>\n );\n }\n return (\n <nav\n className={cn(\"flex min-h-0 flex-1 flex-col\", className)}\n style={style}\n {...props}\n >\n <ScrollArea.Root className=\"min-h-0 flex-1\">\n <ScrollArea.Viewport>\n <ScrollArea.Content\n inset={inset}\n insetTop={insetTop}\n insetBottom={insetBottom}\n className=\"flex flex-col gap-6\"\n >\n {children}\n </ScrollArea.Content>\n </ScrollArea.Viewport>\n <ScrollArea.Scrollbar>\n <ScrollArea.Thumb />\n </ScrollArea.Scrollbar>\n </ScrollArea.Root>\n </nav>\n );\n}\n\nfunction Group({ className, ...props }: ComponentProps<\"div\">) {\n return <div className={className} {...props} />;\n}\n\n/** The cluster's caption — description ink, regular weight. */\nfunction GroupLabel({ className, ...props }: ComponentProps<\"div\">) {\n return (\n <div\n className={cn(\n \"text-pho-description mb-2 px-2 text-xs font-normal\",\n className,\n )}\n {...props}\n />\n );\n}\n\ninterface ListProps extends ComponentProps<\"ul\"> {\n /** Lay the rows out in two columns — for long, flat registries. */\n columns?: boolean;\n}\n\n/** The rows of a group. `columns` switches to a two-column grid. */\nfunction List({ className, columns, ...props }: ListProps) {\n return (\n <ul\n className={cn(\n // list-none/m-0/p-0 shield the rail from host prose styles.\n \"m-0 list-none p-0 *:m-0\",\n columns\n ? \"grid grid-cols-2 gap-x-2 gap-y-0.5\"\n : \"flex flex-col gap-0.5\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Pinned bottom cluster — docs link, account row, theme toggle. */\nfunction Footer({ className, ...props }: ComponentProps<\"div\">) {\n return (\n <div\n className={cn(\"mt-4 flex flex-col gap-0.5 [nav~&]:mt-0\", className)}\n {...props}\n />\n );\n}\n\n/* --------------------------------- item ---------------------------------- */\n\n/** An icon slot — a component (`IconBook`) or element (`<IconBook />`). */\ntype Slottable = ReactNode | FC<{ className?: string }>;\n\nfunction renderIcon(icon: Slottable): ReactNode {\n if (icon == null || icon === false) return null;\n let content: ReactNode = null;\n if (isReactComponent(icon)) {\n const Icon = icon;\n content = <Icon />;\n } else if (isValidElement(icon)) {\n content = icon;\n }\n if (content == null) return null;\n return (\n <span\n data-icon\n className=\"pointer-events-none inline-flex shrink-0 items-center [&>svg]:size-4 [&>svg]:shrink-0\"\n >\n {content}\n </span>\n );\n}\n\ninterface ItemBaseProps {\n /** Slot before the label — a component (`IconBook`) or element. */\n icon?: Slottable;\n /** Marks the row as the current destination (adds `aria-current=\"page\"`). */\n active?: boolean;\n children?: ReactNode;\n className?: string;\n}\n\n/**\n * DOM handlers whose React signatures clash with Motion's gesture props.\n * Same omit list as `Button` — rows render through `motion.*`.\n */\ntype MotionHandlerConflicts =\n | \"onAnimationStart\"\n | \"onAnimationEnd\"\n | \"onAnimationIteration\"\n | \"onDrag\"\n | \"onDragStart\"\n | \"onDragEnd\"\n | \"onDragEnter\"\n | \"onDragExit\"\n | \"onDragLeave\"\n | \"onDragOver\"\n | \"onDrop\";\n\nexport interface SidebarItemLinkProps\n extends\n ItemBaseProps,\n Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n keyof ItemBaseProps | MotionHandlerConflicts\n > {\n /** Destination — rendered through the host's `LinkProvider`. */\n href: string;\n /** Leaves the app: opens in a new tab and renders a ↗ cue. */\n external?: boolean;\n}\n\nexport interface SidebarItemButtonProps\n extends\n ItemBaseProps,\n Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n keyof ItemBaseProps | MotionHandlerConflicts\n > {\n href?: undefined;\n external?: undefined;\n}\n\nexport type SidebarItemProps = SidebarItemLinkProps | SidebarItemButtonProps;\n\n/**\n * A nav row — a link when `href` is given (rendered through the host's\n * `LinkProvider`), otherwise a button. `active` lifts the row onto the\n * primary surface; `external` opens a new tab and appends a ↗ cue.\n */\nfunction Item({\n icon,\n active,\n className,\n children,\n href,\n external,\n ...rest\n}: SidebarItemProps) {\n const Link = useLinkComponent();\n // Same bridge as `ButtonLink` — host `LinkProvider` + Motion press scale.\n const MotionLink = useMemo(\n () =>\n motion.create(Link as unknown as ComponentType<Record<string, unknown>>),\n [Link],\n );\n const rowClass = cn(\n SIDEBAR_ITEM,\n active ? SIDEBAR_ITEM_ACTIVE : SIDEBAR_ITEM_INACTIVE,\n className,\n );\n const label = <span className=\"min-w-0 flex-1 truncate\">{children}</span>;\n\n if (href !== undefined) {\n return (\n <li>\n <MotionLink\n href={href}\n aria-current={active ? \"page\" : undefined}\n {...(external && { target: \"_blank\", rel: \"noopener noreferrer\" })}\n {...(rest as AnchorHTMLAttributes<HTMLAnchorElement>)}\n className={rowClass}\n {...SIDEBAR_PRESS_MOTION}\n >\n {renderIcon(icon)}\n {label}\n {external && (\n <>\n {renderIcon(<IconArrowUpRight aria-hidden />)}\n <span className=\"sr-only\">(opens in a new tab)</span>\n </>\n )}\n </MotionLink>\n </li>\n );\n }\n\n return (\n <li>\n <motion.button\n type=\"button\"\n aria-current={active ? \"page\" : undefined}\n {...(rest as Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n MotionHandlerConflicts\n >)}\n className={rowClass}\n {...SIDEBAR_PRESS_MOTION}\n >\n {renderIcon(icon)}\n {label}\n </motion.button>\n </li>\n );\n}\n\n/* ------------------------------ account row ------------------------------ */\n\n/**\n * Whole-row account trigger. The row itself stays visually quiet — it only\n * carries the `group` state (hover/press/popup-open) and the focus ring;\n * all visual feedback renders on the ⋯ circle via `group-*` classes.\n */\nconst ACCOUNT_ROW =\n \"group outline-pho-brand flex w-full cursor-pointer items-center gap-2 rounded-base p-2 text-left focus-visible:outline-2 focus-visible:outline-offset-2\";\n\n/** Avatar initials: first + last word initial, else the first character. */\nfunction initialsFrom(name: string): string {\n const words = name.split(/\\s+/).filter(Boolean);\n const first = words[0]?.[0] ?? name[0] ?? \"?\";\n const last = words.length >= 2 ? (words[words.length - 1]?.[0] ?? \"\") : \"\";\n return (first + last).toUpperCase();\n}\n\nexport interface SidebarAccountRowProps extends Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n \"children\" | MotionHandlerConflicts\n> {\n /** Display name — also the source for fallback initials. */\n name: string;\n /** Avatar image URL; falls back to initials while loading or on error. */\n avatarSrc?: string;\n /** Override the derived initials. */\n initials?: string;\n ref?: Ref<HTMLButtonElement>;\n}\n\n/**\n * The account chip pinned to the rail's footer — an xs avatar, the name, and\n * a decorative ⋯ circle. The WHOLE row is one button, but the row is only the\n * control — the circle is the indicator: hover, press, and menu-open feedback\n * all render on the ⋯ (a span wearing the stock secondary-circle button\n * classes), driven by the row's `group` state, so it reads as a button\n * without nesting one.\n *\n * It renders a plain `<button>` and forwards every prop, so it slots straight\n * into a menu: `<Menu.Trigger render={<Sidebar.AccountRow name=\"…\" />} />` —\n * Base UI's `data-popup-open` / `data-pressed` states light up the circle.\n */\nfunction AccountRow({\n name,\n avatarSrc,\n initials,\n className,\n ...props\n}: SidebarAccountRowProps) {\n return (\n <motion.button\n type=\"button\"\n className={cn(ACCOUNT_ROW, className)}\n {...props}\n {...SIDEBAR_PRESS_MOTION}\n >\n <Avatar.Root size=\"xs\">\n {avatarSrc && <Avatar.Image src={avatarSrc} alt=\"\" />}\n <Avatar.Fallback>{initials ?? initialsFrom(name)}</Avatar.Fallback>\n </Avatar.Root>\n <span className=\"text-pho-primary min-w-0 flex-1 truncate text-base\">\n {name}\n </span>\n <span\n aria-hidden\n className={buttonVariants({\n variant: \"outlined\",\n size: \"sm\",\n shape: \"circle\",\n // Hover / open washes stay on the ⋯; press scale lives on the row\n // (same as Button / Sidebar.Item) so the circle does not double-scale.\n className:\n \"group-hover:bg-pho-primary-hover group-hover:text-pho-secondary-hover group-data-[popup-open]:bg-pho-primary-hover group-data-[popup-open]:text-pho-secondary-hover p-1\",\n })}\n >\n <IconDots className=\"size-4\" />\n </span>\n </motion.button>\n );\n}\n\n/* -------------------------------- assembly ------------------------------- */\n\n/**\n * The composed sidebar rail. Assemble the frame parts and fill them with\n * rows; wire interactivity by composing other Pho parts around them:\n *\n * ```tsx\n * <Sidebar.Root>\n * <Sidebar.Header>…brand…</Sidebar.Header>\n * <Sidebar.Nav>\n * <Sidebar.Group>\n * <Sidebar.GroupLabel>Pages</Sidebar.GroupLabel>\n * <Sidebar.List>\n * <Sidebar.Item href=\"/\" icon={IconHome} active>\n * Overview\n * </Sidebar.Item>\n * </Sidebar.List>\n * </Sidebar.Group>\n * </Sidebar.Nav>\n * <Sidebar.Footer>\n * <Menu.Root>\n * <Menu.Trigger render={<Sidebar.AccountRow name=\"Ada Lovelace\" />} />\n * …\n * </Menu.Root>\n * </Sidebar.Footer>\n * </Sidebar.Root>\n * ```\n */\nexport const Sidebar = {\n Root,\n Header,\n Search,\n Nav,\n Group,\n GroupLabel,\n List,\n Item,\n Footer,\n AccountRow,\n};\n"],"x_google_ignoreList":[0],"mappings":"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"}