@epicbagel/saas-kit 1.33.0 → 1.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +131 -0
  2. package/dist/chunk-2BNEE3U6.cjs +36 -0
  3. package/dist/{chunk-KOR2QR7I.cjs → chunk-2YW5VA2V.cjs} +5 -5
  4. package/dist/chunk-5PHOKJCT.cjs +42 -0
  5. package/dist/chunk-BB7PO2BW.js +67 -0
  6. package/dist/{chunk-PTMZTP3V.cjs → chunk-BGCNGYPL.cjs} +2 -2
  7. package/dist/chunk-BOMVE7QO.cjs +47 -0
  8. package/dist/{chunk-FPOJ2NGG.js → chunk-C3VZMTVB.js} +1 -1
  9. package/dist/chunk-EXHF3QUY.js +36 -0
  10. package/dist/chunk-FYNHRHVY.cjs +47 -0
  11. package/dist/{chunk-AWCXVYPL.js → chunk-HTPPNW74.js} +1 -1
  12. package/dist/{chunk-REGA2L5S.js → chunk-HTUYUMAH.js} +3 -3
  13. package/dist/chunk-I3GBXIFL.cjs +67 -0
  14. package/dist/{chunk-SL6ECEN6.cjs → chunk-JMNDFISN.cjs} +3 -3
  15. package/dist/{chunk-TS4MC3B3.js → chunk-NNEV5RHQ.js} +6 -6
  16. package/dist/chunk-O3G3TVPU.js +27 -0
  17. package/dist/{chunk-IZF4WCK6.js → chunk-ROUWBWEQ.js} +5 -5
  18. package/dist/chunk-TDCVZKMX.cjs +27 -0
  19. package/dist/{chunk-E2UVQXBO.cjs → chunk-TP74SBRH.cjs} +6 -6
  20. package/dist/{chunk-F3D5HA2F.cjs → chunk-VUMLLKWY.cjs} +8 -8
  21. package/dist/chunk-W5L6LQI3.js +42 -0
  22. package/dist/chunk-WMTCL463.js +47 -0
  23. package/dist/chunk-WTVL2FK7.js +47 -0
  24. package/dist/primitives/accent-theme.cjs +4 -63
  25. package/dist/primitives/accent-theme.js +3 -62
  26. package/dist/primitives/activity-feed.cjs +4 -4
  27. package/dist/primitives/activity-feed.js +5 -5
  28. package/dist/primitives/agent-audio-visualizer-bar.cjs +3 -3
  29. package/dist/primitives/agent-audio-visualizer-bar.js +2 -2
  30. package/dist/primitives/alert.d.cts +1 -1
  31. package/dist/primitives/alert.d.ts +1 -1
  32. package/dist/primitives/anchor.cjs +3 -30
  33. package/dist/primitives/anchor.js +3 -30
  34. package/dist/primitives/app-layout.cjs +5 -5
  35. package/dist/primitives/app-layout.js +2 -2
  36. package/dist/primitives/app-sidebar.cjs +3 -3
  37. package/dist/primitives/app-sidebar.js +2 -2
  38. package/dist/primitives/archive-confirmation-dialog.cjs +2 -2
  39. package/dist/primitives/archive-confirmation-dialog.js +3 -3
  40. package/dist/primitives/badge.d.cts +1 -1
  41. package/dist/primitives/badge.d.ts +1 -1
  42. package/dist/primitives/board-view-internals.cjs +3 -3
  43. package/dist/primitives/board-view-internals.js +2 -2
  44. package/dist/primitives/board-view.cjs +8 -8
  45. package/dist/primitives/board-view.js +7 -7
  46. package/dist/primitives/brand-mark.cjs +3 -21
  47. package/dist/primitives/brand-mark.js +3 -21
  48. package/dist/primitives/branded-page.cjs +201 -0
  49. package/dist/primitives/branded-page.d.cts +118 -0
  50. package/dist/primitives/branded-page.d.ts +118 -0
  51. package/dist/primitives/branded-page.js +201 -0
  52. package/dist/primitives/bubble.d.cts +1 -1
  53. package/dist/primitives/bubble.d.ts +1 -1
  54. package/dist/primitives/button.d.cts +2 -2
  55. package/dist/primitives/button.d.ts +2 -2
  56. package/dist/primitives/call-card.cjs +2 -2
  57. package/dist/primitives/call-card.js +3 -3
  58. package/dist/primitives/chat.cjs +22 -22
  59. package/dist/primitives/chat.js +16 -16
  60. package/dist/primitives/code-block.cjs +4 -4
  61. package/dist/primitives/code-block.js +3 -3
  62. package/dist/primitives/command.d.cts +1 -1
  63. package/dist/primitives/command.d.ts +1 -1
  64. package/dist/primitives/connection.cjs +5 -5
  65. package/dist/primitives/connection.js +7 -7
  66. package/dist/primitives/copy-button.cjs +3 -3
  67. package/dist/primitives/copy-button.js +2 -2
  68. package/dist/primitives/cover-banner.cjs +41 -0
  69. package/dist/primitives/cover-banner.d.cts +67 -0
  70. package/dist/primitives/cover-banner.d.ts +67 -0
  71. package/dist/primitives/cover-banner.js +41 -0
  72. package/dist/primitives/document-preview.cjs +5 -5
  73. package/dist/primitives/document-preview.js +3 -3
  74. package/dist/primitives/file-preview-internals.cjs +4 -4
  75. package/dist/primitives/file-preview-internals.js +3 -3
  76. package/dist/primitives/file-preview.cjs +16 -16
  77. package/dist/primitives/file-preview.js +6 -6
  78. package/dist/primitives/filter-bar.cjs +4 -4
  79. package/dist/primitives/filter-bar.js +3 -3
  80. package/dist/primitives/fixed-action-bar.cjs +4 -41
  81. package/dist/primitives/fixed-action-bar.js +4 -41
  82. package/dist/primitives/marketing-header.cjs +3 -36
  83. package/dist/primitives/marketing-header.js +3 -36
  84. package/dist/primitives/notes.cjs +7 -7
  85. package/dist/primitives/notes.js +7 -7
  86. package/dist/primitives/page-shell.cjs +3 -41
  87. package/dist/primitives/page-shell.js +3 -41
  88. package/dist/primitives/resource-list.cjs +8 -8
  89. package/dist/primitives/resource-list.js +7 -7
  90. package/dist/primitives/responsive-dialog.cjs +3 -3
  91. package/dist/primitives/responsive-dialog.js +2 -2
  92. package/dist/primitives/section.cjs +2 -2
  93. package/dist/primitives/section.js +3 -3
  94. package/dist/primitives/switch-field.cjs +3 -3
  95. package/dist/primitives/switch-field.js +3 -3
  96. package/dist/primitives/theme-selector.cjs +3 -3
  97. package/dist/primitives/theme-selector.js +4 -4
  98. package/dist/primitives/voice-dictation-button.cjs +5 -5
  99. package/dist/primitives/voice-dictation-button.js +4 -4
  100. package/dist/primitives/voice-session-controls.cjs +4 -4
  101. package/dist/primitives/voice-session-controls.js +3 -3
  102. package/dist/primitives/workflow-assistant.cjs +5 -5
  103. package/dist/primitives/workflow-assistant.js +3 -3
  104. package/package.json +21 -1
  105. package/src/styles/components/cover-banner.css +201 -0
  106. package/src/styles/components.css +1 -0
  107. package/src/styles/utilities.css +5 -3
  108. package/dist/{chunk-2FVFPGP5.js → chunk-5GFMSK27.js} +3 -3
  109. package/dist/{chunk-4TJBXXUE.js → chunk-5OF6D3GH.js} +3 -3
  110. package/dist/{chunk-ULZB4A2I.js → chunk-62UTAG2C.js} +3 -3
  111. package/dist/{chunk-SW4T3O7M.js → chunk-6LB37JWO.js} +6 -6
  112. package/dist/{chunk-AO56ET4F.cjs → chunk-BTHYGHOH.cjs} +2 -2
  113. package/dist/{chunk-7ZRFXJVN.js → chunk-DILYV4FI.js} +3 -3
  114. package/dist/{chunk-HHF5DEKM.cjs → chunk-FARCLWLU.cjs} +3 -3
  115. package/dist/{chunk-CRZ4INEJ.js → chunk-JTI4244T.js} +10 -10
  116. package/dist/{chunk-FB3VY2PL.cjs → chunk-NKWHDI4C.cjs} +2 -2
  117. package/dist/{chunk-GUVQAWUW.cjs → chunk-S2IMM37Q.cjs} +5 -5
  118. package/dist/{chunk-G3SF4RRA.cjs → chunk-UKYUWSF2.cjs} +3 -3
  119. package/dist/{chunk-D46FNEZ5.cjs → chunk-YYFVG5A6.cjs} +2 -2
