@olwiba/cn 0.1.24 → 0.1.26

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.d.ts CHANGED
@@ -120,7 +120,7 @@ declare const badgeVariants: (props?: ({
120
120
  } & class_variance_authority_types.ClassProp) | undefined) => string;
121
121
  interface BadgeProps extends React$1.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
122
122
  disabled?: boolean;
123
- mode?: "playful" | "smooth";
123
+ mode?: "playful";
124
124
  }
125
125
  declare function Badge({ className, variant, size, disabled, mode: modeProp, ...props }: BadgeProps): react_jsx_runtime.JSX.Element;
126
126
 
@@ -945,10 +945,16 @@ declare const ToggleGroupItem: React$1.ForwardRefExoticComponent<Omit<ToggleGrou
945
945
  interface AsciiTextProps {
946
946
  text?: string;
947
947
  accent?: string;
948
+ accents?: Array<{
949
+ text: string;
950
+ color: string;
951
+ }>;
952
+ shine?: string;
953
+ shineColor?: string;
948
954
  color?: string;
949
955
  accentColor?: string;
950
956
  }
951
- declare function AsciiText({ text, accent, color, accentColor, }: AsciiTextProps): react_jsx_runtime.JSX.Element;
957
+ declare function AsciiText({ text, accent, accents, shine, shineColor, color, accentColor, }: AsciiTextProps): react_jsx_runtime.JSX.Element;
952
958
 
953
959
  declare function useIsMobile(): boolean;
954
960
 
@@ -986,7 +992,11 @@ declare function ThemeScript(): react_jsx_runtime.JSX.Element;
986
992
 
987
993
  interface EnchantedProps extends React$1.HTMLAttributes<HTMLDivElement> {
988
994
  children: React$1.ReactNode;
995
+ /** Only show the glints while the wrapper is hovered. */
996
+ hoverOnly?: boolean;
997
+ /** Overrides the glint colour (defaults to text-primary). */
998
+ glintClassName?: string;
989
999
  }
990
- declare function Enchanted({ children, className, ...props }: EnchantedProps): react_jsx_runtime.JSX.Element;
1000
+ declare function Enchanted({ children, className, hoverOnly, glintClassName, ...props }: EnchantedProps): react_jsx_runtime.JSX.Element;
991
1001
 
992
1002
  export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AsciiText, type AsciiTextProps, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, type ButtonProps, Calendar, CalendarDayButton, Card, CardContent, CardDescription, CardFooter, CardHeader, type CardProps, CardTitle, Carousel, type CarouselApi, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, type ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, Checkbox, type CheckboxProps, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, type ConfettiOptions, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, Enchanted, type EnchantedProps, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, HoverCard, HoverCardContent, HoverCardTrigger, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, type InputOTPProps, InputOTPSeparator, InputOTPSlot, type InputProps, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, type SliderProps, Spinner, StatusIndicator, type StatusIndicatorProps, Switch, type SwitchProps, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, type TextareaProps, ThemeScript, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UIVariant, UIVariantProvider, badgeVariants, buttonGroupVariants, buttonVariants, cn, fireConfetti, navigationMenuTriggerStyle, statusIndicatorDotVariants, toggleVariants, useCopyToClipboard, useFormField, useIsMobile, useSidebar, useUIVariant };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { clsx } from 'clsx';
2
2
  import { twMerge } from 'tailwind-merge';
3
3
  import * as React37 from 'react';
4
- import { useMemo, useRef, useState, useCallback, useEffect } from 'react';
4
+ import { useMemo, useCallback, useRef, useState, useEffect } from 'react';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
  import * as AccordionPrimitive from '@radix-ui/react-accordion';
7
7
  import { ChevronDown, ArrowLeft, ArrowRight, Check, X, Search, ChevronRight, Circle, Dot, ChevronUp, PanelLeft, MoreHorizontal, ChevronLeftIcon, ChevronRightIcon, ChevronDownIcon, ChevronLeft, GripVertical, LoaderCircle, OctagonX, TriangleAlert, Info, CircleCheck, Loader2Icon } from 'lucide-react';
@@ -475,16 +475,50 @@ var badgeVariants = cva(
475
475
  }
476
476
  }
477
477
  );
