@techzyapp/ui-library 1.0.39 → 1.0.40

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 (125) hide show
  1. package/dist/{AdminRoot-B2lwjQig.cjs → AdminRoot-CRTBGUxq.cjs} +2 -2
  2. package/dist/{AdminRoot-B2lwjQig.cjs.map → AdminRoot-CRTBGUxq.cjs.map} +1 -1
  3. package/dist/{AdminRoot-BTPLhufU.js → AdminRoot-JOgiNkJA.js} +3 -3
  4. package/dist/{AdminRoot-BTPLhufU.js.map → AdminRoot-JOgiNkJA.js.map} +1 -1
  5. package/dist/{CodeBlock-BbIC5BRK.cjs → CodeBlock-C3mG_seZ.cjs} +2 -2
  6. package/dist/{CodeBlock-BbIC5BRK.cjs.map → CodeBlock-C3mG_seZ.cjs.map} +1 -1
  7. package/dist/{CodeBlock-C8A6E-tJ.js → CodeBlock-DrWRSRmO.js} +2 -2
  8. package/dist/{CodeBlock-C8A6E-tJ.js.map → CodeBlock-DrWRSRmO.js.map} +1 -1
  9. package/dist/{ColorRamp-B_-ZPqMa.cjs → ColorRamp-D9_5JULy.cjs} +2 -2
  10. package/dist/{ColorRamp-B_-ZPqMa.cjs.map → ColorRamp-D9_5JULy.cjs.map} +1 -1
  11. package/dist/{ColorRamp-CCfImssD.js → ColorRamp-Mp_ddhmb.js} +2 -2
  12. package/dist/{ColorRamp-CCfImssD.js.map → ColorRamp-Mp_ddhmb.js.map} +1 -1
  13. package/dist/{CopyButton-CUEEp_Na.cjs → CopyButton-DZeYcG68.cjs} +2 -2
  14. package/dist/CopyButton-DZeYcG68.cjs.map +1 -0
  15. package/dist/{CopyButton-BD5sN32G.js → CopyButton-Yg5ZVTiX.js} +2 -2
  16. package/dist/CopyButton-Yg5ZVTiX.js.map +1 -0
  17. package/dist/{DescriptionList-BD65gv7C.js → DescriptionList-CG08_qwT.js} +2 -2
  18. package/dist/{DescriptionList-BD65gv7C.js.map → DescriptionList-CG08_qwT.js.map} +1 -1
  19. package/dist/{DescriptionList-BlAJIabM.cjs → DescriptionList-S9C6Tp90.cjs} +2 -2
  20. package/dist/{DescriptionList-BlAJIabM.cjs.map → DescriptionList-S9C6Tp90.cjs.map} +1 -1
  21. package/dist/{ErrorBoundary-sQY2sFdu.js → ErrorBoundary-BILk_3fM.js} +2 -2
  22. package/dist/{ErrorBoundary-sQY2sFdu.js.map → ErrorBoundary-BILk_3fM.js.map} +1 -1
  23. package/dist/{ErrorBoundary-6HvsCoJ8.cjs → ErrorBoundary-JCNa0_Ys.cjs} +2 -2
  24. package/dist/{ErrorBoundary-6HvsCoJ8.cjs.map → ErrorBoundary-JCNa0_Ys.cjs.map} +1 -1
  25. package/dist/{ErrorState-DdxOeZy2.js → ErrorState-DI28ZCJN.js} +3 -3
  26. package/dist/{ErrorState-DdxOeZy2.js.map → ErrorState-DI28ZCJN.js.map} +1 -1
  27. package/dist/{ErrorState-To_MAOf4.cjs → ErrorState-MQBeyF2n.cjs} +2 -2
  28. package/dist/{ErrorState-To_MAOf4.cjs.map → ErrorState-MQBeyF2n.cjs.map} +1 -1
  29. package/dist/{FileUploader-CWH-RS-z.js → FileUploader-DwAl3UXI.js} +2 -2
  30. package/dist/{FileUploader-CWH-RS-z.js.map → FileUploader-DwAl3UXI.js.map} +1 -1
  31. package/dist/{FileUploader-H4k4Cm5B.cjs → FileUploader-FMVbj7rz.cjs} +2 -2
  32. package/dist/{FileUploader-H4k4Cm5B.cjs.map → FileUploader-FMVbj7rz.cjs.map} +1 -1
  33. package/dist/{LocationPicker-CHGJkMXl.js → LocationPicker-Ce8aLubZ.js} +3 -3
  34. package/dist/{LocationPicker-CHGJkMXl.js.map → LocationPicker-Ce8aLubZ.js.map} +1 -1
  35. package/dist/{LocationPicker-CgzZnTFE.cjs → LocationPicker-D-sT4rSe.cjs} +2 -2
  36. package/dist/{LocationPicker-CgzZnTFE.cjs.map → LocationPicker-D-sT4rSe.cjs.map} +1 -1
  37. package/dist/Modal-DTnB_mGg.js +206 -0
  38. package/dist/Modal-DTnB_mGg.js.map +1 -0
  39. package/dist/Modal-DjSOhZqd.cjs +2 -0
  40. package/dist/Modal-DjSOhZqd.cjs.map +1 -0
  41. package/dist/{PageError-9tfvBssn.js → PageError-BORFaxGW.js} +2 -2
  42. package/dist/{PageError-9tfvBssn.js.map → PageError-BORFaxGW.js.map} +1 -1
  43. package/dist/{PageError-CJHOUfCa.cjs → PageError-aUV0vUfB.cjs} +2 -2
  44. package/dist/{PageError-CJHOUfCa.cjs.map → PageError-aUV0vUfB.cjs.map} +1 -1
  45. package/dist/{PaletteField-AMZpUMID.js → PaletteField-ChG2VQtS.js} +2 -2
  46. package/dist/{PaletteField-AMZpUMID.js.map → PaletteField-ChG2VQtS.js.map} +1 -1
  47. package/dist/{PaletteField-ClCvXFdN.cjs → PaletteField-DQ1OvXqe.cjs} +2 -2
  48. package/dist/{PaletteField-ClCvXFdN.cjs.map → PaletteField-DQ1OvXqe.cjs.map} +1 -1
  49. package/dist/{RemoveDialog-yEZm6JaC.cjs → RemoveDialog-B_Y18MeJ.cjs} +2 -2
  50. package/dist/{RemoveDialog-yEZm6JaC.cjs.map → RemoveDialog-B_Y18MeJ.cjs.map} +1 -1
  51. package/dist/{RemoveDialog-BfPTOs1y.js → RemoveDialog-ayAfGXpB.js} +2 -2
  52. package/dist/{RemoveDialog-BfPTOs1y.js.map → RemoveDialog-ayAfGXpB.js.map} +1 -1
  53. package/dist/{RichTextEditor-CitILCiw.cjs → RichTextEditor-DSvSaszc.cjs} +2 -2
  54. package/dist/{RichTextEditor-CitILCiw.cjs.map → RichTextEditor-DSvSaszc.cjs.map} +1 -1
  55. package/dist/{RichTextEditor-CgItVhAR.js → RichTextEditor-itYD1PfE.js} +2 -2
  56. package/dist/{RichTextEditor-CgItVhAR.js.map → RichTextEditor-itYD1PfE.js.map} +1 -1
  57. package/dist/{RootLayout-CVYEk7Wi.js → RootLayout-BlYjYAYk.js} +3 -3
  58. package/dist/{RootLayout-CVYEk7Wi.js.map → RootLayout-BlYjYAYk.js.map} +1 -1
  59. package/dist/{RootLayout-DJ2Xv35m.cjs → RootLayout-D5nlzYlH.cjs} +2 -2
  60. package/dist/{RootLayout-DJ2Xv35m.cjs.map → RootLayout-D5nlzYlH.cjs.map} +1 -1
  61. package/dist/{SpeedDial-C2hkC_8p.js → SpeedDial-CEBAWIYo.js} +2 -2
  62. package/dist/{SpeedDial-C2hkC_8p.js.map → SpeedDial-CEBAWIYo.js.map} +1 -1
  63. package/dist/{SpeedDial-DRSIilyC.cjs → SpeedDial-D6me6w_m.cjs} +2 -2
  64. package/dist/{SpeedDial-DRSIilyC.cjs.map → SpeedDial-D6me6w_m.cjs.map} +1 -1
  65. package/dist/{Table-DDPzTA1H.cjs → Table-CFl3OYlo.cjs} +2 -2
  66. package/dist/{Table-DDPzTA1H.cjs.map → Table-CFl3OYlo.cjs.map} +1 -1
  67. package/dist/{Table-AXMp0eiw.js → Table-DChIfjqj.js} +2 -2
  68. package/dist/{Table-AXMp0eiw.js.map → Table-DChIfjqj.js.map} +1 -1
  69. package/dist/Tooltip-BbT0uNzy.js +152 -0
  70. package/dist/Tooltip-BbT0uNzy.js.map +1 -0
  71. package/dist/Tooltip-DmMq5R1x.cjs +2 -0
  72. package/dist/Tooltip-DmMq5R1x.cjs.map +1 -0
  73. package/dist/components/CodeBlock/index.cjs +1 -1
  74. package/dist/components/CodeBlock/index.js +1 -1
  75. package/dist/components/ColorRamp/index.cjs +1 -1
  76. package/dist/components/ColorRamp/index.js +1 -1
  77. package/dist/components/CopyButton/CopyButton.d.cts +1 -1
  78. package/dist/components/CopyButton/CopyButton.d.ts +1 -1
  79. package/dist/components/CopyButton/index.cjs +1 -1
  80. package/dist/components/CopyButton/index.js +1 -1
  81. package/dist/components/DescriptionList/index.cjs +1 -1
  82. package/dist/components/DescriptionList/index.js +1 -1
  83. package/dist/components/ErrorState/index.cjs +1 -1
  84. package/dist/components/ErrorState/index.js +2 -2
  85. package/dist/components/FileUploader/index.cjs +1 -1
  86. package/dist/components/FileUploader/index.js +1 -1
  87. package/dist/components/LocationPicker/index.cjs +1 -1
  88. package/dist/components/LocationPicker/index.js +1 -1
  89. package/dist/components/Modal/index.cjs +1 -1
  90. package/dist/components/Modal/index.js +1 -1
  91. package/dist/components/PageError/index.cjs +1 -1
  92. package/dist/components/PageError/index.js +1 -1
  93. package/dist/components/PaletteField/index.cjs +1 -1
  94. package/dist/components/PaletteField/index.js +1 -1
  95. package/dist/components/RemoveDialog/index.cjs +1 -1
  96. package/dist/components/RemoveDialog/index.js +1 -1
  97. package/dist/components/RichTextEditor/index.cjs +1 -1
  98. package/dist/components/RichTextEditor/index.js +1 -1
  99. package/dist/components/RootLayout/index.cjs +1 -1
  100. package/dist/components/RootLayout/index.js +1 -1
  101. package/dist/components/SpeedDial/index.cjs +1 -1
  102. package/dist/components/SpeedDial/index.js +1 -1
  103. package/dist/components/Table/index.cjs +1 -1
  104. package/dist/components/Table/index.js +1 -1
  105. package/dist/components/Tooltip/Tooltip.d.cts +5 -3
  106. package/dist/components/Tooltip/Tooltip.d.ts +5 -3
  107. package/dist/components/Tooltip/index.cjs +1 -1
  108. package/dist/components/Tooltip/index.js +1 -1
  109. package/dist/entries/app.cjs +1 -1
  110. package/dist/entries/app.js +1 -1
  111. package/dist/entries/components.cjs +1 -1
  112. package/dist/entries/components.js +17 -17
  113. package/dist/index.cjs +1 -1
  114. package/dist/index.js +18 -18
  115. package/package.json +1 -1
  116. package/dist/CopyButton-BD5sN32G.js.map +0 -1
  117. package/dist/CopyButton-CUEEp_Na.cjs.map +0 -1
  118. package/dist/Modal-BkO_6afj.js +0 -195
  119. package/dist/Modal-BkO_6afj.js.map +0 -1
  120. package/dist/Modal-D8V-JZBg.cjs +0 -2
  121. package/dist/Modal-D8V-JZBg.cjs.map +0 -1
  122. package/dist/Tooltip-Bw-1Xk2Z.cjs +0 -2
  123. package/dist/Tooltip-Bw-1Xk2Z.cjs.map +0 -1
  124. package/dist/Tooltip-DsDv4NOq.js +0 -135
  125. package/dist/Tooltip-DsDv4NOq.js.map +0 -1
