@refraction-ui/react 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -33297,6 +33297,65 @@ function useConversation(config) {
33297
33297
  };
33298
33298
  }
33299
33299
  var h = React11__namespace.createElement;
33300
+ var svg = (children, size = 16) => h(
33301
+ "svg",
33302
+ {
33303
+ xmlns: "http://www.w3.org/2000/svg",
33304
+ viewBox: "0 0 24 24",
33305
+ width: size,
33306
+ height: size,
33307
+ fill: "none",
33308
+ stroke: "currentColor",
33309
+ strokeWidth: 2,
33310
+ strokeLinecap: "round",
33311
+ strokeLinejoin: "round",
33312
+ "aria-hidden": true
33313
+ },
33314
+ children
33315
+ );
33316
+ var IconAttach = () => svg(h("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 17.93 8.81l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48" }));
33317
+ var IconBold = () => svg([
33318
+ h("path", { key: "a", d: "M6 4h8a4 4 0 0 1 0 8H6z" }),
33319
+ h("path", { key: "b", d: "M6 12h9a4 4 0 0 1 0 8H6z" })
33320
+ ]);
33321
+ var IconItalic = () => svg([
33322
+ h("line", { key: "a", x1: 19, y1: 4, x2: 10, y2: 4 }),
33323
+ h("line", { key: "b", x1: 14, y1: 20, x2: 5, y2: 20 }),
33324
+ h("line", { key: "c", x1: 15, y1: 4, x2: 9, y2: 20 })
33325
+ ]);
33326
+ var IconCode = () => svg([
33327
+ h("polyline", { key: "a", points: "16 18 22 12 16 6" }),
33328
+ h("polyline", { key: "b", points: "8 6 2 12 8 18" })
33329
+ ]);
33330
+ var IconLink = () => svg([
33331
+ h("path", { key: "a", d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
33332
+ h("path", { key: "b", d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
33333
+ ]);
33334
+ var IconQuote = () => svg([
33335
+ h("path", { key: "a", d: "M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z" }),
33336
+ h("path", { key: "b", d: "M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z" })
33337
+ ]);
33338
+ var IconListUl = () => svg([
33339
+ h("line", { key: "a", x1: 8, y1: 6, x2: 21, y2: 6 }),
33340
+ h("line", { key: "b", x1: 8, y1: 12, x2: 21, y2: 12 }),
33341
+ h("line", { key: "c", x1: 8, y1: 18, x2: 21, y2: 18 }),
33342
+ h("line", { key: "d", x1: 3, y1: 6, x2: 3.01, y2: 6 }),
33343
+ h("line", { key: "e", x1: 3, y1: 12, x2: 3.01, y2: 12 }),
33344
+ h("line", { key: "f", x1: 3, y1: 18, x2: 3.01, y2: 18 })
33345
+ ]);
33346
+ var IconListOl = () => svg([
33347
+ h("line", { key: "a", x1: 10, y1: 6, x2: 21, y2: 6 }),
33348
+ h("line", { key: "b", x1: 10, y1: 12, x2: 21, y2: 12 }),
33349
+ h("line", { key: "c", x1: 10, y1: 18, x2: 21, y2: 18 }),
33350
+ h("path", { key: "d", d: "M4 6h1v4" }),
33351
+ h("path", { key: "e", d: "M4 10h2" }),
33352
+ h("path", { key: "f", d: "M6 18H4c0-1 2-2 2-3s-1-1.5-2-1" })
33353
+ ]);
33354
+ var IconArrowUp = () => svg([
33355
+ h("path", { key: "a", d: "M12 19V5" }),
33356
+ h("path", { key: "b", d: "m5 12 7-7 7 7" })
33357
+ ]);
33358
+ var IconStop = () => svg(h("rect", { x: 7, y: 7, width: 10, height: 10, rx: 1.5 }));
33300
33359
  var EMOJI = {
33301
33360
  smile: "\u{1F604}",
33302
33361
  grin: "\u{1F601}",
@@ -33349,13 +33408,15 @@ function detectTrigger(text, caret) {
33349
33408
  return { type, query, start: caret - query.length - 1, end: caret };
33350
33409
  }
33351
33410
  function Composer({
33352
- placeholder = "Type a message\u2026 (/ commands, @ mentions, : emoji)",
33411
+ placeholder = "Send a message\u2026",
33353
33412
  busy = false,
33354
33413
  slashCommands = [],
33355
33414
  mentions,
33356
33415
  toolbar = true,
33357
33416
  emoji = true,
33358
33417
  attachments = true,
33418
+ error,
33419
+ onRetry,
33359
33420
  onSubmit,
33360
33421
  onStop,
33361
33422
  onSlashCommand,
@@ -33496,148 +33557,157 @@ ${sel}
33496
33557
  submit();
33497
33558
  }
33498
33559
  }
33499
- const toolbarBtn = (label, title, kind) => h(
33560
+ const iconBtn = "flex h-8 w-8 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-foreground";
33561
+ const toolbarBtn = (icon, title, kind) => h(
33500
33562
  "button",
33501
33563
  {
33502
33564
  key: kind,
33503
33565
  type: "button",
33504
33566
  title,
33505
- className: "rounded px-1.5 py-0.5 text-xs text-muted-foreground hover:bg-accent hover:text-foreground",
33567
+ "aria-label": title,
33568
+ className: iconBtn,
33506
33569
  onMouseDown: (e2) => e2.preventDefault(),
33507
33570
  // keep textarea selection
33508
33571
  onClick: () => format(kind)
33509
33572
  },
33510
- label
33573
+ icon
33511
33574
  );
33512
- return h(
33575
+ const divider = () => h("span", { className: "mx-1 h-5 w-px bg-border", "aria-hidden": true });
33576
+ const menu = menuOpen ? h(
33513
33577
  "div",
33514
- { className: "border-t border-border" },
33515
- // attachment chips
33516
- pending.length > 0 ? h(
33578
+ {
33579
+ className: "absolute bottom-full left-0 z-20 mb-2 w-72 overflow-hidden rounded-xl border border-border bg-popover shadow-lg",
33580
+ role: "listbox"
33581
+ },
33582
+ h(
33517
33583
  "div",
33518
- { className: "flex flex-wrap gap-2 px-3 pt-2" },
33519
- ...pending.map(
33520
- (a2) => h(
33521
- "span",
33522
- { key: a2.id, className: "inline-flex items-center gap-1 rounded bg-muted px-2 py-0.5 text-xs" },
33523
- a2.name,
33524
- h(
33525
- "button",
33526
- {
33527
- type: "button",
33528
- className: "text-muted-foreground hover:text-destructive",
33529
- onClick: () => setPending((p2) => p2.filter((x2) => x2.id !== a2.id))
33530
- },
33531
- "\u2715"
33532
- )
33533
- )
33584
+ { className: "border-b border-border px-3 py-1.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground" },
33585
+ trigger?.type === "/" ? "Commands" : trigger?.type === "@" ? "Mentions" : "Emoji"
33586
+ ),
33587
+ ...items.map(
33588
+ (it2, i2) => h(
33589
+ "button",
33590
+ {
33591
+ key: it2.key,
33592
+ type: "button",
33593
+ role: "option",
33594
+ "aria-selected": i2 === active,
33595
+ className: cn(
33596
+ "flex w-full items-center gap-2 px-3 py-2 text-left text-sm",
33597
+ i2 === active ? "bg-accent" : "hover:bg-accent/50"
33598
+ ),
33599
+ onMouseEnter: () => setActive(i2),
33600
+ onMouseDown: (e2) => e2.preventDefault(),
33601
+ onClick: () => selectItem(i2)
33602
+ },
33603
+ it2.icon ? h("span", { className: "w-4 text-center text-muted-foreground" }, it2.icon) : null,
33604
+ h("span", { className: "flex-1 truncate" }, it2.primary),
33605
+ it2.secondary ? h("span", { className: "truncate text-xs text-muted-foreground" }, it2.secondary) : null
33534
33606
  )
33535
- ) : null,
33536
- // toolbar
33537
- toolbar ? h(
33538
- "div",
33539
- { className: "flex items-center gap-0.5 px-2 pt-2" },
33540
- toolbarBtn("B", "Bold (\u2318B)", "bold"),
33541
- toolbarBtn("\u{1D456}", "Italic (\u2318I)", "italic"),
33542
- toolbarBtn("</>", "Code (\u2318E)", "code"),
33543
- toolbarBtn("\u{1F517}", "Link (\u2318K)", "link"),
33544
- toolbarBtn("\u275D", "Quote", "quote"),
33545
- toolbarBtn("\u2022", "Bulleted list", "ul"),
33546
- toolbarBtn("1.", "Numbered list", "ol")
33547
- ) : null,
33548
- // input row (relative for the popup menu)
33607
+ )
33608
+ ) : null;
33609
+ return h(
33610
+ "div",
33611
+ { className: "p-3" },
33549
33612
  h(
33550
33613
  "div",
33551
- { className: "relative flex items-end gap-2 p-3" },
33552
- menuOpen ? h(
33614
+ { className: "relative" },
33615
+ menu,
33616
+ // unified input card
33617
+ h(
33553
33618
  "div",
33554
33619
  {
33555
- className: "absolute bottom-full left-3 z-20 mb-1 w-72 overflow-hidden rounded-lg border border-border bg-popover shadow-lg",
33556
- role: "listbox"
33620
+ className: "overflow-hidden rounded-2xl border border-border bg-background transition focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/40"
33557
33621
  },
33622
+ // error banner
33623
+ error ? h(
33624
+ "div",
33625
+ { className: "flex items-center gap-2 border-b border-border bg-destructive/5 px-3 py-2 text-xs text-destructive", role: "alert" },
33626
+ h("span", { className: "flex-1 truncate" }, error),
33627
+ onRetry ? h("button", { type: "button", className: "font-medium underline", onClick: () => onRetry() }, "Retry") : null
33628
+ ) : null,
33629
+ // attachment chips
33630
+ pending.length > 0 ? h(
33631
+ "div",
33632
+ { className: "flex flex-wrap gap-2 px-3 pt-3" },
33633
+ ...pending.map(
33634
+ (a2) => h(
33635
+ "span",
33636
+ { key: a2.id, className: "inline-flex items-center gap-1 rounded-md bg-muted px-2 py-0.5 text-xs" },
33637
+ a2.name,
33638
+ h(
33639
+ "button",
33640
+ { type: "button", className: "text-muted-foreground hover:text-destructive", onClick: () => setPending((p2) => p2.filter((x2) => x2.id !== a2.id)) },
33641
+ "\u2715"
33642
+ )
33643
+ )
33644
+ )
33645
+ ) : null,
33646
+ // textarea (borderless)
33647
+ h("textarea", {
33648
+ ref,
33649
+ className: "block max-h-40 w-full resize-none bg-transparent px-3.5 py-3 text-sm placeholder:text-muted-foreground focus:outline-none",
33650
+ rows: 1,
33651
+ value,
33652
+ placeholder,
33653
+ autoFocus,
33654
+ "aria-label": "Message",
33655
+ onChange: (e2) => syncFromTextarea(e2.target),
33656
+ onClick: (e2) => syncFromTextarea(e2.currentTarget),
33657
+ onKeyUp: (e2) => syncFromTextarea(e2.currentTarget),
33658
+ onKeyDown,
33659
+ onBlur: () => setTimeout(() => setTrigger(null), 120)
33660
+ }),
33661
+ // bottom action bar
33558
33662
  h(
33559
33663
  "div",
33560
- { className: "border-b border-border px-2 py-1 text-[10px] uppercase tracking-wide text-muted-foreground" },
33561
- trigger?.type === "/" ? "Commands" : trigger?.type === "@" ? "Mentions" : "Emoji"
33562
- ),
33563
- ...items.map(
33564
- (it2, i2) => h(
33664
+ { className: "flex items-center gap-0.5 px-2 pb-2" },
33665
+ attachments ? h(
33666
+ React11__namespace.Fragment,
33667
+ null,
33668
+ h("input", {
33669
+ ref: fileRef,
33670
+ type: "file",
33671
+ accept: "image/*",
33672
+ multiple: true,
33673
+ className: "hidden",
33674
+ onChange: (e2) => {
33675
+ onFiles(e2.target.files);
33676
+ e2.target.value = "";
33677
+ }
33678
+ }),
33679
+ h("button", { type: "button", className: iconBtn, "aria-label": "Attach image or GIF", onClick: () => fileRef.current?.click() }, h(IconAttach))
33680
+ ) : null,
33681
+ attachments && toolbar ? divider() : null,
33682
+ toolbar ? h(
33683
+ React11__namespace.Fragment,
33684
+ null,
33685
+ toolbarBtn(h(IconBold), "Bold (\u2318B)", "bold"),
33686
+ toolbarBtn(h(IconItalic), "Italic (\u2318I)", "italic"),
33687
+ toolbarBtn(h(IconCode), "Code (\u2318E)", "code"),
33688
+ toolbarBtn(h(IconLink), "Link (\u2318K)", "link"),
33689
+ divider(),
33690
+ toolbarBtn(h(IconQuote), "Quote", "quote"),
33691
+ toolbarBtn(h(IconListUl), "Bulleted list", "ul"),
33692
+ toolbarBtn(h(IconListOl), "Numbered list", "ol")
33693
+ ) : null,
33694
+ h("div", { className: "flex-1" }),
33695
+ busy ? h(
33696
+ "button",
33697
+ { type: "button", "aria-label": "Stop", className: "flex h-9 w-9 items-center justify-center rounded-full bg-primary text-primary-foreground transition hover:opacity-90", onClick: () => onStop?.() },
33698
+ h(IconStop)
33699
+ ) : h(
33565
33700
  "button",
33566
33701
  {
33567
- key: it2.key,
33568
33702
  type: "button",
33569
- role: "option",
33570
- "aria-selected": i2 === active,
33571
- className: cn(
33572
- "flex w-full items-center gap-2 px-2 py-1.5 text-left text-sm",
33573
- i2 === active ? "bg-accent" : "hover:bg-accent/50"
33574
- ),
33575
- onMouseEnter: () => setActive(i2),
33576
- onMouseDown: (e2) => e2.preventDefault(),
33577
- onClick: () => selectItem(i2)
33703
+ "aria-label": "Send",
33704
+ className: "flex h-9 w-9 items-center justify-center rounded-full bg-primary text-primary-foreground transition hover:opacity-90 disabled:bg-muted disabled:text-muted-foreground disabled:opacity-100",
33705
+ disabled: !value.trim() && pending.length === 0,
33706
+ onClick: submit
33578
33707
  },
33579
- it2.icon ? h("span", { className: "w-4 text-center text-muted-foreground" }, it2.icon) : null,
33580
- h("span", { className: "flex-1 truncate" }, it2.primary),
33581
- it2.secondary ? h("span", { className: "truncate text-xs text-muted-foreground" }, it2.secondary) : null
33708
+ h(IconArrowUp)
33582
33709
  )
33583
33710
  )
33584
- ) : null,
33585
- attachments ? h(
33586
- React11__namespace.Fragment,
33587
- null,
33588
- h("input", {
33589
- ref: fileRef,
33590
- type: "file",
33591
- accept: "image/*",
33592
- multiple: true,
33593
- className: "hidden",
33594
- onChange: (e2) => {
33595
- onFiles(e2.target.files);
33596
- e2.target.value = "";
33597
- }
33598
- }),
33599
- h(
33600
- "button",
33601
- {
33602
- type: "button",
33603
- className: "rounded-md border border-border px-2 py-2 text-sm hover:bg-accent",
33604
- "aria-label": "Attach image or GIF",
33605
- onClick: () => fileRef.current?.click()
33606
- },
33607
- "\u{1F4CE}"
33608
- )
33609
- ) : null,
33610
- h("textarea", {
33611
- ref,
33612
- className: "max-h-40 flex-1 resize-none rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary",
33613
- rows: 1,
33614
- value,
33615
- placeholder,
33616
- autoFocus,
33617
- "aria-label": "Message",
33618
- onChange: (e2) => syncFromTextarea(e2.target),
33619
- onClick: (e2) => syncFromTextarea(e2.currentTarget),
33620
- onKeyUp: (e2) => syncFromTextarea(e2.currentTarget),
33621
- onKeyDown,
33622
- onBlur: () => setTimeout(() => setTrigger(null), 120)
33623
- }),
33624
- busy ? h(
33625
- "button",
33626
- {
33627
- type: "button",
33628
- className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground",
33629
- onClick: () => onStop?.()
33630
- },
33631
- "Stop"
33632
- ) : h(
33633
- "button",
33634
- {
33635
- type: "button",
33636
- className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground disabled:opacity-50",
33637
- disabled: !value.trim() && pending.length === 0,
33638
- onClick: submit
33639
- },
33640
- "Send"
33641
33711
  )
33642
33712
  )
33643
33713
  );
@@ -33985,9 +34055,13 @@ function Chat({
33985
34055
  const timeline = selectMainTimeline(state.messages, state.threadingMode);
33986
34056
  const activeConv = state.conversations.find((c2) => c2.id === state.activeConversationId);
33987
34057
  const busy = state.status === "sending" || state.status === "streaming";
34058
+ const error = state.status === "error" ? state.error : null;
34059
+ const onRetry = () => void conversation.retryLast();
33988
34060
  const mainComposer = h2(Composer, {
33989
34061
  placeholder,
33990
34062
  busy,
34063
+ error,
34064
+ onRetry,
33991
34065
  slashCommands,
33992
34066
  mentions,
33993
34067
  onSlashCommand,
@@ -33998,6 +34072,8 @@ function Chat({
33998
34072
  const threadComposer = state.openThreadRootId ? h2(Composer, {
33999
34073
  placeholder: "Reply\u2026",
34000
34074
  busy,
34075
+ error,
34076
+ onRetry,
34001
34077
  slashCommands,
34002
34078
  mentions,
34003
34079
  onSlashCommand,
@@ -34301,14 +34377,56 @@ function useCookieConsent(config) {
34301
34377
  };
34302
34378
  }
34303
34379
  var h3 = React11__namespace.createElement;
34304
- var btnPrimary = "rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:opacity-90";
34305
- var btnGhost = "rounded-md border border-border px-3 py-1.5 text-sm hover:bg-accent";
34306
- var btnLink = "text-sm text-muted-foreground underline hover:text-foreground";
34380
+ var btnBase = "inline-flex items-center justify-center rounded-lg px-3.5 py-2 text-sm font-medium transition-colors";
34381
+ var btnPrimary = cn(btnBase, "bg-primary text-primary-foreground hover:opacity-90");
34382
+ var btnGhost = cn(btnBase, "border border-border hover:bg-accent");
34383
+ var btnLink = "text-sm font-medium text-muted-foreground underline-offset-4 hover:text-foreground hover:underline";
34384
+ function Toggle({
34385
+ checked,
34386
+ disabled,
34387
+ onChange,
34388
+ label
34389
+ }) {
34390
+ if (disabled) {
34391
+ return h3(
34392
+ "span",
34393
+ { className: "rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground" },
34394
+ "Always on"
34395
+ );
34396
+ }
34397
+ return h3(
34398
+ "button",
34399
+ {
34400
+ type: "button",
34401
+ role: "switch",
34402
+ "aria-checked": checked,
34403
+ "aria-label": label,
34404
+ onClick: () => onChange(!checked),
34405
+ className: cn(
34406
+ "relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors",
34407
+ checked ? "bg-primary" : "bg-muted"
34408
+ )
34409
+ },
34410
+ h3("span", {
34411
+ className: cn(
34412
+ "inline-block h-4 w-4 transform rounded-full bg-background shadow transition-transform",
34413
+ checked ? "translate-x-[1.125rem]" : "translate-x-0.5"
34414
+ )
34415
+ })
34416
+ );
34417
+ }
34418
+ function CookieIcon() {
34419
+ return h3(
34420
+ "div",
34421
+ { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-accent text-xl", "aria-hidden": true },
34422
+ "\u{1F36A}"
34423
+ );
34424
+ }
34307
34425
  function CookieConsent({
34308
34426
  consent,
34309
34427
  position = "bottom",
34310
34428
  title = "We use cookies",
34311
- description = "We use cookies to run the site, remember your preferences, and measure traffic. Choose which categories to allow.",
34429
+ description = "We use cookies to run the site, remember your preferences, and measure traffic. Choose which to allow.",
34312
34430
  policyUrl,
34313
34431
  policyLabel = "Cookie policy",
34314
34432
  className
@@ -34316,64 +34434,80 @@ function CookieConsent({
34316
34434
  const { state, acceptAll, rejectAll, savePreferences, setPreference } = consent;
34317
34435
  const [settings, setSettings] = React11__namespace.useState(false);
34318
34436
  if (!state.open) return null;
34319
- const wrapper = cn(
34320
- "fixed inset-x-0 z-50 p-4",
34321
- position === "bottom" ? "bottom-0" : "top-0",
34322
- className
34323
- );
34324
- const panel = "mx-auto max-w-3xl rounded-xl border border-border bg-background p-4 shadow-lg";
34325
- const header = h3(
34326
- "div",
34327
- null,
34328
- h3("h2", { className: "text-base font-semibold" }, title),
34329
- h3("p", { className: "mt-1 text-sm text-muted-foreground" }, description),
34330
- policyUrl ? h3("a", { href: policyUrl, target: "_blank", rel: "noreferrer", className: cn(btnLink, "mt-1 inline-block") }, policyLabel) : null
34331
- );
34332
- const promptActions = h3(
34437
+ const wrapper = cn("fixed inset-x-0 z-50 p-4", position === "bottom" ? "bottom-0" : "top-0", className);
34438
+ const panel = "mx-auto max-w-2xl overflow-hidden rounded-2xl border border-border bg-background shadow-lg";
34439
+ const policy = policyUrl ? h3("a", { href: policyUrl, target: "_blank", rel: "noreferrer", className: cn(btnLink, "whitespace-nowrap") }, policyLabel) : null;
34440
+ const promptView = h3(
34333
34441
  "div",
34334
- { className: "mt-3 flex flex-wrap items-center gap-2" },
34335
- h3("button", { type: "button", className: btnPrimary, onClick: () => acceptAll() }, "Accept all"),
34336
- h3("button", { type: "button", className: btnGhost, onClick: () => rejectAll() }, "Reject all"),
34337
- h3("button", { type: "button", className: cn(btnGhost, "ml-auto"), onClick: () => setSettings(true) }, "Manage preferences")
34442
+ { className: "flex flex-col gap-4 p-5 sm:flex-row sm:items-center" },
34443
+ h3(CookieIcon),
34444
+ h3(
34445
+ "div",
34446
+ { className: "min-w-0 flex-1" },
34447
+ h3("p", { className: "text-sm font-semibold" }, title),
34448
+ h3(
34449
+ "p",
34450
+ { className: "mt-0.5 text-sm leading-relaxed text-muted-foreground" },
34451
+ description,
34452
+ policy ? h3(React11__namespace.Fragment, null, " ", policy) : null
34453
+ )
34454
+ ),
34455
+ h3(
34456
+ "div",
34457
+ { className: "flex flex-wrap items-center gap-2 sm:shrink-0" },
34458
+ h3("button", { type: "button", className: btnLink, onClick: () => setSettings(true) }, "Customize"),
34459
+ h3("button", { type: "button", className: btnGhost, onClick: () => rejectAll() }, "Reject all"),
34460
+ h3("button", { type: "button", className: btnPrimary, onClick: () => acceptAll() }, "Accept all")
34461
+ )
34338
34462
  );
34339
34463
  const settingsView = h3(
34340
34464
  "div",
34341
- { className: "mt-3 space-y-2" },
34342
- ...state.categories.map(
34343
- (cat) => h3(
34344
- "label",
34345
- {
34346
- key: cat.id,
34347
- className: "flex items-start justify-between gap-3 rounded-md border border-border p-3"
34348
- },
34349
- h3(
34350
- "span",
34351
- { className: "min-w-0" },
34352
- h3("span", { className: "block text-sm font-medium" }, cat.label, cat.required ? " (required)" : ""),
34353
- cat.description ? h3("span", { className: "block text-xs text-muted-foreground" }, cat.description) : null
34354
- ),
34355
- h3("input", {
34356
- type: "checkbox",
34357
- className: "mt-0.5 h-4 w-4 accent-[hsl(var(--primary))]",
34358
- checked: !!state.preferences[cat.id],
34359
- disabled: cat.required,
34360
- "aria-label": cat.label,
34361
- onChange: (e2) => setPreference(cat.id, e2.target.checked)
34362
- })
34465
+ { className: "p-5" },
34466
+ h3(
34467
+ "div",
34468
+ { className: "flex items-center gap-3" },
34469
+ h3(CookieIcon),
34470
+ h3(
34471
+ "div",
34472
+ null,
34473
+ h3("p", { className: "text-sm font-semibold" }, "Cookie preferences"),
34474
+ h3("p", { className: "text-xs text-muted-foreground" }, "Toggle the categories you want to allow.")
34475
+ )
34476
+ ),
34477
+ h3(
34478
+ "div",
34479
+ { className: "mt-4 space-y-2" },
34480
+ ...state.categories.map(
34481
+ (cat) => h3(
34482
+ "div",
34483
+ { key: cat.id, className: "flex items-center justify-between gap-4 rounded-xl border border-border p-3" },
34484
+ h3(
34485
+ "div",
34486
+ { className: "min-w-0" },
34487
+ h3("p", { className: "text-sm font-medium" }, cat.label),
34488
+ cat.description ? h3("p", { className: "mt-0.5 text-xs text-muted-foreground" }, cat.description) : null
34489
+ ),
34490
+ h3(Toggle, {
34491
+ checked: !!state.preferences[cat.id],
34492
+ disabled: cat.required,
34493
+ label: cat.label,
34494
+ onChange: (v2) => setPreference(cat.id, v2)
34495
+ })
34496
+ )
34363
34497
  )
34364
34498
  ),
34365
34499
  h3(
34366
34500
  "div",
34367
- { className: "flex flex-wrap items-center gap-2 pt-1" },
34368
- h3("button", { type: "button", className: btnPrimary, onClick: () => savePreferences(state.preferences) }, "Save preferences"),
34369
- h3("button", { type: "button", className: btnGhost, onClick: () => acceptAll() }, "Accept all"),
34370
- h3("button", { type: "button", className: cn(btnLink, "ml-auto"), onClick: () => setSettings(false) }, "Back")
34501
+ { className: "mt-4 flex flex-wrap items-center gap-2" },
34502
+ h3("button", { type: "button", className: btnLink, onClick: () => setSettings(false) }, "\u2190 Back"),
34503
+ h3("button", { type: "button", className: cn(btnGhost, "sm:ml-auto"), onClick: () => acceptAll() }, "Accept all"),
34504
+ h3("button", { type: "button", className: btnPrimary, onClick: () => savePreferences(state.preferences) }, "Save preferences")
34371
34505
  )
34372
34506
  );
34373
34507
  return h3(
34374
34508
  "div",
34375
34509
  { className: wrapper, role: "dialog", "aria-label": "Cookie consent", "aria-modal": false },
34376
- h3("div", { className: panel }, header, settings ? settingsView : promptActions)
34510
+ h3("div", { className: panel }, settings ? settingsView : promptView)
34377
34511
  );
34378
34512
  }
34379
34513