@lukeashford/aurelius 2.0.0 → 2.2.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.
package/dist/index.mjs CHANGED
@@ -74,28 +74,40 @@ Input.displayName = "Input";
74
74
 
75
75
  // src/components/Card.tsx
76
76
  import React3 from "react";
77
+ import { Check } from "lucide-react";
78
+ var VARIANT_STYLES = {
79
+ default: "bg-charcoal shadow-sm border border-gold/30",
80
+ elevated: "bg-charcoal shadow-lg border-0",
81
+ outlined: "bg-charcoal shadow-none border border-ash",
82
+ ghost: "bg-transparent shadow-none border-0",
83
+ featured: "bg-charcoal border border-gold shadow-glow-sm"
84
+ };
77
85
  function cx3(...classes) {
78
86
  return classes.filter(Boolean).join(" ");
79
87
  }
80
88
  var Card = React3.forwardRef(
81
- ({ variant = "default", interactive = false, className, ...rest }, ref) => {
82
- const base = "rounded-none p-6";
83
- const variantClasses = {
84
- default: "bg-charcoal shadow-sm border border-gold/30",
85
- elevated: "bg-charcoal shadow-lg border-0",
86
- outlined: "bg-charcoal shadow-none border border-ash",
87
- ghost: "bg-transparent shadow-none border-0",
88
- featured: "bg-charcoal border border-gold shadow-[0_0_10px_rgba(201,162,39,0.2)]"
89
- };
90
- const interactiveClass = interactive ? "transition-all duration-200 hover:border-gold hover:shadow-glow cursor-pointer" : "";
91
- const variantClass = variantClasses[variant];
89
+ ({ variant = "default", interactive = false, selected = false, className, children, ...props }, ref) => {
92
90
  return /* @__PURE__ */ React3.createElement(
93
91
  "div",
94
92
  {
95
93
  ref,
96
- className: cx3(base, variantClass, interactiveClass, className),
97
- ...rest
98
- }
94
+ className: cx3(
95
+ "rounded-none p-6 relative",
96
+ VARIANT_STYLES[variant],
97
+ interactive && "transition-all duration-200 hover:border-gold hover:shadow-glow cursor-pointer",
98
+ selected && "border-gold shadow-glow-md",
99
+ className
100
+ ),
101
+ ...props
102
+ },
103
+ children,
104
+ selected && /* @__PURE__ */ React3.createElement(
105
+ "div",
106
+ {
107
+ className: "absolute top-3 right-3 flex items-center justify-center h-6 w-6 rounded-full bg-gold text-obsidian"
108
+ },
109
+ /* @__PURE__ */ React3.createElement(Check, { className: "h-4 w-4" })
110
+ )
99
111
  );
100
112
  }
101
113
  );
@@ -133,10 +145,7 @@ var Avatar = React4.forwardRef(
133
145
  ),
134
146
  ...rest
135
147
  },