package/README.md CHANGED
@@ -514,6 +514,137 @@ export function Appearance() {
514
514
  `paletteDescription`. Each palette's card is named by its `label` and
515
515
  described by its `description`.
516
516
 
517
+ ## Cover banner (1.35.0)
518
+
519
+ `primitives/cover-banner` is a stock cover: the banner the kit draws for
520
+ anything that has no cover image of its own, so a card without one is as tall
521
+ as its neighbours and doesn't look bare. It is the default banner a profile
522
+ shows before anyone uploads one, in whichever palette the app is in.
523
+
524
+ ```tsx
525
+ import { AspectRatio } from "@epicbagel/saas-kit/primitives/aspect-ratio";
526
+ import { CoverBanner } from "@epicbagel/saas-kit/primitives/cover-banner";
527
+
528
+ <AspectRatio ratio={4}>
529
+ {person.coverUrl ? (
530
+ <img src={person.coverUrl} alt="" className="size-full object-cover" />
531
+ ) : (
532
+ <CoverBanner seed={person.id} />
533
+ )}
534
+ </AspectRatio>
535
+ ```
536
+
537
+ - **Paired with the theme.** Every colour is mixed in CSS from the palette's
538
+ tokens: `primary` into `muted` for the tints, and into `foreground` for a
539
+ faint pattern. So a banner follows the palette, light and dark, and an
540
+ `AccentTheme` around it, with no image, no request and no canvas.
541
+ A black-and-white palette gets a black-and-white banner. Where the browser
542
+ can read a colour's channels, a neighbouring hue is mixed in beside the
543
+ primary's, turned away from yellow-green, where a tint goes muddy; elsewhere
544
+ a banner is one hue.
545
+ - **Six designs** (`COVER_BANNER_VARIANTS`): `wash`, one soft diagonal
546
+ gradient; `glow`, two soft lights; `grid`, a wash under a faint ruled grid;
547
+ `dots`, a wash under a faint dot matrix; `rings`, faint contour lines from
548
+ one corner; `arcs`, broad tonal bands round one corner. Each keeps its
549
+ deeper colour and its pattern on the far side, and its calmest part low on
550
+ the near side, where an avatar usually overlaps it.
551
+ - **A seed picks one.** `seed` is a string or a number, such as a person's id
552
+ or name, and picks the same design every time, on the server and in the
553
+ browser, with no randomness: one person keeps one banner, and a list of
554
+ people isn't a wall of the same one. It is read exactly as given, so pass
555
+ something that stays put, an id before a name. `coverBannerVariant(seed)`
556
+ is the same pick as a function. With no seed it is the first design.
557
+ - **Or name one.** `variant` names the design outright, and wins over `seed`.
558
+ - **It fills its frame.** Put it where the cover image would go, in a frame
559
+ you own, at any size: it is 100% of the frame's width and height. With no
560
+ frame, or one that gives no height, it holds 4:1. `className` and the other
561
+ `div` attributes go to the element, and a utility class wins over its rules.
562
+ - **Quiet.** The deepest tint is still four parts surface, so a face and a
563
+ name stay the loudest things on the card. Give an avatar that overlaps its
564
+ edge a ring in the card's colour (`ring-4 ring-card`), as you would over a
565
+ photograph.
566
+ - **Decoration only.** It is hidden from screen readers and takes no
567
+ children. It is not a placeholder while a real cover loads (that is
568
+ `Skeleton`), not a stand-in for a photo or a logo, and not a surface to set
569
+ text on: its colours promise no contrast to anything laid over it.
570
+ - **Why a banner, when `PersonAvatar` draws no generated tile.** A coloured
571
+ tile masks a missing photo by pretending to be one, so a person with no
572
+ photo gets plain initials. A cover is decoration and carries no identity, so
573
+ a stock one misleads nobody: it stands in for no particular picture, and the
574
+ seed only keeps a list from repeating itself.
575
+ - **Styles.** Its rules are in `styles/utilities.css`
576
+ (`components/cover-banner.css`), on `data-slot="cover-banner"` and each
577
+ design's `data-variant`, so an app that already imports the kit's styles
578
+ needs nothing more. In forced colours it is the plain canvas, with no
579
+ pattern. On paper browsers leave backgrounds out, so it prints blank.
580
+
581
+ ## Branded page (1.34.0)
582
+
583
+ `primitives/branded-page` is the whole of a public page one company shows to
584
+ another company's people: a shared profile, a list sent for review, an
585
+ invitation, a client's portal. It puts the kit's page chrome together
586
+ (`MarketingHeader`, `BrandMark`, `AccentTheme`, `PageShell`,
587
+ `FixedActionBar` and `EmptyState`), so a host passes a brand and its content.
588
+
589
+ ```tsx
590
+ import { BrandedPage } from "@epicbagel/saas-kit/primitives/branded-page";
591
+
592
+ <BrandedPage
593
+ brand={{ name: "Example Agency", logoUrl: brand.logoUrl, href: brand.website }}
594
+ accent={{ light: "#0f766e", dark: "#5eead4" }}
595
+ headerTrailing={<ThemeSwitch />} // your own control
596
+ credit="Shared by Example Agency"
597
+ actionBar={{
598
+ title: "Three people ready",
599
+ description: "Book a call to meet them.",
600
+ actions: <Button size="lg">Book a call</Button>,
601
+ }}
602
+ loading={loading}
603
+ unavailable={gone ? { reason: "expired" } : false}
604
+ >
605
+ <Profile />
606
+ </BrandedPage>
607
+ ```
608
+
609
+ - **Brand.** The logo (`logoUrl`), else the `name` as text, and a link when
610
+ it has an `href`; nothing for a brand with neither. Or pass an element of
611
+ your own: your wordmark, or your router's link around one. The name is
612
+ text, not a heading, so the page's headings start in its content. A logo's
613
+ text alternative is `logoAlt`, else the brand's `name`, else "Logo".
614
+ - **Accent and palette.** `accent` is the brand's light and dark colours as
615
+ six-digit hex, which `AccentTheme` applies inside the page only. `palette`
616
+ pins the page to one palette (its `data-theme` value) and paints the
617
+ canvas in it, so the page looks the same to every visitor whatever palette
618
+ their own browser has stored. Left out, the page follows the document.
619
+ - **Header.** `headerTrailing` holds your own controls, such as a theme
620
+ switch or Sign in. The header is the page's banner; with neither a brand
621
+ nor controls there is none.
622
+ - **Content.** `children`, in the page's one `main`: `width` (`narrow` by
623
+ default, `wide` or `full`), `padding` (`base` by default, which lines up
624
+ with the header) and `surface` (`muted` by default, or `default`).
625
+ - **Credit.** `credit` is a closing line under the content. It prints;
626
+ `creditPrintVisibility="hidden"` keeps it off paper.
627
+ - **Action bar.** `actionBar` is a `FixedActionBar` as wide as the content:
628
+ `title`, `description`, `actions` and `actionsWidth`. The content keeps
629
+ clear of it.
630
+ - **States.** `loading` and `unavailable` stand in for the content, the
631
+ credit and the action bar, under the same header and both starting at the
632
+ same height: pass `true`, or the state's props. `loading` wins while both
633
+ are set. `unavailable` takes a `reason` (`not-found` by default, worded
634
+ for a link whose fate you can't tell; `expired`, `archived` or `revoked`),
635
+ each with its own icon and words, and your `icon`, `title`, `description`
636
+ and `action` in their place. Its title is a heading, and it is announced
637
+ when it appears (`role="alert"`). Loading is a polite `role="status"`
638
+ under a `label` ("Loading…").
639
+ - **Alone.** `BrandedPageUnavailable` and `BrandedPageLoading` are the same
640
+ two states as blocks, for a frame you draw yourself. With no brand to
641
+ show, `<BrandedPage unavailable />` is the whole page, without a header.
642
+ - **Paper.** Printed, the canvas is plain and the spacing compact, and the
643
+ header's controls and the action bar are left off.
644
+ - **Presentation only.** No router, fetch or storage: the host decides which
645
+ state shows, and every word can be replaced. The action bar's accessible
646
+ name ("Page action") is `FixedActionBar`'s own.
647
+
517
648
  ## License
518
649
 
519
650
  MIT. Some components and styles are adapted from shadcn/ui, and the file
@@ -0,0 +1,36 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
+
3
+
4
+ var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
5
+
6
+ // src/primitives/anchor.tsx
7
+ var _reactslot = require('@radix-ui/react-slot');
8
+ var _react = require('react');
9
+ var _jsxruntime = require('react/jsx-runtime');
10
+ var Anchor = _react.forwardRef.call(void 0, function Anchor2({ variant = "unstyled", size = "inherit", display = "inline", underline = false, asChild = false, border = "none", weight = "normal", className, ...props }, ref) {
11
+ const Component = asChild ? _reactslot.Slot : "a";
12
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
13
+ Component,
14
+ {
15
+ ref,
16
+ className: _chunkKWLSBTB2cjs.cn.call(void 0,
17
+ variant === "text" && "font-medium text-primary underline-offset-4 hover:underline",
18
+ variant === "muted" && "text-muted-foreground underline underline-offset-2 hover:text-foreground",
19
+ variant === "foreground" && "text-foreground underline-offset-2 hover:underline",
20
+ size === "xs" && "text-xs",
21
+ size === "sm" && "text-sm",
22
+ display === "inline-flex" && "inline-flex items-center gap-1",
23
+ display === "block" && "block",
24
+ border === "bottom" && "border-b last:border-b-0",
25
+ weight === "medium" && "font-medium",
26
+ underline && "underline",
27
+ className
28
+ ),
29
+ ...props
30
+ }
31
+ );
32
+ });
33
+
34
+
35
+
36
+ exports.Anchor = Anchor;
@@ -1,16 +1,16 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client";
2
2
 
