koishi-plugin-chatluna-affinity 0.3.1 → 0.3.2

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.
@@ -0,0 +1,30 @@
1
+ <!--
2
+ 好感度仪表盘页面
3
+ 作为 Koishi ctx.page 的 Vue 外壳挂载 React 仪表盘
4
+ -->
5
+ <template>
6
+ <k-layout container="page-affinity-dashboard-shell" main="page-affinity-dashboard">
7
+ <k-content>
8
+ <div ref="dashboardRoot" />
9
+ </k-content>
10
+ </k-layout>
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
15
+ import { mountAffinityDashboard } from './dashboard/mount'
16
+
17
+ const dashboardRoot = ref<HTMLElement | null>(null)
18
+ let disposeDashboard: (() => void) | null = null
19
+
20
+ onMounted(async () => {
21
+ await nextTick()
22
+ if (!dashboardRoot.value) return
23
+ disposeDashboard = mountAffinityDashboard(dashboardRoot.value)
24
+ })
25
+
26
+ onBeforeUnmount(() => {
27
+ disposeDashboard?.()
28
+ disposeDashboard = null
29
+ })
30
+ </script>
@@ -0,0 +1,50 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import * as React from "react";
3
+ import { cn } from "../../lib/utils";
4
+
5
+ const alertVariants = cva(
6
+ "relative w-full rounded-2xl border p-4 text-sm [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg~*]:pl-7",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "bg-background text-foreground",
11
+ destructive:
12
+ "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
13
+ },
14
+ },
15
+ defaultVariants: {
16
+ variant: "default",
17
+ },
18
+ },
19
+ );
20
+
21
+ function Alert({
22
+ className,
23
+ variant,
24
+ ...props
25
+ }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
26
+ return (
27
+ <div
28
+ role="alert"
29
+ className={cn(alertVariants({ variant, className }))}
30
+ {...props}
31
+ />
32
+ );
33
+ }
34
+
35
+ function AlertTitle({ className, ...props }: React.ComponentProps<"h5">) {
36
+ return (
37
+ <h5 className={cn("mb-1 font-medium leading-none", className)} {...props} />
38
+ );
39
+ }
40
+
41
+ function AlertDescription({ className, ...props }: React.ComponentProps<"div">) {
42
+ return (
43
+ <div
44
+ className={cn("text-sm [&_p]:leading-relaxed", className)}
45
+ {...props}
46
+ />
47
+ );
48
+ }
49
+
50
+ export { Alert, AlertTitle, AlertDescription };
@@ -0,0 +1,47 @@
1
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
2
+ import * as React from "react";
3
+ import { cn } from "../../lib/utils";
4
+
5
+ function Avatar({
6
+ className,
7
+ ...props
8
+ }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
9
+ return (
10
+ <AvatarPrimitive.Root
11
+ className={cn(
12
+ "relative flex h-8 w-8 shrink-0 overflow-hidden rounded-full",
13
+ className,
14
+ )}
15
+ {...props}
16
+ />
17
+ );
18
+ }
19
+
20
+ function AvatarImage({
21
+ className,
22
+ ...props
23
+ }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
24
+ return (
25
+ <AvatarPrimitive.Image
26
+ className={cn("aspect-square h-full w-full", className)}
27
+ {...props}
28
+ />
29
+ );
30
+ }
31
+
32
+ function AvatarFallback({
33
+ className,
34
+ ...props
35
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
36
+ return (
37
+ <AvatarPrimitive.Fallback
38
+ className={cn(
39
+ "flex h-full w-full items-center justify-center rounded-full bg-muted text-xs",
40
+ className,
41
+ )}
42
+ {...props}
43
+ />
44
+ );
45
+ }
46
+
47
+ export { Avatar, AvatarImage, AvatarFallback };
@@ -0,0 +1,35 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import * as React from "react";
3
+ import { cn } from "../../lib/utils";
4
+
5
+ const badgeVariants = cva(
6
+ "inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium transition-colors",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default:
11
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
12
+ secondary:
13
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
14
+ destructive:
15
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
16
+ outline: "text-foreground",
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ variant: "default",
21
+ },
22
+ },
23
+ );
24
+
25
+ function Badge({
26
+ className,
27
+ variant,
28
+ ...props
29
+ }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
30
+ return (
31
+ <span className={cn(badgeVariants({ variant, className }))} {...props} />
32
+ );
33
+ }
34
+
35
+ export { Badge, badgeVariants };
@@ -0,0 +1,53 @@
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import * as React from "react";
4
+ import { cn } from "../../lib/utils";
5
+
6
+ const buttonVariants = cva(
7
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
12
+ destructive:
13
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
14
+ outline:
15
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
16
+ secondary:
17
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
18
+ ghost: "hover:bg-accent hover:text-accent-foreground",
19
+ },
20
+ size: {
21
+ default: "h-10 px-4 py-2",
22
+ sm: "h-8 rounded-xl px-3 text-xs",
23
+ icon: "h-8 w-8 rounded-xl",
24
+ },
25
+ },
26
+ defaultVariants: {
27
+ variant: "default",
28
+ size: "default",
29
+ },
30
+ },
31
+ );
32
+
33
+ function Button({
34
+ className,
35
+ variant,
36
+ size,
37
+ asChild = false,
38
+ ...props
39
+ }: React.ComponentProps<"button"> &
40
+ VariantProps<typeof buttonVariants> & {
41
+ asChild?: boolean;
42
+ }) {
43
+ const Comp = asChild ? Slot : "button";
44
+
45
+ return (
46
+ <Comp
47
+ className={cn(buttonVariants({ variant, size, className }))}
48
+ {...props}
49
+ />
50
+ );
51
+ }
52
+
53
+ export { Button, buttonVariants };
@@ -0,0 +1,57 @@
1
+ import * as React from "react";
2
+ import { cn } from "../../lib/utils";
3
+
4
+ function Card({ className, ...props }: React.ComponentProps<"div">) {
5
+ return (
6
+ <div
7
+ className={cn(
8
+ "rounded-2xl border bg-card text-card-foreground shadow-sm",
9
+ className,
10
+ )}
11
+ {...props}
12
+ />
13
+ );
14
+ }
15
+
16
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
17
+ return (
18
+ <div className={cn("flex flex-col space-y-1.5 p-4", className)} {...props} />
19
+ );
20
+ }
21
+
22
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
23
+ return (
24
+ <div
25
+ className={cn("text-base font-semibold leading-none", className)}
26
+ {...props}
27
+ />
28
+ );
29
+ }
30
+
31
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
32
+ return (
33
+ <div
34
+ className={cn("text-sm text-muted-foreground", className)}
35
+ {...props}
36
+ />
37
+ );
38
+ }
39
+
40
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
41
+ return <div className={cn("p-4 pt-0", className)} {...props} />;
42
+ }
43
+
44
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
45
+ return (
46
+ <div className={cn("flex items-center p-4 pt-0", className)} {...props} />
47
+ );
48
+ }
49
+
50
+ export {
51
+ Card,
52
+ CardHeader,
53
+ CardFooter,
54
+ CardTitle,
55
+ CardDescription,
56
+ CardContent,
57
+ };
@@ -0,0 +1,136 @@
1
+ import * as React from "react";
2
+ import * as RechartsPrimitive from "recharts";
3
+ import { cn } from "../../lib/utils";
4
+
5
+ export type ChartConfig = Record<
6
+ string,
7
+ {
8
+ label?: React.ReactNode;
9
+ color?: string;
10
+ }
11
+ >;
12
+
13
+ const ChartContext = React.createContext<{ config: ChartConfig } | null>(null);
14
+
15
+ function useChart() {
16
+ const context = React.useContext(ChartContext);
17
+ if (!context) {
18
+ throw new Error("useChart must be used within a <ChartContainer />");
19
+ }
20
+ return context;
21
+ }
22
+
23
+ function ChartContainer({
24
+ id,
25
+ className,
26
+ children,
27
+ config,
28
+ ...props
29
+ }: React.ComponentProps<"div"> & {
30
+ config: ChartConfig;
31
+ children: React.ReactNode;
32
+ }) {
33
+ const uniqueId = React.useId();
34
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
35
+
36
+ return (
37
+ <ChartContext.Provider value={{ config }}>
38
+ <div
39
+ data-chart={chartId}
40
+ className={cn(
41
+ "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line]:stroke-border/70 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
42
+ className,
43
+ )}
44
+ {...props}
45
+ >
46
+ <ChartStyle id={chartId} config={config} />
47
+ <RechartsPrimitive.ResponsiveContainer>
48
+ {children as React.ReactElement}
49
+ </RechartsPrimitive.ResponsiveContainer>
50
+ </div>
51
+ </ChartContext.Provider>
52
+ );
53
+ }
54
+
55
+ function ChartStyle({ id, config }: { id: string; config: ChartConfig }) {
56
+ const colorConfig = Object.entries(config).filter(([, item]) => item.color);
57
+
58
+ if (!colorConfig.length) return null;
59
+
60
+ return (
61
+ <style
62
+ dangerouslySetInnerHTML={{
63
+ __html: `
64
+ [data-chart=${id}] {
65
+ ${colorConfig
66
+ .map(([key, item]) => ` --color-${key}: ${item.color};`)
67
+ .join("\n")}
68
+ }
69
+ `,
70
+ }}
71
+ />
72
+ );
73
+ }
74
+
75
+ const ChartTooltip = RechartsPrimitive.Tooltip;
76
+
77
+ function ChartTooltipContent({
78
+ active,
79
+ payload,
80
+ label,
81
+ className,
82
+ hideLabel = false,
83
+ }: {
84
+ active?: boolean;
85
+ payload?: Array<{
86
+ color?: string;
87
+ dataKey?: string | number;
88
+ name?: string | number;
89
+ value?: unknown;
90
+ }>;
91
+ label?: React.ReactNode;
92
+ className?: string;
93
+ hideLabel?: boolean;
94
+ }) {
95
+ const { config } = useChart();
96
+
97
+ if (!active || !payload?.length) return null;
98
+
99
+ return (
100
+ <div
101
+ className={cn(
102
+ "grid min-w-32 gap-1.5 rounded-2xl border bg-background px-3 py-2 text-xs shadow-xl",
103
+ className,
104
+ )}
105
+ >
106
+ {!hideLabel && label ? (
107
+ <div className="font-medium text-foreground">{label}</div>
108
+ ) : null}
109
+ <div className="grid gap-1.5">
110
+ {payload.map((item) => {
111
+ const key = String(item.dataKey || item.name || "");
112
+ const itemConfig = config[key];
113
+ const color = item.color || itemConfig?.color;
114
+
115
+ return (
116
+ <div
117
+ className="flex min-w-0 items-center gap-2 text-muted-foreground"
118
+ key={key}
119
+ >
120
+ <span
121
+ className="h-2.5 w-2.5 shrink-0 rounded-[2px]"
122
+ style={{ backgroundColor: color }}
123
+ />
124
+ <span className="truncate">{itemConfig?.label || key}</span>
125
+ <span className="ml-auto font-mono font-medium text-foreground">
126
+ {String(item.value ?? "")}
127
+ </span>
128
+ </div>
129
+ );
130
+ })}
131
+ </div>
132
+ </div>
133
+ );
134
+ }
135
+
136
+ export { ChartContainer, ChartTooltip, ChartTooltipContent };
@@ -0,0 +1,23 @@
1
+ import { Toaster as Sonner, type ToasterProps } from "sonner";
2
+
3
+ function Toaster(props: ToasterProps) {
4
+ return (
5
+ <Sonner
6
+ className="toaster group"
7
+ toastOptions={{
8
+ classNames: {
9
+ toast:
10
+ "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
11
+ description: "group-[.toast]:text-muted-foreground",
12
+ actionButton:
13
+ "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
14
+ cancelButton:
15
+ "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
16
+ },
17
+ }}
18
+ {...props}
19
+ />
20
+ );
21
+ }
22
+
23
+ export { Toaster };
@@ -0,0 +1,77 @@
1
+ import * as React from "react";
2
+ import { cn } from "../../lib/utils";
3
+
4
+ function Table({ className, ...props }: React.ComponentProps<"table">) {
5
+ return (
6
+ <div className="relative w-full overflow-auto rounded-2xl border bg-background">
7
+ <table className={cn("w-full caption-bottom text-sm", className)} {...props} />
8
+ </div>
9
+ );
10
+ }
11
+
12
+ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
13
+ return <thead className={cn("[&_tr]:border-b", className)} {...props} />;
14
+ }
15
+
16
+ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
17
+ return (
18
+ <tbody className={cn("[&_tr:last-child]:border-0", className)} {...props} />
19
+ );
20
+ }
21
+
22
+ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
23
+ return (
24
+ <tfoot
25
+ className={cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className)}
26
+ {...props}
27
+ />
28
+ );
29
+ }
30
+
31
+ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
32
+ return (
33
+ <tr
34
+ className={cn(
35
+ "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
36
+ className,
37
+ )}
38
+ {...props}
39
+ />
40
+ );
41
+ }
42
+
43
+ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
44
+ return (
45
+ <th
46
+ className={cn(
47
+ "h-10 px-3 text-left align-middle font-medium text-muted-foreground",
48
+ className,
49
+ )}
50
+ {...props}
51
+ />
52
+ );
53
+ }
54
+
55
+ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
56
+ return <td className={cn("p-3 align-middle", className)} {...props} />;
57
+ }
58
+
59
+ function TableCaption({ className, ...props }: React.ComponentProps<"caption">) {
60
+ return (
61
+ <caption
62
+ className={cn("mt-4 text-sm text-muted-foreground", className)}
63
+ {...props}
64
+ />
65
+ );
66
+ }
67
+
68
+ export {
69
+ Table,
70
+ TableHeader,
71
+ TableBody,
72
+ TableFooter,
73
+ TableHead,
74
+ TableRow,
75
+ TableCell,
76
+ TableCaption,
77
+ };
@@ -0,0 +1,82 @@
1
+ import { Tabs as HeroTabs } from "@heroui/react/tabs";
2
+ import * as React from "react";
3
+ import { cn } from "../../lib/utils";
4
+
5
+ type TabsProps = Omit<
6
+ React.ComponentProps<typeof HeroTabs>,
7
+ "defaultSelectedKey" | "onSelectionChange" | "selectedKey"
8
+ > & {
9
+ defaultValue?: string;
10
+ onValueChange?: (value: string) => void;
11
+ value?: string;
12
+ };
13
+
14
+ function Tabs({
15
+ className,
16
+ defaultValue,
17
+ onValueChange,
18
+ value,
19
+ ...props
20
+ }: TabsProps) {
21
+ return (
22
+ <HeroTabs
23
+ className={cn("flex flex-col gap-4", className)}
24
+ defaultSelectedKey={defaultValue}
25
+ selectedKey={value}
26
+ onSelectionChange={(key) => onValueChange?.(String(key))}
27
+ {...props}
28
+ />
29
+ );
30
+ }
31
+
32
+ function TabsList({
33
+ className,
34
+ ...props
35
+ }: React.ComponentProps<typeof HeroTabs.List>) {
36
+ return (
37
+ <HeroTabs.ListContainer className="w-fit">
38
+ <HeroTabs.List
39
+ className={cn("w-fit shadow-none", className)}
40
+ {...props}
41
+ />
42
+ </HeroTabs.ListContainer>
43
+ );
44
+ }
45
+
46
+ function TabsTrigger({
47
+ className,
48
+ value,
49
+ children,
50
+ ...props
51
+ }: Omit<React.ComponentProps<typeof HeroTabs.Tab>, "id"> & {
52
+ value: string;
53
+ }) {
54
+ return (
55
+ <HeroTabs.Tab
56
+ className={cn("min-w-fit whitespace-nowrap px-4 text-sm", className)}
57
+ id={value}
58
+ {...props}
59
+ >
60
+ {children}
61
+ <HeroTabs.Indicator />
62
+ </HeroTabs.Tab>
63
+ );
64
+ }
65
+
66
+ function TabsContent({
67
+ className,
68
+ value,
69
+ ...props
70
+ }: Omit<React.ComponentProps<typeof HeroTabs.Panel>, "id"> & {
71
+ value: string;
72
+ }) {
73
+ return (
74
+ <HeroTabs.Panel
75
+ className={cn("mt-0 p-0 outline-none", className)}
76
+ id={value}
77
+ {...props}
78
+ />
79
+ );
80
+ }
81
+
82
+ export { Tabs, TabsList, TabsTrigger, TabsContent };