136
- src ? (
137
- // eslint-disable-next-line @next/next/no-img-element
138
- /* @__PURE__ */ React4.createElement("img", { src, alt: alt || name || "Avatar", className: "h-full w-full object-cover" })
139
- ) : /* @__PURE__ */ React4.createElement("span", { "aria-hidden": true }, initials(name) || "\xB7"),
148
+ src ? /* @__PURE__ */ React4.createElement("img", { src, alt: alt || name || "Avatar", className: "h-full w-full object-cover" }) : /* @__PURE__ */ React4.createElement("span", { "aria-hidden": true }, initials(name) || "\xB7"),
140
149
  status && /* @__PURE__ */ React4.createElement(
141
150
  "span",
142
151
  {
@@ -186,12 +195,12 @@ var Tooltip = ({ content, children, open = false, side = "top" }) => {
186
195
  {
187
196
  role: "tooltip",
188
197
  className: cx6(
189
- "pointer-events-none absolute z-50 whitespace-nowrap rounded-md border border-ash bg-graphite px-3 py-1.5 text-sm text-white shadow-lg transition-opacity duration-fast ease-out",
198
+ "pointer-events-none absolute z-50 whitespace-nowrap rounded-md border border-ash bg-graphite px-3 py-1.5 text-sm text-white shadow-lg transition-opacity duration-200 ease-out",
190
199
  open ? "opacity-100" : "opacity-0",
191
- side === "top" && "left-1/2 -translate-x-1/2 -top-2 translate-y-[-100%]",
192
- side === "bottom" && "left-1/2 -translate-x-1/2 -bottom-2 translate-y-[100%]",
193
- side === "left" && "top-1/2 -translate-y-1/2 -left-2 -translate-x-[100%]",
194
- side === "right" && "top-1/2 -translate-y-1/2 -right-2 translate-x-[100%]"
200
+ side === "top" && "left-1/2 -translate-x-1/2 -top-2 -translate-y-full",
201
+ side === "bottom" && "left-1/2 -translate-x-1/2 -bottom-2 translate-y-full",
202
+ side === "left" && "top-1/2 -translate-y-1/2 -left-2 -translate-x-full",
203
+ side === "right" && "top-1/2 -translate-y-1/2 -right-2 translate-x-full"
195
204
  )
196
205
  },
197
206
  content
@@ -629,23 +638,456 @@ var Modal = ({ isOpen, onClose, title, children, className }) => {
629
638
  };
630
639
  Modal.displayName = "Modal";
631
640
 
641
+ // src/components/Stepper.tsx
642
+ import React18 from "react";
643
+ import { Check as Check2 } from "lucide-react";
644
+ function cx18(...classes) {
645
+ return classes.filter(Boolean).join(" ");
646
+ }
647
+ var Stepper = React18.forwardRef(
648
+ ({ steps, currentStep, status, className, ...rest }, ref) => {
649
+ const currentIndex = steps.findIndex((step) => step.id === currentStep);
650
+ const getStepState = (index) => {
651
+ if (index < currentIndex) {
652
+ return "complete";
653
+ }
654
+ if (index === currentIndex) {
655
+ return status || "current";
656
+ }
657
+ return "future";
658
+ };
659
+ return /* @__PURE__ */ React18.createElement(
660
+ "div",
661
+ {
662
+ ref,
663
+ className: cx18("flex items-center w-full", className),
664
+ ...rest
665
+ },
666
+ steps.map((step, index) => {
667
+ const state = getStepState(index);
668
+ const isLast = index === steps.length - 1;
669
+ return /* @__PURE__ */ React18.createElement(React18.Fragment, { key: step.id }, /* @__PURE__ */ React18.createElement("div", { className: "flex flex-col items-center" }, /* @__PURE__ */ React18.createElement(
670
+ "div",
671
+ {
672
+ className: cx18(
673
+ "flex items-center justify-center w-10 h-10 rounded-full border-2 font-semibold text-sm transition-all duration-200",
674
+ state === "complete" && "bg-gold border-gold text-obsidian",
675
+ state === "current" && "bg-charcoal border-gold text-gold",
676
+ state === "error" && "bg-error border-error text-white",
677
+ state === "future" && "bg-charcoal border-ash text-silver"
678
+ )
679
+ },
680
+ state === "complete" ? /* @__PURE__ */ React18.createElement(Check2, { className: "h-5 w-5" }) : /* @__PURE__ */ React18.createElement("span", null, index + 1)
681
+ ), /* @__PURE__ */ React18.createElement(
682
+ "span",
683
+ {
684
+ className: cx18(
685
+ "mt-2 text-xs font-medium",
686
+ state === "complete" && "text-gold",
687
+ state === "current" && "text-white",
688
+ state === "error" && "text-error",
689
+ state === "future" && "text-silver"
690
+ )
691
+ },
692
+ step.label
693
+ )), !isLast && /* @__PURE__ */ React18.createElement(
694
+ "div",
695
+ {
696
+ className: cx18(
697
+ "flex-1 h-0.5 mx-4 transition-all duration-200",
698
+ index < currentIndex ? "bg-gold" : "bg-ash"
699
+ )
700
+ }
701
+ ));
702
+ })
703
+ );
704
+ }
705
+ );
706
+ Stepper.displayName = "Stepper";
707
+
708
+ // src/components/Message.tsx
709
+ import React21 from "react";
710
+
711
+ // src/components/MarkdownContent.tsx
712
+ import React19, { useMemo } from "react";
713
+ import DOMPurify from "dompurify";
714
+ function cx19(...classes) {
715
+ return classes.filter(Boolean).join(" ");
716
+ }
717
+ var DEFAULT_SANITIZE_CONFIG = {
718
+ ALLOWED_TAGS: [
719
+ "h1",
720
+ "h2",
721
+ "h3",
722
+ "h4",
723
+ "h5",
724
+ "h6",
725
+ "p",
726
+ "br",
727
+ "hr",
728
+ "strong",
729
+ "b",
730
+ "em",
731
+ "i",
732
+ "u",
733
+ "s",
734
+ "strike",
735
+ "del",
736
+ "ins",
737
+ "sup",
738
+ "sub",
739
+ "mark",
740
+ "small",
741
+ "ul",
742
+ "ol",
743
+ "li",
744
+ "a",
745
+ "code",
746
+ "pre",
747
+ "kbd",
748
+ "samp",
749
+ "var",
750
+ "blockquote",
751
+ "q",
752
+ "cite",
753
+ "abbr",
754
+ "table",
755
+ "thead",
756
+ "tbody",
757
+ "tfoot",
758
+ "tr",
759
+ "th",
760
+ "td",
761
+ "caption",
762
+ "colgroup",
763
+ "col",
764
+ "div",
765
+ "span",
766
+ "details",
767
+ "summary"
768
+ ],
769
+ ALLOWED_ATTR: [
770
+ "href",
771
+ "title",
772
+ "target",
773
+ "rel",
774
+ "class",
775
+ "id",
776
+ "colspan",
777
+ "rowspan",
778
+ "scope",
779
+ "open"
780
+ ],
781
+ ADD_ATTR: ["target", "rel"],
782
+ ALLOWED_URI_REGEXP: /^(?:(?:https?|mailto|tel):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i
783
+ };
784
+ function useDOMPurifySetup() {
785
+ useMemo(() => {
786
+ DOMPurify.addHook("afterSanitizeAttributes", (node) => {
787
+ if (node.tagName === "A") {
788
+ node.setAttribute("target", "_blank");
789
+ node.setAttribute("rel", "noopener noreferrer");
790
+ }
791
+ });
792
+ }, []);
793
+ }
794
+ var MarkdownContent = React19.forwardRef(
795
+ ({ className, content, sanitizeConfig, ...rest }, ref) => {
796
+ useDOMPurifySetup();
797
+ const sanitizedHtml = useMemo(() => {
798
+ if (!content) {
799
+ return "";
800
+ }
801
+ const config = sanitizeConfig ?? DEFAULT_SANITIZE_CONFIG;
802
+ return DOMPurify.sanitize(content, config);
803
+ }, [content, sanitizeConfig]);
804
+ return /* @__PURE__ */ React19.createElement(
805
+ "div",
806
+ {
807
+ ref,
808
+ className: cx19("prose", className),
809
+ dangerouslySetInnerHTML: { __html: sanitizedHtml },
810
+ ...rest
811
+ }
812
+ );
813
+ }
814
+ );
815
+ MarkdownContent.displayName = "MarkdownContent";
816
+
817
+ // src/components/StreamingCursor.tsx
818
+ import React20 from "react";
819
+ function cx20(...classes) {
820
+ return classes.filter(Boolean).join(" ");
821
+ }
822
+ var StreamingCursor = React20.forwardRef(
823
+ ({ className, variant = "line", ...rest }, ref) => {
824
+ const variantStyles3 = {
825
+ block: "w-2.5 h-cursor translate-y-cursor-offset",
826
+ line: "w-0.5 h-cursor translate-y-cursor-offset",
827
+ underscore: "w-2.5 h-0.5 self-end mb-0.5"
828
+ };
829
+ return /* @__PURE__ */ React20.createElement(
830
+ "span",
831
+ {
832
+ ref,
833
+ className: cx20(
834
+ "inline-block bg-current animate-cursor-blink",
835
+ variantStyles3[variant],
836
+ className
837
+ ),
838
+ "aria-hidden": "true",
839
+ ...rest
840
+ }
841
+ );
842
+ }
843
+ );
844
+ StreamingCursor.displayName = "StreamingCursor";
845
+
846
+ // src/components/Message.tsx
847
+ function cx21(...classes) {
848
+ return classes.filter(Boolean).join(" ");
849
+ }
850
+ var variantStyles2 = {
851
+ user: "bg-gold text-obsidian ml-auto",
852
+ assistant: "bg-charcoal border border-ash text-white mr-auto"
853
+ };
854
+ var Message = React21.forwardRef(
855
+ ({ variant = "assistant", className, content, isStreaming, ...rest }, ref) => {
856
+ const isUser = variant === "user";
857
+ return /* @__PURE__ */ React21.createElement(
858
+ "div",
859
+ {
860
+ ref,
861
+ className: cx21(
862
+ "px-3 py-2 w-fit",
863
+ variantStyles2[variant],
864
+ className
865
+ ),
866
+ ...rest
867
+ },
868
+ /* @__PURE__ */ React21.createElement(
869
+ MarkdownContent,
870
+ {
871
+ content,
872
+ className: cx21("prose-sm", isUser ? "prose-inherit" : "prose-invert")
873
+ }
874
+ ),
875
+ isStreaming && /* @__PURE__ */ React21.createElement(StreamingCursor, { className: "ml-0.5" })
876
+ );
877
+ }
878
+ );
879
+ Message.displayName = "Message";
880
+
881
+ // src/components/ChatHistory.tsx
882
+ import React22 from "react";
883
+ function cx22(...classes) {
884
+ return classes.filter(Boolean).join(" ");
885
+ }
886
+ var ChatHistory = React22.forwardRef(
887
+ ({ messages, className, ...rest }, ref) => {
888
+ return /* @__PURE__ */ React22.createElement(
889
+ "div",
890
+ {
891
+ ref,
892
+ className: cx22("flex flex-col gap-3 w-full", className),
893
+ ...rest
894
+ },
895
+ messages.map(({ id, variant, className: messageClassName, ...messageProps }, index) => /* @__PURE__ */ React22.createElement(
896
+ Message,
897
+ {
898
+ key: id ?? index,
899
+ variant,
900
+ className: messageClassName,
901
+ ...messageProps
902
+ }
903
+ ))
904
+ );
905
+ }
906
+ );
907
+ ChatHistory.displayName = "ChatHistory";
908
+
909
+ // src/components/BrandIcon.tsx
910
+ import React23 from "react";
911
+ function cx23(...classes) {
912
+ return classes.filter(Boolean).join(" ");
913
+ }
914
+ var sizeMap2 = {
915
+ sm: "h-8 w-8 text-sm",
916
+ md: "h-12 w-12 text-base",
917
+ lg: "h-16 w-16 text-lg"
918
+ };
919
+ var BrandIcon = React23.forwardRef(
920
+ ({ size = "md", variant = "solid", children, className, ...rest }, ref) => {
921
+ const variantClasses = variant === "solid" ? "bg-gold text-obsidian border-2 border-gold" : "bg-transparent text-gold border-2 border-gold";
922
+ return /* @__PURE__ */ React23.createElement(
923
+ "div",
924
+ {
925
+ ref,
926
+ className: cx23(
927
+ "inline-flex items-center justify-center rounded-none font-bold select-none overflow-hidden",
928
+ sizeMap2[size],
929
+ variantClasses,
930
+ className
931
+ ),
932
+ ...rest
933
+ },
934
+ children
935
+ );
936
+ }
937
+ );
938
+ BrandIcon.displayName = "BrandIcon";
939
+
940
+ // src/components/ColorSwatch.tsx
941
+ import React24 from "react";
942
+ function cx24(...classes) {
943
+ return classes.filter(Boolean).join(" ");
944
+ }
945
+ var ColorSwatch = React24.forwardRef(
946
+ ({ color, label, className, ...rest }, ref) => {
947
+ return /* @__PURE__ */ React24.createElement(
948
+ "div",
949
+ {
950
+ ref,
951
+ className: cx24("flex flex-col items-center gap-2", className),
952
+ ...rest
953
+ },
954
+ /* @__PURE__ */ React24.createElement(
955
+ "div",
956
+ {
957
+ className: "h-16 w-16 border-2 border-ash rounded-none shadow-sm",
958
+ style: { backgroundColor: color },
959
+ "aria-label": label || color
960
+ }
961
+ ),
962
+ label && /* @__PURE__ */ React24.createElement("span", { className: "text-xs text-silver font-medium" }, label)
963
+ );
964
+ }
965
+ );
966
+ ColorSwatch.displayName = "ColorSwatch";
967
+
968
+ // src/components/ImageCard.tsx
969
+ import React25 from "react";
970
+ var ASPECT_RATIO_PRESETS = {
971
+ landscape: "3 / 2",
972
+ portrait: "2 / 3",
973
+ square: "1 / 1"
974
+ };
975
+ function resolveAspectRatio(ratio) {
976
+ if (ratio in ASPECT_RATIO_PRESETS) {
977
+ return ASPECT_RATIO_PRESETS[ratio];
978
+ }
979
+ return ratio.replace("/", " / ");
980
+ }
981
+ function cx25(...classes) {
982
+ return classes.filter(Boolean).join(" ");
983
+ }
984
+ var ImageCard = React25.forwardRef(
985
+ ({
986
+ src,
987
+ alt,
988
+ title,
989
+ subtitle,
990
+ aspectRatio,
991
+ objectFit = "cover",
992
+ overlay,
993
+ mediaClassName,
994
+ contentClassName,
995
+ className,
996
+ children,
997
+ ...props
998
+ }, ref) => {
999
+ const hasAspectRatio = aspectRatio !== void 0;
1000
+ const isContain = objectFit === "contain";
1001
+ return /* @__PURE__ */ React25.createElement(Card, { ref, className: cx25("p-0 overflow-hidden group w-fit", className), ...props }, /* @__PURE__ */ React25.createElement(
1002
+ "div",
1003
+ {
1004
+ className: cx25(
1005
+ "relative",
1006
+ hasAspectRatio && "overflow-hidden",
1007
+ mediaClassName
1008
+ ),
1009
+ style: hasAspectRatio ? { aspectRatio: resolveAspectRatio(aspectRatio) } : void 0
1010
+ },
1011
+ /* @__PURE__ */ React25.createElement(
1012
+ "img",
1013
+ {
1014
+ src,
1015
+ alt,
1016
+ className: cx25(
1017
+ "block max-w-full",
1018
+ hasAspectRatio && "w-full h-full",
1019
+ hasAspectRatio && (isContain ? "object-contain" : "object-cover"),
1020
+ !hasAspectRatio && "h-auto"
1021
+ )
1022
+ }
1023
+ ),
1024
+ overlay && /* @__PURE__ */ React25.createElement(
1025
+ "div",
1026
+ {
1027
+ className: "absolute inset-0 bg-obsidian/80 opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex items-center justify-center"
1028
+ },
1029
+ overlay
1030
+ )
1031
+ ), (title || subtitle || children) && /* @__PURE__ */ React25.createElement("div", { className: cx25("px-4 pt-4", contentClassName) }, title && /* @__PURE__ */ React25.createElement("h4", { className: "text-lg font-semibold leading-tight" }, title), subtitle && /* @__PURE__ */ React25.createElement("p", { className: "text-sm text-silver leading-normal" }, subtitle), children));
1032
+ }
1033
+ );
1034
+ ImageCard.displayName = "ImageCard";
1035
+
1036
+ // src/components/SectionHeading.tsx
1037
+ import React26 from "react";
1038
+ function cx26(...classes) {
1039
+ return classes.filter(Boolean).join(" ");
1040
+ }
1041
+ var levelStyles = {
1042
+ h2: "text-2xl mb-4",
1043
+ h3: "text-xl mb-3"
1044
+ };
1045
+ var SectionHeading = React26.forwardRef(
1046
+ ({ level = "h2", children, className, ...rest }, ref) => {
1047
+ const Component = level;
1048
+ return /* @__PURE__ */ React26.createElement(
1049
+ Component,
1050
+ {
1051
+ ref,
1052
+ className: cx26(
1053
+ "text-gold font-semibold tracking-tight",
1054
+ levelStyles[level],
1055
+ className
1056
+ ),
1057
+ ...rest
1058
+ },
1059
+ children
1060
+ );
1061
+ }
1062
+ );
1063
+ SectionHeading.displayName = "SectionHeading";
1064
+
632
1065
  // src/index.ts
633
1066
  var version = "2.0.0";
634
1067
  export {
635
1068
  Alert,
636
1069
  Avatar,
637
1070
  Badge,
1071
+ BrandIcon,
638
1072
  Button,
639
1073
  Card,
1074
+ ChatHistory,
640
1075
  Checkbox,
1076
+ ColorSwatch,
641
1077
  HelperText,
1078
+ ImageCard,
642
1079
  Input,
643
1080
  Label,
1081
+ MarkdownContent,
1082
+ Message,
644
1083
  Modal,
645
1084
  Radio,
1085
+ SectionHeading,
646
1086
  Select,
647
1087
  Skeleton,
648
1088
  Spinner,
1089
+ Stepper,
1090
+ StreamingCursor,
649
1091
  Switch,
650
1092
  Textarea,
651
1093
  Tooltip,