3
3
 
4
- var _chunk7Q4EFCUIcjs = require('./chunk-7Q4EFCUI.cjs');
4
+ var _chunkJDEXZGIYcjs = require('./chunk-JDEXZGIY.cjs');
5
5
 
6
6
 
7
- var _chunkFB3VY2PLcjs = require('./chunk-FB3VY2PL.cjs');
7
+ var _chunkNKWHDI4Ccjs = require('./chunk-NKWHDI4C.cjs');
8
8
 
9
9
 
10
- var _chunkJDEXZGIYcjs = require('./chunk-JDEXZGIY.cjs');
10
+ var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
11
11
 
12
12
 
13
- var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
13
+ var _chunk7Q4EFCUIcjs = require('./chunk-7Q4EFCUI.cjs');
14
14
 
15
15
  // src/primitives/voice-dictation-button.tsx
16
16
 
@@ -269,7 +269,7 @@ var silence = () => SILENCE;
269
269
  function DictationWaveform({ source, state }) {
270
270
  const bands = _react.useSyncExternalStore.call(void 0, source.subscribe, source.getSnapshot, silence);
271
271
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
272
- _chunkFB3VY2PLcjs.AgentAudioVisualizerBar,
272
+ _chunkNKWHDI4Ccjs.AgentAudioVisualizerBar,
273
273
  {
274
274
  "aria-hidden": "true",
275
275
  "data-slot": "dictation-waveform",
@@ -0,0 +1,42 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
+
3
+
4
+ var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
5
+
6
+ // src/primitives/marketing-header.tsx
7
+ var _jsxruntime = require('react/jsx-runtime');
8
+ function MarketingHeader({
9
+ brand,
10
+ links = [],
11
+ trailing,
12
+ renderLink,
13
+ variant = "default",
14
+ className
15
+ }) {
16
+ const link = (l) => {
17
+ const cls = variant === "mono" ? "font-mono text-xs uppercase tracking-[0.18em] text-muted-foreground transition-colors hover:text-foreground" : "text-sm text-muted-foreground transition-colors hover:text-foreground";
18
+ if (renderLink)
19
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: renderLink({ href: l.href, children: l.label, className: cls }) }, l.href);
20
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "a", { href: l.href, className: cls, children: l.label }, l.href);
21
+ };
22
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
23
+ "header",
24
+ {
25
+ className: _chunkKWLSBTB2cjs.cn.call(void 0,
26
+ "sticky top-0 z-50 border-b border-border bg-background/80 backdrop-blur-sm supports-[backdrop-filter]:bg-background/70",
27
+ className
28
+ ),
29
+ children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "mx-auto flex w-full max-w-5xl items-center justify-between px-4 py-4 md:px-6", children: [
30
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex items-center", children: brand }),
31
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "nav", { className: "flex items-center gap-3 md:gap-5", children: [
32
+ links.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "hidden items-center gap-5 sm:flex", children: links.map(link) }),
33
+ trailing && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex items-center gap-2 md:gap-3", children: trailing })
34
+ ] })
35
+ ] })
36
+ }
37
+ );
38
+ }
39
+
40
+
41
+
42
+ exports.MarketingHeader = MarketingHeader;
@@ -0,0 +1,67 @@
1
+ "use client";
2
+
3
+ // src/primitives/accent-theme.tsx
4
+ import { useId } from "react";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ var PALETTE_ID = /^[A-Za-z0-9_-]+$/;
7
+ function hexToHslTriplet(hex) {
8
+ const match = /^#([0-9a-f]{6})$/i.exec(hex.trim());
9
+ if (!match) return null;
10
+ const value = Number.parseInt(match[1], 16);
11
+ const red = (value >> 16 & 255) / 255;
12
+ const green = (value >> 8 & 255) / 255;
13
+ const blue = (value & 255) / 255;
14
+ const maximum = Math.max(red, green, blue);
15
+ const minimum = Math.min(red, green, blue);
16
+ const lightness = (maximum + minimum) / 2;
17
+ let hue = 0;
18
+ let saturation = 0;
19
+ if (maximum !== minimum) {
20
+ const delta = maximum - minimum;
21
+ saturation = lightness > 0.5 ? delta / (2 - maximum - minimum) : delta / (maximum + minimum);
22
+ if (maximum === red) hue = ((green - blue) / delta + (green < blue ? 6 : 0)) / 6;
23
+ else if (maximum === green) hue = ((blue - red) / delta + 2) / 6;
24
+ else hue = ((red - green) / delta + 4) / 6;
25
+ }
26
+ const round = (number) => Math.round(number * 10) / 10;
27
+ return `${round(hue * 360)} ${round(saturation * 100)}% ${round(lightness * 100)}%`;
28
+ }
29
+ function foregroundFor(hex) {
30
+ const match = /^#([0-9a-f]{6})$/i.exec(hex.trim());
31
+ if (!match) return "0 0% 100%";
32
+ const value = Number.parseInt(match[1], 16);
33
+ const channel = (number) => {
34
+ const normalized = number / 255;
35
+ return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
36
+ };
37
+ const luminance = 0.2126 * channel(value >> 16 & 255) + 0.7152 * channel(value >> 8 & 255) + 0.0722 * channel(value & 255);
38
+ return luminance > 0.45 ? "0 0% 9%" : "0 0% 100%";
39
+ }
40
+ function AccentTheme({ colours, palette, scope: target = "region", children }) {
41
+ const scope = `accent-${useId().replace(/[^a-zA-Z0-9]/g, "")}`;
42
+ const light = colours?.light ? hexToHslTriplet(colours.light) : null;
43
+ const dark = colours?.dark ? hexToHslTriplet(colours.dark) : null;
44
+ if (!light && !dark) return /* @__PURE__ */ jsx(Fragment, { children });
45
+ const root = palette === void 0 ? null : PALETTE_ID.test(palette) ? `:root[data-theme="${palette}"]` : ":root:not(*)";
46
+ const lightSelector = target === "document" ? root ?? ":root" : root ? `${root} .${scope}` : `.${scope}`;
47
+ const darkSelector = `${root ?? ":root"}.dark${target === "document" ? "" : ` .${scope}`}`;
48
+ const rules = [];
49
+ if (light && colours?.light) {
50
+ rules.push(
51
+ `${lightSelector}{--primary:${light};--primary-foreground:${foregroundFor(colours.light)};--ring:${light};--color-primary:hsl(var(--primary));--color-primary-foreground:hsl(var(--primary-foreground));--color-ring:hsl(var(--ring));}`
52
+ );
53
+ }
54
+ if (dark && colours?.dark) {
55
+ rules.push(
56
+ `${darkSelector}{--primary:${dark};--primary-foreground:${foregroundFor(colours.dark)};--ring:${dark};--color-primary:hsl(var(--primary));--color-primary-foreground:hsl(var(--primary-foreground));--color-ring:hsl(var(--ring));}`
57
+ );
58
+ }
59
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
60
+ /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: rules.join("") } }),
61
+ target === "document" ? children : /* @__PURE__ */ jsx("div", { className: scope, children })
62
+ ] });
63
+ }
64
+
65
+ export {
66
+ AccentTheme
67
+ };
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
2
 