478
+ var playfulBackdrop = {
479
+ default: "bg-primary/30 dark:bg-primary/20",
480
+ destructive: "bg-destructive/30 dark:bg-destructive/20",
481
+ secondary: "bg-foreground/10 dark:bg-foreground/10",
482
+ outline: "bg-foreground/10 dark:bg-foreground/10"
483
+ };
478
484
  function Badge({ className, variant, size, disabled = false, mode: modeProp, ...props }) {
479
485
  const mode = modeProp ?? useUIVariant();
486
+ if (mode === "playful") {
487
+ return /* @__PURE__ */ jsxs(
488
+ "span",
489
+ {
490
+ className: cn(
491
+ "group/playful relative inline-flex",
492
+ disabled && "opacity-50 pointer-events-none"
493
+ ),
494
+ children: [
495
+ /* @__PURE__ */ jsx(
496
+ "span",
497
+ {
498
+ "aria-hidden": "true",
499
+ className: cn(
500
+ "absolute inset-0 rounded-full transition-transform duration-200 translate-x-[3px] translate-y-[3px] -rotate-[0.5deg] group-hover/playful:-rotate-[1.5deg] group-hover/playful:scale-[1.01]",
501
+ playfulBackdrop[variant ?? "default"]
502
+ )
503
+ }
504
+ ),
505
+ /* @__PURE__ */ jsx(
506
+ "div",
507
+ {
508
+ className: cn(badgeVariants({ variant, size }), "relative rotate-[0.3deg]", className),
509
+ ...props
510
+ }
511
+ )
512
+ ]
513
+ }
514
+ );
515
+ }
480
516
  return /* @__PURE__ */ jsx(
481
517
  "div",
482
518
  {
483
519
  className: cn(
484
520
  badgeVariants({ variant, size }),
485
521
  disabled && "opacity-50 pointer-events-none",
486
- mode === "smooth" && "shadow-sm",
487
- mode === "playful" && "rotate-[0.5deg] [box-shadow:2px_2px_0px_0px_color-mix(in_srgb,currentColor_15%,transparent)]",
488
522
  className
489
523
  ),
490
524
  ...props
@@ -2603,20 +2637,33 @@ HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
2603
2637
  var Input = React37.forwardRef(
2604
2638
  ({ className, type, mode: modeProp, ...props }, ref) => {
2605
2639
  const mode = modeProp ?? useUIVariant();
2606
- return /* @__PURE__ */ jsx(
2640
+ const input = /* @__PURE__ */ jsx(
2607
2641
  "input",
2608
2642
  {
2609
2643
  type,
2610
2644
  className: cn(
2611
2645
  "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive/20",
2612
2646
  mode === "smooth" && "rounded-full",
2613
- mode === "playful" && "rotate-[0.3deg] [box-shadow:3px_3px_0px_0px_color-mix(in_srgb,currentColor_12%,transparent)]",
2647
+ mode === "playful" && "relative z-10",
2614
2648
  className
2615
2649
  ),
2616
2650
  ref,
2617
2651
  ...props
2618
2652
  }
2619
2653
  );
2654
+ if (mode === "playful") {
2655
+ return /* @__PURE__ */ jsxs("span", { className: "group/playful relative inline-flex w-full", children: [
2656
+ /* @__PURE__ */ jsx(
2657
+ "span",
2658
+ {
2659
+ "aria-hidden": "true",
2660
+ className: "absolute inset-0 z-0 translate-x-[3px] translate-y-[3px] -rotate-[0.4deg] rounded-md bg-foreground/8 transition-transform duration-200 group-hover/playful:-rotate-[1deg] group-focus-within/playful:-rotate-[1deg] dark:bg-foreground/10"
2661
+ }
2662
+ ),
2663
+ input
2664
+ ] });
2665
+ }
2666
+ return input;
2620
2667
  }
2621
2668
  );
2622
2669
  Input.displayName = "Input";
@@ -5260,16 +5307,30 @@ function composeAsciiText(font, text) {
5260
5307
  function getAsciiAccentColumns(font, text, accent) {
5261
5308
  if (!accent) return /* @__PURE__ */ new Set();
5262
5309
  const columns = /* @__PURE__ */ new Set();
5310
+ const accentRanges = [];
5311
+ let searchFrom = 0;
5312
+ while (true) {
5313
+ const idx = text.indexOf(accent, searchFrom);
5314
+ if (idx === -1) break;
5315
+ accentRanges.push({ start: idx, end: idx + accent.length });
5316
+ searchFrom = idx + 1;
5317
+ }
5318
+ if (accentRanges.length === 0) return columns;
5263
5319
  let offsetX = 0;
5320
+ let charIndex = 0;
5264
5321
  for (const ch of text) {
5265
5322
  const code = ch.charCodeAt(0);
5266
5323
  const glyph = font.chars.get(code) ?? font.chars.get(32);
5267
- if (!glyph) continue;
5324
+ if (!glyph) {
5325
+ charIndex++;
5326
+ continue;
5327
+ }
5268
5328
  const width = Math.max(...glyph.map((line) => line.length));
5269
- if (accent.includes(ch)) {
5329
+ if (accentRanges.some((r) => charIndex >= r.start && charIndex < r.end)) {
5270
5330
  for (let col = 0; col < width; col++) columns.add(offsetX + col);
5271
5331
  }
5272
5332
  offsetX += width;
5333
+ charIndex++;
5273
5334
  }
5274
5335
  return columns;
5275
5336
  }
@@ -6526,6 +6587,9 @@ $$$@@
6526
6587
  function AsciiText({
6527
6588
  text = "olwibaCN",
6528
6589
  accent = "",
6590
+ accents,
6591
+ shine = "",
6592
+ shineColor = "rgba(255,255,255,0.92)",
6529
6593
  color,
6530
6594
  accentColor
6531
6595
  }) {
@@ -6534,6 +6598,26 @@ function AsciiText({
6534
6598
  const accentCols = useMemo(() => {
6535
6599
  return getAsciiAccentColumns(font, text, accent);
6536
6600
  }, [font, text, accent]);
6601
+ const multiAccentCols = useMemo(() => {
6602
+ if (!accents?.length) return null;
6603
+ const map = /* @__PURE__ */ new Map();
6604
+ for (const item of accents) {
6605
+ const cols2 = getAsciiAccentColumns(font, text, item.text);
6606
+ for (const col of cols2) map.set(col, item.color);
6607
+ }
6608
+ return map;
6609
+ }, [accents, font, text]);
6610
+ const getCellColor = useCallback((col, baseColor, highlightColor) => {
6611
+ return multiAccentCols?.get(col) ?? (accentCols.has(col) ? highlightColor : baseColor);
6612
+ }, [accentCols, multiAccentCols]);
6613
+ const shineCols = useMemo(() => {
6614
+ return getAsciiAccentColumns(font, text, shine);
6615
+ }, [font, shine, text]);
6616
+ const shineBounds = useMemo(() => {
6617
+ if (!shineCols.size) return null;
6618
+ const cols2 = Array.from(shineCols);
6619
+ return { min: Math.min(...cols2), max: Math.max(...cols2) };
6620
+ }, [shineCols]);
6537
6621
  const canvasRef = useRef(null);
6538
6622
  const animRef = useRef(0);
6539
6623
  const timeRef = useRef(0);
@@ -6584,24 +6668,46 @@ function AsciiText({
6584
6668
  const time = timeRef.current;
6585
6669
  ctx.clearRect(0, 0, canvas.width, canvas.height);
6586
6670
  const { base: baseColor, accent: highlightColor } = colorsRef.current;
6587
- renderAsciiFrameToContext(ctx, { cells, cols, rows }, {
6588
- accentColumns: accentCols,
6589
- color: baseColor,
6590
- accentColor: highlightColor,
6591
- time,
6592
- pointer: mouseRef.current,
6593
- waves: wavesRef.current
6594
- });
6671
+ if (multiAccentCols || shineCols.size) {
6672
+ for (const cell of cells) {
6673
+ const intensity = getAsciiCellIntensity(cell, {
6674
+ time,
6675
+ pointer: mouseRef.current,
6676
+ waves: wavesRef.current
6677
+ });
6678
+ ctx.globalAlpha = intensity;
6679
+ ctx.fillStyle = getCellColor(cell.col, baseColor, highlightColor);
6680
+ ctx.fillRect(cell.col * ASCII_CHAR_WIDTH, cell.row * ASCII_CHAR_HEIGHT, ASCII_CHAR_WIDTH, ASCII_CHAR_HEIGHT);
6681
+ if (shineBounds && shineCols.has(cell.col)) {
6682
+ const alpha = getShineAlpha(cell.col, cell.row, time, shineBounds) * (cell.ch === "\u2591" ? 0.55 : 1);
6683
+ if (alpha > 0) {
6684
+ ctx.globalAlpha = intensity * alpha;
6685
+ ctx.fillStyle = shineColor;
6686
+ ctx.fillRect(cell.col * ASCII_CHAR_WIDTH, cell.row * ASCII_CHAR_HEIGHT, ASCII_CHAR_WIDTH, ASCII_CHAR_HEIGHT);
6687
+ }
6688
+ }
6689
+ }
6690
+ ctx.globalAlpha = 1;
6691
+ } else {
6692
+ renderAsciiFrameToContext(ctx, { cells, cols, rows }, {
6693
+ accentColumns: accentCols,
6694
+ color: baseColor,
6695
+ accentColor: highlightColor,
6696
+ time,
6697
+ pointer: mouseRef.current,
6698
+ waves: wavesRef.current
6699
+ });
6700
+ }
6595
6701
  if (!hasPaintedRef.current) {
6596
6702
  hasPaintedRef.current = true;
6597
6703
  setIsCanvasReady(true);
6598
6704
  }
6599
6705
  wavesRef.current = wavesRef.current.filter((w) => time - w.t < 3);
6600
- }, [cells, accentCols, cols, rows]);
6706
+ }, [accentCols, cells, cols, getCellColor, multiAccentCols, rows, shineBounds, shineColor, shineCols]);
6601
6707
  useEffect(() => {
6602
6708
  hasPaintedRef.current = false;
6603
6709
  setIsCanvasReady(false);
6604
- }, [text, accent, color, accentColor]);
6710
+ }, [text, accent, accents, shine, shineColor, color, accentColor]);
6605
6711
  useEffect(() => {
6606
6712
  let running = true;
6607
6713
  const loop = () => {
@@ -6666,18 +6772,32 @@ function AsciiText({
6666
6772
  preserveAspectRatio: "xMidYMid meet",
6667
6773
  children: cells.map((cell) => {
6668
6774
  const isAccent = accentCols.has(cell.col);
6669
- return /* @__PURE__ */ jsx(
6670
- "rect",
6671
- {
6672
- x: cell.col * ASCII_CHAR_WIDTH,
6673
- y: cell.row * ASCII_CHAR_HEIGHT,
6674
- width: ASCII_CHAR_WIDTH,
6675
- height: ASCII_CHAR_HEIGHT,
6676
- fill: isAccent ? fallbackAccentColor : "currentColor",
6677
- opacity: cell.ch === "\u2591" ? 0.15 : 0.78
6678
- },
6679
- `${cell.col}-${cell.row}`
6680
- );
6775
+ const multiAccentColor = multiAccentCols?.get(cell.col);
6776
+ const shineAlpha = shineBounds && shineCols.has(cell.col) ? getShineAlpha(cell.col, cell.row, 0.2, shineBounds) * (cell.ch === "\u2591" ? 0.55 : 1) : 0;
6777
+ return /* @__PURE__ */ jsxs(React37.Fragment, { children: [
6778
+ /* @__PURE__ */ jsx(
6779
+ "rect",
6780
+ {
6781
+ x: cell.col * ASCII_CHAR_WIDTH,
6782
+ y: cell.row * ASCII_CHAR_HEIGHT,
6783
+ width: ASCII_CHAR_WIDTH,
6784
+ height: ASCII_CHAR_HEIGHT,
6785
+ fill: multiAccentColor ?? (isAccent ? fallbackAccentColor : "currentColor"),
6786
+ opacity: cell.ch === "\u2591" ? 0.15 : 0.78
6787
+ }
6788
+ ),
6789
+ shineAlpha > 0 && /* @__PURE__ */ jsx(
6790
+ "rect",
6791
+ {
6792
+ x: cell.col * ASCII_CHAR_WIDTH,
6793
+ y: cell.row * ASCII_CHAR_HEIGHT,
6794
+ width: ASCII_CHAR_WIDTH,
6795
+ height: ASCII_CHAR_HEIGHT,
6796
+ fill: shineColor,
6797
+ opacity: shineAlpha
6798
+ }
6799
+ )
6800
+ ] }, `${cell.col}-${cell.row}`);
6681
6801
  })
6682
6802
  }
6683
6803
  ),
@@ -6698,6 +6818,20 @@ function AsciiText({
6698
6818
  }
6699
6819
  ) });
6700
6820
  }
6821
+ function getShineAlpha(col, row, time, bounds) {
6822
+ const span = bounds.max - bounds.min + 1;
6823
+ const travel = span + 6;
6824
+ const diagonalCol = col + row * 0.62;
6825
+ const primarySweep = bounds.max + 3 - time * 1.35 % 1 * travel;
6826
+ const secondarySweep = bounds.max + 3 - (time * 1.35 + 0.5) % 1 * travel;
6827
+ const primary = getShineBandAlpha(diagonalCol, primarySweep);
6828
+ const secondary = getShineBandAlpha(diagonalCol, secondarySweep);
6829
+ return Math.min(0.86, primary + secondary * 0.82);
6830
+ }
6831
+ function getShineBandAlpha(diagonalCol, sweep) {
6832
+ const distance = Math.abs(diagonalCol - sweep);
6833
+ return Math.max(0, 1 - distance / 2.7) ** 2 * 0.78;
6834
+ }
6701
6835
  function useCopyToClipboard(timeout = 2e3) {
6702
6836
  const [isCopied, setIsCopied] = useState(false);
6703
6837
  const copyToClipboard = useCallback(
@@ -6812,25 +6946,33 @@ function ThemeScript() {
6812
6946
  }
6813
6947
  var GLINTS = [
6814
6948
  // perimeter
6815
- { top: "-7px", left: "10%", size: 7, delay: "0s", duration: "2.1s" },
6816
- { top: "2px", right: "-7px", size: 6, delay: "0.3s", duration: "3.4s" },
6817
- { bottom: "-6px", right: "15%", size: 6, delay: "1.1s", duration: "1.7s" },
6818
- { top: "3px", left: "-6px", size: 5, delay: "0.7s", duration: "2.9s" },
6949
+ { top: "-8px", left: "10%", size: 9, delay: "0s", duration: "2.1s" },
6950
+ { top: "2px", right: "-8px", size: 8, delay: "0.3s", duration: "3.4s" },
6951
+ { bottom: "-7px", right: "15%", size: 8, delay: "1.1s", duration: "1.7s" },
6952
+ { top: "3px", left: "-7px", size: 7, delay: "0.7s", duration: "2.9s" },
6953
+ { bottom: "-6px", left: "40%", size: 6, delay: "2.2s", duration: "2.6s" },
6954
+ { top: "-6px", right: "35%", size: 6, delay: "1.4s", duration: "2.2s" },
6819
6955
  // inside / overlapping the child
6820
- { top: "20%", left: "38%", size: 5, delay: "1.5s", duration: "2.3s" },
6821
- { top: "15%", right: "22%", size: 4, delay: "0.5s", duration: "3.1s" },
6822
- { bottom: "15%", left: "22%", size: 4, delay: "1.9s", duration: "1.8s" }
6956
+ { top: "20%", left: "38%", size: 6, delay: "1.5s", duration: "2.3s" },
6957
+ { top: "15%", right: "22%", size: 5, delay: "0.5s", duration: "3.1s" },
6958
+ { bottom: "15%", left: "22%", size: 5, delay: "1.9s", duration: "1.8s" }
6823
6959
  ];
6824
6960
  function GlintIcon({ size }) {
6825
6961
  return /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 8 8", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M4 0.5 L4.6 3.4 L7.5 4 L4.6 4.6 L4 7.5 L3.4 4.6 L0.5 4 L3.4 3.4 Z" }) });
6826
6962
  }
6827
- function Enchanted({ children, className, ...props }) {
6828
- return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), ...props, children: [
6963
+ function Enchanted({ children, className, hoverOnly = false, glintClassName, ...props }) {
6964
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative", hoverOnly && "group/enchanted", className), ...props, children: [
6829
6965
  children,
6830
6966
  GLINTS.map((g, i) => /* @__PURE__ */ jsx(
6831
6967
  "span",
6832
6968
  {
6833
- className: "pointer-events-none absolute z-10 text-primary animate-enchanted",
6969
+ className: cn(
6970
+ "pointer-events-none absolute z-10 animate-enchanted drop-shadow-[0_0_3px_currentColor]",
6971
+ glintClassName ?? "text-primary",
6972
+ // Display toggle (not opacity) - the keyframes own opacity, and
6973
+ // re-mounting restarts the glint timeline on each hover.
6974
+ hoverOnly && "hidden group-hover/enchanted:block"
6975
+ ),
6834
6976
  style: { top: g.top, bottom: g.bottom, left: g.left, right: g.right, animationDelay: g.delay, animationDuration: g.duration },
6835
6977
  children: /* @__PURE__ */ jsx(GlintIcon, { size: g.size })
6836
6978
  },