@@ -1,2 +1,2 @@
1
- "use strict";const e=require("react/jsx-runtime"),n=require("@tanstack/react-router"),_=require("./errorMessage-C7N4KvUS.cjs"),B=require("./AuthGuard-kfOZviar.cjs"),W=require("./ConfigProvider-B_Bb6pk1.cjs"),z=require("./AuthLayout-exuNQicH.cjs"),F=require("./Col-BdQeyKEX.cjs"),x=require("./ErrorBoundary-6HvsCoJ8.cjs"),R=require("./Button-CYZoF18-.cjs"),f=require("./Toaster-Brcspf7v.cjs"),G=require("./RootLayout-DJ2Xv35m.cjs"),N=require("./UserCard-1ubnvBV6.cjs"),U=require("./Loader-B5Pm8AaT.cjs"),b="_pending_1jy0z_2",k={pending:b},I=({logo:d,logoTo:y,getUser:q,onLogout:A,authRouteId:L="/_auth",loginPath:l="/login",showUserCard:C=!0,header:p,errorBoundary:c=!0,pending:h=!1,pendingFallback:T,unavailable:t,unavailableFallback:m})=>{const i=W.useT(),S=n.useNavigate(),v=n.useRouterState({select:o=>o.matches.some(s=>s.routeId.startsWith(L))}),E=n.useRouterState({select:o=>{var s;return(s=o.location)==null?void 0:s.pathname}}),u=()=>{A(),S({to:l})};if(h||t){let o;if(h)o=T??e.jsx("div",{className:k.pending,"aria-busy":"true",children:e.jsx(U.Loader,{size:"lg"})});else if(t){const s=t.reason??(_.getErrorStatus(t.error)===403?"forbidden":"unreachable");if(m)o=m({error:t.error,reason:s,retry:t.onRetry,signOut:u});else{const a=s==="unreachable"?t.onRetry:void 0,g=s==="forbidden";o=e.jsx(z.AuthLayout,{logo:d,icon:t.icon||void 0,color:t.color??"error",title:i(g?"adminRoot.noAccessTitle":"adminRoot.unreachableTitle"),subtitle:i(g?"adminRoot.noAccessSubtitle":"adminRoot.unreachableSubtitle"),children:e.jsxs(F.Col,{gap:16,children:[e.jsx(x.QueryError,{variant:"alert",error:t.error}),a?e.jsx(R.Button,{fullWidth:!0,onClick:a,children:i("errorState.retry")}):null,e.jsx(R.Button,{fullWidth:!0,variant:a?"filled":"contained",onClick:u,children:i("userCard.signOut")})]})})}}return e.jsxs(e.Fragment,{children:[o,e.jsx(f.Toaster,{})]})}const r=q(),j=c===!1?e.jsx(n.Outlet,{}):e.jsx(x.ErrorBoundary,{...c===!0?{}:c,resetKeys:[E],children:e.jsx(n.Outlet,{})}),O=v?j:e.jsx(B.AuthGuard,{isAuthenticated:r!=null,redirectTo:l,children:e.jsx(G.RootLayout,{logo:d,logoTo:y,toaster:!1,header:{user:r?{name:r.name,email:r.email,avatar:r.avatar}:void 0,onLogout:u,...p},sidebarFooter:C&&r?e.jsx(N.UserCard,{name:r.name,email:r.email,avatar:r.avatar,onLogout:u}):void 0,children:j})});return e.jsxs(e.Fragment,{children:[O,e.jsx(f.Toaster,{})]})};exports.AdminRoot=I;
2
- //# sourceMappingURL=AdminRoot-B2lwjQig.cjs.map
1
+ "use strict";const e=require("react/jsx-runtime"),n=require("@tanstack/react-router"),_=require("./errorMessage-C7N4KvUS.cjs"),B=require("./AuthGuard-kfOZviar.cjs"),W=require("./ConfigProvider-B_Bb6pk1.cjs"),z=require("./AuthLayout-exuNQicH.cjs"),F=require("./Col-BdQeyKEX.cjs"),x=require("./ErrorBoundary-JCNa0_Ys.cjs"),R=require("./Button-CYZoF18-.cjs"),f=require("./Toaster-Brcspf7v.cjs"),G=require("./RootLayout-D5nlzYlH.cjs"),N=require("./UserCard-1ubnvBV6.cjs"),U=require("./Loader-B5Pm8AaT.cjs"),b="_pending_1jy0z_2",k={pending:b},I=({logo:d,logoTo:y,getUser:q,onLogout:A,authRouteId:L="/_auth",loginPath:l="/login",showUserCard:C=!0,header:p,errorBoundary:c=!0,pending:h=!1,pendingFallback:T,unavailable:t,unavailableFallback:m})=>{const i=W.useT(),S=n.useNavigate(),v=n.useRouterState({select:o=>o.matches.some(s=>s.routeId.startsWith(L))}),E=n.useRouterState({select:o=>{var s;return(s=o.location)==null?void 0:s.pathname}}),u=()=>{A(),S({to:l})};if(h||t){let o;if(h)o=T??e.jsx("div",{className:k.pending,"aria-busy":"true",children:e.jsx(U.Loader,{size:"lg"})});else if(t){const s=t.reason??(_.getErrorStatus(t.error)===403?"forbidden":"unreachable");if(m)o=m({error:t.error,reason:s,retry:t.onRetry,signOut:u});else{const a=s==="unreachable"?t.onRetry:void 0,g=s==="forbidden";o=e.jsx(z.AuthLayout,{logo:d,icon:t.icon||void 0,color:t.color??"error",title:i(g?"adminRoot.noAccessTitle":"adminRoot.unreachableTitle"),subtitle:i(g?"adminRoot.noAccessSubtitle":"adminRoot.unreachableSubtitle"),children:e.jsxs(F.Col,{gap:16,children:[e.jsx(x.QueryError,{variant:"alert",error:t.error}),a?e.jsx(R.Button,{fullWidth:!0,onClick:a,children:i("errorState.retry")}):null,e.jsx(R.Button,{fullWidth:!0,variant:a?"filled":"contained",onClick:u,children:i("userCard.signOut")})]})})}}return e.jsxs(e.Fragment,{children:[o,e.jsx(f.Toaster,{})]})}const r=q(),j=c===!1?e.jsx(n.Outlet,{}):e.jsx(x.ErrorBoundary,{...c===!0?{}:c,resetKeys:[E],children:e.jsx(n.Outlet,{})}),O=v?j:e.jsx(B.AuthGuard,{isAuthenticated:r!=null,redirectTo:l,children:e.jsx(G.RootLayout,{logo:d,logoTo:y,toaster:!1,header:{user:r?{name:r.name,email:r.email,avatar:r.avatar}:void 0,onLogout:u,...p},sidebarFooter:C&&r?e.jsx(N.UserCard,{name:r.name,email:r.email,avatar:r.avatar,onLogout:u}):void 0,children:j})});return e.jsxs(e.Fragment,{children:[O,e.jsx(f.Toaster,{})]})};exports.AdminRoot=I;
2
+ //# sourceMappingURL=AdminRoot-CRTBGUxq.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminRoot-B2lwjQig.cjs","sources":["../src/app/AdminRoot.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { Outlet, useNavigate, useRouterState, type LinkProps } from '@tanstack/react-router'\nimport {\n AuthLayout,\n Button,\n Col,\n ErrorBoundary,\n Loader,\n QueryError,\n RootLayout,\n UserCard,\n Toaster,\n type ErrorBoundaryProps,\n type IconName,\n type RootLayoutHeader,\n type RootLayoutProps,\n} from '../components'\nimport { getErrorStatus } from '../components/ErrorState/errorMessage'\nimport { useT, type ThemeColor } from '../theme'\nimport { AuthGuard } from '../auth/AuthGuard'\nimport styles from './AdminRoot.module.css'\n\n/** Why the session can't be used — see `AdminRootUnavailable.reason`. */\nexport type AdminRootUnavailableReason = 'forbidden' | 'unreachable'\n\n/** The `unavailable` state of `AdminRoot` — the session could not be checked, or may not be used here. */\nexport interface AdminRootUnavailable {\n /** What failed — passed to `QueryError` (message, trace id). */\n error: unknown\n /**\n * Why. `'forbidden'` = a valid session this panel may not use (no retry, only Sign Out).\n * `'unreachable'` = the check itself failed (Try Again + Sign Out). Defaults to `'forbidden'` when the\n * error carries HTTP status 403 (`error.status` or `error.response.status`), else `'unreachable'`.\n */\n reason?: AdminRootUnavailableReason\n /** Re-runs the session check. Shown as \"Try Again\" for `'unreachable'` only. */\n onRetry?: () => void\n /**\n * The `AuthLayout` icon. Default: none — apps differ here (one shows a tinted `CloudCross`, another keeps\n * every auth page icon-free), so it's the app's choice.\n */\n icon?: IconName | false\n /** Tint for `icon` (`AuthLayout`'s `color`). Default `'error'`. */\n color?: ThemeColor\n}\n\n/** What an `unavailableFallback` receives. */\nexport interface AdminRootUnavailableFallbackProps {\n /** The `unavailable.error`. */\n error: unknown\n /** The resolved reason (explicit, or inferred from a 403). */\n reason: AdminRootUnavailableReason\n /** `unavailable.onRetry`, when given — whatever the reason (the fallback decides whether to offer it). */\n retry?: () => void\n /** The shell's Sign Out: `onLogout()`, then navigate to `loginPath`. */\n signOut: () => void\n}\n\n/** The minimal user shape `AdminRoot` reads — apps pass their own richer type via the generic. */\nexport interface AdminUser {\n name?: string\n email?: string\n avatar?: string\n}\n\nexport interface AdminRootProps<U extends AdminUser = AdminUser> {\n /** Brand mark for the sidebar top. */\n logo?: ReactNode\n /**\n * Where clicking the logo goes — the first menu page by default (see `RootLayout`'s `logoTo`); a path to\n * send it elsewhere, or `false` for a non-clickable logo.\n */\n logoTo?: RootLayoutProps['logoTo']\n /** Current user, or `null` when signed out. */\n getUser: () => U | null\n /** Clears the session; `AdminRoot` navigates to `loginPath` afterwards. */\n onLogout: () => void\n /** Route-id prefix for standalone public pages (rendered outside the shell). Default `'/_auth'`. */\n authRouteId?: string\n /** Guard target for signed-out visitors + post-logout redirect. Default `'/login'`. */\n loginPath?: string\n /** Show a `UserCard` in the sidebar footer. Default `true`. */\n showUserCard?: boolean\n /**\n * Extra `RootLayout` header config, merged **over** the defaults (the app's fields win). Flows through\n * unchanged — e.g. **`header.menu`** (`HeaderMenuItem[]`) adds items to the account menu above Sign out.\n */\n header?: RootLayoutHeader\n /**\n * Wrap the page content in an `ErrorBoundary` (reset on navigation), so a crashing page shows an\n * `ErrorState` inside the shell instead of a white screen. Default `true`; `false` opts out; an object\n * configures it (e.g. `{ onError: Sentry.captureException }` or a custom `fallback`). It only catches what\n * the router doesn't: TanStack Router catches a route's component / `loader` / `beforeLoad` errors in its own\n * per-route boundary, so page crashes need `createRouter({ defaultErrorComponent: PageError })`.\n */\n errorBoundary?: boolean | Omit<ErrorBoundaryProps, 'children' | 'resetKeys'>\n /**\n * The app is still finding out who is signed in (e.g. verifying a stored token or refreshing a cookie\n * session). While `true`, AdminRoot renders only `pendingFallback` — no shell, no public page, no\n * `AuthGuard`, and `getUser()` isn't read — so a signed-in admin is never redirected to sign-in before the\n * check resolves. Route `beforeLoad` guards (e.g. `authLayoutRoute`) still run in the router, independent\n * of this — they must tolerate the unknown state themselves. No minimum display time. Default `false`.\n */\n pending?: boolean\n /** What to show while `pending`. Default: a `Loader size=\"lg\"` centred in the full viewport. */\n pendingFallback?: ReactNode\n /**\n * The session could not be checked, or the server will not serve this account here. While set, AdminRoot\n * renders a standalone `AuthLayout` screen (the app's `logo`, a `QueryError`, Try Again / Sign Out) instead\n * of the shell or a public page — and never redirects. Sign Out calls `onLogout` then navigates to\n * `loginPath`, so **`onLogout` must clear whatever set `unavailable`** (or this screen shows again).\n * `pending` wins when both are set. `null` / `undefined` = not unavailable (the default).\n */\n unavailable?: AdminRootUnavailable | null\n /** Replaces the whole `unavailable` screen — for an app that needs its own wording or extra actions. */\n unavailableFallback?: (props: AdminRootUnavailableFallbackProps) => ReactNode\n}\n\n/**\n * Root-route component for admin apps — it wires up the whole shell orchestration so an app only has to\n * supply `logo` + `getUser` + `onLogout`. Public pages (route id under `authRouteId`) render standalone;\n * everything else renders inside the guarded admin shell (`RootLayout` + a sidebar `UserCard`), gated by\n * `AuthGuard` (signed-out visitors bounce to `loginPath`). Logout clears the session and navigates to\n * `loginPath`. Mounts exactly one app-wide `<Toaster/>` (the inner `RootLayout` gets `toaster={false}`),\n * and wraps the page in an `ErrorBoundary` that resets on navigation (`errorBoundary`, default on).\n * While the app is still checking the session, pass `pending` (a full-screen loader instead of a premature\n * sign-in redirect); when the check failed or the server refuses this account, pass `unavailable` (a\n * standalone \"Cannot Reach the Server\" / \"No Access\" screen with Try Again / Sign Out). The `Toaster` stays\n * mounted in both.\n *\n * `loginPath` is a plain string, cast loosely to the router's `to` type (the library can't know the\n * app's typed route tree). The generic `<U>` preserves the app's own user type through `getUser`.\n *\n * @example\n * // routes/__root.tsx\n * export const Route = createRootRoute({\n * component: () => {\n * const { status, error, retry } = useSession()\n * return (\n * <AdminRoot<AuthUser>\n * pending={status === 'loading'}\n * unavailable={status === 'unavailable' ? { error, onRetry: retry } : null}\n * logo={<Logo />}\n * getUser={getUser}\n * onLogout={logout}\n * />\n * )\n * },\n * })\n */\nexport const AdminRoot = <U extends AdminUser = AdminUser>({\n logo,\n logoTo,\n getUser,\n onLogout,\n authRouteId = '/_auth',\n loginPath = '/login',\n showUserCard = true,\n header,\n errorBoundary = true,\n pending = false,\n pendingFallback,\n unavailable,\n unavailableFallback,\n}: AdminRootProps<U>) => {\n const t = useT()\n const navigate = useNavigate()\n const isAuthRoute = useRouterState({\n select: (s) => s.matches.some((m) => m.routeId.startsWith(authRouteId)),\n })\n const pathname = useRouterState({ select: (s) => s.location?.pathname })\n const handleLogout = () => {\n onLogout()\n navigate({ to: loginPath as LinkProps['to'] })\n }\n\n // the session isn't known yet / can't be used — a standalone screen instead of the shell or a public\n // page (so no AuthGuard redirect, no sign-in flash). The Toaster stays, so a bootstrap toast isn't lost.\n if (pending || unavailable) {\n let gate: ReactNode\n if (pending) {\n gate = pendingFallback ?? (\n <div className={styles.pending} aria-busy=\"true\">\n <Loader size=\"lg\" />\n </div>\n )\n } else if (unavailable) {\n const reason: AdminRootUnavailableReason =\n unavailable.reason ??\n (getErrorStatus(unavailable.error) === 403 ? 'forbidden' : 'unreachable')\n if (unavailableFallback) {\n gate = unavailableFallback({\n error: unavailable.error,\n reason,\n retry: unavailable.onRetry,\n signOut: handleLogout,\n })\n } else {\n const retry = reason === 'unreachable' ? unavailable.onRetry : undefined\n const forbidden = reason === 'forbidden'\n gate = (\n <AuthLayout\n logo={logo}\n icon={unavailable.icon || undefined}\n color={unavailable.color ?? 'error'}\n title={t(forbidden ? 'adminRoot.noAccessTitle' : 'adminRoot.unreachableTitle')}\n subtitle={t(forbidden ? 'adminRoot.noAccessSubtitle' : 'adminRoot.unreachableSubtitle')}\n >\n <Col gap={16}>\n <QueryError variant=\"alert\" error={unavailable.error} />\n {retry ? (\n <Button fullWidth onClick={retry}>\n {t('errorState.retry')}\n </Button>\n ) : null}\n <Button fullWidth variant={retry ? 'filled' : 'contained'} onClick={handleLogout}>\n {t('userCard.signOut')}\n </Button>\n </Col>\n </AuthLayout>\n )\n }\n }\n return (\n <>\n {gate}\n <Toaster />\n </>\n )\n }\n\n const user = getUser()\n // the routed page, inside an ErrorBoundary that resets whenever the path changes\n const page =\n errorBoundary === false ? (\n <Outlet />\n ) : (\n <ErrorBoundary {...(errorBoundary === true ? {} : errorBoundary)} resetKeys={[pathname]}>\n <Outlet />\n </ErrorBoundary>\n )\n const content = isAuthRoute ? (\n page\n ) : (\n <AuthGuard isAuthenticated={user != null} redirectTo={loginPath}>\n <RootLayout\n logo={logo}\n logoTo={logoTo}\n toaster={false}\n header={{\n user: user ? { name: user.name, email: user.email, avatar: user.avatar } : undefined,\n onLogout: handleLogout,\n ...header,\n }}\n sidebarFooter={\n showUserCard && user ? (\n <UserCard\n name={user.name}\n email={user.email}\n avatar={user.avatar}\n onLogout={handleLogout}\n />\n ) : undefined\n }\n >\n {page}\n </RootLayout>\n </AuthGuard>\n )\n return (\n <>\n {content}\n <Toaster />\n </>\n )\n}\n"],"names":["AdminRoot","logo","logoTo","getUser","onLogout","authRouteId","loginPath","showUserCard","header","errorBoundary","pending","pendingFallback","unavailable","unavailableFallback","t","useT","navigate","useNavigate","isAuthRoute","useRouterState","s","m","pathname","_a","handleLogout","gate","jsx","styles","Loader","reason","getErrorStatus","retry","forbidden","AuthLayout","jsxs","Col","QueryError","Button","Fragment","Toaster","user","page","Outlet","ErrorBoundary","content","AuthGuard","RootLayout","UserCard"],"mappings":"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"}
1
+ {"version":3,"file":"AdminRoot-CRTBGUxq.cjs","sources":["../src/app/AdminRoot.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { Outlet, useNavigate, useRouterState, type LinkProps } from '@tanstack/react-router'\nimport {\n AuthLayout,\n Button,\n Col,\n ErrorBoundary,\n Loader,\n QueryError,\n RootLayout,\n UserCard,\n Toaster,\n type ErrorBoundaryProps,\n type IconName,\n type RootLayoutHeader,\n type RootLayoutProps,\n} from '../components'\nimport { getErrorStatus } from '../components/ErrorState/errorMessage'\nimport { useT, type ThemeColor } from '../theme'\nimport { AuthGuard } from '../auth/AuthGuard'\nimport styles from './AdminRoot.module.css'\n\n/** Why the session can't be used — see `AdminRootUnavailable.reason`. */\nexport type AdminRootUnavailableReason = 'forbidden' | 'unreachable'\n\n/** The `unavailable` state of `AdminRoot` — the session could not be checked, or may not be used here. */\nexport interface AdminRootUnavailable {\n /** What failed — passed to `QueryError` (message, trace id). */\n error: unknown\n /**\n * Why. `'forbidden'` = a valid session this panel may not use (no retry, only Sign Out).\n * `'unreachable'` = the check itself failed (Try Again + Sign Out). Defaults to `'forbidden'` when the\n * error carries HTTP status 403 (`error.status` or `error.response.status`), else `'unreachable'`.\n */\n reason?: AdminRootUnavailableReason\n /** Re-runs the session check. Shown as \"Try Again\" for `'unreachable'` only. */\n onRetry?: () => void\n /**\n * The `AuthLayout` icon. Default: none — apps differ here (one shows a tinted `CloudCross`, another keeps\n * every auth page icon-free), so it's the app's choice.\n */\n icon?: IconName | false\n /** Tint for `icon` (`AuthLayout`'s `color`). Default `'error'`. */\n color?: ThemeColor\n}\n\n/** What an `unavailableFallback` receives. */\nexport interface AdminRootUnavailableFallbackProps {\n /** The `unavailable.error`. */\n error: unknown\n /** The resolved reason (explicit, or inferred from a 403). */\n reason: AdminRootUnavailableReason\n /** `unavailable.onRetry`, when given — whatever the reason (the fallback decides whether to offer it). */\n retry?: () => void\n /** The shell's Sign Out: `onLogout()`, then navigate to `loginPath`. */\n signOut: () => void\n}\n\n/** The minimal user shape `AdminRoot` reads — apps pass their own richer type via the generic. */\nexport interface AdminUser {\n name?: string\n email?: string\n avatar?: string\n}\n\nexport interface AdminRootProps<U extends AdminUser = AdminUser> {\n /** Brand mark for the sidebar top. */\n logo?: ReactNode\n /**\n * Where clicking the logo goes — the first menu page by default (see `RootLayout`'s `logoTo`); a path to\n * send it elsewhere, or `false` for a non-clickable logo.\n */\n logoTo?: RootLayoutProps['logoTo']\n /** Current user, or `null` when signed out. */\n getUser: () => U | null\n /** Clears the session; `AdminRoot` navigates to `loginPath` afterwards. */\n onLogout: () => void\n /** Route-id prefix for standalone public pages (rendered outside the shell). Default `'/_auth'`. */\n authRouteId?: string\n /** Guard target for signed-out visitors + post-logout redirect. Default `'/login'`. */\n loginPath?: string\n /** Show a `UserCard` in the sidebar footer. Default `true`. */\n showUserCard?: boolean\n /**\n * Extra `RootLayout` header config, merged **over** the defaults (the app's fields win). Flows through\n * unchanged — e.g. **`header.menu`** (`HeaderMenuItem[]`) adds items to the account menu above Sign out.\n */\n header?: RootLayoutHeader\n /**\n * Wrap the page content in an `ErrorBoundary` (reset on navigation), so a crashing page shows an\n * `ErrorState` inside the shell instead of a white screen. Default `true`; `false` opts out; an object\n * configures it (e.g. `{ onError: Sentry.captureException }` or a custom `fallback`). It only catches what\n * the router doesn't: TanStack Router catches a route's component / `loader` / `beforeLoad` errors in its own\n * per-route boundary, so page crashes need `createRouter({ defaultErrorComponent: PageError })`.\n */\n errorBoundary?: boolean | Omit<ErrorBoundaryProps, 'children' | 'resetKeys'>\n /**\n * The app is still finding out who is signed in (e.g. verifying a stored token or refreshing a cookie\n * session). While `true`, AdminRoot renders only `pendingFallback` — no shell, no public page, no\n * `AuthGuard`, and `getUser()` isn't read — so a signed-in admin is never redirected to sign-in before the\n * check resolves. Route `beforeLoad` guards (e.g. `authLayoutRoute`) still run in the router, independent\n * of this — they must tolerate the unknown state themselves. No minimum display time. Default `false`.\n */\n pending?: boolean\n /** What to show while `pending`. Default: a `Loader size=\"lg\"` centred in the full viewport. */\n pendingFallback?: ReactNode\n /**\n * The session could not be checked, or the server will not serve this account here. While set, AdminRoot\n * renders a standalone `AuthLayout` screen (the app's `logo`, a `QueryError`, Try Again / Sign Out) instead\n * of the shell or a public page — and never redirects. Sign Out calls `onLogout` then navigates to\n * `loginPath`, so **`onLogout` must clear whatever set `unavailable`** (or this screen shows again).\n * `pending` wins when both are set. `null` / `undefined` = not unavailable (the default).\n */\n unavailable?: AdminRootUnavailable | null\n /** Replaces the whole `unavailable` screen — for an app that needs its own wording or extra actions. */\n unavailableFallback?: (props: AdminRootUnavailableFallbackProps) => ReactNode\n}\n\n/**\n * Root-route component for admin apps — it wires up the whole shell orchestration so an app only has to\n * supply `logo` + `getUser` + `onLogout`. Public pages (route id under `authRouteId`) render standalone;\n * everything else renders inside the guarded admin shell (`RootLayout` + a sidebar `UserCard`), gated by\n * `AuthGuard` (signed-out visitors bounce to `loginPath`). Logout clears the session and navigates to\n * `loginPath`. Mounts exactly one app-wide `<Toaster/>` (the inner `RootLayout` gets `toaster={false}`),\n * and wraps the page in an `ErrorBoundary` that resets on navigation (`errorBoundary`, default on).\n * While the app is still checking the session, pass `pending` (a full-screen loader instead of a premature\n * sign-in redirect); when the check failed or the server refuses this account, pass `unavailable` (a\n * standalone \"Cannot Reach the Server\" / \"No Access\" screen with Try Again / Sign Out). The `Toaster` stays\n * mounted in both.\n *\n * `loginPath` is a plain string, cast loosely to the router's `to` type (the library can't know the\n * app's typed route tree). The generic `<U>` preserves the app's own user type through `getUser`.\n *\n * @example\n * // routes/__root.tsx\n * export const Route = createRootRoute({\n * component: () => {\n * const { status, error, retry } = useSession()\n * return (\n * <AdminRoot<AuthUser>\n * pending={status === 'loading'}\n * unavailable={status === 'unavailable' ? { error, onRetry: retry } : null}\n * logo={<Logo />}\n * getUser={getUser}\n * onLogout={logout}\n * />\n * )\n * },\n * })\n */\nexport const AdminRoot = <U extends AdminUser = AdminUser>({\n logo,\n logoTo,\n getUser,\n onLogout,\n authRouteId = '/_auth',\n loginPath = '/login',\n showUserCard = true,\n header,\n errorBoundary = true,\n pending = false,\n pendingFallback,\n unavailable,\n unavailableFallback,\n}: AdminRootProps<U>) => {\n const t = useT()\n const navigate = useNavigate()\n const isAuthRoute = useRouterState({\n select: (s) => s.matches.some((m) => m.routeId.startsWith(authRouteId)),\n })\n const pathname = useRouterState({ select: (s) => s.location?.pathname })\n const handleLogout = () => {\n onLogout()\n navigate({ to: loginPath as LinkProps['to'] })\n }\n\n // the session isn't known yet / can't be used — a standalone screen instead of the shell or a public\n // page (so no AuthGuard redirect, no sign-in flash). The Toaster stays, so a bootstrap toast isn't lost.\n if (pending || unavailable) {\n let gate: ReactNode\n if (pending) {\n gate = pendingFallback ?? (\n <div className={styles.pending} aria-busy=\"true\">\n <Loader size=\"lg\" />\n </div>\n )\n } else if (unavailable) {\n const reason: AdminRootUnavailableReason =\n unavailable.reason ??\n (getErrorStatus(unavailable.error) === 403 ? 'forbidden' : 'unreachable')\n if (unavailableFallback) {\n gate = unavailableFallback({\n error: unavailable.error,\n reason,\n retry: unavailable.onRetry,\n signOut: handleLogout,\n })\n } else {\n const retry = reason === 'unreachable' ? unavailable.onRetry : undefined\n const forbidden = reason === 'forbidden'\n gate = (\n <AuthLayout\n logo={logo}\n icon={unavailable.icon || undefined}\n color={unavailable.color ?? 'error'}\n title={t(forbidden ? 'adminRoot.noAccessTitle' : 'adminRoot.unreachableTitle')}\n subtitle={t(forbidden ? 'adminRoot.noAccessSubtitle' : 'adminRoot.unreachableSubtitle')}\n >\n <Col gap={16}>\n <QueryError variant=\"alert\" error={unavailable.error} />\n {retry ? (\n <Button fullWidth onClick={retry}>\n {t('errorState.retry')}\n </Button>\n ) : null}\n <Button fullWidth variant={retry ? 'filled' : 'contained'} onClick={handleLogout}>\n {t('userCard.signOut')}\n </Button>\n </Col>\n </AuthLayout>\n )\n }\n }\n return (\n <>\n {gate}\n <Toaster />\n </>\n )\n }\n\n const user = getUser()\n // the routed page, inside an ErrorBoundary that resets whenever the path changes\n const page =\n errorBoundary === false ? (\n <Outlet />\n ) : (\n <ErrorBoundary {...(errorBoundary === true ? {} : errorBoundary)} resetKeys={[pathname]}>\n <Outlet />\n </ErrorBoundary>\n )\n const content = isAuthRoute ? (\n page\n ) : (\n <AuthGuard isAuthenticated={user != null} redirectTo={loginPath}>\n <RootLayout\n logo={logo}\n logoTo={logoTo}\n toaster={false}\n header={{\n user: user ? { name: user.name, email: user.email, avatar: user.avatar } : undefined,\n onLogout: handleLogout,\n ...header,\n }}\n sidebarFooter={\n showUserCard && user ? (\n <UserCard\n name={user.name}\n email={user.email}\n avatar={user.avatar}\n onLogout={handleLogout}\n />\n ) : undefined\n }\n >\n {page}\n </RootLayout>\n </AuthGuard>\n )\n return (\n <>\n {content}\n <Toaster />\n </>\n )\n}\n"],"names":["AdminRoot","logo","logoTo","getUser","onLogout","authRouteId","loginPath","showUserCard","header","errorBoundary","pending","pendingFallback","unavailable","unavailableFallback","t","useT","navigate","useNavigate","isAuthRoute","useRouterState","s","m","pathname","_a","handleLogout","gate","jsx","styles","Loader","reason","getErrorStatus","retry","forbidden","AuthLayout","jsxs","Col","QueryError","Button","Fragment","Toaster","user","page","Outlet","ErrorBoundary","content","AuthGuard","RootLayout","UserCard"],"mappings":"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"}
@@ -5,10 +5,10 @@ import { A as U } from "./AuthGuard-CVklwDbj.js";
5
5
  import { a as b } from "./ConfigProvider-DeMASTVo.js";