3
3
 
4
- var _chunkHHF5DEKMcjs = require('./chunk-HHF5DEKM.cjs');
4
+ var _chunkFARCLWLUcjs = require('./chunk-FARCLWLU.cjs');
5
5
 
6
6
 
7
7
  var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
@@ -38,7 +38,7 @@ function CodeBlock({
38
38
  }
39
39
  ),
40
40
  copy ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
41
- _chunkHHF5DEKMcjs.CopyButton,
41
+ _chunkFARCLWLUcjs.CopyButton,
42
42
  {
43
43
  text: copyText,
44
44
  label: copyLabel,
@@ -0,0 +1,47 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
+
3
+
4
+ var _chunk373W6VADcjs = require('./chunk-373W6VAD.cjs');
5
+
6
+
7
+ var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
8
+
9
+ // src/primitives/fixed-action-bar.tsx
10
+ var _jsxruntime = require('react/jsx-runtime');
11
+ var widthStyles = {
12
+ compact: "max-w-2xl",
13
+ narrow: "max-w-3xl",
14
+ wide: "max-w-5xl",
15
+ full: "max-w-none"
16
+ };
17
+ function FixedActionBar({
18
+ title,
19
+ description,
20
+ children,
21
+ width = "narrow",
22
+ actionsWidth = "auto",
23
+ className
24
+ }) {
25
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
26
+ "aside",
27
+ {
28
+ "aria-label": "Page action",
29
+ className: _chunkKWLSBTB2cjs.cn.call(void 0,
30
+ "fixed inset-x-0 bottom-0 z-40 border-t border-border bg-background/95 backdrop-blur-sm",
31
+ "pb-[env(safe-area-inset-bottom)] supports-backdrop-filter:bg-background/80",
32
+ className
33
+ ),
34
+ children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "mx-auto flex items-center gap-4 px-4 py-3 md:px-6", title ? "justify-between" : "justify-end", widthStyles[width]), children: [
35
+ title && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "min-w-0", children: [
36
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk373W6VADcjs.Text, { className: "truncate font-medium", children: title }),
37
+ description && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk373W6VADcjs.Text, { variant: "muted", className: "text-sm", children: description })
38
+ ] }),
39
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "shrink-0", actionsWidth === "full" && "w-full"), children })
40
+ ] })
41
+ }
42
+ );
43
+ }
44
+
45
+
46
+
47
+ exports.FixedActionBar = FixedActionBar;
@@ -6,7 +6,7 @@ import {
6
6
  ResponsiveDialogDescription,
7
7
  ResponsiveDialogHeader,
8
8
  ResponsiveDialogTitle
9
- } from "./chunk-CRZ4INEJ.js";
9
+ } from "./chunk-JTI4244T.js";
10
10
  import {
11
11
  Icon
12
12
  } from "./chunk-KN4RPX35.js";
