@windrun-huaiin/third-ui 15.1.0 → 16.0.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/LICENSE +1 -1
- package/dist/ai/ai-chat-composer.d.ts +2 -0
- package/dist/ai/ai-chat-composer.js +47 -0
- package/dist/ai/ai-chat-composer.mjs +45 -0
- package/dist/ai/ai-markdown.d.ts +2 -0
- package/dist/ai/ai-markdown.js +36 -0
- package/dist/ai/ai-markdown.mjs +34 -0
- package/dist/ai/ai-message-actions.d.ts +2 -0
- package/dist/ai/ai-message-actions.js +14 -0
- package/dist/ai/ai-message-actions.mjs +12 -0
- package/dist/ai/ai-message-bubble.d.ts +2 -0
- package/dist/ai/ai-message-bubble.js +66 -0
- package/dist/ai/ai-message-bubble.mjs +64 -0
- package/dist/ai/ai-message-content.d.ts +2 -0
- package/dist/ai/ai-message-content.js +63 -0
- package/dist/ai/ai-message-content.mjs +61 -0
- package/dist/ai/ai-message-list.d.ts +2 -0
- package/dist/ai/ai-message-list.js +24 -0
- package/dist/ai/ai-message-list.mjs +22 -0
- package/dist/ai/ai-message-meta.d.ts +2 -0
- package/dist/ai/ai-message-meta.js +38 -0
- package/dist/ai/ai-message-meta.mjs +36 -0
- package/dist/ai/ai-status-indicator.d.ts +2 -0
- package/dist/ai/ai-status-indicator.js +51 -0
- package/dist/ai/ai-status-indicator.mjs +49 -0
- package/dist/ai/index.d.ts +11 -0
- package/dist/ai/index.js +33 -0
- package/dist/ai/index.mjs +11 -0
- package/dist/ai/types.d.ts +110 -0
- package/dist/ai/use-ai-conversation.d.ts +13 -0
- package/dist/ai/use-ai-conversation.js +276 -0
- package/dist/ai/use-ai-conversation.mjs +274 -0
- package/dist/clerk/clerk-organization-client.js +2 -2
- package/dist/clerk/clerk-organization-client.mjs +2 -2
- package/dist/clerk/clerk-page-generator.d.ts +1 -1
- package/dist/clerk/clerk-user-client.js +2 -2
- package/dist/clerk/clerk-user-client.mjs +2 -2
- package/dist/clerk/fingerprint/fingerprint-provider.js +9 -9
- package/dist/clerk/fingerprint/fingerprint-provider.mjs +9 -9
- package/dist/fuma/base/custom-header.js +4 -4
- package/dist/fuma/base/custom-header.mjs +4 -4
- package/dist/fuma/mdx/banner.js +3 -3
- package/dist/fuma/mdx/banner.mjs +3 -3
- package/dist/fuma/mdx/fuma-github-info.js +3 -3
- package/dist/fuma/mdx/fuma-github-info.mjs +3 -3
- package/dist/fuma/mdx/gradient-button.js +3 -3
- package/dist/fuma/mdx/gradient-button.mjs +3 -3
- package/dist/fuma/mdx/index.d.ts +1 -0
- package/dist/fuma/mdx/index.js +2 -0
- package/dist/fuma/mdx/index.mjs +1 -0
- package/dist/fuma/mdx/markdown-component-map.d.ts +3 -0
- package/dist/fuma/mdx/markdown-component-map.js +73 -0
- package/dist/fuma/mdx/markdown-component-map.mjs +71 -0
- package/dist/fuma/mdx/mermaid.d.ts +2 -1
- package/dist/fuma/mdx/mermaid.js +130 -6
- package/dist/fuma/mdx/mermaid.mjs +130 -6
- package/dist/fuma/mdx/toc-base.js +4 -4
- package/dist/fuma/mdx/toc-base.mjs +4 -4
- package/dist/fuma/mdx/trophy-card.js +2 -2
- package/dist/fuma/mdx/trophy-card.mjs +2 -2
- package/dist/fuma/mdx/zia-card.js +3 -3
- package/dist/fuma/mdx/zia-card.mjs +3 -3
- package/dist/fuma/mdx/zia-file.js +3 -3
- package/dist/fuma/mdx/zia-file.mjs +3 -3
- package/dist/main/ads-alert-dialog.js +2 -2
- package/dist/main/ads-alert-dialog.mjs +2 -2
- package/dist/main/credit/credit-nav-button.js +2 -2
- package/dist/main/credit/credit-nav-button.mjs +2 -2
- package/dist/main/credit/credit-overview-client.js +4 -4
- package/dist/main/credit/credit-overview-client.mjs +4 -4
- package/dist/main/footer.js +2 -2
- package/dist/main/footer.mjs +2 -2
- package/dist/main/go-to-top.js +2 -2
- package/dist/main/go-to-top.mjs +2 -2
- package/dist/main/hero-media.d.ts +14 -0
- package/dist/main/hero-media.js +12 -0
- package/dist/main/hero-media.mjs +10 -0
- package/dist/main/hero-section.d.ts +10 -0
- package/dist/main/hero-section.js +11 -0
- package/dist/main/hero-section.mjs +9 -0
- package/dist/main/index.d.ts +3 -0
- package/dist/main/index.js +6 -0
- package/dist/main/index.mjs +3 -0
- package/dist/main/info-tooltip.d.ts +8 -0
- package/dist/main/info-tooltip.js +48 -0
- package/dist/main/info-tooltip.mjs +46 -0
- package/dist/main/pill-select/x-pill-select.js +2 -2
- package/dist/main/pill-select/x-pill-select.mjs +2 -2
- package/dist/main/pill-select/x-token-input.js +2 -2
- package/dist/main/pill-select/x-token-input.mjs +2 -2
- package/dist/main/x-button.js +3 -3
- package/dist/main/x-button.mjs +3 -3
- package/package.json +16 -3
- package/src/ai/ai-chat-composer.tsx +187 -0
- package/src/ai/ai-markdown.tsx +45 -0
- package/src/ai/ai-message-actions.tsx +16 -0
- package/src/ai/ai-message-bubble.tsx +138 -0
- package/src/ai/ai-message-content.tsx +149 -0
- package/src/ai/ai-message-list.tsx +59 -0
- package/src/ai/ai-message-meta.tsx +56 -0
- package/src/ai/ai-status-indicator.tsx +61 -0
- package/src/ai/index.ts +13 -0
- package/src/ai/types.ts +131 -0
- package/src/ai/use-ai-conversation.ts +422 -0
- package/src/clerk/clerk-organization-client.tsx +5 -5
- package/src/clerk/clerk-page-generator.tsx +1 -1
- package/src/clerk/clerk-user-client.tsx +4 -4
- package/src/clerk/fingerprint/fingerprint-provider.tsx +34 -22
- package/src/fuma/base/custom-header.tsx +5 -5
- package/src/fuma/mdx/banner.tsx +3 -3
- package/src/fuma/mdx/fuma-github-info.tsx +4 -4
- package/src/fuma/mdx/gradient-button.tsx +3 -3
- package/src/fuma/mdx/index.ts +2 -1
- package/src/fuma/mdx/markdown-component-map.tsx +174 -0
- package/src/fuma/mdx/mermaid.tsx +145 -10
- package/src/fuma/mdx/toc-base.tsx +5 -5
- package/src/fuma/mdx/trophy-card.tsx +2 -2
- package/src/fuma/mdx/zia-card.tsx +3 -3
- package/src/fuma/mdx/zia-file.tsx +3 -3
- package/src/main/ads-alert-dialog.tsx +5 -5
- package/src/main/credit/credit-nav-button.tsx +3 -3
- package/src/main/credit/credit-overview-client.tsx +15 -7
- package/src/main/features.tsx +5 -3
- package/src/main/footer.tsx +4 -5
- package/src/main/go-to-top.tsx +2 -2
- package/src/main/hero-media.tsx +53 -0
- package/src/main/hero-section.tsx +36 -0
- package/src/main/index.ts +5 -0
- package/src/main/info-tooltip.tsx +99 -0
- package/src/main/language-detector.tsx +4 -4
- package/src/main/pill-select/x-pill-select.tsx +2 -2
- package/src/main/pill-select/x-token-input.tsx +2 -2
- package/src/main/x-button.tsx +4 -4
package/src/fuma/mdx/banner.tsx
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { cva } from 'class-variance-authority';
|
|
4
4
|
import { type HTMLAttributes, useEffect, useState } from 'react';
|
|
5
|
-
import {
|
|
5
|
+
import { XIcon } from '@windrun-huaiin/base-ui/icons';
|
|
6
6
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
7
7
|
|
|
8
8
|
const buttonVariants = cva(
|
|
@@ -179,12 +179,12 @@ export function Banner({
|
|
|
179
179
|
buttonVariants({
|
|
180
180
|
color: 'ghost',
|
|
181
181
|
className:
|
|
182
|
-
'absolute
|
|
182
|
+
'absolute inset-e-2 top-1/2 -translate-y-1/2 text-neutral-600 dark:text-neutral-400',
|
|
183
183
|
size: 'icon',
|
|
184
184
|
}),
|
|
185
185
|
)}
|
|
186
186
|
>
|
|
187
|
-
<
|
|
187
|
+
<XIcon />
|
|
188
188
|
</button>
|
|
189
189
|
) : null}
|
|
190
190
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { ExternalLinkIcon, StarIcon } from '@windrun-huaiin/base-ui/icons';
|
|
5
5
|
|
|
6
6
|
interface FumaGithubInfoProps {
|
|
7
7
|
owner: string;
|
|
@@ -45,7 +45,7 @@ function GitHubInfoFallback({ owner, repo, className }: Pick<FumaGithubInfoProps
|
|
|
45
45
|
{owner}/{repo}
|
|
46
46
|
</p>
|
|
47
47
|
<p className="flex text-xs items-center gap-1 text-fd-muted-foreground">
|
|
48
|
-
<
|
|
48
|
+
<ExternalLinkIcon className="size-3" />
|
|
49
49
|
GitHub
|
|
50
50
|
</p>
|
|
51
51
|
</a>
|
|
@@ -76,7 +76,7 @@ function GitHubInfoSuccess({
|
|
|
76
76
|
{owner}/{repo}
|
|
77
77
|
</p>
|
|
78
78
|
<p className="flex text-xs items-center gap-1 text-fd-muted-foreground">
|
|
79
|
-
<
|
|
79
|
+
<StarIcon className="size-3" />
|
|
80
80
|
{humanizedStars}
|
|
81
81
|
</p>
|
|
82
82
|
</a>
|
|
@@ -191,4 +191,4 @@ export function FumaGithubInfo({ owner, repo, token, className }: FumaGithubInfo
|
|
|
191
191
|
className={className}
|
|
192
192
|
/>
|
|
193
193
|
);
|
|
194
|
-
}
|
|
194
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
4
|
-
import {
|
|
4
|
+
import { ArrowRightIcon, Loader2Icon } from "@windrun-huaiin/base-ui/icons";
|
|
5
5
|
import {
|
|
6
6
|
themeBgColor,
|
|
7
7
|
themeBorderColor,
|
|
@@ -106,7 +106,7 @@ export function GradientButton({
|
|
|
106
106
|
|
|
107
107
|
const iconNode = (() => {
|
|
108
108
|
if (isLoading) {
|
|
109
|
-
return <
|
|
109
|
+
return <Loader2Icon className={cn(finalIconClass, 'animate-spin')} />;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
if (iconProvided) {
|
|
@@ -123,7 +123,7 @@ export function GradientButton({
|
|
|
123
123
|
return icon;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
return <
|
|
126
|
+
return <ArrowRightIcon className={cn(finalIconClass)} />;
|
|
127
127
|
})();
|
|
128
128
|
|
|
129
129
|
const shouldRenderIcon = iconNode !== null && iconNode !== undefined;
|
package/src/fuma/mdx/index.ts
CHANGED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { cn } from '@windrun-huaiin/lib/utils';
|
|
2
|
+
import defaultMdxComponents from 'fumadocs-ui/mdx';
|
|
3
|
+
import { type ComponentType } from 'react';
|
|
4
|
+
import { ImageZoom } from './image-zoom';
|
|
5
|
+
|
|
6
|
+
export type MarkdownComponentMap = Record<string, ComponentType<any>>;
|
|
7
|
+
|
|
8
|
+
function normalizeMarkdownProps<T extends Record<string, any>>(props: T) {
|
|
9
|
+
const { class: legacyClassName, className, ...restProps } = props;
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
...restProps,
|
|
13
|
+
className: cn(
|
|
14
|
+
typeof legacyClassName === 'string' ? legacyClassName : undefined,
|
|
15
|
+
className,
|
|
16
|
+
),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const baseMarkdownComponents: MarkdownComponentMap = {
|
|
21
|
+
...defaultMdxComponents,
|
|
22
|
+
a: ({ className, ...props }) => (
|
|
23
|
+
<a
|
|
24
|
+
{...normalizeMarkdownProps(props)}
|
|
25
|
+
className={cn(
|
|
26
|
+
'underline underline-offset-4 transition hover:opacity-80',
|
|
27
|
+
normalizeMarkdownProps(props).className,
|
|
28
|
+
className,
|
|
29
|
+
)}
|
|
30
|
+
target={props.target ?? '_blank'}
|
|
31
|
+
rel={props.rel ?? 'noreferrer noopener'}
|
|
32
|
+
/>
|
|
33
|
+
),
|
|
34
|
+
blockquote: ({ className, ...props }) => (
|
|
35
|
+
<blockquote
|
|
36
|
+
{...normalizeMarkdownProps(props)}
|
|
37
|
+
className={cn(
|
|
38
|
+
'border-l-2 border-border pl-4 text-muted-foreground',
|
|
39
|
+
normalizeMarkdownProps(props).className,
|
|
40
|
+
className,
|
|
41
|
+
)}
|
|
42
|
+
/>
|
|
43
|
+
),
|
|
44
|
+
code: ({ className, ...props }) => (
|
|
45
|
+
<code
|
|
46
|
+
{...normalizeMarkdownProps(props)}
|
|
47
|
+
className={cn(
|
|
48
|
+
'rounded-md bg-muted px-1.5 py-0.5 font-mono text-[0.9em]',
|
|
49
|
+
normalizeMarkdownProps(props).className,
|
|
50
|
+
className,
|
|
51
|
+
)}
|
|
52
|
+
/>
|
|
53
|
+
),
|
|
54
|
+
h1: ({ className, ...props }) => (
|
|
55
|
+
<h1
|
|
56
|
+
{...normalizeMarkdownProps(props)}
|
|
57
|
+
className={cn('text-2xl font-semibold tracking-tight', normalizeMarkdownProps(props).className, className)}
|
|
58
|
+
/>
|
|
59
|
+
),
|
|
60
|
+
h2: ({ className, ...props }) => (
|
|
61
|
+
<h2
|
|
62
|
+
{...normalizeMarkdownProps(props)}
|
|
63
|
+
className={cn('text-xl font-semibold tracking-tight', normalizeMarkdownProps(props).className, className)}
|
|
64
|
+
/>
|
|
65
|
+
),
|
|
66
|
+
h3: ({ className, ...props }) => (
|
|
67
|
+
<h3
|
|
68
|
+
{...normalizeMarkdownProps(props)}
|
|
69
|
+
className={cn('text-lg font-semibold', normalizeMarkdownProps(props).className, className)}
|
|
70
|
+
/>
|
|
71
|
+
),
|
|
72
|
+
h4: ({ className, ...props }) => (
|
|
73
|
+
<h4
|
|
74
|
+
{...normalizeMarkdownProps(props)}
|
|
75
|
+
className={cn('text-base font-semibold', normalizeMarkdownProps(props).className, className)}
|
|
76
|
+
/>
|
|
77
|
+
),
|
|
78
|
+
h5: ({ className, ...props }) => (
|
|
79
|
+
<h5
|
|
80
|
+
{...normalizeMarkdownProps(props)}
|
|
81
|
+
className={cn('text-sm font-semibold', normalizeMarkdownProps(props).className, className)}
|
|
82
|
+
/>
|
|
83
|
+
),
|
|
84
|
+
h6: ({ className, ...props }) => (
|
|
85
|
+
<h6
|
|
86
|
+
{...normalizeMarkdownProps(props)}
|
|
87
|
+
className={cn('text-sm font-semibold', normalizeMarkdownProps(props).className, className)}
|
|
88
|
+
/>
|
|
89
|
+
),
|
|
90
|
+
hr: ({ className, ...props }) => (
|
|
91
|
+
<hr
|
|
92
|
+
{...normalizeMarkdownProps(props)}
|
|
93
|
+
className={cn('border-border', normalizeMarkdownProps(props).className, className)}
|
|
94
|
+
/>
|
|
95
|
+
),
|
|
96
|
+
img: ({ className, alt, src, ...props }) => (
|
|
97
|
+
<ImageZoom
|
|
98
|
+
{...normalizeMarkdownProps(props)}
|
|
99
|
+
alt={typeof alt === 'string' ? alt : ''}
|
|
100
|
+
src={typeof src === 'string' ? src : ''}
|
|
101
|
+
className={cn(
|
|
102
|
+
'overflow-hidden rounded-2xl',
|
|
103
|
+
normalizeMarkdownProps(props).className,
|
|
104
|
+
className,
|
|
105
|
+
)}
|
|
106
|
+
/>
|
|
107
|
+
),
|
|
108
|
+
li: ({ className, ...props }) => (
|
|
109
|
+
<li
|
|
110
|
+
{...normalizeMarkdownProps(props)}
|
|
111
|
+
className={cn('leading-7', normalizeMarkdownProps(props).className, className)}
|
|
112
|
+
/>
|
|
113
|
+
),
|
|
114
|
+
ol: ({ className, ...props }) => (
|
|
115
|
+
<ol
|
|
116
|
+
{...normalizeMarkdownProps(props)}
|
|
117
|
+
className={cn('list-decimal space-y-2 pl-6', normalizeMarkdownProps(props).className, className)}
|
|
118
|
+
/>
|
|
119
|
+
),
|
|
120
|
+
p: ({ className, ...props }) => (
|
|
121
|
+
<p
|
|
122
|
+
{...normalizeMarkdownProps(props)}
|
|
123
|
+
className={cn('leading-7', normalizeMarkdownProps(props).className, className)}
|
|
124
|
+
/>
|
|
125
|
+
),
|
|
126
|
+
pre: ({ className, ...props }) => (
|
|
127
|
+
<pre
|
|
128
|
+
{...normalizeMarkdownProps(props)}
|
|
129
|
+
className={cn(
|
|
130
|
+
'overflow-x-auto rounded-2xl border border-border bg-muted px-4 py-3 text-sm',
|
|
131
|
+
normalizeMarkdownProps(props).className,
|
|
132
|
+
className,
|
|
133
|
+
)}
|
|
134
|
+
/>
|
|
135
|
+
),
|
|
136
|
+
table: ({ className, ...props }) => (
|
|
137
|
+
<div className="overflow-x-auto">
|
|
138
|
+
<table
|
|
139
|
+
{...normalizeMarkdownProps(props)}
|
|
140
|
+
className={cn(
|
|
141
|
+
'w-full border-collapse text-sm',
|
|
142
|
+
normalizeMarkdownProps(props).className,
|
|
143
|
+
className,
|
|
144
|
+
)}
|
|
145
|
+
/>
|
|
146
|
+
</div>
|
|
147
|
+
),
|
|
148
|
+
td: ({ className, ...props }) => (
|
|
149
|
+
<td
|
|
150
|
+
{...normalizeMarkdownProps(props)}
|
|
151
|
+
className={cn(
|
|
152
|
+
'border border-border px-3 py-2 align-top',
|
|
153
|
+
normalizeMarkdownProps(props).className,
|
|
154
|
+
className,
|
|
155
|
+
)}
|
|
156
|
+
/>
|
|
157
|
+
),
|
|
158
|
+
th: ({ className, ...props }) => (
|
|
159
|
+
<th
|
|
160
|
+
{...normalizeMarkdownProps(props)}
|
|
161
|
+
className={cn(
|
|
162
|
+
'border border-border px-3 py-2 text-left font-medium',
|
|
163
|
+
normalizeMarkdownProps(props).className,
|
|
164
|
+
className,
|
|
165
|
+
)}
|
|
166
|
+
/>
|
|
167
|
+
),
|
|
168
|
+
ul: ({ className, ...props }) => (
|
|
169
|
+
<ul
|
|
170
|
+
{...normalizeMarkdownProps(props)}
|
|
171
|
+
className={cn('list-disc space-y-2 pl-6', normalizeMarkdownProps(props).className, className)}
|
|
172
|
+
/>
|
|
173
|
+
),
|
|
174
|
+
};
|
package/src/fuma/mdx/mermaid.tsx
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { DownloadIcon, MmdIcon, RefreshCcwIcon, XIcon } from '@windrun-huaiin/base-ui/icons';
|
|
4
4
|
// Attention: do not use external dialog library, avoid react context conflict when building third-party applications
|
|
5
5
|
import type { MermaidConfig } from 'mermaid';
|
|
6
6
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
7
7
|
import { useTheme } from 'next-themes';
|
|
8
|
+
import rough from 'roughjs';
|
|
8
9
|
import { useCallback, useEffect, useId, useRef, useState } from 'react';
|
|
9
10
|
import { themeIconColor, themeSvgIconColor } from '@windrun-huaiin/base-ui/lib';
|
|
10
11
|
|
|
@@ -20,13 +21,14 @@ interface MermaidProps {
|
|
|
20
21
|
title?: string;
|
|
21
22
|
watermarkEnabled?: boolean;
|
|
22
23
|
watermarkText?: string;
|
|
24
|
+
handDrawn?: boolean;
|
|
23
25
|
/**
|
|
24
26
|
* enable preview dialog by clicking the chart, default is true
|
|
25
27
|
*/
|
|
26
28
|
enablePreview?: boolean;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
export function Mermaid({ chart, title, watermarkEnabled, watermarkText, enablePreview = true }: MermaidProps) {
|
|
31
|
+
export function Mermaid({ chart, title, watermarkEnabled, watermarkText, handDrawn = true, enablePreview = true }: MermaidProps) {
|
|
30
32
|
const id = useId();
|
|
31
33
|
const [svg, setSvg] = useState('');
|
|
32
34
|
const { resolvedTheme } = useTheme();
|
|
@@ -62,9 +64,9 @@ export function Mermaid({ chart, title, watermarkEnabled, watermarkText, enableP
|
|
|
62
64
|
id.replaceAll(':', ''),
|
|
63
65
|
chart.replaceAll('\\n', '\n')
|
|
64
66
|
);
|
|
65
|
-
let svgWithWatermark = svg;
|
|
67
|
+
let svgWithWatermark = handDrawn ? applyHandDrawnStyle(svg) : svg;
|
|
66
68
|
if (watermarkEnabled && watermarkText) {
|
|
67
|
-
svgWithWatermark = addWatermarkToSvg(
|
|
69
|
+
svgWithWatermark = addWatermarkToSvg(svgWithWatermark, watermarkText, themeSvgIconColor);
|
|
68
70
|
}
|
|
69
71
|
if (isMounted) setSvg(svgWithWatermark);
|
|
70
72
|
} catch (error) {
|
|
@@ -75,7 +77,7 @@ export function Mermaid({ chart, title, watermarkEnabled, watermarkText, enableP
|
|
|
75
77
|
isMounted = false;
|
|
76
78
|
setSvg('');
|
|
77
79
|
};
|
|
78
|
-
}, [chart, id, resolvedTheme, watermarkEnabled, watermarkText]);
|
|
80
|
+
}, [chart, id, resolvedTheme, watermarkEnabled, watermarkText, handDrawn]);
|
|
79
81
|
|
|
80
82
|
// helpers for preview zoom
|
|
81
83
|
const clamp = (v: number, min: number, max: number) => Math.min(Math.max(v, min), max);
|
|
@@ -247,7 +249,7 @@ export function Mermaid({ chart, title, watermarkEnabled, watermarkText, enableP
|
|
|
247
249
|
<div
|
|
248
250
|
className={cn("mt-2 flex items-center justify-center text-center text-[13px] font-italic", themeIconColor)}
|
|
249
251
|
>
|
|
250
|
-
<
|
|
252
|
+
<MmdIcon className='mr-1 h-4 w-4' />
|
|
251
253
|
<span>{title}</span>
|
|
252
254
|
</div>
|
|
253
255
|
)}
|
|
@@ -270,7 +272,7 @@ export function Mermaid({ chart, title, watermarkEnabled, watermarkText, enableP
|
|
|
270
272
|
{/* Top bar */}
|
|
271
273
|
<div className="flex items-center justify-between gap-3 px-3 py-2 border-b border-neutral-200 dark:border-neutral-700">
|
|
272
274
|
<div className={cn("min-w-0 flex items-center gap-2 text-sm", themeIconColor)}>
|
|
273
|
-
<
|
|
275
|
+
<MmdIcon className="h-4 w-4" />
|
|
274
276
|
<span className="truncate max-w-[50vw]">{title ?? 'Mermaid Preview'}</span>
|
|
275
277
|
</div>
|
|
276
278
|
<div className="flex shrink-0 items-center gap-0.5">
|
|
@@ -324,21 +326,21 @@ export function Mermaid({ chart, title, watermarkEnabled, watermarkText, enableP
|
|
|
324
326
|
className={cn("ml-1 flex h-6 w-6 items-center justify-center rounded transition-colors hover:bg-neutral-100 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600", themeIconColor)}
|
|
325
327
|
onClick={resetTransform}
|
|
326
328
|
>
|
|
327
|
-
<
|
|
329
|
+
<RefreshCcwIcon className="h-3.5 w-3.5" />
|
|
328
330
|
</button>
|
|
329
331
|
<button
|
|
330
332
|
aria-label="Download SVG"
|
|
331
333
|
className={cn("ml-1 flex h-6 w-6 items-center justify-center rounded transition-colors hover:bg-neutral-100 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600", themeIconColor)}
|
|
332
334
|
onClick={handleDownload}
|
|
333
335
|
>
|
|
334
|
-
<
|
|
336
|
+
<DownloadIcon className="h-3.5 w-3.5" />
|
|
335
337
|
</button>
|
|
336
338
|
<button
|
|
337
339
|
aria-label="Close"
|
|
338
340
|
className={cn("ml-1 flex h-6 w-6 items-center justify-center rounded transition-colors hover:bg-neutral-100 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600", themeIconColor)}
|
|
339
341
|
onClick={() => { setOpen(false); resetTransform(); }}
|
|
340
342
|
>
|
|
341
|
-
<
|
|
343
|
+
<XIcon className="h-3.5 w-3.5" />
|
|
342
344
|
</button>
|
|
343
345
|
</div>
|
|
344
346
|
</div>
|
|
@@ -410,3 +412,136 @@ function addWatermarkToSvg(svg: string, watermark: string, watermarkColor: strin
|
|
|
410
412
|
`;
|
|
411
413
|
return svg.replace('</svg>', `${watermarkText}</svg>`);
|
|
412
414
|
}
|
|
415
|
+
|
|
416
|
+
function applyHandDrawnStyle(svg: string) {
|
|
417
|
+
if (typeof window === 'undefined') return svg;
|
|
418
|
+
|
|
419
|
+
try {
|
|
420
|
+
const parser = new DOMParser();
|
|
421
|
+
const doc = parser.parseFromString(svg, 'image/svg+xml');
|
|
422
|
+
const svgElement = doc.documentElement;
|
|
423
|
+
if (!svgElement || svgElement.tagName.toLowerCase() !== 'svg') return svg;
|
|
424
|
+
|
|
425
|
+
const rc = rough.svg(svgElement as unknown as SVGSVGElement);
|
|
426
|
+
const serializer = new XMLSerializer();
|
|
427
|
+
const getNumber = (value: string | null) => Number.parseFloat(value ?? '') || 0;
|
|
428
|
+
const getStyleValue = (element: Element, name: string) => {
|
|
429
|
+
const inlineStyle = element.getAttribute('style');
|
|
430
|
+
if (inlineStyle) {
|
|
431
|
+
const match = inlineStyle.match(new RegExp(`(?:^|;)\\s*${name}\\s*:\\s*([^;]+)`));
|
|
432
|
+
if (match?.[1]) return match[1].trim();
|
|
433
|
+
}
|
|
434
|
+
return element.getAttribute(name);
|
|
435
|
+
};
|
|
436
|
+
const applyAttributes = (source: Element, target: Element) => {
|
|
437
|
+
for (const attr of source.getAttributeNames()) {
|
|
438
|
+
if (attr === 'x' || attr === 'y' || attr === 'x1' || attr === 'y1' || attr === 'x2' || attr === 'y2' || attr === 'width' || attr === 'height' || attr === 'rx' || attr === 'ry' || attr === 'points' || attr === 'd') continue;
|
|
439
|
+
target.setAttribute(attr, source.getAttribute(attr) ?? '');
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
const createOptions = (element: Element) => {
|
|
443
|
+
const stroke = getStyleValue(element, 'stroke') ?? '#000';
|
|
444
|
+
const fill = getStyleValue(element, 'fill') ?? 'none';
|
|
445
|
+
const strokeWidth = getNumber(getStyleValue(element, 'stroke-width')) || 1.5;
|
|
446
|
+
return {
|
|
447
|
+
stroke,
|
|
448
|
+
fill: fill === 'none' ? undefined : fill,
|
|
449
|
+
strokeWidth,
|
|
450
|
+
roughness: 1.6,
|
|
451
|
+
bowing: 1.25,
|
|
452
|
+
fillStyle: fill === 'none' ? 'hachure' : 'solid',
|
|
453
|
+
fillWeight: 0.8,
|
|
454
|
+
hachureGap: 10,
|
|
455
|
+
preserveVertices: true,
|
|
456
|
+
seed: 7,
|
|
457
|
+
};
|
|
458
|
+
};
|
|
459
|
+
const replaceShape = (element: Element, node: SVGElement | SVGGElement | null) => {
|
|
460
|
+
if (!node || !element.parentNode) return;
|
|
461
|
+
applyAttributes(element, node);
|
|
462
|
+
if (element.getAttribute('class')) {
|
|
463
|
+
node.setAttribute('class', element.getAttribute('class') ?? '');
|
|
464
|
+
}
|
|
465
|
+
if (element.getAttribute('style')) {
|
|
466
|
+
node.setAttribute('style', element.getAttribute('style') ?? '');
|
|
467
|
+
}
|
|
468
|
+
element.parentNode.replaceChild(node, element);
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
svgElement.querySelectorAll('rect').forEach((element) => {
|
|
472
|
+
const x = getNumber(element.getAttribute('x'));
|
|
473
|
+
const y = getNumber(element.getAttribute('y'));
|
|
474
|
+
const width = getNumber(element.getAttribute('width'));
|
|
475
|
+
const height = getNumber(element.getAttribute('height'));
|
|
476
|
+
const rx = getNumber(element.getAttribute('rx'));
|
|
477
|
+
const ry = getNumber(element.getAttribute('ry'));
|
|
478
|
+
const node = rx > 0 || ry > 0
|
|
479
|
+
? rc.path(
|
|
480
|
+
`M ${x + rx} ${y}
|
|
481
|
+
H ${x + width - rx}
|
|
482
|
+
Q ${x + width} ${y} ${x + width} ${y + ry}
|
|
483
|
+
V ${y + height - ry}
|
|
484
|
+
Q ${x + width} ${y + height} ${x + width - rx} ${y + height}
|
|
485
|
+
H ${x + rx}
|
|
486
|
+
Q ${x} ${y + height} ${x} ${y + height - ry}
|
|
487
|
+
V ${y + ry}
|
|
488
|
+
Q ${x} ${y} ${x + rx} ${y}
|
|
489
|
+
Z`,
|
|
490
|
+
createOptions(element)
|
|
491
|
+
)
|
|
492
|
+
: rc.rectangle(
|
|
493
|
+
x,
|
|
494
|
+
y,
|
|
495
|
+
width,
|
|
496
|
+
height,
|
|
497
|
+
createOptions(element)
|
|
498
|
+
);
|
|
499
|
+
replaceShape(element, node);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
svgElement.querySelectorAll('line').forEach((element) => {
|
|
503
|
+
const node = rc.line(
|
|
504
|
+
getNumber(element.getAttribute('x1')),
|
|
505
|
+
getNumber(element.getAttribute('y1')),
|
|
506
|
+
getNumber(element.getAttribute('x2')),
|
|
507
|
+
getNumber(element.getAttribute('y2')),
|
|
508
|
+
createOptions(element)
|
|
509
|
+
);
|
|
510
|
+
replaceShape(element, node);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
svgElement.querySelectorAll('polyline').forEach((element) => {
|
|
514
|
+
const points = (element.getAttribute('points') ?? '')
|
|
515
|
+
.trim()
|
|
516
|
+
.split(/\s+/)
|
|
517
|
+
.map((pair) => pair.split(',').map(Number))
|
|
518
|
+
.filter((point) => point.length === 2 && Number.isFinite(point[0]) && Number.isFinite(point[1])) as [number, number][];
|
|
519
|
+
if (points.length < 2) return;
|
|
520
|
+
const node = rc.linearPath(points, createOptions(element));
|
|
521
|
+
replaceShape(element, node);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
svgElement.querySelectorAll('polygon').forEach((element) => {
|
|
525
|
+
const points = (element.getAttribute('points') ?? '')
|
|
526
|
+
.trim()
|
|
527
|
+
.split(/\s+/)
|
|
528
|
+
.map((pair) => pair.split(',').map(Number))
|
|
529
|
+
.filter((point) => point.length === 2 && Number.isFinite(point[0]) && Number.isFinite(point[1])) as [number, number][];
|
|
530
|
+
if (points.length < 2) return;
|
|
531
|
+
const node = rc.polygon(points, createOptions(element));
|
|
532
|
+
replaceShape(element, node);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
svgElement.querySelectorAll('path').forEach((element) => {
|
|
536
|
+
const d = element.getAttribute('d');
|
|
537
|
+
if (!d) return;
|
|
538
|
+
const node = rc.path(d, createOptions(element));
|
|
539
|
+
replaceShape(element, node);
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
return serializer.serializeToString(svgElement);
|
|
543
|
+
} catch (error) {
|
|
544
|
+
console.error('Error while applying hand-drawn mermaid style', error);
|
|
545
|
+
return svg;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
@@ -5,7 +5,7 @@ import { useParams } from 'next/navigation';
|
|
|
5
5
|
import { useTranslations } from 'next-intl';
|
|
6
6
|
import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button';
|
|
7
7
|
import Link from 'fumadocs-core/link';
|
|
8
|
-
import {
|
|
8
|
+
import { CheckIcon, MarkdownIcon, GitHubIcon, LastUpdatedIcon} from '@windrun-huaiin/base-ui/icons';
|
|
9
9
|
import { Button } from '@windrun-huaiin/base-ui/ui';
|
|
10
10
|
|
|
11
11
|
const cache = new Map<string, string>();
|
|
@@ -69,12 +69,12 @@ export function LLMCopyButton({ llmApiUrl, sourceKey }: LLMCopyButtonProps = {})
|
|
|
69
69
|
>
|
|
70
70
|
{checked ? (
|
|
71
71
|
<>
|
|
72
|
-
<
|
|
72
|
+
<CheckIcon/>
|
|
73
73
|
{t('copyMarkdownDone')}
|
|
74
74
|
</>
|
|
75
75
|
) : (
|
|
76
76
|
<>
|
|
77
|
-
<
|
|
77
|
+
<MarkdownIcon/>
|
|
78
78
|
{t('copyMarkdown')}
|
|
79
79
|
</>
|
|
80
80
|
)}
|
|
@@ -89,7 +89,7 @@ export function EditOnGitHub({ url }: { url: string }) {
|
|
|
89
89
|
className="flex items-center gap-x-2 text-stone-600 hover:text-stone-500 dark:text-stone-400 dark:hover:text-stone-300 text-sm"
|
|
90
90
|
href={url}
|
|
91
91
|
>
|
|
92
|
-
<
|
|
92
|
+
<GitHubIcon/>
|
|
93
93
|
{t('editOnGithub')}
|
|
94
94
|
</Link>
|
|
95
95
|
);
|
|
@@ -101,7 +101,7 @@ export function LastUpdatedDate({ date }: { date: string | undefined }) {
|
|
|
101
101
|
const viewDate = date ? `${t('lastUpdate')} ${date}` : `${t('emptyLastUpdate')}`
|
|
102
102
|
return (
|
|
103
103
|
<div className="flex items-center gap-x-2 text-stone-600 dark:text-stone-400 text-sm">
|
|
104
|
-
<
|
|
104
|
+
<LastUpdatedIcon/>
|
|
105
105
|
{viewDate}
|
|
106
106
|
</div>
|
|
107
107
|
);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { StarIcon } from '@windrun-huaiin/base-ui/icons';
|
|
4
4
|
import React from 'react';
|
|
5
5
|
|
|
6
6
|
export function TrophyCard({
|
|
7
|
-
icon = <
|
|
7
|
+
icon = <StarIcon />,
|
|
8
8
|
title,
|
|
9
9
|
children,
|
|
10
10
|
}: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { CircleSmallIcon } from '@windrun-huaiin/base-ui/icons';
|
|
4
4
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
5
5
|
import Link from 'next/link';
|
|
6
6
|
import type { HTMLAttributes, ReactNode } from 'react';
|
|
@@ -31,7 +31,7 @@ export function ZiaCard({ icon, title, description, ...props }: ZiaCardProps) {
|
|
|
31
31
|
{...props}
|
|
32
32
|
>
|
|
33
33
|
<div className="not-prose mb-2 w-fit rounded-md border bg-fd-muted p-1.5 text-fd-muted-foreground [&_svg]:size-4">
|
|
34
|
-
{icon ? icon : <
|
|
34
|
+
{icon ? icon : <CircleSmallIcon />}
|
|
35
35
|
</div>
|
|
36
36
|
<h3 className="not-prose mb-1 text-sm font-medium line-clamp-2 min-h-10">{title}</h3>
|
|
37
37
|
{validDescription ? (
|
|
@@ -57,7 +57,7 @@ export function ZiaCard({ icon, title, description, ...props }: ZiaCardProps) {
|
|
|
57
57
|
{...props}
|
|
58
58
|
>
|
|
59
59
|
<div className="not-prose mb-2 w-fit rounded-md border bg-fd-muted p-1.5 text-fd-muted-foreground [&_svg]:size-4">
|
|
60
|
-
{icon ? icon : <
|
|
60
|
+
{icon ? icon : <CircleSmallIcon />}
|
|
61
61
|
</div>
|
|
62
62
|
<h3 className="not-prose mb-1 text-sm font-medium line-clamp-2 min-h-10">{title}</h3>
|
|
63
63
|
{validDescription ? (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FileIcon, FolderOpenIcon, FolderIcon } from '@windrun-huaiin/base-ui/icons';
|
|
2
2
|
import { type HTMLAttributes, type ReactNode, useState } from 'react';
|
|
3
3
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
4
4
|
import {
|
|
@@ -29,7 +29,7 @@ export interface ZiaFolderProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
29
29
|
|
|
30
30
|
export function ZiaFile({
|
|
31
31
|
name,
|
|
32
|
-
icon = <
|
|
32
|
+
icon = <FileIcon />,
|
|
33
33
|
className,
|
|
34
34
|
anotion,
|
|
35
35
|
href,
|
|
@@ -70,7 +70,7 @@ export function ZiaFolder({
|
|
|
70
70
|
return (
|
|
71
71
|
<Collapsible open={open} onOpenChange={setOpen} {...props}>
|
|
72
72
|
<CollapsibleTrigger className={cn(itemVariants, className, 'w-full')}>
|
|
73
|
-
{open ? <
|
|
73
|
+
{open ? <FolderOpenIcon /> : <FolderIcon />}
|
|
74
74
|
<span>{name}</span>
|
|
75
75
|
{validAnotion && (
|
|
76
76
|
<span className={anotionClass}>{anotion}</span>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { useState } from "react";
|
|
4
4
|
import Image from "next/image";
|
|
5
|
-
import {
|
|
5
|
+
import { ImageOffIcon, InfoIcon, XIcon } from "@windrun-huaiin/base-ui/icons";
|
|
6
6
|
import {
|
|
7
7
|
AlertDialog,
|
|
8
8
|
AlertDialogContent,
|
|
@@ -47,7 +47,7 @@ export function AdsAlertDialog({
|
|
|
47
47
|
<div className="flex flex-row items-center justify-between mb-2">
|
|
48
48
|
<AlertDialogTitle asChild>
|
|
49
49
|
<div className="flex flex-row items-center gap-1 min-w-0 text-xl font-semibold">
|
|
50
|
-
<
|
|
50
|
+
<InfoIcon className="w-5 h-5" />
|
|
51
51
|
<span className="truncate">{title}</span>
|
|
52
52
|
</div>
|
|
53
53
|
</AlertDialogTitle>
|
|
@@ -57,7 +57,7 @@ export function AdsAlertDialog({
|
|
|
57
57
|
aria-label="Close"
|
|
58
58
|
tabIndex={0}
|
|
59
59
|
>
|
|
60
|
-
<
|
|
60
|
+
<XIcon className="w-5 h-5" />
|
|
61
61
|
</button>
|
|
62
62
|
</div>
|
|
63
63
|
|
|
@@ -70,7 +70,7 @@ export function AdsAlertDialog({
|
|
|
70
70
|
<div className="w-full max-w-[400px] h-[220px] relative flex items-center justify-center mb-2">
|
|
71
71
|
{imgError ? (
|
|
72
72
|
<div className="absolute inset-0 flex flex-col items-center justify-center bg-gray-100 dark:bg-neutral-800 border border-dashed border-neutral-300 dark:border-neutral-700 rounded-lg text-neutral-400 text-sm">
|
|
73
|
-
<
|
|
73
|
+
<ImageOffIcon className="w-12 h-12 mb-2" />
|
|
74
74
|
<span>Image loading failed</span>
|
|
75
75
|
</div>
|
|
76
76
|
) : imgHref ? (
|
|
@@ -132,4 +132,4 @@ export function AdsAlertDialog({
|
|
|
132
132
|
</AlertDialogContent>
|
|
133
133
|
</AlertDialog>
|
|
134
134
|
);
|
|
135
|
-
}
|
|
135
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
4
|
-
import {
|
|
4
|
+
import { GemIcon, XIcon } from '@windrun-huaiin/base-ui/icons';
|
|
5
5
|
import {
|
|
6
6
|
AlertDialog,
|
|
7
7
|
AlertDialogContent,
|
|
@@ -191,7 +191,7 @@ export function CreditNavButton({
|
|
|
191
191
|
aria-hidden="true"
|
|
192
192
|
/>
|
|
193
193
|
<span className="relative z-10 flex h-6 w-6 items-center justify-center rounded-full bg-slate-100 text-slate-600 transition-transform duration-200 group-hover:scale-110 group-hover:bg-white/20 group-hover:text-white dark:bg-slate-800 dark:text-slate-200 dark:group-hover:bg-white/20 dark:group-hover:text-white">
|
|
194
|
-
<
|
|
194
|
+
<GemIcon className="h-3.5 w-3.5" />
|
|
195
195
|
</span>
|
|
196
196
|
<span className="relative z-10 flex items-center">
|
|
197
197
|
<span className="text-base font-semibold leading-none">
|
|
@@ -244,7 +244,7 @@ export function CreditNavButton({
|
|
|
244
244
|
className="rounded-full p-2 text-gray-400 transition hover:bg-gray-400 hover:text-gray-400 dark:text-white/80 dark:hover:bg-white/80 dark:hover:text-white/80"
|
|
245
245
|
onClick={closePricingModal}
|
|
246
246
|
>
|
|
247
|
-
<
|
|
247
|
+
<XIcon className="h-6 w-6" />
|
|
248
248
|
</button>
|
|
249
249
|
</AlertDialogHeader>
|
|
250
250
|
<div className="max-h-[60vh] sm:max-h-[80vh] overflow-y-auto px-4 pt-2 pb-6">
|