6
6
  import { A as k } from "./AuthLayout-q8pbCGdt.js";
7
7
  import { C as B } from "./Col-B51RlwWP.js";
8
- import { Q as F, E as G } from "./ErrorBoundary-sQY2sFdu.js";
8
+ import { Q as F, E as G } from "./ErrorBoundary-BILk_3fM.js";
9
9
  import { B as A } from "./Button-Bst_NtPJ.js";
10
10
  import { T as L } from "./Toaster-Fq6-ImDJ.js";
11
- import { R as I } from "./RootLayout-CVYEk7Wi.js";
11
+ import { R as I } from "./RootLayout-BlYjYAYk.js";
12
12
  import { U as K } from "./UserCard-DcTLEHFu.js";
13
13
  import { L as q } from "./Loader-Bj5uwVR9.js";
14
14
  const w = "_pending_1jy0z_2", D = {
@@ -104,4 +104,4 @@ const w = "_pending_1jy0z_2", D = {
104
104
  export {
105
105
  st as A
106
106
  };
107
- //# sourceMappingURL=AdminRoot-BTPLhufU.js.map
107
+ //# sourceMappingURL=AdminRoot-JOgiNkJA.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminRoot-BTPLhufU.js","sources":["../src/app/AdminRoot.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { Outlet, useNavigate, useRouterState, type LinkProps } from '@tanstack/react-router'\nimport {\n AuthLayout,\n Button,\n Col,\n ErrorBoundary,\n Loader,\n QueryError,\n RootLayout,\n UserCard,\n Toaster,\n type ErrorBoundaryProps,\n type IconName,\n type RootLayoutHeader,\n type RootLayoutProps,\n} from '../components'\nimport { getErrorStatus } from '../components/ErrorState/errorMessage'\nimport { useT, type ThemeColor } from '../theme'\nimport { AuthGuard } from '../auth/AuthGuard'\nimport styles from './AdminRoot.module.css'\n\n/** Why the session can't be used — see `AdminRootUnavailable.reason`. */\nexport type AdminRootUnavailableReason = 'forbidden' | 'unreachable'\n\n/** The `unavailable` state of `AdminRoot` — the session could not be checked, or may not be used here. */\nexport interface AdminRootUnavailable {\n /** What failed — passed to `QueryError` (message, trace id). */\n error: unknown\n /**\n * Why. `'forbidden'` = a valid session this panel may not use (no retry, only Sign Out).\n * `'unreachable'` = the check itself failed (Try Again + Sign Out). Defaults to `'forbidden'` when the\n * error carries HTTP status 403 (`error.status` or `error.response.status`), else `'unreachable'`.\n */\n reason?: AdminRootUnavailableReason\n /** Re-runs the session check. Shown as \"Try Again\" for `'unreachable'` only. */\n onRetry?: () => void\n /**\n * The `AuthLayout` icon. Default: none — apps differ here (one shows a tinted `CloudCross`, another keeps\n * every auth page icon-free), so it's the app's choice.\n */\n icon?: IconName | false\n /** Tint for `icon` (`AuthLayout`'s `color`). Default `'error'`. */\n color?: ThemeColor\n}\n\n/** What an `unavailableFallback` receives. */\nexport interface AdminRootUnavailableFallbackProps {\n /** The `unavailable.error`. */\n error: unknown\n /** The resolved reason (explicit, or inferred from a 403). */\n reason: AdminRootUnavailableReason\n /** `unavailable.onRetry`, when given — whatever the reason (the fallback decides whether to offer it). */\n retry?: () => void\n /** The shell's Sign Out: `onLogout()`, then navigate to `loginPath`. */\n signOut: () => void\n}\n\n/** The minimal user shape `AdminRoot` reads — apps pass their own richer type via the generic. */\nexport interface AdminUser {\n name?: string\n email?: string\n avatar?: string\n}\n\nexport interface AdminRootProps<U extends AdminUser = AdminUser> {\n /** Brand mark for the sidebar top. */\n logo?: ReactNode\n /**\n * Where clicking the logo goes — the first menu page by default (see `RootLayout`'s `logoTo`); a path to\n * send it elsewhere, or `false` for a non-clickable logo.\n */\n logoTo?: RootLayoutProps['logoTo']\n /** Current user, or `null` when signed out. */\n getUser: () => U | null\n /** Clears the session; `AdminRoot` navigates to `loginPath` afterwards. */\n onLogout: () => void\n /** Route-id prefix for standalone public pages (rendered outside the shell). Default `'/_auth'`. */\n authRouteId?: string\n /** Guard target for signed-out visitors + post-logout redirect. Default `'/login'`. */\n loginPath?: string\n /** Show a `UserCard` in the sidebar footer. Default `true`. */\n showUserCard?: boolean\n /**\n * Extra `RootLayout` header config, merged **over** the defaults (the app's fields win). Flows through\n * unchanged — e.g. **`header.menu`** (`HeaderMenuItem[]`) adds items to the account menu above Sign out.\n */\n header?: RootLayoutHeader\n /**\n * Wrap the page content in an `ErrorBoundary` (reset on navigation), so a crashing page shows an\n * `ErrorState` inside the shell instead of a white screen. Default `true`; `false` opts out; an object\n * configures it (e.g. `{ onError: Sentry.captureException }` or a custom `fallback`). It only catches what\n * the router doesn't: TanStack Router catches a route's component / `loader` / `beforeLoad` errors in its own\n * per-route boundary, so page crashes need `createRouter({ defaultErrorComponent: PageError })`.\n */\n errorBoundary?: boolean | Omit<ErrorBoundaryProps, 'children' | 'resetKeys'>\n /**\n * The app is still finding out who is signed in (e.g. verifying a stored token or refreshing a cookie\n * session). While `true`, AdminRoot renders only `pendingFallback` — no shell, no public page, no\n * `AuthGuard`, and `getUser()` isn't read — so a signed-in admin is never redirected to sign-in before the\n * check resolves. Route `beforeLoad` guards (e.g. `authLayoutRoute`) still run in the router, independent\n * of this — they must tolerate the unknown state themselves. No minimum display time. Default `false`.\n */\n pending?: boolean\n /** What to show while `pending`. Default: a `Loader size=\"lg\"` centred in the full viewport. */\n pendingFallback?: ReactNode\n /**\n * The session could not be checked, or the server will not serve this account here. While set, AdminRoot\n * renders a standalone `AuthLayout` screen (the app's `logo`, a `QueryError`, Try Again / Sign Out) instead\n * of the shell or a public page — and never redirects. Sign Out calls `onLogout` then navigates to\n * `loginPath`, so **`onLogout` must clear whatever set `unavailable`** (or this screen shows again).\n * `pending` wins when both are set. `null` / `undefined` = not unavailable (the default).\n */\n unavailable?: AdminRootUnavailable | null\n /** Replaces the whole `unavailable` screen — for an app that needs its own wording or extra actions. */\n unavailableFallback?: (props: AdminRootUnavailableFallbackProps) => ReactNode\n}\n\n/**\n * Root-route component for admin apps — it wires up the whole shell orchestration so an app only has to\n * supply `logo` + `getUser` + `onLogout`. Public pages (route id under `authRouteId`) render standalone;\n * everything else renders inside the guarded admin shell (`RootLayout` + a sidebar `UserCard`), gated by\n * `AuthGuard` (signed-out visitors bounce to `loginPath`). Logout clears the session and navigates to\n * `loginPath`. Mounts exactly one app-wide `<Toaster/>` (the inner `RootLayout` gets `toaster={false}`),\n * and wraps the page in an `ErrorBoundary` that resets on navigation (`errorBoundary`, default on).\n * While the app is still checking the session, pass `pending` (a full-screen loader instead of a premature\n * sign-in redirect); when the check failed or the server refuses this account, pass `unavailable` (a\n * standalone \"Cannot Reach the Server\" / \"No Access\" screen with Try Again / Sign Out). The `Toaster` stays\n * mounted in both.\n *\n * `loginPath` is a plain string, cast loosely to the router's `to` type (the library can't know the\n * app's typed route tree). The generic `<U>` preserves the app's own user type through `getUser`.\n *\n * @example\n * // routes/__root.tsx\n * export const Route = createRootRoute({\n * component: () => {\n * const { status, error, retry } = useSession()\n * return (\n * <AdminRoot<AuthUser>\n * pending={status === 'loading'}\n * unavailable={status === 'unavailable' ? { error, onRetry: retry } : null}\n * logo={<Logo />}\n * getUser={getUser}\n * onLogout={logout}\n * />\n * )\n * },\n * })\n */\nexport const AdminRoot = <U extends AdminUser = AdminUser>({\n logo,\n logoTo,\n getUser,\n onLogout,\n authRouteId = '/_auth',\n loginPath = '/login',\n showUserCard = true,\n header,\n errorBoundary = true,\n pending = false,\n pendingFallback,\n unavailable,\n unavailableFallback,\n}: AdminRootProps<U>) => {\n const t = useT()\n const navigate = useNavigate()\n const isAuthRoute = useRouterState({\n select: (s) => s.matches.some((m) => m.routeId.startsWith(authRouteId)),\n })\n const pathname = useRouterState({ select: (s) => s.location?.pathname })\n const handleLogout = () => {\n onLogout()\n navigate({ to: loginPath as LinkProps['to'] })\n }\n\n // the session isn't known yet / can't be used — a standalone screen instead of the shell or a public\n // page (so no AuthGuard redirect, no sign-in flash). The Toaster stays, so a bootstrap toast isn't lost.\n if (pending || unavailable) {\n let gate: ReactNode\n if (pending) {\n gate = pendingFallback ?? (\n <div className={styles.pending} aria-busy=\"true\">\n <Loader size=\"lg\" />\n </div>\n )\n } else if (unavailable) {\n const reason: AdminRootUnavailableReason =\n unavailable.reason ??\n (getErrorStatus(unavailable.error) === 403 ? 'forbidden' : 'unreachable')\n if (unavailableFallback) {\n gate = unavailableFallback({\n error: unavailable.error,\n reason,\n retry: unavailable.onRetry,\n signOut: handleLogout,\n })\n } else {\n const retry = reason === 'unreachable' ? unavailable.onRetry : undefined\n const forbidden = reason === 'forbidden'\n gate = (\n <AuthLayout\n logo={logo}\n icon={unavailable.icon || undefined}\n color={unavailable.color ?? 'error'}\n title={t(forbidden ? 'adminRoot.noAccessTitle' : 'adminRoot.unreachableTitle')}\n subtitle={t(forbidden ? 'adminRoot.noAccessSubtitle' : 'adminRoot.unreachableSubtitle')}\n >\n <Col gap={16}>\n <QueryError variant=\"alert\" error={unavailable.error} />\n {retry ? (\n <Button fullWidth onClick={retry}>\n {t('errorState.retry')}\n </Button>\n ) : null}\n <Button fullWidth variant={retry ? 'filled' : 'contained'} onClick={handleLogout}>\n {t('userCard.signOut')}\n </Button>\n </Col>\n </AuthLayout>\n )\n }\n }\n return (\n <>\n {gate}\n <Toaster />\n </>\n )\n }\n\n const user = getUser()\n // the routed page, inside an ErrorBoundary that resets whenever the path changes\n const page =\n errorBoundary === false ? (\n <Outlet />\n ) : (\n <ErrorBoundary {...(errorBoundary === true ? {} : errorBoundary)} resetKeys={[pathname]}>\n <Outlet />\n </ErrorBoundary>\n )\n const content = isAuthRoute ? (\n page\n ) : (\n <AuthGuard isAuthenticated={user != null} redirectTo={loginPath}>\n <RootLayout\n logo={logo}\n logoTo={logoTo}\n toaster={false}\n header={{\n user: user ? { name: user.name, email: user.email, avatar: user.avatar } : undefined,\n onLogout: handleLogout,\n ...header,\n }}\n sidebarFooter={\n showUserCard && user ? (\n <UserCard\n name={user.name}\n email={user.email}\n avatar={user.avatar}\n onLogout={handleLogout}\n />\n ) : undefined\n }\n >\n {page}\n </RootLayout>\n </AuthGuard>\n )\n return (\n <>\n {content}\n <Toaster />\n </>\n )\n}\n"],"names":["AdminRoot","logo","logoTo","getUser","onLogout","authRouteId","loginPath","showUserCard","header","errorBoundary","pending","pendingFallback","unavailable","unavailableFallback","t","useT","navigate","useNavigate","isAuthRoute","useRouterState","s","m","pathname","_a","handleLogout","gate","jsx","styles","Loader","reason","getErrorStatus","retry","forbidden","AuthLayout","jsxs","Col","QueryError","Button","Fragment","Toaster","user","page","Outlet","ErrorBoundary","content","AuthGuard","RootLayout","UserCard"],"mappings":"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"}
1
+ {"version":3,"file":"AdminRoot-JOgiNkJA.js","sources":["../src/app/AdminRoot.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { Outlet, useNavigate, useRouterState, type LinkProps } from '@tanstack/react-router'\nimport {\n AuthLayout,\n Button,\n Col,\n ErrorBoundary,\n Loader,\n QueryError,\n RootLayout,\n UserCard,\n Toaster,\n type ErrorBoundaryProps,\n type IconName,\n type RootLayoutHeader,\n type RootLayoutProps,\n} from '../components'\nimport { getErrorStatus } from '../components/ErrorState/errorMessage'\nimport { useT, type ThemeColor } from '../theme'\nimport { AuthGuard } from '../auth/AuthGuard'\nimport styles from './AdminRoot.module.css'\n\n/** Why the session can't be used — see `AdminRootUnavailable.reason`. */\nexport type AdminRootUnavailableReason = 'forbidden' | 'unreachable'\n\n/** The `unavailable` state of `AdminRoot` — the session could not be checked, or may not be used here. */\nexport interface AdminRootUnavailable {\n /** What failed — passed to `QueryError` (message, trace id). */\n error: unknown\n /**\n * Why. `'forbidden'` = a valid session this panel may not use (no retry, only Sign Out).\n * `'unreachable'` = the check itself failed (Try Again + Sign Out). Defaults to `'forbidden'` when the\n * error carries HTTP status 403 (`error.status` or `error.response.status`), else `'unreachable'`.\n */\n reason?: AdminRootUnavailableReason\n /** Re-runs the session check. Shown as \"Try Again\" for `'unreachable'` only. */\n onRetry?: () => void\n /**\n * The `AuthLayout` icon. Default: none — apps differ here (one shows a tinted `CloudCross`, another keeps\n * every auth page icon-free), so it's the app's choice.\n */\n icon?: IconName | false\n /** Tint for `icon` (`AuthLayout`'s `color`). Default `'error'`. */\n color?: ThemeColor\n}\n\n/** What an `unavailableFallback` receives. */\nexport interface AdminRootUnavailableFallbackProps {\n /** The `unavailable.error`. */\n error: unknown\n /** The resolved reason (explicit, or inferred from a 403). */\n reason: AdminRootUnavailableReason\n /** `unavailable.onRetry`, when given — whatever the reason (the fallback decides whether to offer it). */\n retry?: () => void\n /** The shell's Sign Out: `onLogout()`, then navigate to `loginPath`. */\n signOut: () => void\n}\n\n/** The minimal user shape `AdminRoot` reads — apps pass their own richer type via the generic. */\nexport interface AdminUser {\n name?: string\n email?: string\n avatar?: string\n}\n\nexport interface AdminRootProps<U extends AdminUser = AdminUser> {\n /** Brand mark for the sidebar top. */\n logo?: ReactNode\n /**\n * Where clicking the logo goes — the first menu page by default (see `RootLayout`'s `logoTo`); a path to\n * send it elsewhere, or `false` for a non-clickable logo.\n */\n logoTo?: RootLayoutProps['logoTo']\n /** Current user, or `null` when signed out. */\n getUser: () => U | null\n /** Clears the session; `AdminRoot` navigates to `loginPath` afterwards. */\n onLogout: () => void\n /** Route-id prefix for standalone public pages (rendered outside the shell). Default `'/_auth'`. */\n authRouteId?: string\n /** Guard target for signed-out visitors + post-logout redirect. Default `'/login'`. */\n loginPath?: string\n /** Show a `UserCard` in the sidebar footer. Default `true`. */\n showUserCard?: boolean\n /**\n * Extra `RootLayout` header config, merged **over** the defaults (the app's fields win). Flows through\n * unchanged — e.g. **`header.menu`** (`HeaderMenuItem[]`) adds items to the account menu above Sign out.\n */\n header?: RootLayoutHeader\n /**\n * Wrap the page content in an `ErrorBoundary` (reset on navigation), so a crashing page shows an\n * `ErrorState` inside the shell instead of a white screen. Default `true`; `false` opts out; an object\n * configures it (e.g. `{ onError: Sentry.captureException }` or a custom `fallback`). It only catches what\n * the router doesn't: TanStack Router catches a route's component / `loader` / `beforeLoad` errors in its own\n * per-route boundary, so page crashes need `createRouter({ defaultErrorComponent: PageError })`.\n */\n errorBoundary?: boolean | Omit<ErrorBoundaryProps, 'children' | 'resetKeys'>\n /**\n * The app is still finding out who is signed in (e.g. verifying a stored token or refreshing a cookie\n * session). While `true`, AdminRoot renders only `pendingFallback` — no shell, no public page, no\n * `AuthGuard`, and `getUser()` isn't read — so a signed-in admin is never redirected to sign-in before the\n * check resolves. Route `beforeLoad` guards (e.g. `authLayoutRoute`) still run in the router, independent\n * of this — they must tolerate the unknown state themselves. No minimum display time. Default `false`.\n */\n pending?: boolean\n /** What to show while `pending`. Default: a `Loader size=\"lg\"` centred in the full viewport. */\n pendingFallback?: ReactNode\n /**\n * The session could not be checked, or the server will not serve this account here. While set, AdminRoot\n * renders a standalone `AuthLayout` screen (the app's `logo`, a `QueryError`, Try Again / Sign Out) instead\n * of the shell or a public page — and never redirects. Sign Out calls `onLogout` then navigates to\n * `loginPath`, so **`onLogout` must clear whatever set `unavailable`** (or this screen shows again).\n * `pending` wins when both are set. `null` / `undefined` = not unavailable (the default).\n */\n unavailable?: AdminRootUnavailable | null\n /** Replaces the whole `unavailable` screen — for an app that needs its own wording or extra actions. */\n unavailableFallback?: (props: AdminRootUnavailableFallbackProps) => ReactNode\n}\n\n/**\n * Root-route component for admin apps — it wires up the whole shell orchestration so an app only has to\n * supply `logo` + `getUser` + `onLogout`. Public pages (route id under `authRouteId`) render standalone;\n * everything else renders inside the guarded admin shell (`RootLayout` + a sidebar `UserCard`), gated by\n * `AuthGuard` (signed-out visitors bounce to `loginPath`). Logout clears the session and navigates to\n * `loginPath`. Mounts exactly one app-wide `<Toaster/>` (the inner `RootLayout` gets `toaster={false}`),\n * and wraps the page in an `ErrorBoundary` that resets on navigation (`errorBoundary`, default on).\n * While the app is still checking the session, pass `pending` (a full-screen loader instead of a premature\n * sign-in redirect); when the check failed or the server refuses this account, pass `unavailable` (a\n * standalone \"Cannot Reach the Server\" / \"No Access\" screen with Try Again / Sign Out). The `Toaster` stays\n * mounted in both.\n *\n * `loginPath` is a plain string, cast loosely to the router's `to` type (the library can't know the\n * app's typed route tree). The generic `<U>` preserves the app's own user type through `getUser`.\n *\n * @example\n * // routes/__root.tsx\n * export const Route = createRootRoute({\n * component: () => {\n * const { status, error, retry } = useSession()\n * return (\n * <AdminRoot<AuthUser>\n * pending={status === 'loading'}\n * unavailable={status === 'unavailable' ? { error, onRetry: retry } : null}\n * logo={<Logo />}\n * getUser={getUser}\n * onLogout={logout}\n * />\n * )\n * },\n * })\n */\nexport const AdminRoot = <U extends AdminUser = AdminUser>({\n logo,\n logoTo,\n getUser,\n onLogout,\n authRouteId = '/_auth',\n loginPath = '/login',\n showUserCard = true,\n header,\n errorBoundary = true,\n pending = false,\n pendingFallback,\n unavailable,\n unavailableFallback,\n}: AdminRootProps<U>) => {\n const t = useT()\n const navigate = useNavigate()\n const isAuthRoute = useRouterState({\n select: (s) => s.matches.some((m) => m.routeId.startsWith(authRouteId)),\n })\n const pathname = useRouterState({ select: (s) => s.location?.pathname })\n const handleLogout = () => {\n onLogout()\n navigate({ to: loginPath as LinkProps['to'] })\n }\n\n // the session isn't known yet / can't be used — a standalone screen instead of the shell or a public\n // page (so no AuthGuard redirect, no sign-in flash). The Toaster stays, so a bootstrap toast isn't lost.\n if (pending || unavailable) {\n let gate: ReactNode\n if (pending) {\n gate = pendingFallback ?? (\n <div className={styles.pending} aria-busy=\"true\">\n <Loader size=\"lg\" />\n </div>\n )\n } else if (unavailable) {\n const reason: AdminRootUnavailableReason =\n unavailable.reason ??\n (getErrorStatus(unavailable.error) === 403 ? 'forbidden' : 'unreachable')\n if (unavailableFallback) {\n gate = unavailableFallback({\n error: unavailable.error,\n reason,\n retry: unavailable.onRetry,\n signOut: handleLogout,\n })\n } else {\n const retry = reason === 'unreachable' ? unavailable.onRetry : undefined\n const forbidden = reason === 'forbidden'\n gate = (\n <AuthLayout\n logo={logo}\n icon={unavailable.icon || undefined}\n color={unavailable.color ?? 'error'}\n title={t(forbidden ? 'adminRoot.noAccessTitle' : 'adminRoot.unreachableTitle')}\n subtitle={t(forbidden ? 'adminRoot.noAccessSubtitle' : 'adminRoot.unreachableSubtitle')}\n >\n <Col gap={16}>\n <QueryError variant=\"alert\" error={unavailable.error} />\n {retry ? (\n <Button fullWidth onClick={retry}>\n {t('errorState.retry')}\n </Button>\n ) : null}\n <Button fullWidth variant={retry ? 'filled' : 'contained'} onClick={handleLogout}>\n {t('userCard.signOut')}\n </Button>\n </Col>\n </AuthLayout>\n )\n }\n }\n return (\n <>\n {gate}\n <Toaster />\n </>\n )\n }\n\n const user = getUser()\n // the routed page, inside an ErrorBoundary that resets whenever the path changes\n const page =\n errorBoundary === false ? (\n <Outlet />\n ) : (\n <ErrorBoundary {...(errorBoundary === true ? {} : errorBoundary)} resetKeys={[pathname]}>\n <Outlet />\n </ErrorBoundary>\n )\n const content = isAuthRoute ? (\n page\n ) : (\n <AuthGuard isAuthenticated={user != null} redirectTo={loginPath}>\n <RootLayout\n logo={logo}\n logoTo={logoTo}\n toaster={false}\n header={{\n user: user ? { name: user.name, email: user.email, avatar: user.avatar } : undefined,\n onLogout: handleLogout,\n ...header,\n }}\n sidebarFooter={\n showUserCard && user ? (\n <UserCard\n name={user.name}\n email={user.email}\n avatar={user.avatar}\n onLogout={handleLogout}\n />\n ) : undefined\n }\n >\n {page}\n </RootLayout>\n </AuthGuard>\n )\n return (\n <>\n {content}\n <Toaster />\n </>\n )\n}\n"],"names":["AdminRoot","logo","logoTo","getUser","onLogout","authRouteId","loginPath","showUserCard","header","errorBoundary","pending","pendingFallback","unavailable","unavailableFallback","t","useT","navigate","useNavigate","isAuthRoute","useRouterState","s","m","pathname","_a","handleLogout","gate","jsx","styles","Loader","reason","getErrorStatus","retry","forbidden","AuthLayout","jsxs","Col","QueryError","Button","Fragment","Toaster","user","page","Outlet","ErrorBoundary","content","AuthGuard","RootLayout","UserCard"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";var v=Object.create;var y=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,g=Object.prototype.hasOwnProperty;var E=(t,e,s,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of I(e))!g.call(t,n)&&n!==s&&y(t,n,{get:()=>e[n],enumerable:!(i=q(e,n))||i.enumerable});return t};var z=(t,e,s)=>(s=t!=null?v(S(t)):{},E(e||!t||!t.__esModule?y(s,"default",{value:t,enumerable:!0}):s,t));const o=require("react/jsx-runtime"),r=require("react"),w=require("clsx"),R=require("./IconButton-CQ25TtBc.cjs"),M=require("./Icon-B6lz-V0A.cjs"),P=require("./Tooltip-Bw-1Xk2Z.cjs"),D=require("./ConfigProvider-B_Bb6pk1.cjs"),H="_root_floau_4",L="_header_floau_15",O="_title_floau_24",A="_code_floau_34",F="_plain_floau_50",G="_wrap_floau_56",J="_lineNumbers_floau_62",K="_copy_floau_77",c={root:H,header:L,title:O,code:A,plain:F,wrap:G,lineNumbers:J,copy:K},Q=1600,U=r.forwardRef(function({code:e,language:s="tsx",title:i,copyable:n=!0,showLineNumbers:b=!0,maxHeight:_,wrap:j=!1,className:N,style:B,...C},T){const a=D.useT(),[f,h]=r.useState(null),[u,m]=r.useState(!1),d=r.useRef(void 0);r.useEffect(()=>{let l=!1;return import("shiki").then(({codeToHtml:p})=>p(e,{lang:s,theme:"dark-plus"})).then(p=>{l||h(p)}).catch(()=>{l||h(null)}),()=>{l=!0}},[e,s]),r.useEffect(()=>()=>window.clearTimeout(d.current),[]);const k=()=>{var l;(l=navigator.clipboard)==null||l.writeText(e).then(()=>{m(!0),window.clearTimeout(d.current),d.current=window.setTimeout(()=>m(!1),Q)}).catch(()=>{})},x=n&&o.jsx(P.Tooltip,{content:a(u?"codeBlock.copied":"codeBlock.copy"),placement:"left",style:{"--tz-btn-rgb":"255, 255, 255","--tz-btn-on":"#1e1e1e"},children:o.jsx(R.IconButton,{variant:"text",size:"sm",className:c.copy,style:{"--tz-btn-rgb":"255, 255, 255"},"aria-label":a(u?"codeBlock.copied":"codeBlock.copy"),onClick:k,children:o.jsx(M.Icon,{name:u?"CopySuccess":"Copy"})})});return o.jsxs("div",{ref:T,className:w.clsx(c.root,N),style:B,...C,children:[(i!=null||x)&&o.jsxs("div",{className:c.header,children:[o.jsx("span",{className:c.title,children:i}),x]}),o.jsx("div",{className:w.clsx(c.code,j&&c.wrap,b&&c.lineNumbers),style:_!=null?{maxHeight:_}:void 0,children:f!=null?o.jsx("div",{className:c.highlighted,dangerouslySetInnerHTML:{__html:f}}):o.jsx("pre",{className:c.plain,children:o.jsx("code",{children:e})})})]})});exports.CodeBlock=U;
2
- //# sourceMappingURL=CodeBlock-BbIC5BRK.cjs.map
1
+ "use strict";var v=Object.create;var y=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,g=Object.prototype.hasOwnProperty;var E=(t,e,s,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of I(e))!g.call(t,n)&&n!==s&&y(t,n,{get:()=>e[n],enumerable:!(i=q(e,n))||i.enumerable});return t};var z=(t,e,s)=>(s=t!=null?v(S(t)):{},E(e||!t||!t.__esModule?y(s,"default",{value:t,enumerable:!0}):s,t));const o=require("react/jsx-runtime"),r=require("react"),w=require("clsx"),R=require("./IconButton-CQ25TtBc.cjs"),M=require("./Icon-B6lz-V0A.cjs"),P=require("./Tooltip-DmMq5R1x.cjs"),D=require("./ConfigProvider-B_Bb6pk1.cjs"),H="_root_floau_4",L="_header_floau_15",O="_title_floau_24",A="_code_floau_34",F="_plain_floau_50",G="_wrap_floau_56",J="_lineNumbers_floau_62",K="_copy_floau_77",c={root:H,header:L,title:O,code:A,plain:F,wrap:G,lineNumbers:J,copy:K},Q=1600,U=r.forwardRef(function({code:e,language:s="tsx",title:i,copyable:n=!0,showLineNumbers:b=!0,maxHeight:_,wrap:j=!1,className:N,style:B,...C},T){const a=D.useT(),[f,h]=r.useState(null),[u,m]=r.useState(!1),d=r.useRef(void 0);r.useEffect(()=>{let l=!1;return import("shiki").then(({codeToHtml:p})=>p(e,{lang:s,theme:"dark-plus"})).then(p=>{l||h(p)}).catch(()=>{l||h(null)}),()=>{l=!0}},[e,s]),r.useEffect(()=>()=>window.clearTimeout(d.current),[]);const k=()=>{var l;(l=navigator.clipboard)==null||l.writeText(e).then(()=>{m(!0),window.clearTimeout(d.current),d.current=window.setTimeout(()=>m(!1),Q)}).catch(()=>{})},x=n&&o.jsx(P.Tooltip,{content:a(u?"codeBlock.copied":"codeBlock.copy"),placement:"left",style:{"--tz-btn-rgb":"255, 255, 255","--tz-btn-on":"#1e1e1e"},children:o.jsx(R.IconButton,{variant:"text",size:"sm",className:c.copy,style:{"--tz-btn-rgb":"255, 255, 255"},"aria-label":a(u?"codeBlock.copied":"codeBlock.copy"),onClick:k,children:o.jsx(M.Icon,{name:u?"CopySuccess":"Copy"})})});return o.jsxs("div",{ref:T,className:w.clsx(c.root,N),style:B,...C,children:[(i!=null||x)&&o.jsxs("div",{className:c.header,children:[o.jsx("span",{className:c.title,children:i}),x]}),o.jsx("div",{className:w.clsx(c.code,j&&c.wrap,b&&c.lineNumbers),style:_!=null?{maxHeight:_}:void 0,children:f!=null?o.jsx("div",{className:c.highlighted,dangerouslySetInnerHTML:{__html:f}}):o.jsx("pre",{className:c.plain,children:o.jsx("code",{children:e})})})]})});exports.CodeBlock=U;
2
+ //# sourceMappingURL=CodeBlock-C3mG_seZ.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlock-BbIC5BRK.cjs","sources":["../src/components/CodeBlock/CodeBlock.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { clsx } from 'clsx'\nimport { useT } from '../../theme'\nimport { IconButton } from '../IconButton'\nimport { Icon } from '../Icon'\nimport { Tooltip } from '../Tooltip'\nimport styles from './CodeBlock.module.css'\n\n// how long the copy button shows the \"copied\" tick before reverting\nconst COPIED_RESET_MS = 1600\n\nexport interface CodeBlockProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'> {\n /** The source code to render. */\n code: string\n /** Shiki language id (`'tsx'`, `'json'`, `'bash'`, `'css'`, …). Defaults to `'tsx'`. */\n language?: string\n /**\n * Optional label shown on the left of the header bar (e.g. a filename). The header bar itself always\n * renders (whenever `copyable`), so the copy button lives in it with or without a `title`.\n */\n title?: ReactNode\n /** Show a copy-to-clipboard button (in the header bar). Defaults to `true`. */\n copyable?: boolean\n /** Show line numbers. Defaults to `true`. */\n showLineNumbers?: boolean\n /** Cap the block height in px — taller code scrolls inside. */\n maxHeight?: number\n /** Soft-wrap long lines instead of scrolling horizontally. Defaults to `false`. */\n wrap?: boolean\n}\n\n/**\n * A syntax-highlighted code block with **VS Code colors** — powered by **Shiki** (an optional peer,\n * `npm i shiki`), the actual VS Code highlighting engine, using its **`dark-plus`** theme. The block\n * is **always dark** (in both app modes) — code reads best on a dark surface, and the deep background\n * anchors it regardless of the surrounding light/dark theme. Highlighting is **async and lazy**\n * (`import('shiki')` on first render); until it lands — or if `shiki` isn't installed / the language\n * is unknown — the block shows the plain code (on the same dark surface), so nothing breaks. A header\n * bar always renders (whenever `copyable`, default `true`) and holds the copy button — which flips to a\n * tick on click — with an optional `title` (e.g. a filename) on its left; `showLineNumbers` (default\n * `true`), `maxHeight` (scrolls inside) and `wrap` shape the body. The whole block\n * keeps VS Code's own dark colors (a deliberate literal-color exception, like the Modal scrim); only\n * the outer border/radius uses `--tz-*` tokens.\n */\nexport const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(function CodeBlock(\n {\n code,\n language = 'tsx',\n title,\n copyable = true,\n showLineNumbers = true,\n maxHeight,\n wrap = false,\n className,\n style,\n ...props\n },\n ref,\n) {\n const t = useT()\n const [html, setHtml] = useState<string | null>(null)\n const [copied, setCopied] = useState(false)\n const copyTimer = useRef<number | undefined>(undefined)\n\n // highlight asynchronously — shiki is an optional peer, loaded lazily on first use; a missing\n // package or an unknown language just leaves the plain (unhighlighted) fallback in place\n useEffect(() => {\n let cancelled = false\n import('shiki')\n .then(({ codeToHtml }) =>\n codeToHtml(code, {\n lang: language,\n // always VS Code's dark theme — the block stays dark in both app modes\n theme: 'dark-plus',\n }),\n )\n .then((out) => {\n if (!cancelled) setHtml(out)\n })\n .catch(() => {\n if (!cancelled) setHtml(null)\n })\n return () => {\n cancelled = true\n }\n }, [code, language])\n\n useEffect(() => () => window.clearTimeout(copyTimer.current), [])\n\n const handleCopy = () => {\n navigator.clipboard\n ?.writeText(code)\n .then(() => {\n setCopied(true)\n window.clearTimeout(copyTimer.current)\n copyTimer.current = window.setTimeout(() => setCopied(false), COPIED_RESET_MS)\n })\n .catch(() => {})\n }\n\n const copyButton = copyable && (\n // the tooltip is hover/focus-driven; clicking flips `copied`, so its content updates to \"Copied!\"\n // while it's still open (the pointer is on the button). `left` keeps it inside the block's\n // `overflow: hidden` bounds.\n <Tooltip\n content={copied ? t('codeBlock.copied') : t('codeBlock.copy')}\n placement=\"left\"\n // always a white pill with dark text over the (always-dark) block — set via `style` so it beats\n // Tooltip's own color-derived --tz-btn-rgb (which is spread before the consumer `style`)\n style={{ '--tz-btn-rgb': '255, 255, 255', '--tz-btn-on': '#1e1e1e' } as CSSProperties}\n >\n <IconButton\n variant=\"text\"\n size=\"sm\"\n className={styles.copy}\n // force the button light over the dark surface — passed via `style` so it beats IconButton's\n // own inline `--tz-btn-rgb` (which is spread before the consumer `style`, so this wins)\n style={{ '--tz-btn-rgb': '255, 255, 255' } as CSSProperties}\n aria-label={copied ? t('codeBlock.copied') : t('codeBlock.copy')}\n onClick={handleCopy}\n >\n <Icon name={copied ? 'CopySuccess' : 'Copy'} />\n </IconButton>\n </Tooltip>\n )\n\n return (\n <div ref={ref} className={clsx(styles.root, className)} style={style} {...props}>\n {/* the header bar always renders whenever there's a title or a copy button — so the copy icon\n lives in the bar (top-right), never floating over the code */}\n {(title != null || copyButton) && (\n <div className={styles.header}>\n <span className={styles.title}>{title}</span>\n {copyButton}\n </div>\n )}\n <div\n className={clsx(styles.code, wrap && styles.wrap, showLineNumbers && styles.lineNumbers)}\n style={maxHeight != null ? ({ maxHeight } as CSSProperties) : undefined}\n >\n {html != null ? (\n // shiki output is trusted markup — it escapes the source code itself\n <div className={styles.highlighted} dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <pre className={styles.plain}>\n <code>{code}</code>\n </pre>\n )}\n </div>\n </div>\n )\n})\n"],"names":["COPIED_RESET_MS","CodeBlock","forwardRef","code","language","title","copyable","showLineNumbers","maxHeight","wrap","className","style","props","ref","t","useT","html","setHtml","useState","copied","setCopied","copyTimer","useRef","useEffect","cancelled","codeToHtml","out","handleCopy","_a","copyButton","jsx","Tooltip","IconButton","styles","Icon","jsxs","clsx"],"mappings":"k6BAiBMA,EAAkB,KAmCXC,EAAYC,EAAAA,WAA2C,SAClE,CACE,KAAAC,EACA,SAAAC,EAAW,MACX,MAAAC,EACA,SAAAC,EAAW,GACX,gBAAAC,EAAkB,GAClB,UAAAC,EACA,KAAAC,EAAO,GACP,UAAAC,EACA,MAAAC,EACA,GAAGC,CACL,EACAC,EACA,CACA,MAAMC,EAAIC,EAAAA,KAAA,EACJ,CAACC,EAAMC,CAAO,EAAIC,EAAAA,SAAwB,IAAI,EAC9C,CAACC,EAAQC,CAAS,EAAIF,EAAAA,SAAS,EAAK,EACpCG,EAAYC,EAAAA,OAA2B,MAAS,EAItDC,EAAAA,UAAU,IAAM,CACd,IAAIC,EAAY,GAChB,cAAO,OAAO,EACX,KAAK,CAAC,CAAE,WAAAC,CAAA,IACPA,EAAWtB,EAAM,CACf,KAAMC,EAEN,MAAO,WAAA,CACR,CAAA,EAEF,KAAMsB,GAAQ,CACRF,GAAWP,EAAQS,CAAG,CAC7B,CAAC,EACA,MAAM,IAAM,CACNF,GAAWP,EAAQ,IAAI,CAC9B,CAAC,EACI,IAAM,CACXO,EAAY,EACd,CACF,EAAG,CAACrB,EAAMC,CAAQ,CAAC,EAEnBmB,EAAAA,UAAU,IAAM,IAAM,OAAO,aAAaF,EAAU,OAAO,EAAG,EAAE,EAEhE,MAAMM,EAAa,IAAM,QACvBC,EAAA,UAAU,YAAV,MAAAA,EACI,UAAUzB,GACX,KAAK,IAAM,CACViB,EAAU,EAAI,EACd,OAAO,aAAaC,EAAU,OAAO,EACrCA,EAAU,QAAU,OAAO,WAAW,IAAMD,EAAU,EAAK,EAAGpB,CAAe,CAC/E,GACC,MAAM,IAAM,CAAC,EAClB,EAEM6B,EAAavB,GAIjBwB,EAAAA,IAACC,EAAAA,QAAA,CACC,QAAkBjB,EAATK,EAAW,mBAAwB,gBAAN,EACtC,UAAU,OAGV,MAAO,CAAE,eAAgB,gBAAiB,cAAe,SAAA,EAEzD,SAAAW,EAAAA,IAACE,EAAAA,WAAA,CACC,QAAQ,OACR,KAAK,KACL,UAAWC,EAAO,KAGlB,MAAO,CAAE,eAAgB,eAAA,EACzB,aAAqBnB,EAATK,EAAW,mBAAwB,gBAAN,EACzC,QAASQ,EAET,SAAAG,EAAAA,IAACI,OAAA,CAAK,KAAMf,EAAS,cAAgB,MAAA,CAAQ,CAAA,CAAA,CAC/C,CAAA,EAIJ,OACEgB,EAAAA,KAAC,MAAA,CAAI,IAAAtB,EAAU,UAAWuB,EAAAA,KAAKH,EAAO,KAAMvB,CAAS,EAAG,MAAAC,EAAe,GAAGC,EAGtE,SAAA,EAAAP,GAAS,MAAQwB,IACjBM,EAAAA,KAAC,MAAA,CAAI,UAAWF,EAAO,OACrB,SAAA,CAAAH,EAAAA,IAAC,OAAA,CAAK,UAAWG,EAAO,MAAQ,SAAA5B,EAAM,EACrCwB,CAAA,EACH,EAEFC,EAAAA,IAAC,MAAA,CACC,UAAWM,EAAAA,KAAKH,EAAO,KAAMxB,GAAQwB,EAAO,KAAM1B,GAAmB0B,EAAO,WAAW,EACvF,MAAOzB,GAAa,KAAQ,CAAE,UAAAA,GAAgC,OAE7D,SAAAQ,GAAQ,KAEPc,MAAC,OAAI,UAAWG,EAAO,YAAa,wBAAyB,CAAE,OAAQjB,EAAK,CAAG,EAE/Ec,EAAAA,IAAC,OAAI,UAAWG,EAAO,MACrB,SAAAH,EAAAA,IAAC,OAAA,CAAM,WAAK,CAAA,CACd,CAAA,CAAA,CAEJ,EACF,CAEJ,CAAC"}
1
+ {"version":3,"file":"CodeBlock-C3mG_seZ.cjs","sources":["../src/components/CodeBlock/CodeBlock.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { clsx } from 'clsx'\nimport { useT } from '../../theme'\nimport { IconButton } from '../IconButton'\nimport { Icon } from '../Icon'\nimport { Tooltip } from '../Tooltip'\nimport styles from './CodeBlock.module.css'\n\n// how long the copy button shows the \"copied\" tick before reverting\nconst COPIED_RESET_MS = 1600\n\nexport interface CodeBlockProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'> {\n /** The source code to render. */\n code: string\n /** Shiki language id (`'tsx'`, `'json'`, `'bash'`, `'css'`, …). Defaults to `'tsx'`. */\n language?: string\n /**\n * Optional label shown on the left of the header bar (e.g. a filename). The header bar itself always\n * renders (whenever `copyable`), so the copy button lives in it with or without a `title`.\n */\n title?: ReactNode\n /** Show a copy-to-clipboard button (in the header bar). Defaults to `true`. */\n copyable?: boolean\n /** Show line numbers. Defaults to `true`. */\n showLineNumbers?: boolean\n /** Cap the block height in px — taller code scrolls inside. */\n maxHeight?: number\n /** Soft-wrap long lines instead of scrolling horizontally. Defaults to `false`. */\n wrap?: boolean\n}\n\n/**\n * A syntax-highlighted code block with **VS Code colors** — powered by **Shiki** (an optional peer,\n * `npm i shiki`), the actual VS Code highlighting engine, using its **`dark-plus`** theme. The block\n * is **always dark** (in both app modes) — code reads best on a dark surface, and the deep background\n * anchors it regardless of the surrounding light/dark theme. Highlighting is **async and lazy**\n * (`import('shiki')` on first render); until it lands — or if `shiki` isn't installed / the language\n * is unknown — the block shows the plain code (on the same dark surface), so nothing breaks. A header\n * bar always renders (whenever `copyable`, default `true`) and holds the copy button — which flips to a\n * tick on click — with an optional `title` (e.g. a filename) on its left; `showLineNumbers` (default\n * `true`), `maxHeight` (scrolls inside) and `wrap` shape the body. The whole block\n * keeps VS Code's own dark colors (a deliberate literal-color exception, like the Modal scrim); only\n * the outer border/radius uses `--tz-*` tokens.\n */\nexport const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(function CodeBlock(\n {\n code,\n language = 'tsx',\n title,\n copyable = true,\n showLineNumbers = true,\n maxHeight,\n wrap = false,\n className,\n style,\n ...props\n },\n ref,\n) {\n const t = useT()\n const [html, setHtml] = useState<string | null>(null)\n const [copied, setCopied] = useState(false)\n const copyTimer = useRef<number | undefined>(undefined)\n\n // highlight asynchronously — shiki is an optional peer, loaded lazily on first use; a missing\n // package or an unknown language just leaves the plain (unhighlighted) fallback in place\n useEffect(() => {\n let cancelled = false\n import('shiki')\n .then(({ codeToHtml }) =>\n codeToHtml(code, {\n lang: language,\n // always VS Code's dark theme — the block stays dark in both app modes\n theme: 'dark-plus',\n }),\n )\n .then((out) => {\n if (!cancelled) setHtml(out)\n })\n .catch(() => {\n if (!cancelled) setHtml(null)\n })\n return () => {\n cancelled = true\n }\n }, [code, language])\n\n useEffect(() => () => window.clearTimeout(copyTimer.current), [])\n\n const handleCopy = () => {\n navigator.clipboard\n ?.writeText(code)\n .then(() => {\n setCopied(true)\n window.clearTimeout(copyTimer.current)\n copyTimer.current = window.setTimeout(() => setCopied(false), COPIED_RESET_MS)\n })\n .catch(() => {})\n }\n\n const copyButton = copyable && (\n // the tooltip is hover/focus-driven; clicking flips `copied`, so its content updates to \"Copied!\"\n // while it's still open (the pointer is on the button). `left` keeps it inside the block's\n // `overflow: hidden` bounds.\n <Tooltip\n content={copied ? t('codeBlock.copied') : t('codeBlock.copy')}\n placement=\"left\"\n // always a white pill with dark text over the (always-dark) block — set via `style` so it beats\n // Tooltip's own color-derived --tz-btn-rgb (which is spread before the consumer `style`)\n style={{ '--tz-btn-rgb': '255, 255, 255', '--tz-btn-on': '#1e1e1e' } as CSSProperties}\n >\n <IconButton\n variant=\"text\"\n size=\"sm\"\n className={styles.copy}\n // force the button light over the dark surface — passed via `style` so it beats IconButton's\n // own inline `--tz-btn-rgb` (which is spread before the consumer `style`, so this wins)\n style={{ '--tz-btn-rgb': '255, 255, 255' } as CSSProperties}\n aria-label={copied ? t('codeBlock.copied') : t('codeBlock.copy')}\n onClick={handleCopy}\n >\n <Icon name={copied ? 'CopySuccess' : 'Copy'} />\n </IconButton>\n </Tooltip>\n )\n\n return (\n <div ref={ref} className={clsx(styles.root, className)} style={style} {...props}>\n {/* the header bar always renders whenever there's a title or a copy button — so the copy icon\n lives in the bar (top-right), never floating over the code */}\n {(title != null || copyButton) && (\n <div className={styles.header}>\n <span className={styles.title}>{title}</span>\n {copyButton}\n </div>\n )}\n <div\n className={clsx(styles.code, wrap && styles.wrap, showLineNumbers && styles.lineNumbers)}\n style={maxHeight != null ? ({ maxHeight } as CSSProperties) : undefined}\n >\n {html != null ? (\n // shiki output is trusted markup — it escapes the source code itself\n <div className={styles.highlighted} dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <pre className={styles.plain}>\n <code>{code}</code>\n </pre>\n )}\n </div>\n </div>\n )\n})\n"],"names":["COPIED_RESET_MS","CodeBlock","forwardRef","code","language","title","copyable","showLineNumbers","maxHeight","wrap","className","style","props","ref","t","useT","html","setHtml","useState","copied","setCopied","copyTimer","useRef","useEffect","cancelled","codeToHtml","out","handleCopy","_a","copyButton","jsx","Tooltip","IconButton","styles","Icon","jsxs","clsx"],"mappings":"k6BAiBMA,EAAkB,KAmCXC,EAAYC,EAAAA,WAA2C,SAClE,CACE,KAAAC,EACA,SAAAC,EAAW,MACX,MAAAC,EACA,SAAAC,EAAW,GACX,gBAAAC,EAAkB,GAClB,UAAAC,EACA,KAAAC,EAAO,GACP,UAAAC,EACA,MAAAC,EACA,GAAGC,CACL,EACAC,EACA,CACA,MAAMC,EAAIC,EAAAA,KAAA,EACJ,CAACC,EAAMC,CAAO,EAAIC,EAAAA,SAAwB,IAAI,EAC9C,CAACC,EAAQC,CAAS,EAAIF,EAAAA,SAAS,EAAK,EACpCG,EAAYC,EAAAA,OAA2B,MAAS,EAItDC,EAAAA,UAAU,IAAM,CACd,IAAIC,EAAY,GAChB,cAAO,OAAO,EACX,KAAK,CAAC,CAAE,WAAAC,CAAA,IACPA,EAAWtB,EAAM,CACf,KAAMC,EAEN,MAAO,WAAA,CACR,CAAA,EAEF,KAAMsB,GAAQ,CACRF,GAAWP,EAAQS,CAAG,CAC7B,CAAC,EACA,MAAM,IAAM,CACNF,GAAWP,EAAQ,IAAI,CAC9B,CAAC,EACI,IAAM,CACXO,EAAY,EACd,CACF,EAAG,CAACrB,EAAMC,CAAQ,CAAC,EAEnBmB,EAAAA,UAAU,IAAM,IAAM,OAAO,aAAaF,EAAU,OAAO,EAAG,EAAE,EAEhE,MAAMM,EAAa,IAAM,QACvBC,EAAA,UAAU,YAAV,MAAAA,EACI,UAAUzB,GACX,KAAK,IAAM,CACViB,EAAU,EAAI,EACd,OAAO,aAAaC,EAAU,OAAO,EACrCA,EAAU,QAAU,OAAO,WAAW,IAAMD,EAAU,EAAK,EAAGpB,CAAe,CAC/E,GACC,MAAM,IAAM,CAAC,EAClB,EAEM6B,EAAavB,GAIjBwB,EAAAA,IAACC,EAAAA,QAAA,CACC,QAAkBjB,EAATK,EAAW,mBAAwB,gBAAN,EACtC,UAAU,OAGV,MAAO,CAAE,eAAgB,gBAAiB,cAAe,SAAA,EAEzD,SAAAW,EAAAA,IAACE,EAAAA,WAAA,CACC,QAAQ,OACR,KAAK,KACL,UAAWC,EAAO,KAGlB,MAAO,CAAE,eAAgB,eAAA,EACzB,aAAqBnB,EAATK,EAAW,mBAAwB,gBAAN,EACzC,QAASQ,EAET,SAAAG,EAAAA,IAACI,OAAA,CAAK,KAAMf,EAAS,cAAgB,MAAA,CAAQ,CAAA,CAAA,CAC/C,CAAA,EAIJ,OACEgB,EAAAA,KAAC,MAAA,CAAI,IAAAtB,EAAU,UAAWuB,EAAAA,KAAKH,EAAO,KAAMvB,CAAS,EAAG,MAAAC,EAAe,GAAGC,EAGtE,SAAA,EAAAP,GAAS,MAAQwB,IACjBM,EAAAA,KAAC,MAAA,CAAI,UAAWF,EAAO,OACrB,SAAA,CAAAH,EAAAA,IAAC,OAAA,CAAK,UAAWG,EAAO,MAAQ,SAAA5B,EAAM,EACrCwB,CAAA,EACH,EAEFC,EAAAA,IAAC,MAAA,CACC,UAAWM,EAAAA,KAAKH,EAAO,KAAMxB,GAAQwB,EAAO,KAAM1B,GAAmB0B,EAAO,WAAW,EACvF,MAAOzB,GAAa,KAAQ,CAAE,UAAAA,GAAgC,OAE7D,SAAAQ,GAAQ,KAEPc,MAAC,OAAI,UAAWG,EAAO,YAAa,wBAAyB,CAAE,OAAQjB,EAAK,CAAG,EAE/Ec,EAAAA,IAAC,OAAI,UAAWG,EAAO,MACrB,SAAAH,EAAAA,IAAC,OAAA,CAAM,WAAK,CAAA,CACd,CAAA,CAAA,CAEJ,EACF,CAEJ,CAAC"}
@@ -3,7 +3,7 @@ import { forwardRef as I, useState as h, useRef as S, useEffect as y } from "rea
3
3
  import { clsx as b } from "clsx";
4
4
  import { I as g } from "./IconButton-06pmftMo.js";
5
5
  import { b as z } from "./Icon-D5TxgfCc.js";
6
- import { T as E } from "./Tooltip-DsDv4NOq.js";
6
+ import { T as E } from "./Tooltip-BbT0uNzy.js";
7
7
  import { a as R } from "./ConfigProvider-DeMASTVo.js";
8
8
  const j = "_root_floau_4", M = "_header_floau_15", D = "_title_floau_24", H = "_code_floau_34", L = "_plain_floau_50", O = "_wrap_floau_56", P = "_lineNumbers_floau_62", q = "_copy_floau_77", e = {
9
9
  root: j,
@@ -93,4 +93,4 @@ const j = "_root_floau_4", M = "_header_floau_15", D = "_title_floau_24", H = "_
93
93
  export {
94
94
  X as C
95
95
  };
96
- //# sourceMappingURL=CodeBlock-C8A6E-tJ.js.map
96
+ //# sourceMappingURL=CodeBlock-DrWRSRmO.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlock-C8A6E-tJ.js","sources":["../src/components/CodeBlock/CodeBlock.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { clsx } from 'clsx'\nimport { useT } from '../../theme'\nimport { IconButton } from '../IconButton'\nimport { Icon } from '../Icon'\nimport { Tooltip } from '../Tooltip'\nimport styles from './CodeBlock.module.css'\n\n// how long the copy button shows the \"copied\" tick before reverting\nconst COPIED_RESET_MS = 1600\n\nexport interface CodeBlockProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'> {\n /** The source code to render. */\n code: string\n /** Shiki language id (`'tsx'`, `'json'`, `'bash'`, `'css'`, …). Defaults to `'tsx'`. */\n language?: string\n /**\n * Optional label shown on the left of the header bar (e.g. a filename). The header bar itself always\n * renders (whenever `copyable`), so the copy button lives in it with or without a `title`.\n */\n title?: ReactNode\n /** Show a copy-to-clipboard button (in the header bar). Defaults to `true`. */\n copyable?: boolean\n /** Show line numbers. Defaults to `true`. */\n showLineNumbers?: boolean\n /** Cap the block height in px — taller code scrolls inside. */\n maxHeight?: number\n /** Soft-wrap long lines instead of scrolling horizontally. Defaults to `false`. */\n wrap?: boolean\n}\n\n/**\n * A syntax-highlighted code block with **VS Code colors** — powered by **Shiki** (an optional peer,\n * `npm i shiki`), the actual VS Code highlighting engine, using its **`dark-plus`** theme. The block\n * is **always dark** (in both app modes) — code reads best on a dark surface, and the deep background\n * anchors it regardless of the surrounding light/dark theme. Highlighting is **async and lazy**\n * (`import('shiki')` on first render); until it lands — or if `shiki` isn't installed / the language\n * is unknown — the block shows the plain code (on the same dark surface), so nothing breaks. A header\n * bar always renders (whenever `copyable`, default `true`) and holds the copy button — which flips to a\n * tick on click — with an optional `title` (e.g. a filename) on its left; `showLineNumbers` (default\n * `true`), `maxHeight` (scrolls inside) and `wrap` shape the body. The whole block\n * keeps VS Code's own dark colors (a deliberate literal-color exception, like the Modal scrim); only\n * the outer border/radius uses `--tz-*` tokens.\n */\nexport const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(function CodeBlock(\n {\n code,\n language = 'tsx',\n title,\n copyable = true,\n showLineNumbers = true,\n maxHeight,\n wrap = false,\n className,\n style,\n ...props\n },\n ref,\n) {\n const t = useT()\n const [html, setHtml] = useState<string | null>(null)\n const [copied, setCopied] = useState(false)\n const copyTimer = useRef<number | undefined>(undefined)\n\n // highlight asynchronously — shiki is an optional peer, loaded lazily on first use; a missing\n // package or an unknown language just leaves the plain (unhighlighted) fallback in place\n useEffect(() => {\n let cancelled = false\n import('shiki')\n .then(({ codeToHtml }) =>\n codeToHtml(code, {\n lang: language,\n // always VS Code's dark theme — the block stays dark in both app modes\n theme: 'dark-plus',\n }),\n )\n .then((out) => {\n if (!cancelled) setHtml(out)\n })\n .catch(() => {\n if (!cancelled) setHtml(null)\n })\n return () => {\n cancelled = true\n }\n }, [code, language])\n\n useEffect(() => () => window.clearTimeout(copyTimer.current), [])\n\n const handleCopy = () => {\n navigator.clipboard\n ?.writeText(code)\n .then(() => {\n setCopied(true)\n window.clearTimeout(copyTimer.current)\n copyTimer.current = window.setTimeout(() => setCopied(false), COPIED_RESET_MS)\n })\n .catch(() => {})\n }\n\n const copyButton = copyable && (\n // the tooltip is hover/focus-driven; clicking flips `copied`, so its content updates to \"Copied!\"\n // while it's still open (the pointer is on the button). `left` keeps it inside the block's\n // `overflow: hidden` bounds.\n <Tooltip\n content={copied ? t('codeBlock.copied') : t('codeBlock.copy')}\n placement=\"left\"\n // always a white pill with dark text over the (always-dark) block — set via `style` so it beats\n // Tooltip's own color-derived --tz-btn-rgb (which is spread before the consumer `style`)\n style={{ '--tz-btn-rgb': '255, 255, 255', '--tz-btn-on': '#1e1e1e' } as CSSProperties}\n >\n <IconButton\n variant=\"text\"\n size=\"sm\"\n className={styles.copy}\n // force the button light over the dark surface — passed via `style` so it beats IconButton's\n // own inline `--tz-btn-rgb` (which is spread before the consumer `style`, so this wins)\n style={{ '--tz-btn-rgb': '255, 255, 255' } as CSSProperties}\n aria-label={copied ? t('codeBlock.copied') : t('codeBlock.copy')}\n onClick={handleCopy}\n >\n <Icon name={copied ? 'CopySuccess' : 'Copy'} />\n </IconButton>\n </Tooltip>\n )\n\n return (\n <div ref={ref} className={clsx(styles.root, className)} style={style} {...props}>\n {/* the header bar always renders whenever there's a title or a copy button — so the copy icon\n lives in the bar (top-right), never floating over the code */}\n {(title != null || copyButton) && (\n <div className={styles.header}>\n <span className={styles.title}>{title}</span>\n {copyButton}\n </div>\n )}\n <div\n className={clsx(styles.code, wrap && styles.wrap, showLineNumbers && styles.lineNumbers)}\n style={maxHeight != null ? ({ maxHeight } as CSSProperties) : undefined}\n >\n {html != null ? (\n // shiki output is trusted markup — it escapes the source code itself\n <div className={styles.highlighted} dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <pre className={styles.plain}>\n <code>{code}</code>\n </pre>\n )}\n </div>\n </div>\n )\n})\n"],"names":["COPIED_RESET_MS","CodeBlock","forwardRef","code","language","title","copyable","showLineNumbers","maxHeight","wrap","className","style","props","ref","t","useT","html","setHtml","useState","copied","setCopied","copyTimer","useRef","useEffect","cancelled","codeToHtml","out","handleCopy","_a","copyButton","jsx","Tooltip","IconButton","styles","Icon","jsxs","clsx"],"mappings":";;;;;;;;;;;;;;;;GAiBMA,IAAkB,MAmCXC,IAAYC,EAA2C,SAClE;AAAA,EACE,MAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,OAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,iBAAAC,IAAkB;AAAA,EAClB,WAAAC;AAAA,EACA,MAAAC,IAAO;AAAA,EACP,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAMC,IAAIC,EAAA,GACJ,CAACC,GAAMC,CAAO,IAAIC,EAAwB,IAAI,GAC9C,CAACC,GAAQC,CAAS,IAAIF,EAAS,EAAK,GACpCG,IAAYC,EAA2B,MAAS;AAItD,EAAAC,EAAU,MAAM;AACd,QAAIC,IAAY;AAChB,kBAAO,OAAO,EACX;AAAA,MAAK,CAAC,EAAE,YAAAC,EAAA,MACPA,EAAWtB,GAAM;AAAA,QACf,MAAMC;AAAA;AAAA,QAEN,OAAO;AAAA,MAAA,CACR;AAAA,IAAA,EAEF,KAAK,CAACsB,MAAQ;AACb,MAAKF,KAAWP,EAAQS,CAAG;AAAA,IAC7B,CAAC,EACA,MAAM,MAAM;AACX,MAAKF,KAAWP,EAAQ,IAAI;AAAA,IAC9B,CAAC,GACI,MAAM;AACX,MAAAO,IAAY;AAAA,IACd;AAAA,EACF,GAAG,CAACrB,GAAMC,CAAQ,CAAC,GAEnBmB,EAAU,MAAM,MAAM,OAAO,aAAaF,EAAU,OAAO,GAAG,EAAE;AAEhE,QAAMM,IAAa,MAAM;;AACvB,KAAAC,IAAA,UAAU,cAAV,QAAAA,EACI,UAAUzB,GACX,KAAK,MAAM;AACV,MAAAiB,EAAU,EAAI,GACd,OAAO,aAAaC,EAAU,OAAO,GACrCA,EAAU,UAAU,OAAO,WAAW,MAAMD,EAAU,EAAK,GAAGpB,CAAe;AAAA,IAC/E,GACC,MAAM,MAAM;AAAA,IAAC;AAAA,EAClB,GAEM6B,IAAavB;AAAA;AAAA;AAAA,EAIjB,gBAAAwB;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,SAAkBjB,EAATK,IAAW,qBAAwB,gBAAN;AAAA,MACtC,WAAU;AAAA,MAGV,OAAO,EAAE,gBAAgB,iBAAiB,eAAe,UAAA;AAAA,MAEzD,UAAA,gBAAAW;AAAA,QAACE;AAAA,QAAA;AAAA,UACC,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,WAAWC,EAAO;AAAA,UAGlB,OAAO,EAAE,gBAAgB,gBAAA;AAAA,UACzB,cAAqBnB,EAATK,IAAW,qBAAwB,gBAAN;AAAA,UACzC,SAASQ;AAAA,UAET,UAAA,gBAAAG,EAACI,GAAA,EAAK,MAAMf,IAAS,gBAAgB,OAAA,CAAQ;AAAA,QAAA;AAAA,MAAA;AAAA,IAC/C;AAAA,EAAA;AAIJ,SACE,gBAAAgB,EAAC,OAAA,EAAI,KAAAtB,GAAU,WAAWuB,EAAKH,EAAO,MAAMvB,CAAS,GAAG,OAAAC,GAAe,GAAGC,GAGtE,UAAA;AAAA,KAAAP,KAAS,QAAQwB,MACjB,gBAAAM,EAAC,OAAA,EAAI,WAAWF,EAAO,QACrB,UAAA;AAAA,MAAA,gBAAAH,EAAC,QAAA,EAAK,WAAWG,EAAO,OAAQ,UAAA5B,GAAM;AAAA,MACrCwB;AAAA,IAAA,GACH;AAAA,IAEF,gBAAAC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWM,EAAKH,EAAO,MAAMxB,KAAQwB,EAAO,MAAM1B,KAAmB0B,EAAO,WAAW;AAAA,QACvF,OAAOzB,KAAa,OAAQ,EAAE,WAAAA,MAAgC;AAAA,QAE7D,UAAAQ,KAAQ;AAAA;AAAA,UAEP,gBAAAc,EAAC,SAAI,WAAWG,EAAO,aAAa,yBAAyB,EAAE,QAAQjB,IAAK,CAAG;AAAA,YAE/E,gBAAAc,EAAC,SAAI,WAAWG,EAAO,OACrB,UAAA,gBAAAH,EAAC,QAAA,EAAM,aAAK,EAAA,CACd;AAAA,MAAA;AAAA,IAAA;AAAA,EAEJ,GACF;AAEJ,CAAC;"}
1
+ {"version":3,"file":"CodeBlock-DrWRSRmO.js","sources":["../src/components/CodeBlock/CodeBlock.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { clsx } from 'clsx'\nimport { useT } from '../../theme'\nimport { IconButton } from '../IconButton'\nimport { Icon } from '../Icon'\nimport { Tooltip } from '../Tooltip'\nimport styles from './CodeBlock.module.css'\n\n// how long the copy button shows the \"copied\" tick before reverting\nconst COPIED_RESET_MS = 1600\n\nexport interface CodeBlockProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'> {\n /** The source code to render. */\n code: string\n /** Shiki language id (`'tsx'`, `'json'`, `'bash'`, `'css'`, …). Defaults to `'tsx'`. */\n language?: string\n /**\n * Optional label shown on the left of the header bar (e.g. a filename). The header bar itself always\n * renders (whenever `copyable`), so the copy button lives in it with or without a `title`.\n */\n title?: ReactNode\n /** Show a copy-to-clipboard button (in the header bar). Defaults to `true`. */\n copyable?: boolean\n /** Show line numbers. Defaults to `true`. */\n showLineNumbers?: boolean\n /** Cap the block height in px — taller code scrolls inside. */\n maxHeight?: number\n /** Soft-wrap long lines instead of scrolling horizontally. Defaults to `false`. */\n wrap?: boolean\n}\n\n/**\n * A syntax-highlighted code block with **VS Code colors** — powered by **Shiki** (an optional peer,\n * `npm i shiki`), the actual VS Code highlighting engine, using its **`dark-plus`** theme. The block\n * is **always dark** (in both app modes) — code reads best on a dark surface, and the deep background\n * anchors it regardless of the surrounding light/dark theme. Highlighting is **async and lazy**\n * (`import('shiki')` on first render); until it lands — or if `shiki` isn't installed / the language\n * is unknown — the block shows the plain code (on the same dark surface), so nothing breaks. A header\n * bar always renders (whenever `copyable`, default `true`) and holds the copy button — which flips to a\n * tick on click — with an optional `title` (e.g. a filename) on its left; `showLineNumbers` (default\n * `true`), `maxHeight` (scrolls inside) and `wrap` shape the body. The whole block\n * keeps VS Code's own dark colors (a deliberate literal-color exception, like the Modal scrim); only\n * the outer border/radius uses `--tz-*` tokens.\n */\nexport const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(function CodeBlock(\n {\n code,\n language = 'tsx',\n title,\n copyable = true,\n showLineNumbers = true,\n maxHeight,\n wrap = false,\n className,\n style,\n ...props\n },\n ref,\n) {\n const t = useT()\n const [html, setHtml] = useState<string | null>(null)\n const [copied, setCopied] = useState(false)\n const copyTimer = useRef<number | undefined>(undefined)\n\n // highlight asynchronously — shiki is an optional peer, loaded lazily on first use; a missing\n // package or an unknown language just leaves the plain (unhighlighted) fallback in place\n useEffect(() => {\n let cancelled = false\n import('shiki')\n .then(({ codeToHtml }) =>\n codeToHtml(code, {\n lang: language,\n // always VS Code's dark theme — the block stays dark in both app modes\n theme: 'dark-plus',\n }),\n )\n .then((out) => {\n if (!cancelled) setHtml(out)\n })\n .catch(() => {\n if (!cancelled) setHtml(null)\n })\n return () => {\n cancelled = true\n }\n }, [code, language])\n\n useEffect(() => () => window.clearTimeout(copyTimer.current), [])\n\n const handleCopy = () => {\n navigator.clipboard\n ?.writeText(code)\n .then(() => {\n setCopied(true)\n window.clearTimeout(copyTimer.current)\n copyTimer.current = window.setTimeout(() => setCopied(false), COPIED_RESET_MS)\n })\n .catch(() => {})\n }\n\n const copyButton = copyable && (\n // the tooltip is hover/focus-driven; clicking flips `copied`, so its content updates to \"Copied!\"\n // while it's still open (the pointer is on the button). `left` keeps it inside the block's\n // `overflow: hidden` bounds.\n <Tooltip\n content={copied ? t('codeBlock.copied') : t('codeBlock.copy')}\n placement=\"left\"\n // always a white pill with dark text over the (always-dark) block — set via `style` so it beats\n // Tooltip's own color-derived --tz-btn-rgb (which is spread before the consumer `style`)\n style={{ '--tz-btn-rgb': '255, 255, 255', '--tz-btn-on': '#1e1e1e' } as CSSProperties}\n >\n <IconButton\n variant=\"text\"\n size=\"sm\"\n className={styles.copy}\n // force the button light over the dark surface — passed via `style` so it beats IconButton's\n // own inline `--tz-btn-rgb` (which is spread before the consumer `style`, so this wins)\n style={{ '--tz-btn-rgb': '255, 255, 255' } as CSSProperties}\n aria-label={copied ? t('codeBlock.copied') : t('codeBlock.copy')}\n onClick={handleCopy}\n >\n <Icon name={copied ? 'CopySuccess' : 'Copy'} />\n </IconButton>\n </Tooltip>\n )\n\n return (\n <div ref={ref} className={clsx(styles.root, className)} style={style} {...props}>\n {/* the header bar always renders whenever there's a title or a copy button — so the copy icon\n lives in the bar (top-right), never floating over the code */}\n {(title != null || copyButton) && (\n <div className={styles.header}>\n <span className={styles.title}>{title}</span>\n {copyButton}\n </div>\n )}\n <div\n className={clsx(styles.code, wrap && styles.wrap, showLineNumbers && styles.lineNumbers)}\n style={maxHeight != null ? ({ maxHeight } as CSSProperties) : undefined}\n >\n {html != null ? (\n // shiki output is trusted markup — it escapes the source code itself\n <div className={styles.highlighted} dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <pre className={styles.plain}>\n <code>{code}</code>\n </pre>\n )}\n </div>\n </div>\n )\n})\n"],"names":["COPIED_RESET_MS","CodeBlock","forwardRef","code","language","title","copyable","showLineNumbers","maxHeight","wrap","className","style","props","ref","t","useT","html","setHtml","useState","copied","setCopied","copyTimer","useRef","useEffect","cancelled","codeToHtml","out","handleCopy","_a","copyButton","jsx","Tooltip","IconButton","styles","Icon","jsxs","clsx"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";const s=require("react/jsx-runtime"),v=require("react"),m=require("clsx"),L=require("./useClipboard-BjTIX-4g.cjs"),h=require("./palette-BycMHbED.cjs"),D=require("./Tooltip-Bw-1Xk2Z.cjs"),i=require("./Typography-d97DdbNd.cjs"),T=require("./toastStore-B4Tk2Xk7.cjs"),n=require("./TextField.module-DsNCo8vN.cjs"),W=require("./ConfigProvider-B_Bb6pk1.cjs"),z="_band_1d0th_5",G="_sm_1d0th_15",H="_md_1d0th_18",J="_lg_1d0th_21",K="_num_1d0th_42",M="_step_1d0th_47",O="_bounds_1d0th_94",Q="_tip_1d0th_101",U="_tipTitle_1d0th_107",o={band:z,sm:G,md:H,lg:J,num:K,step:M,static:"_static_1d0th_61",bounds:O,tip:Q,tipTitle:U},$=(x,l)=>`${x} ${l.ratio.toFixed(2)}${l.small?` ${l.small}`:""}`,V=v.forwardRef(function({baseColor:l,name:a="color",options:f,size:N="md",copyable:_=!0,onCopy:c,showBounds:S=!0,label:d,helperText:p,error:b=!1,required:q=!1,className:k,...C},E){const t=W.useT(),y=v.useId(),j=`${y}-label`,g=`${y}-helper`,{copy:F}=L.useClipboard(),R=h.generatePalette(l,f),P=async e=>{const r=`${a}-${e.step}`;await F(e.hex)?(T.toast.success(t("colorRamp.copied",{step:r,hex:e.hex})),c==null||c(e)):T.toast.error(t("colorRamp.copyFailed",{step:r}))},A=e=>s.jsxs("span",{className:o.tip,children:[s.jsxs("span",{className:o.tipTitle,children:[a,"-",e.step,e.step===h.BASE_STEP?` · ${t("colorRamp.base")}`:""]}),s.jsx("span",{children:e.hex}),s.jsxs("span",{children:[$(t("colorRamp.black"),e.onDark)," ·"," ",$(t("colorRamp.white"),e.onWhite)]}),_&&s.jsx("span",{children:t("colorRamp.clickToCopy")})]});return s.jsxs("div",{ref:E,className:m.clsx(n.fieldStyles.field,b&&n.fieldStyles.error,k),...C,children:[d!=null&&s.jsxs("label",{id:j,className:n.fieldStyles.label,children:[s.jsx(i.Typography,{as:"span",variant:"bodySmall",color:"muted",children:d}),q&&s.jsx("span",{className:n.fieldStyles.required,"aria-hidden":"true",children:"*"})]}),R?s.jsxs(s.Fragment,{children:[s.jsx("div",{role:"group","aria-label":t("colorRamp.groupLabel",{name:a}),"aria-labelledby":d!=null?j:void 0,"aria-describedby":p!=null?g:void 0,className:m.clsx(o.band,o[N]),children:R.map(e=>{const r=e.step===h.BASE_STEP,u={backgroundColor:e.hex,color:e.textColor},B=`${a}-${e.step}`,I=_?s.jsx("button",{type:"button",className:o.step,style:u,"data-base":r||void 0,"aria-label":t("colorRamp.copyAria",{name:a,step:e.step,hex:e.hex}),onClick:()=>void P(e),children:s.jsx("span",{className:o.num,"aria-hidden":"true",children:e.step})}):s.jsx("span",{className:m.clsx(o.step,o.static),style:u,"data-base":r||void 0,role:"img","aria-label":`${B} ${e.hex}`,children:s.jsx("span",{className:o.num,"aria-hidden":"true",children:e.step})});return s.jsx(D.Tooltip,{content:A(e),children:I},e.step)})}),S&&s.jsxs("div",{className:o.bounds,children:[s.jsx(i.Typography,{as:"span",variant:"caption",color:"muted",children:t("colorRamp.lightest")}),s.jsx(i.Typography,{as:"span",variant:"caption",color:"muted",children:t("colorRamp.darkest")})]})]}):s.jsx(i.Typography,{as:"span",variant:"caption",color:"muted",children:t("colorRamp.invalid")}),p!=null&&s.jsx(i.Typography,{as:"span",id:g,variant:"bodySmall",color:b?"error":"muted",className:n.fieldStyles.helper,children:p})]})});exports.ColorRamp=V;
2
- //# sourceMappingURL=ColorRamp-B_-ZPqMa.cjs.map
1
+ "use strict";const s=require("react/jsx-runtime"),v=require("react"),m=require("clsx"),L=require("./useClipboard-BjTIX-4g.cjs"),h=require("./palette-BycMHbED.cjs"),D=require("./Tooltip-DmMq5R1x.cjs"),i=require("./Typography-d97DdbNd.cjs"),T=require("./toastStore-B4Tk2Xk7.cjs"),n=require("./TextField.module-DsNCo8vN.cjs"),W=require("./ConfigProvider-B_Bb6pk1.cjs"),z="_band_1d0th_5",G="_sm_1d0th_15",H="_md_1d0th_18",J="_lg_1d0th_21",K="_num_1d0th_42",M="_step_1d0th_47",O="_bounds_1d0th_94",Q="_tip_1d0th_101",U="_tipTitle_1d0th_107",o={band:z,sm:G,md:H,lg:J,num:K,step:M,static:"_static_1d0th_61",bounds:O,tip:Q,tipTitle:U},$=(x,l)=>`${x} ${l.ratio.toFixed(2)}${l.small?` ${l.small}`:""}`,V=v.forwardRef(function({baseColor:l,name:a="color",options:f,size:N="md",copyable:_=!0,onCopy:c,showBounds:S=!0,label:d,helperText:p,error:b=!1,required:q=!1,className:k,...C},E){const t=W.useT(),y=v.useId(),j=`${y}-label`,g=`${y}-helper`,{copy:F}=L.useClipboard(),R=h.generatePalette(l,f),P=async e=>{const r=`${a}-${e.step}`;await F(e.hex)?(T.toast.success(t("colorRamp.copied",{step:r,hex:e.hex})),c==null||c(e)):T.toast.error(t("colorRamp.copyFailed",{step:r}))},A=e=>s.jsxs("span",{className:o.tip,children:[s.jsxs("span",{className:o.tipTitle,children:[a,"-",e.step,e.step===h.BASE_STEP?` · ${t("colorRamp.base")}`:""]}),s.jsx("span",{children:e.hex}),s.jsxs("span",{children:[$(t("colorRamp.black"),e.onDark)," ·"," ",$(t("colorRamp.white"),e.onWhite)]}),_&&s.jsx("span",{children:t("colorRamp.clickToCopy")})]});return s.jsxs("div",{ref:E,className:m.clsx(n.fieldStyles.field,b&&n.fieldStyles.error,k),...C,children:[d!=null&&s.jsxs("label",{id:j,className:n.fieldStyles.label,children:[s.jsx(i.Typography,{as:"span",variant:"bodySmall",color:"muted",children:d}),q&&s.jsx("span",{className:n.fieldStyles.required,"aria-hidden":"true",children:"*"})]}),R?s.jsxs(s.Fragment,{children:[s.jsx("div",{role:"group","aria-label":t("colorRamp.groupLabel",{name:a}),"aria-labelledby":d!=null?j:void 0,"aria-describedby":p!=null?g:void 0,className:m.clsx(o.band,o[N]),children:R.map(e=>{const r=e.step===h.BASE_STEP,u={backgroundColor:e.hex,color:e.textColor},B=`${a}-${e.step}`,I=_?s.jsx("button",{type:"button",className:o.step,style:u,"data-base":r||void 0,"aria-label":t("colorRamp.copyAria",{name:a,step:e.step,hex:e.hex}),onClick:()=>void P(e),children:s.jsx("span",{className:o.num,"aria-hidden":"true",children:e.step})}):s.jsx("span",{className:m.clsx(o.step,o.static),style:u,"data-base":r||void 0,role:"img","aria-label":`${B} ${e.hex}`,children:s.jsx("span",{className:o.num,"aria-hidden":"true",children:e.step})});return s.jsx(D.Tooltip,{content:A(e),children:I},e.step)})}),S&&s.jsxs("div",{className:o.bounds,children:[s.jsx(i.Typography,{as:"span",variant:"caption",color:"muted",children:t("colorRamp.lightest")}),s.jsx(i.Typography,{as:"span",variant:"caption",color:"muted",children:t("colorRamp.darkest")})]})]}):s.jsx(i.Typography,{as:"span",variant:"caption",color:"muted",children:t("colorRamp.invalid")}),p!=null&&s.jsx(i.Typography,{as:"span",id:g,variant:"bodySmall",color:b?"error":"muted",className:n.fieldStyles.helper,children:p})]})});exports.ColorRamp=V;
2
+ //# sourceMappingURL=ColorRamp-D9_5JULy.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ColorRamp-B_-ZPqMa.cjs","sources":["../src/components/ColorRamp/ColorRamp.tsx"],"sourcesContent":["import { forwardRef, useId, type CSSProperties, type HTMLAttributes, type ReactNode } from 'react'\nimport { clsx } from 'clsx'\nimport { useT } from '../../theme'\nimport { useClipboard } from '../../hooks/useClipboard'\nimport {\n BASE_STEP,\n generatePalette,\n type GeneratePaletteOptions,\n type Legibility,\n type PaletteSwatch,\n} from '../../helpers/palette'\nimport { Tooltip } from '../Tooltip'\nimport { Typography } from '../Typography'\nimport { toast } from '../Toast'\n// shared field chrome (wrapper / label / helper / required / error)\nimport fieldStyles from '../TextField/TextField.module.css'\nimport styles from './ColorRamp.module.css'\n\nexport type ColorRampSize = 'sm' | 'md' | 'lg'\n\n// `onCopy` here is the swatch callback, so the DOM clipboard `onCopy` handler is omitted alongside `color`\nexport interface ColorRampProps extends Omit<HTMLAttributes<HTMLDivElement>, 'color' | 'onCopy'> {\n /** The base (500) color — any value `generatePalette` accepts. Invalid → the empty-state caption. */\n baseColor: string\n /** Token prefix for step names (`primary` → \"primary-500\") — used in tooltips, aria-labels and the copy toast. Default `'color'`. */\n name?: string\n /** Passed to `generatePalette` (override any subset of the tint / shade fractions). */\n options?: GeneratePaletteOptions\n /** Band height. `sm` 32 · `md` 40 (default) · `lg` 48 px. */\n size?: ColorRampSize\n /** Click a step to copy its hex (default `true`). `false` renders the steps as non-interactive cells. */\n copyable?: boolean\n /** After a successful copy — in addition to the built-in toast. */\n onCopy?: (swatch: PaletteSwatch) => void\n /** Show the \"Lightest … Darkest\" caption row under the band (default `true`). */\n showBounds?: boolean\n /** Label rendered above the band (field-family chrome, like `Slider` / `SwatchPicker`). */\n label?: ReactNode\n /** Helper / validation text under the band. Adopts the error color while `error`. */\n helperText?: ReactNode\n /** Marks the field invalid — reddens the label/asterisk and shows `helperText` in the error color. */\n error?: boolean\n /** Adds a red asterisk after the label. */\n required?: boolean\n}\n\n/** `black 4.52 AA` — ratio to 2 decimals, the body-text rating appended when it exists. */\nconst contrastLine = (who: string, l: Legibility) =>\n `${who} ${l.ratio.toFixed(2)}${l.small ? ` ${l.small}` : ''}`\n\n/**\n * The generated tonal scale for one color as **a single horizontal band of ten equal steps** sharing\n * one rounded, clipped frame (reads as a continuous ramp, not ten chips). Display + copy only — it never\n * edits the color; pair it with `ColorPicker` (or use `PaletteField`, which composes both). Each step\n * is a `Tooltip`-wrapped button whose fill / ink are the swatch's own colors (inline — the color IS the\n * data), showing its number on the base step and on hover / focus; clicking copies the hex with a toast.\n * `role=\"group\"` labelled \"{name} color scale\"; an invalid `baseColor` renders only a muted caption.\n */\nexport const ColorRamp = forwardRef<HTMLDivElement, ColorRampProps>(function ColorRamp(\n {\n baseColor,\n name = 'color',\n options,\n size = 'md',\n copyable = true,\n onCopy,\n showBounds = true,\n label,\n helperText,\n error = false,\n required = false,\n className,\n ...props\n },\n ref,\n) {\n const t = useT()\n const id = useId()\n const labelId = `${id}-label`\n const helperId = `${id}-helper`\n const { copy } = useClipboard()\n const palette = generatePalette(baseColor, options)\n\n const handleCopy = async (swatch: PaletteSwatch) => {\n const step = `${name}-${swatch.step}`\n const ok = await copy(swatch.hex)\n if (ok) {\n toast.success(t('colorRamp.copied', { step, hex: swatch.hex }))\n onCopy?.(swatch)\n } else {\n toast.error(t('colorRamp.copyFailed', { step }))\n }\n }\n\n const tip = (swatch: PaletteSwatch) => (\n <span className={styles.tip}>\n <span className={styles.tipTitle}>\n {name}-{swatch.step}\n {swatch.step === BASE_STEP ? ` · ${t('colorRamp.base')}` : ''}\n </span>\n <span>{swatch.hex}</span>\n <span>\n {contrastLine(t('colorRamp.black'), swatch.onDark)} ·{' '}\n {contrastLine(t('colorRamp.white'), swatch.onWhite)}\n </span>\n {copyable && <span>{t('colorRamp.clickToCopy')}</span>}\n </span>\n )\n\n return (\n <div\n ref={ref}\n className={clsx(fieldStyles.field, error && fieldStyles.error, className)}\n {...props}\n >\n {label != null && (\n <label id={labelId} className={fieldStyles.label}>\n <Typography as=\"span\" variant=\"bodySmall\" color=\"muted\">\n {label}\n </Typography>\n {required && (\n <span className={fieldStyles.required} aria-hidden=\"true\">\n *\n </span>\n )}\n </label>\n )}\n\n {palette ? (\n <>\n <div\n role=\"group\"\n aria-label={t('colorRamp.groupLabel', { name })}\n aria-labelledby={label != null ? labelId : undefined}\n aria-describedby={helperText != null ? helperId : undefined}\n className={clsx(styles.band, styles[size])}\n >\n {palette.map((swatch) => {\n const isBase = swatch.step === BASE_STEP\n // the swatch's own colors are the data — the one legitimate inline-color exception\n const ink = { backgroundColor: swatch.hex, color: swatch.textColor } as CSSProperties\n const stepName = `${name}-${swatch.step}`\n const cell = copyable ? (\n <button\n type=\"button\"\n className={styles.step}\n style={ink}\n data-base={isBase || undefined}\n aria-label={t('colorRamp.copyAria', { name, step: swatch.step, hex: swatch.hex })}\n onClick={() => void handleCopy(swatch)}\n >\n <span className={styles.num} aria-hidden=\"true\">\n {swatch.step}\n </span>\n </button>\n ) : (\n <span\n className={clsx(styles.step, styles.static)}\n style={ink}\n data-base={isBase || undefined}\n role=\"img\"\n aria-label={`${stepName} ${swatch.hex}`}\n >\n <span className={styles.num} aria-hidden=\"true\">\n {swatch.step}\n </span>\n </span>\n )\n // the Tooltip's wrapper span is the band's real flex child (sized by `.band > *`)\n return (\n <Tooltip key={swatch.step} content={tip(swatch)}>\n {cell}\n </Tooltip>\n )\n })}\n </div>\n {showBounds && (\n <div className={styles.bounds}>\n <Typography as=\"span\" variant=\"caption\" color=\"muted\">\n {t('colorRamp.lightest')}\n </Typography>\n <Typography as=\"span\" variant=\"caption\" color=\"muted\">\n {t('colorRamp.darkest')}\n </Typography>\n </div>\n )}\n </>\n ) : (\n <Typography as=\"span\" variant=\"caption\" color=\"muted\">\n {t('colorRamp.invalid')}\n </Typography>\n )}\n\n {helperText != null && (\n <Typography\n as=\"span\"\n id={helperId}\n variant=\"bodySmall\"\n color={error ? 'error' : 'muted'}\n className={fieldStyles.helper}\n >\n {helperText}\n </Typography>\n )}\n </div>\n )\n})\n"],"names":["contrastLine","who","ColorRamp","forwardRef","baseColor","name","options","size","copyable","onCopy","showBounds","label","helperText","error","required","className","props","ref","useT","id","useId","labelId","helperId","copy","useClipboard","palette","generatePalette","handleCopy","swatch","step","toast","tip","styles","jsxs","BASE_STEP","jsx","clsx","fieldStyles","Typography","Fragment","isBase","ink","stepName","cell","Tooltip"],"mappings":"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"}
1
+ {"version":3,"file":"ColorRamp-D9_5JULy.cjs","sources":["../src/components/ColorRamp/ColorRamp.tsx"],"sourcesContent":["import { forwardRef, useId, type CSSProperties, type HTMLAttributes, type ReactNode } from 'react'\nimport { clsx } from 'clsx'\nimport { useT } from '../../theme'\nimport { useClipboard } from '../../hooks/useClipboard'\nimport {\n BASE_STEP,\n generatePalette,\n type GeneratePaletteOptions,\n type Legibility,\n type PaletteSwatch,\n} from '../../helpers/palette'\nimport { Tooltip } from '../Tooltip'\nimport { Typography } from '../Typography'\nimport { toast } from '../Toast'\n// shared field chrome (wrapper / label / helper / required / error)\nimport fieldStyles from '../TextField/TextField.module.css'\nimport styles from './ColorRamp.module.css'\n\nexport type ColorRampSize = 'sm' | 'md' | 'lg'\n\n// `onCopy` here is the swatch callback, so the DOM clipboard `onCopy` handler is omitted alongside `color`\nexport interface ColorRampProps extends Omit<HTMLAttributes<HTMLDivElement>, 'color' | 'onCopy'> {\n /** The base (500) color — any value `generatePalette` accepts. Invalid → the empty-state caption. */\n baseColor: string\n /** Token prefix for step names (`primary` → \"primary-500\") — used in tooltips, aria-labels and the copy toast. Default `'color'`. */\n name?: string\n /** Passed to `generatePalette` (override any subset of the tint / shade fractions). */\n options?: GeneratePaletteOptions\n /** Band height. `sm` 32 · `md` 40 (default) · `lg` 48 px. */\n size?: ColorRampSize\n /** Click a step to copy its hex (default `true`). `false` renders the steps as non-interactive cells. */\n copyable?: boolean\n /** After a successful copy — in addition to the built-in toast. */\n onCopy?: (swatch: PaletteSwatch) => void\n /** Show the \"Lightest … Darkest\" caption row under the band (default `true`). */\n showBounds?: boolean\n /** Label rendered above the band (field-family chrome, like `Slider` / `SwatchPicker`). */\n label?: ReactNode\n /** Helper / validation text under the band. Adopts the error color while `error`. */\n helperText?: ReactNode\n /** Marks the field invalid — reddens the label/asterisk and shows `helperText` in the error color. */\n error?: boolean\n /** Adds a red asterisk after the label. */\n required?: boolean\n}\n\n/** `black 4.52 AA` — ratio to 2 decimals, the body-text rating appended when it exists. */\nconst contrastLine = (who: string, l: Legibility) =>\n `${who} ${l.ratio.toFixed(2)}${l.small ? ` ${l.small}` : ''}`\n\n/**\n * The generated tonal scale for one color as **a single horizontal band of ten equal steps** sharing\n * one rounded, clipped frame (reads as a continuous ramp, not ten chips). Display + copy only — it never\n * edits the color; pair it with `ColorPicker` (or use `PaletteField`, which composes both). Each step\n * is a `Tooltip`-wrapped button whose fill / ink are the swatch's own colors (inline — the color IS the\n * data), showing its number on the base step and on hover / focus; clicking copies the hex with a toast.\n * `role=\"group\"` labelled \"{name} color scale\"; an invalid `baseColor` renders only a muted caption.\n */\nexport const ColorRamp = forwardRef<HTMLDivElement, ColorRampProps>(function ColorRamp(\n {\n baseColor,\n name = 'color',\n options,\n size = 'md',\n copyable = true,\n onCopy,\n showBounds = true,\n label,\n helperText,\n error = false,\n required = false,\n className,\n ...props\n },\n ref,\n) {\n const t = useT()\n const id = useId()\n const labelId = `${id}-label`\n const helperId = `${id}-helper`\n const { copy } = useClipboard()\n const palette = generatePalette(baseColor, options)\n\n const handleCopy = async (swatch: PaletteSwatch) => {\n const step = `${name}-${swatch.step}`\n const ok = await copy(swatch.hex)\n if (ok) {\n toast.success(t('colorRamp.copied', { step, hex: swatch.hex }))\n onCopy?.(swatch)\n } else {\n toast.error(t('colorRamp.copyFailed', { step }))\n }\n }\n\n const tip = (swatch: PaletteSwatch) => (\n <span className={styles.tip}>\n <span className={styles.tipTitle}>\n {name}-{swatch.step}\n {swatch.step === BASE_STEP ? ` · ${t('colorRamp.base')}` : ''}\n </span>\n <span>{swatch.hex}</span>\n <span>\n {contrastLine(t('colorRamp.black'), swatch.onDark)} ·{' '}\n {contrastLine(t('colorRamp.white'), swatch.onWhite)}\n </span>\n {copyable && <span>{t('colorRamp.clickToCopy')}</span>}\n </span>\n )\n\n return (\n <div\n ref={ref}\n className={clsx(fieldStyles.field, error && fieldStyles.error, className)}\n {...props}\n >\n {label != null && (\n <label id={labelId} className={fieldStyles.label}>\n <Typography as=\"span\" variant=\"bodySmall\" color=\"muted\">\n {label}\n </Typography>\n {required && (\n <span className={fieldStyles.required} aria-hidden=\"true\">\n *\n </span>\n )}\n </label>\n )}\n\n {palette ? (\n <>\n <div\n role=\"group\"\n aria-label={t('colorRamp.groupLabel', { name })}\n aria-labelledby={label != null ? labelId : undefined}\n aria-describedby={helperText != null ? helperId : undefined}\n className={clsx(styles.band, styles[size])}\n >\n {palette.map((swatch) => {\n const isBase = swatch.step === BASE_STEP\n // the swatch's own colors are the data — the one legitimate inline-color exception\n const ink = { backgroundColor: swatch.hex, color: swatch.textColor } as CSSProperties\n const stepName = `${name}-${swatch.step}`\n const cell = copyable ? (\n <button\n type=\"button\"\n className={styles.step}\n style={ink}\n data-base={isBase || undefined}\n aria-label={t('colorRamp.copyAria', { name, step: swatch.step, hex: swatch.hex })}\n onClick={() => void handleCopy(swatch)}\n >\n <span className={styles.num} aria-hidden=\"true\">\n {swatch.step}\n </span>\n </button>\n ) : (\n <span\n className={clsx(styles.step, styles.static)}\n style={ink}\n data-base={isBase || undefined}\n role=\"img\"\n aria-label={`${stepName} ${swatch.hex}`}\n >\n <span className={styles.num} aria-hidden=\"true\">\n {swatch.step}\n </span>\n </span>\n )\n // the Tooltip's wrapper span is the band's real flex child (sized by `.band > *`)\n return (\n <Tooltip key={swatch.step} content={tip(swatch)}>\n {cell}\n </Tooltip>\n )\n })}\n </div>\n {showBounds && (\n <div className={styles.bounds}>\n <Typography as=\"span\" variant=\"caption\" color=\"muted\">\n {t('colorRamp.lightest')}\n </Typography>\n <Typography as=\"span\" variant=\"caption\" color=\"muted\">\n {t('colorRamp.darkest')}\n </Typography>\n </div>\n )}\n </>\n ) : (\n <Typography as=\"span\" variant=\"caption\" color=\"muted\">\n {t('colorRamp.invalid')}\n </Typography>\n )}\n\n {helperText != null && (\n <Typography\n as=\"span\"\n id={helperId}\n variant=\"bodySmall\"\n color={error ? 'error' : 'muted'}\n className={fieldStyles.helper}\n >\n {helperText}\n </Typography>\n )}\n </div>\n )\n})\n"],"names":["contrastLine","who","ColorRamp","forwardRef","baseColor","name","options","size","copyable","onCopy","showBounds","label","helperText","error","required","className","props","ref","useT","id","useId","labelId","helperId","copy","useClipboard","palette","generatePalette","handleCopy","swatch","step","toast","tip","styles","jsxs","BASE_STEP","jsx","clsx","fieldStyles","Typography","Fragment","isBase","ink","stepName","cell","Tooltip"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { forwardRef as D, useId as W } from "react";
3
3
  import { clsx as _ } from "clsx";
4
4
  import { u as z } from "./useClipboard-CEw_Juiq.js";
5
5
  import { g as G, B as g } from "./palette-B031dM4E.js";
6
- import { T as H } from "./Tooltip-DsDv4NOq.js";
6
+ import { T as H } from "./Tooltip-BbT0uNzy.js";
7
7
  import { T as n } from "./Typography-ChSuxgkc.js";
8
8
  import { t as x } from "./toastStore-Cx_JOKwS.js";
9
9
  import { f as d } from "./TextField.module-6gYcR9TL.js";
@@ -123,4 +123,4 @@ const K = "_band_1d0th_5", M = "_sm_1d0th_15", O = "_md_1d0th_18", Q = "_lg_1d0t
123
123
  export {
124
124
  de as C
125
125
  };
126
- //# sourceMappingURL=ColorRamp-CCfImssD.js.map
126
+ //# sourceMappingURL=ColorRamp-Mp_ddhmb.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ColorRamp-CCfImssD.js","sources":["../src/components/ColorRamp/ColorRamp.tsx"],"sourcesContent":["import { forwardRef, useId, type CSSProperties, type HTMLAttributes, type ReactNode } from 'react'\nimport { clsx } from 'clsx'\nimport { useT } from '../../theme'\nimport { useClipboard } from '../../hooks/useClipboard'\nimport {\n BASE_STEP,\n generatePalette,\n type GeneratePaletteOptions,\n type Legibility,\n type PaletteSwatch,\n} from '../../helpers/palette'\nimport { Tooltip } from '../Tooltip'\nimport { Typography } from '../Typography'\nimport { toast } from '../Toast'\n// shared field chrome (wrapper / label / helper / required / error)\nimport fieldStyles from '../TextField/TextField.module.css'\nimport styles from './ColorRamp.module.css'\n\nexport type ColorRampSize = 'sm' | 'md' | 'lg'\n\n// `onCopy` here is the swatch callback, so the DOM clipboard `onCopy` handler is omitted alongside `color`\nexport interface ColorRampProps extends Omit<HTMLAttributes<HTMLDivElement>, 'color' | 'onCopy'> {\n /** The base (500) color — any value `generatePalette` accepts. Invalid → the empty-state caption. */\n baseColor: string\n /** Token prefix for step names (`primary` → \"primary-500\") — used in tooltips, aria-labels and the copy toast. Default `'color'`. */\n name?: string\n /** Passed to `generatePalette` (override any subset of the tint / shade fractions). */\n options?: GeneratePaletteOptions\n /** Band height. `sm` 32 · `md` 40 (default) · `lg` 48 px. */\n size?: ColorRampSize\n /** Click a step to copy its hex (default `true`). `false` renders the steps as non-interactive cells. */\n copyable?: boolean\n /** After a successful copy — in addition to the built-in toast. */\n onCopy?: (swatch: PaletteSwatch) => void\n /** Show the \"Lightest … Darkest\" caption row under the band (default `true`). */\n showBounds?: boolean\n /** Label rendered above the band (field-family chrome, like `Slider` / `SwatchPicker`). */\n label?: ReactNode\n /** Helper / validation text under the band. Adopts the error color while `error`. */\n helperText?: ReactNode\n /** Marks the field invalid — reddens the label/asterisk and shows `helperText` in the error color. */\n error?: boolean\n /** Adds a red asterisk after the label. */\n required?: boolean\n}\n\n/** `black 4.52 AA` — ratio to 2 decimals, the body-text rating appended when it exists. */\nconst contrastLine = (who: string, l: Legibility) =>\n `${who} ${l.ratio.toFixed(2)}${l.small ? ` ${l.small}` : ''}`\n\n/**\n * The generated tonal scale for one color as **a single horizontal band of ten equal steps** sharing\n * one rounded, clipped frame (reads as a continuous ramp, not ten chips). Display + copy only — it never\n * edits the color; pair it with `ColorPicker` (or use `PaletteField`, which composes both). Each step\n * is a `Tooltip`-wrapped button whose fill / ink are the swatch's own colors (inline — the color IS the\n * data), showing its number on the base step and on hover / focus; clicking copies the hex with a toast.\n * `role=\"group\"` labelled \"{name} color scale\"; an invalid `baseColor` renders only a muted caption.\n */\nexport const ColorRamp = forwardRef<HTMLDivElement, ColorRampProps>(function ColorRamp(\n {\n baseColor,\n name = 'color',\n options,\n size = 'md',\n copyable = true,\n onCopy,\n showBounds = true,\n label,\n helperText,\n error = false,\n required = false,\n className,\n ...props\n },\n ref,\n) {\n const t = useT()\n const id = useId()\n const labelId = `${id}-label`\n const helperId = `${id}-helper`\n const { copy } = useClipboard()\n const palette = generatePalette(baseColor, options)\n\n const handleCopy = async (swatch: PaletteSwatch) => {\n const step = `${name}-${swatch.step}`\n const ok = await copy(swatch.hex)\n if (ok) {\n toast.success(t('colorRamp.copied', { step, hex: swatch.hex }))\n onCopy?.(swatch)\n } else {\n toast.error(t('colorRamp.copyFailed', { step }))\n }\n }\n\n const tip = (swatch: PaletteSwatch) => (\n <span className={styles.tip}>\n <span className={styles.tipTitle}>\n {name}-{swatch.step}\n {swatch.step === BASE_STEP ? ` · ${t('colorRamp.base')}` : ''}\n </span>\n <span>{swatch.hex}</span>\n <span>\n {contrastLine(t('colorRamp.black'), swatch.onDark)} ·{' '}\n {contrastLine(t('colorRamp.white'), swatch.onWhite)}\n </span>\n {copyable && <span>{t('colorRamp.clickToCopy')}</span>}\n </span>\n )\n\n return (\n <div\n ref={ref}\n className={clsx(fieldStyles.field, error && fieldStyles.error, className)}\n {...props}\n >\n {label != null && (\n <label id={labelId} className={fieldStyles.label}>\n <Typography as=\"span\" variant=\"bodySmall\" color=\"muted\">\n {label}\n </Typography>\n {required && (\n <span className={fieldStyles.required} aria-hidden=\"true\">\n *\n </span>\n )}\n </label>\n )}\n\n {palette ? (\n <>\n <div\n role=\"group\"\n aria-label={t('colorRamp.groupLabel', { name })}\n aria-labelledby={label != null ? labelId : undefined}\n aria-describedby={helperText != null ? helperId : undefined}\n className={clsx(styles.band, styles[size])}\n >\n {palette.map((swatch) => {\n const isBase = swatch.step === BASE_STEP\n // the swatch's own colors are the data — the one legitimate inline-color exception\n const ink = { backgroundColor: swatch.hex, color: swatch.textColor } as CSSProperties\n const stepName = `${name}-${swatch.step}`\n const cell = copyable ? (\n <button\n type=\"button\"\n className={styles.step}\n style={ink}\n data-base={isBase || undefined}\n aria-label={t('colorRamp.copyAria', { name, step: swatch.step, hex: swatch.hex })}\n onClick={() => void handleCopy(swatch)}\n >\n <span className={styles.num} aria-hidden=\"true\">\n {swatch.step}\n </span>\n </button>\n ) : (\n <span\n className={clsx(styles.step, styles.static)}\n style={ink}\n data-base={isBase || undefined}\n role=\"img\"\n aria-label={`${stepName} ${swatch.hex}`}\n >\n <span className={styles.num} aria-hidden=\"true\">\n {swatch.step}\n </span>\n </span>\n )\n // the Tooltip's wrapper span is the band's real flex child (sized by `.band > *`)\n return (\n <Tooltip key={swatch.step} content={tip(swatch)}>\n {cell}\n </Tooltip>\n )\n })}\n </div>\n {showBounds && (\n <div className={styles.bounds}>\n <Typography as=\"span\" variant=\"caption\" color=\"muted\">\n {t('colorRamp.lightest')}\n </Typography>\n <Typography as=\"span\" variant=\"caption\" color=\"muted\">\n {t('colorRamp.darkest')}\n </Typography>\n </div>\n )}\n </>\n ) : (\n <Typography as=\"span\" variant=\"caption\" color=\"muted\">\n {t('colorRamp.invalid')}\n </Typography>\n )}\n\n {helperText != null && (\n <Typography\n as=\"span\"\n id={helperId}\n variant=\"bodySmall\"\n color={error ? 'error' : 'muted'}\n className={fieldStyles.helper}\n >\n {helperText}\n </Typography>\n )}\n </div>\n )\n})\n"],"names":["contrastLine","who","ColorRamp","forwardRef","baseColor","name","options","size","copyable","onCopy","showBounds","label","helperText","error","required","className","props","ref","t","useT","id","useId","labelId","helperId","copy","useClipboard","palette","generatePalette","handleCopy","swatch","step","toast","tip","styles","jsxs","BASE_STEP","jsx","clsx","fieldStyles","Typography","Fragment","isBase","ink","stepName","cell","Tooltip"],"mappings":"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"}
1
+ {"version":3,"file":"ColorRamp-Mp_ddhmb.js","sources":["../src/components/ColorRamp/ColorRamp.tsx"],"sourcesContent":["import { forwardRef, useId, type CSSProperties, type HTMLAttributes, type ReactNode } from 'react'\nimport { clsx } from 'clsx'\nimport { useT } from '../../theme'\nimport { useClipboard } from '../../hooks/useClipboard'\nimport {\n BASE_STEP,\n generatePalette,\n type GeneratePaletteOptions,\n type Legibility,\n type PaletteSwatch,\n} from '../../helpers/palette'\nimport { Tooltip } from '../Tooltip'\nimport { Typography } from '../Typography'\nimport { toast } from '../Toast'\n// shared field chrome (wrapper / label / helper / required / error)\nimport fieldStyles from '../TextField/TextField.module.css'\nimport styles from './ColorRamp.module.css'\n\nexport type ColorRampSize = 'sm' | 'md' | 'lg'\n\n// `onCopy` here is the swatch callback, so the DOM clipboard `onCopy` handler is omitted alongside `color`\nexport interface ColorRampProps extends Omit<HTMLAttributes<HTMLDivElement>, 'color' | 'onCopy'> {\n /** The base (500) color — any value `generatePalette` accepts. Invalid → the empty-state caption. */\n baseColor: string\n /** Token prefix for step names (`primary` → \"primary-500\") — used in tooltips, aria-labels and the copy toast. Default `'color'`. */\n name?: string\n /** Passed to `generatePalette` (override any subset of the tint / shade fractions). */\n options?: GeneratePaletteOptions\n /** Band height. `sm` 32 · `md` 40 (default) · `lg` 48 px. */\n size?: ColorRampSize\n /** Click a step to copy its hex (default `true`). `false` renders the steps as non-interactive cells. */\n copyable?: boolean\n /** After a successful copy — in addition to the built-in toast. */\n onCopy?: (swatch: PaletteSwatch) => void\n /** Show the \"Lightest … Darkest\" caption row under the band (default `true`). */\n showBounds?: boolean\n /** Label rendered above the band (field-family chrome, like `Slider` / `SwatchPicker`). */\n label?: ReactNode\n /** Helper / validation text under the band. Adopts the error color while `error`. */\n helperText?: ReactNode\n /** Marks the field invalid — reddens the label/asterisk and shows `helperText` in the error color. */\n error?: boolean\n /** Adds a red asterisk after the label. */\n required?: boolean\n}\n\n/** `black 4.52 AA` — ratio to 2 decimals, the body-text rating appended when it exists. */\nconst contrastLine = (who: string, l: Legibility) =>\n `${who} ${l.ratio.toFixed(2)}${l.small ? ` ${l.small}` : ''}`\n\n/**\n * The generated tonal scale for one color as **a single horizontal band of ten equal steps** sharing\n * one rounded, clipped frame (reads as a continuous ramp, not ten chips). Display + copy only — it never\n * edits the color; pair it with `ColorPicker` (or use `PaletteField`, which composes both). Each step\n * is a `Tooltip`-wrapped button whose fill / ink are the swatch's own colors (inline — the color IS the\n * data), showing its number on the base step and on hover / focus; clicking copies the hex with a toast.\n * `role=\"group\"` labelled \"{name} color scale\"; an invalid `baseColor` renders only a muted caption.\n */\nexport const ColorRamp = forwardRef<HTMLDivElement, ColorRampProps>(function ColorRamp(\n {\n baseColor,\n name = 'color',\n options,\n size = 'md',\n copyable = true,\n onCopy,\n showBounds = true,\n label,\n helperText,\n error = false,\n required = false,\n className,\n ...props\n },\n ref,\n) {\n const t = useT()\n const id = useId()\n const labelId = `${id}-label`\n const helperId = `${id}-helper`\n const { copy } = useClipboard()\n const palette = generatePalette(baseColor, options)\n\n const handleCopy = async (swatch: PaletteSwatch) => {\n const step = `${name}-${swatch.step}`\n const ok = await copy(swatch.hex)\n if (ok) {\n toast.success(t('colorRamp.copied', { step, hex: swatch.hex }))\n onCopy?.(swatch)\n } else {\n toast.error(t('colorRamp.copyFailed', { step }))\n }\n }\n\n const tip = (swatch: PaletteSwatch) => (\n <span className={styles.tip}>\n <span className={styles.tipTitle}>\n {name}-{swatch.step}\n {swatch.step === BASE_STEP ? ` · ${t('colorRamp.base')}` : ''}\n </span>\n <span>{swatch.hex}</span>\n <span>\n {contrastLine(t('colorRamp.black'), swatch.onDark)} ·{' '}\n {contrastLine(t('colorRamp.white'), swatch.onWhite)}\n </span>\n {copyable && <span>{t('colorRamp.clickToCopy')}</span>}\n </span>\n )\n\n return (\n <div\n ref={ref}\n className={clsx(fieldStyles.field, error && fieldStyles.error, className)}\n {...props}\n >\n {label != null && (\n <label id={labelId} className={fieldStyles.label}>\n <Typography as=\"span\" variant=\"bodySmall\" color=\"muted\">\n {label}\n </Typography>\n {required && (\n <span className={fieldStyles.required} aria-hidden=\"true\">\n *\n </span>\n )}\n </label>\n )}\n\n {palette ? (\n <>\n <div\n role=\"group\"\n aria-label={t('colorRamp.groupLabel', { name })}\n aria-labelledby={label != null ? labelId : undefined}\n aria-describedby={helperText != null ? helperId : undefined}\n className={clsx(styles.band, styles[size])}\n >\n {palette.map((swatch) => {\n const isBase = swatch.step === BASE_STEP\n // the swatch's own colors are the data — the one legitimate inline-color exception\n const ink = { backgroundColor: swatch.hex, color: swatch.textColor } as CSSProperties\n const stepName = `${name}-${swatch.step}`\n const cell = copyable ? (\n <button\n type=\"button\"\n className={styles.step}\n style={ink}\n data-base={isBase || undefined}\n aria-label={t('colorRamp.copyAria', { name, step: swatch.step, hex: swatch.hex })}\n onClick={() => void handleCopy(swatch)}\n >\n <span className={styles.num} aria-hidden=\"true\">\n {swatch.step}\n </span>\n </button>\n ) : (\n <span\n className={clsx(styles.step, styles.static)}\n style={ink}\n data-base={isBase || undefined}\n role=\"img\"\n aria-label={`${stepName} ${swatch.hex}`}\n >\n <span className={styles.num} aria-hidden=\"true\">\n {swatch.step}\n </span>\n </span>\n )\n // the Tooltip's wrapper span is the band's real flex child (sized by `.band > *`)\n return (\n <Tooltip key={swatch.step} content={tip(swatch)}>\n {cell}\n </Tooltip>\n )\n })}\n </div>\n {showBounds && (\n <div className={styles.bounds}>\n <Typography as=\"span\" variant=\"caption\" color=\"muted\">\n {t('colorRamp.lightest')}\n </Typography>\n <Typography as=\"span\" variant=\"caption\" color=\"muted\">\n {t('colorRamp.darkest')}\n </Typography>\n </div>\n )}\n </>\n ) : (\n <Typography as=\"span\" variant=\"caption\" color=\"muted\">\n {t('colorRamp.invalid')}\n </Typography>\n )}\n\n {helperText != null && (\n <Typography\n as=\"span\"\n id={helperId}\n variant=\"bodySmall\"\n color={error ? 'error' : 'muted'}\n className={fieldStyles.helper}\n >\n {helperText}\n </Typography>\n )}\n </div>\n )\n})\n"],"names":["contrastLine","who","ColorRamp","forwardRef","baseColor","name","options","size","copyable","onCopy","showBounds","label","helperText","error","required","className","props","ref","t","useT","id","useId","labelId","helperId","copy","useClipboard","palette","generatePalette","handleCopy","swatch","step","toast","tip","styles","jsxs","BASE_STEP","jsx","clsx","fieldStyles","Typography","Fragment","isBase","ink","stepName","cell","Tooltip"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";const o=require("react/jsx-runtime"),y=require("react"),T=require("./useClipboard-BjTIX-4g.cjs"),C=require("./IconButton-CQ25TtBc.cjs"),R=require("./Icon-B6lz-V0A.cjs"),g=require("./Tooltip-Bw-1Xk2Z.cjs"),v=require("./ConfigProvider-B_Bb6pk1.cjs"),w=y.forwardRef(function({value:r,icon:u="Copy",copiedIcon:d="CopySuccess",copyLabel:a,copiedLabel:p,timeout:l=1600,tooltip:b=!0,variant:q="text",size:f="sm",onCopy:t,onClick:e,...x},B){const c=v.useT(),{copy:h,copied:n}=T.useClipboard({timeout:l}),s=n?p??c("clipboard.copied"):a??c("clipboard.copy"),j=I=>{e==null||e(I),h(r).then(m=>{m&&(t==null||t(r))})},i=o.jsx(C.IconButton,{ref:B,variant:q,size:f,"aria-label":s,onClick:j,...x,children:o.jsx(R.Icon,{name:n?d:u})});return b?o.jsx(g.Tooltip,{content:s,children:i}):i});exports.CopyButton=w;
2
- //# sourceMappingURL=CopyButton-CUEEp_Na.cjs.map
1
+ "use strict";const o=require("react/jsx-runtime"),y=require("react"),T=require("./useClipboard-BjTIX-4g.cjs"),C=require("./IconButton-CQ25TtBc.cjs"),R=require("./Icon-B6lz-V0A.cjs"),g=require("./Tooltip-DmMq5R1x.cjs"),v=require("./ConfigProvider-B_Bb6pk1.cjs"),w=y.forwardRef(function({value:r,icon:u="Copy",copiedIcon:d="CopySuccess",copyLabel:a,copiedLabel:p,timeout:l=1600,tooltip:b=!0,variant:q="text",size:f="sm",onCopy:t,onClick:e,...x},B){const c=v.useT(),{copy:h,copied:n}=T.useClipboard({timeout:l}),s=n?p??c("clipboard.copied"):a??c("clipboard.copy"),j=I=>{e==null||e(I),h(r).then(m=>{m&&(t==null||t(r))})},i=o.jsx(C.IconButton,{ref:B,variant:q,size:f,"aria-label":s,onClick:j,...x,children:o.jsx(R.Icon,{name:n?d:u})});return b?o.jsx(g.Tooltip,{content:s,children:i}):i});exports.CopyButton=w;
2
+ //# sourceMappingURL=CopyButton-DZeYcG68.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CopyButton-DZeYcG68.cjs","sources":["../src/components/CopyButton/CopyButton.tsx"],"sourcesContent":["import { forwardRef, type MouseEvent } from 'react'\nimport { useT } from '../../theme'\nimport { useClipboard } from '../../hooks/useClipboard'\nimport { IconButton, type IconButtonProps } from '../IconButton'\nimport { Icon } from '../Icon'\nimport { Tooltip } from '../Tooltip'\nimport type { IconName } from '../../icons/names'\n\nexport interface CopyButtonProps extends Omit<\n IconButtonProps,\n 'children' | 'aria-label' | 'onCopy'\n> {\n /** The text copied to the clipboard on click. */\n value: string\n /** Idle icon. Defaults to `'Copy'`. */\n icon?: IconName\n /** Icon shown briefly after a successful copy. Defaults to `'CopySuccess'`. */\n copiedIcon?: IconName\n /** Tooltip + accessible label while idle. Defaults to the localized `clipboard.copy` (`\"Copy\"`). */\n copyLabel?: string\n /** Tooltip + accessible label right after copying. Defaults to the localized `clipboard.copied` (`\"Copied!\"`). */\n copiedLabel?: string\n /** How long (ms) the copied state shows before reverting. Defaults to `1600`. */\n timeout?: number\n /** Show the tooltip on hover / keyboard focus (and \"Copied!\" after a click). Defaults to `true`. */\n tooltip?: boolean\n /** Called with `value` after a successful copy. */\n onCopy?: (value: string) => void\n}\n\n/**\n * A ready-made **click-to-copy** button — copies `value` to the clipboard and flips its icon to a tick\n * (+ a tooltip that reads \"Copied!\") for a moment, then reverts. Wraps `IconButton` (defaults to the\n * quiet `text` variant) with a `Tooltip`; labels come from the built-in i18n (`clipboard.copy` /\n * `clipboard.copied`), so they localize with the panel. For a labeled button or a custom trigger, use\n * the `useClipboard` hook instead. Built on `navigator.clipboard` (no-ops where unavailable).\n *\n * @example\n * <CopyButton value={apiKey} />\n * <CopyButton value={url} copyLabel=\"Copy link\" size=\"sm\" />\n */\nexport const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(function CopyButton(\n {\n value,\n icon = 'Copy',\n copiedIcon = 'CopySuccess',\n copyLabel,\n copiedLabel,\n timeout = 1600,\n tooltip = true,\n variant = 'text',\n size = 'sm',\n onCopy,\n onClick,\n ...props\n },\n ref,\n) {\n const t = useT()\n const { copy, copied } = useClipboard({ timeout })\n const label = copied ? (copiedLabel ?? t('clipboard.copied')) : (copyLabel ?? t('clipboard.copy'))\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event)\n void copy(value).then((ok) => {\n if (ok) onCopy?.(value)\n })\n }\n\n const button = (\n <IconButton\n ref={ref}\n variant={variant}\n size={size}\n aria-label={label}\n onClick={handleClick}\n {...props}\n >\n <Icon name={copied ? copiedIcon : icon} />\n </IconButton>\n )\n\n return tooltip ? <Tooltip content={label}>{button}</Tooltip> : button\n})\n"],"names":["CopyButton","forwardRef","value","icon","copiedIcon","copyLabel","copiedLabel","timeout","tooltip","variant","size","onCopy","onClick","props","ref","t","useT","copy","copied","useClipboard","label","handleClick","event","ok","button","jsx","IconButton","Icon","Tooltip"],"mappings":"qQAyCaA,EAAaC,EAAAA,WAA+C,SACvE,CACE,MAAAC,EACA,KAAAC,EAAO,OACP,WAAAC,EAAa,cACb,UAAAC,EACA,YAAAC,EACA,QAAAC,EAAU,KACV,QAAAC,EAAU,GACV,QAAAC,EAAU,OACV,KAAAC,EAAO,KACP,OAAAC,EACA,QAAAC,EACA,GAAGC,CACL,EACAC,EACA,CACA,MAAMC,EAAIC,EAAAA,KAAA,EACJ,CAAE,KAAAC,EAAM,OAAAC,CAAA,EAAWC,EAAAA,aAAa,CAAE,QAAAZ,EAAS,EAC3Ca,EAAQF,EAAUZ,GAAeS,EAAE,kBAAkB,EAAMV,GAAaU,EAAE,gBAAgB,EAE1FM,EAAeC,GAAyC,CAC5DV,GAAA,MAAAA,EAAUU,GACLL,EAAKf,CAAK,EAAE,KAAMqB,GAAO,CACxBA,eAAarB,GACnB,CAAC,CACH,EAEMsB,EACJC,EAAAA,IAACC,EAAAA,WAAA,CACC,IAAAZ,EACA,QAAAL,EACA,KAAAC,EACA,aAAYU,EACZ,QAASC,EACR,GAAGR,EAEJ,SAAAY,EAAAA,IAACE,OAAA,CAAK,KAAMT,EAASd,EAAaD,CAAA,CAAM,CAAA,CAAA,EAI5C,OAAOK,EAAUiB,MAACG,EAAAA,QAAA,CAAQ,QAASR,EAAQ,WAAO,EAAaI,CACjE,CAAC"}
@@ -3,7 +3,7 @@ import { forwardRef as y } from "react";
3
3
  import { u as j } from "./useClipboard-CEw_Juiq.js";
4
4
  import { I as w } from "./IconButton-06pmftMo.js";
5
5
  import { b as C } from "./Icon-D5TxgfCc.js";
6
- import { T as R } from "./Tooltip-DsDv4NOq.js";
6
+ import { T as R } from "./Tooltip-BbT0uNzy.js";
7
7
  import { a as S } from "./ConfigProvider-DeMASTVo.js";
8
8
  const H = y(function({
9
9
  value: c,
@@ -40,4 +40,4 @@ const H = y(function({
40
40
  export {
41
41
  H as C
42
42
  };
43
- //# sourceMappingURL=CopyButton-BD5sN32G.js.map
43
+ //# sourceMappingURL=CopyButton-Yg5ZVTiX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CopyButton-Yg5ZVTiX.js","sources":["../src/components/CopyButton/CopyButton.tsx"],"sourcesContent":["import { forwardRef, type MouseEvent } from 'react'\nimport { useT } from '../../theme'\nimport { useClipboard } from '../../hooks/useClipboard'\nimport { IconButton, type IconButtonProps } from '../IconButton'\nimport { Icon } from '../Icon'\nimport { Tooltip } from '../Tooltip'\nimport type { IconName } from '../../icons/names'\n\nexport interface CopyButtonProps extends Omit<\n IconButtonProps,\n 'children' | 'aria-label' | 'onCopy'\n> {\n /** The text copied to the clipboard on click. */\n value: string\n /** Idle icon. Defaults to `'Copy'`. */\n icon?: IconName\n /** Icon shown briefly after a successful copy. Defaults to `'CopySuccess'`. */\n copiedIcon?: IconName\n /** Tooltip + accessible label while idle. Defaults to the localized `clipboard.copy` (`\"Copy\"`). */\n copyLabel?: string\n /** Tooltip + accessible label right after copying. Defaults to the localized `clipboard.copied` (`\"Copied!\"`). */\n copiedLabel?: string\n /** How long (ms) the copied state shows before reverting. Defaults to `1600`. */\n timeout?: number\n /** Show the tooltip on hover / keyboard focus (and \"Copied!\" after a click). Defaults to `true`. */\n tooltip?: boolean\n /** Called with `value` after a successful copy. */\n onCopy?: (value: string) => void\n}\n\n/**\n * A ready-made **click-to-copy** button — copies `value` to the clipboard and flips its icon to a tick\n * (+ a tooltip that reads \"Copied!\") for a moment, then reverts. Wraps `IconButton` (defaults to the\n * quiet `text` variant) with a `Tooltip`; labels come from the built-in i18n (`clipboard.copy` /\n * `clipboard.copied`), so they localize with the panel. For a labeled button or a custom trigger, use\n * the `useClipboard` hook instead. Built on `navigator.clipboard` (no-ops where unavailable).\n *\n * @example\n * <CopyButton value={apiKey} />\n * <CopyButton value={url} copyLabel=\"Copy link\" size=\"sm\" />\n */\nexport const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(function CopyButton(\n {\n value,\n icon = 'Copy',\n copiedIcon = 'CopySuccess',\n copyLabel,\n copiedLabel,\n timeout = 1600,\n tooltip = true,\n variant = 'text',\n size = 'sm',\n onCopy,\n onClick,\n ...props\n },\n ref,\n) {\n const t = useT()\n const { copy, copied } = useClipboard({ timeout })\n const label = copied ? (copiedLabel ?? t('clipboard.copied')) : (copyLabel ?? t('clipboard.copy'))\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event)\n void copy(value).then((ok) => {\n if (ok) onCopy?.(value)\n })\n }\n\n const button = (\n <IconButton\n ref={ref}\n variant={variant}\n size={size}\n aria-label={label}\n onClick={handleClick}\n {...props}\n >\n <Icon name={copied ? copiedIcon : icon} />\n </IconButton>\n )\n\n return tooltip ? <Tooltip content={label}>{button}</Tooltip> : button\n})\n"],"names":["CopyButton","forwardRef","value","icon","copiedIcon","copyLabel","copiedLabel","timeout","tooltip","variant","size","onCopy","onClick","props","ref","t","useT","copy","copied","useClipboard","label","button","jsx","IconButton","event","ok","Icon","Tooltip"],"mappings":";;;;;;;AAyCO,MAAMA,IAAaC,EAA+C,SACvE;AAAA,EACE,OAAAC;AAAA,EACA,MAAAC,IAAO;AAAA,EACP,YAAAC,IAAa;AAAA,EACb,WAAAC;AAAA,EACA,aAAAC;AAAA,EACA,SAAAC,IAAU;AAAA,EACV,SAAAC,IAAU;AAAA,EACV,SAAAC,IAAU;AAAA,EACV,MAAAC,IAAO;AAAA,EACP,QAAAC;AAAA,EACA,SAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAMC,IAAIC,EAAA,GACJ,EAAE,MAAAC,GAAM,QAAAC,EAAA,IAAWC,EAAa,EAAE,SAAAZ,GAAS,GAC3Ca,IAAQF,IAAUZ,KAAeS,EAAE,kBAAkB,IAAMV,KAAaU,EAAE,gBAAgB,GAS1FM,IACJ,gBAAAC;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,KAAAT;AAAA,MACA,SAAAL;AAAA,MACA,MAAAC;AAAA,MACA,cAAYU;AAAA,MACZ,SAbgB,CAACI,MAAyC;AAC5D,QAAAZ,KAAA,QAAAA,EAAUY,IACLP,EAAKf,CAAK,EAAE,KAAK,CAACuB,MAAO;AAC5B,UAAIA,qBAAavB;AAAA,QACnB,CAAC;AAAA,MACH;AAAA,MASK,GAAGW;AAAA,MAEJ,UAAA,gBAAAS,EAACI,GAAA,EAAK,MAAMR,IAASd,IAAaD,EAAA,CAAM;AAAA,IAAA;AAAA,EAAA;AAI5C,SAAOK,IAAU,gBAAAc,EAACK,GAAA,EAAQ,SAASP,GAAQ,aAAO,IAAaC;AACjE,CAAC;"}
@@ -4,7 +4,7 @@ import { clsx as S } from "clsx";
4
4
  import { r as Q } from "./breakpoints-BbDakdqF.js";
5
5
  import { u as V } from "./useMediaQuery-DrDUrkmJ.js";
6
6
  import { b as X, a as Y } from "./Icon-D5TxgfCc.js";
7
- import { C as Z } from "./CopyButton-BD5sN32G.js";
7
+ import { C as Z } from "./CopyButton-Yg5ZVTiX.js";
8
8
  import { I as W } from "./IconButton-06pmftMo.js";
9
9
  import { S as B } from "./Skeleton-_-uGax2j.js";
10
10
  import { T as z } from "./Typography-ChSuxgkc.js";
@@ -172,4 +172,4 @@ const ae = "_root_10k89_4", se = "_sm_10k89_14", le = "_md_10k89_22", te = "_lg_
172
172
  export {
173
173
  Je as D
174
174
  };
175
- //# sourceMappingURL=DescriptionList-BD65gv7C.js.map
175
+ //# sourceMappingURL=DescriptionList-CG08_qwT.js.map