@@ -0,0 +1,36 @@
1
+ "use client";
2
+ import {
3
+ cn
4
+ } from "./chunk-G6SRSWXB.js";
5
+
6
+ // src/primitives/anchor.tsx
7
+ import { Slot } from "@radix-ui/react-slot";
8
+ import { forwardRef } from "react";
9
+ import { jsx } from "react/jsx-runtime";
10
+ var Anchor = forwardRef(function Anchor2({ variant = "unstyled", size = "inherit", display = "inline", underline = false, asChild = false, border = "none", weight = "normal", className, ...props }, ref) {
11
+ const Component = asChild ? Slot : "a";
12
+ return /* @__PURE__ */ jsx(
13
+ Component,
14
+ {
15
+ ref,
16
+ className: cn(
17
+ variant === "text" && "font-medium text-primary underline-offset-4 hover:underline",
18
+ variant === "muted" && "text-muted-foreground underline underline-offset-2 hover:text-foreground",
19
+ variant === "foreground" && "text-foreground underline-offset-2 hover:underline",
20
+ size === "xs" && "text-xs",
21
+ size === "sm" && "text-sm",
22
+ display === "inline-flex" && "inline-flex items-center gap-1",
23
+ display === "block" && "block",
24
+ border === "bottom" && "border-b last:border-b-0",
25
+ weight === "medium" && "font-medium",
26
+ underline && "underline",
27
+ className
28
+ ),
29
+ ...props
30
+ }
31
+ );
32
+ });
33
+
34
+ export {
35
+ Anchor
36
+ };
@@ -0,0 +1,47 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
+
3
+
4
+ var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
5
+
6
+ // src/primitives/page-shell.tsx
7
+ var _react = require('react');
8
+ var _jsxruntime = require('react/jsx-runtime');
9
+ var widthStyles = {
10
+ narrow: "max-w-3xl",
11
+ wide: "max-w-5xl",
12
+ full: "max-w-none"
13
+ };
14
+ var paddingStyles = {
15
+ none: "",
16
+ base: "px-4 md:px-6",
17
+ relaxed: "px-6 md:px-10",
18
+ spacious: "px-6 md:px-10 xl:px-16"
19
+ };
20
+ var PageShell = _react.forwardRef.call(void 0,
21
+ function PageShell2({ width = "full", padding = "base", spacing = "default", bottomInset = "default", minHeight = "none", align = "start", printMode = "default", mobileTreatment = "page", as = "main", className, ...props }, ref) {
22
+ const Tag = as;
23
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
24
+ Tag,
25
+ {
26
+ ref,
27
+ className: _chunkKWLSBTB2cjs.cn.call(void 0,
28
+ "mx-auto w-full pt-6 md:pt-8 mb-28",
29
+ spacing === "default" ? "space-y-6" : "space-y-4",
30
+ widthStyles[width],
31
+ paddingStyles[padding],
32
+ bottomInset === "action" && "pb-32",
33
+ minHeight === "content" && "min-h-[70vh]",
34
+ align === "center" && "flex items-center",
35
+ printMode === "compact" && "print:my-0 print:pt-0",
36
+ mobileTreatment === "application" && "mb-0 flex min-h-[calc(100svh-4rem)] flex-1 flex-col space-y-0 px-0 pt-0 md:mb-28 md:block md:min-h-0 md:pt-8",
37
+ className
38
+ ),
39
+ ...props
40
+ }
41
+ );
42
+ }
43
+ );
44
+
45
+
46
+
47
+ exports.PageShell = PageShell;
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  CopyButton
4
- } from "./chunk-7ZRFXJVN.js";
4
+ } from "./chunk-DILYV4FI.js";
5
5
  import {
6
6
  cn
7
7
  } from "./chunk-G6SRSWXB.js";
@@ -1,10 +1,10 @@
1
1
  "use client";
2
- import {
3
- AgentAudioVisualizerBar
4
- } from "./chunk-4TJBXXUE.js";
5
2
  import {
6
3
  Button
7
4
  } from "./chunk-W326GKML.js";
5
+ import {
6
+ AgentAudioVisualizerBar
7
+ } from "./chunk-5OF6D3GH.js";
8
8
  import {
9
9
  cn
10
10
  } from "./chunk-G6SRSWXB.js";
@@ -0,0 +1,67 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client";
2
+
3
+ // src/primitives/accent-theme.tsx
4
+ var _react = require('react');
5
+ var _jsxruntime = require('react/jsx-runtime');
6
+ var PALETTE_ID = /^[A-Za-z0-9_-]+$/;
7
+ function hexToHslTriplet(hex) {
8
+ const match = /^#([0-9a-f]{6})$/i.exec(hex.trim());
9
+ if (!match) return null;
10
+ const value = Number.parseInt(match[1], 16);
11
+ const red = (value >> 16 & 255) / 255;
12
+ const green = (value >> 8 & 255) / 255;
13
+ const blue = (value & 255) / 255;
14
+ const maximum = Math.max(red, green, blue);
15
+ const minimum = Math.min(red, green, blue);
16
+ const lightness = (maximum + minimum) / 2;
17
+ let hue = 0;
18
+ let saturation = 0;
19
+ if (maximum !== minimum) {
20
+ const delta = maximum - minimum;
21
+ saturation = lightness > 0.5 ? delta / (2 - maximum - minimum) : delta / (maximum + minimum);
22
+ if (maximum === red) hue = ((green - blue) / delta + (green < blue ? 6 : 0)) / 6;
23
+ else if (maximum === green) hue = ((blue - red) / delta + 2) / 6;
24
+ else hue = ((red - green) / delta + 4) / 6;
25
+ }
26
+ const round = (number) => Math.round(number * 10) / 10;
27
+ return `${round(hue * 360)} ${round(saturation * 100)}% ${round(lightness * 100)}%`;
28
+ }
29
+ function foregroundFor(hex) {
30
+ const match = /^#([0-9a-f]{6})$/i.exec(hex.trim());
31
+ if (!match) return "0 0% 100%";
32
+ const value = Number.parseInt(match[1], 16);
33
+ const channel = (number) => {
34
+ const normalized = number / 255;
35
+ return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
36
+ };
37
+ const luminance = 0.2126 * channel(value >> 16 & 255) + 0.7152 * channel(value >> 8 & 255) + 0.0722 * channel(value & 255);
38
+ return luminance > 0.45 ? "0 0% 9%" : "0 0% 100%";
39
+ }
40
+ function AccentTheme({ colours, palette, scope: target = "region", children }) {
41
+ const scope = `accent-${_react.useId.call(void 0, ).replace(/[^a-zA-Z0-9]/g, "")}`;
42
+ const light = _optionalChain([colours, 'optionalAccess', _ => _.light]) ? hexToHslTriplet(colours.light) : null;
43
+ const dark = _optionalChain([colours, 'optionalAccess', _2 => _2.dark]) ? hexToHslTriplet(colours.dark) : null;
44
+ if (!light && !dark) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children });
45
+ const root = palette === void 0 ? null : PALETTE_ID.test(palette) ? `:root[data-theme="${palette}"]` : ":root:not(*)";
46
+ const lightSelector = target === "document" ? _nullishCoalesce(root, () => ( ":root")) : root ? `${root} .${scope}` : `.${scope}`;
47
+ const darkSelector = `${_nullishCoalesce(root, () => ( ":root"))}.dark${target === "document" ? "" : ` .${scope}`}`;
48
+ const rules = [];
49
+ if (light && _optionalChain([colours, 'optionalAccess', _3 => _3.light])) {
50
+ rules.push(
51
+ `${lightSelector}{--primary:${light};--primary-foreground:${foregroundFor(colours.light)};--ring:${light};--color-primary:hsl(var(--primary));--color-primary-foreground:hsl(var(--primary-foreground));--color-ring:hsl(var(--ring));}`
52
+ );
53
+ }
54
+ if (dark && _optionalChain([colours, 'optionalAccess', _4 => _4.dark])) {
55
+ rules.push(
56
+ `${darkSelector}{--primary:${dark};--primary-foreground:${foregroundFor(colours.dark)};--ring:${dark};--color-primary:hsl(var(--primary));--color-primary-foreground:hsl(var(--primary-foreground));--color-ring:hsl(var(--ring));}`
57
+ );
58
+ }
59
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
60
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "style", { dangerouslySetInnerHTML: { __html: rules.join("") } }),
61
+ target === "document" ? children : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: scope, children })
62
+ ] });
63
+ }
64
+
65
+
66
+
67
+ exports.AccentTheme = AccentTheme;
@@ -1,10 +1,10 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client";
2
2
 
3
3
 
4
- var _chunkFB3VY2PLcjs = require('./chunk-FB3VY2PL.cjs');
4
+ var _chunkJDEXZGIYcjs = require('./chunk-JDEXZGIY.cjs');
5
5
 
6
6
 
7
- var _chunkJDEXZGIYcjs = require('./chunk-JDEXZGIY.cjs');
7
+ var _chunkNKWHDI4Ccjs = require('./chunk-NKWHDI4C.cjs');
8
8
 
9
9
 
10
10
  var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
@@ -91,7 +91,7 @@ function VoiceSessionControls({
91
91
  muted ? "bg-muted text-muted-foreground" : "bg-primary text-primary-foreground"
92
92
  ), children: muted ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.MicOff, { className: "size-5" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Mic, { className: "size-5" }) }),
93
93
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
94
- _chunkFB3VY2PLcjs.AgentAudioVisualizerBar,
94
+ _chunkNKWHDI4Ccjs.AgentAudioVisualizerBar,
95
95
  {
96
96
  "aria-hidden": "true",
97
97
  size: "sm",
@@ -1,16 +1,16 @@
1
1
  "use client";
2
- import {
3
- useDictation
4
- } from "./chunk-RPSRCB5O.js";
5
- import {
6
- AgentAudioVisualizerBar
7
- } from "./chunk-4TJBXXUE.js";
8
2
  import {
9
3
  Button
10
4
  } from "./chunk-W326GKML.js";
5
+ import {
6
+ AgentAudioVisualizerBar
7
+ } from "./chunk-5OF6D3GH.js";
11
8
  import {
12
9
  cn
13
10
  } from "./chunk-G6SRSWXB.js";
11
+ import {
12
+ useDictation
13
+ } from "./chunk-RPSRCB5O.js";
14
14
 
15
15
  // src/primitives/voice-dictation-button.tsx
16
16
  import {
@@ -0,0 +1,27 @@
1
+ "use client";
2
+ import {
3
+ cn
4
+ } from "./chunk-G6SRSWXB.js";
5
+
6
+ // src/primitives/brand-mark.tsx
7
+ import { forwardRef } from "react";
8
+ import { jsx } from "react/jsx-runtime";
9
+ var sizeStyles = {
10
+ sm: "h-6 max-w-36",
11
+ md: "h-7 max-w-44",
12
+ lg: "h-9 max-w-52"
13
+ };
14
+ var BrandMark = forwardRef(function BrandMark2({ size = "md", className, ...props }, ref) {
15
+ return /* @__PURE__ */ jsx(
16
+ "img",
17
+ {
18
+ ref,
19
+ className: cn("w-auto object-contain", sizeStyles[size], className),
20
+ ...props
21
+ }
22
+ );
23
+ });
24
+
25
+ export {
26
+ BrandMark
27
+ };