@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.
Files changed (133) hide show
  1. package/LICENSE +1 -1
  2. package/dist/ai/ai-chat-composer.d.ts +2 -0
  3. package/dist/ai/ai-chat-composer.js +47 -0
  4. package/dist/ai/ai-chat-composer.mjs +45 -0
  5. package/dist/ai/ai-markdown.d.ts +2 -0
  6. package/dist/ai/ai-markdown.js +36 -0
  7. package/dist/ai/ai-markdown.mjs +34 -0
  8. package/dist/ai/ai-message-actions.d.ts +2 -0
  9. package/dist/ai/ai-message-actions.js +14 -0
  10. package/dist/ai/ai-message-actions.mjs +12 -0
  11. package/dist/ai/ai-message-bubble.d.ts +2 -0
  12. package/dist/ai/ai-message-bubble.js +66 -0
  13. package/dist/ai/ai-message-bubble.mjs +64 -0
  14. package/dist/ai/ai-message-content.d.ts +2 -0
  15. package/dist/ai/ai-message-content.js +63 -0
  16. package/dist/ai/ai-message-content.mjs +61 -0
  17. package/dist/ai/ai-message-list.d.ts +2 -0
  18. package/dist/ai/ai-message-list.js +24 -0
  19. package/dist/ai/ai-message-list.mjs +22 -0
  20. package/dist/ai/ai-message-meta.d.ts +2 -0
  21. package/dist/ai/ai-message-meta.js +38 -0
  22. package/dist/ai/ai-message-meta.mjs +36 -0
  23. package/dist/ai/ai-status-indicator.d.ts +2 -0
  24. package/dist/ai/ai-status-indicator.js +51 -0
  25. package/dist/ai/ai-status-indicator.mjs +49 -0
  26. package/dist/ai/index.d.ts +11 -0
  27. package/dist/ai/index.js +33 -0
  28. package/dist/ai/index.mjs +11 -0
  29. package/dist/ai/types.d.ts +110 -0
  30. package/dist/ai/use-ai-conversation.d.ts +13 -0
  31. package/dist/ai/use-ai-conversation.js +276 -0
  32. package/dist/ai/use-ai-conversation.mjs +274 -0
  33. package/dist/clerk/clerk-organization-client.js +2 -2
  34. package/dist/clerk/clerk-organization-client.mjs +2 -2
  35. package/dist/clerk/clerk-page-generator.d.ts +1 -1
  36. package/dist/clerk/clerk-user-client.js +2 -2
  37. package/dist/clerk/clerk-user-client.mjs +2 -2
  38. package/dist/clerk/fingerprint/fingerprint-provider.js +9 -9
  39. package/dist/clerk/fingerprint/fingerprint-provider.mjs +9 -9
  40. package/dist/fuma/base/custom-header.js +4 -4
  41. package/dist/fuma/base/custom-header.mjs +4 -4
  42. package/dist/fuma/mdx/banner.js +3 -3
  43. package/dist/fuma/mdx/banner.mjs +3 -3
  44. package/dist/fuma/mdx/fuma-github-info.js +3 -3
  45. package/dist/fuma/mdx/fuma-github-info.mjs +3 -3
  46. package/dist/fuma/mdx/gradient-button.js +3 -3
  47. package/dist/fuma/mdx/gradient-button.mjs +3 -3
  48. package/dist/fuma/mdx/index.d.ts +1 -0
  49. package/dist/fuma/mdx/index.js +2 -0
  50. package/dist/fuma/mdx/index.mjs +1 -0
  51. package/dist/fuma/mdx/markdown-component-map.d.ts +3 -0
  52. package/dist/fuma/mdx/markdown-component-map.js +73 -0
  53. package/dist/fuma/mdx/markdown-component-map.mjs +71 -0
  54. package/dist/fuma/mdx/mermaid.d.ts +2 -1
  55. package/dist/fuma/mdx/mermaid.js +130 -6
  56. package/dist/fuma/mdx/mermaid.mjs +130 -6
  57. package/dist/fuma/mdx/toc-base.js +4 -4
  58. package/dist/fuma/mdx/toc-base.mjs +4 -4
  59. package/dist/fuma/mdx/trophy-card.js +2 -2
  60. package/dist/fuma/mdx/trophy-card.mjs +2 -2
  61. package/dist/fuma/mdx/zia-card.js +3 -3
  62. package/dist/fuma/mdx/zia-card.mjs +3 -3
  63. package/dist/fuma/mdx/zia-file.js +3 -3
  64. package/dist/fuma/mdx/zia-file.mjs +3 -3
  65. package/dist/main/ads-alert-dialog.js +2 -2
  66. package/dist/main/ads-alert-dialog.mjs +2 -2
  67. package/dist/main/credit/credit-nav-button.js +2 -2
  68. package/dist/main/credit/credit-nav-button.mjs +2 -2
  69. package/dist/main/credit/credit-overview-client.js +4 -4
  70. package/dist/main/credit/credit-overview-client.mjs +4 -4
  71. package/dist/main/footer.js +2 -2
  72. package/dist/main/footer.mjs +2 -2
  73. package/dist/main/go-to-top.js +2 -2
  74. package/dist/main/go-to-top.mjs +2 -2
  75. package/dist/main/hero-media.d.ts +14 -0
  76. package/dist/main/hero-media.js +12 -0
  77. package/dist/main/hero-media.mjs +10 -0
  78. package/dist/main/hero-section.d.ts +10 -0
  79. package/dist/main/hero-section.js +11 -0
  80. package/dist/main/hero-section.mjs +9 -0
  81. package/dist/main/index.d.ts +3 -0
  82. package/dist/main/index.js +6 -0
  83. package/dist/main/index.mjs +3 -0
  84. package/dist/main/info-tooltip.d.ts +8 -0
  85. package/dist/main/info-tooltip.js +48 -0
  86. package/dist/main/info-tooltip.mjs +46 -0
  87. package/dist/main/pill-select/x-pill-select.js +2 -2
  88. package/dist/main/pill-select/x-pill-select.mjs +2 -2
  89. package/dist/main/pill-select/x-token-input.js +2 -2
  90. package/dist/main/pill-select/x-token-input.mjs +2 -2
  91. package/dist/main/x-button.js +3 -3
  92. package/dist/main/x-button.mjs +3 -3
  93. package/package.json +16 -3
  94. package/src/ai/ai-chat-composer.tsx +187 -0
  95. package/src/ai/ai-markdown.tsx +45 -0
  96. package/src/ai/ai-message-actions.tsx +16 -0
  97. package/src/ai/ai-message-bubble.tsx +138 -0
  98. package/src/ai/ai-message-content.tsx +149 -0
  99. package/src/ai/ai-message-list.tsx +59 -0
  100. package/src/ai/ai-message-meta.tsx +56 -0
  101. package/src/ai/ai-status-indicator.tsx +61 -0
  102. package/src/ai/index.ts +13 -0
  103. package/src/ai/types.ts +131 -0
  104. package/src/ai/use-ai-conversation.ts +422 -0
  105. package/src/clerk/clerk-organization-client.tsx +5 -5
  106. package/src/clerk/clerk-page-generator.tsx +1 -1
  107. package/src/clerk/clerk-user-client.tsx +4 -4
  108. package/src/clerk/fingerprint/fingerprint-provider.tsx +34 -22
  109. package/src/fuma/base/custom-header.tsx +5 -5
  110. package/src/fuma/mdx/banner.tsx +3 -3
  111. package/src/fuma/mdx/fuma-github-info.tsx +4 -4
  112. package/src/fuma/mdx/gradient-button.tsx +3 -3
  113. package/src/fuma/mdx/index.ts +2 -1
  114. package/src/fuma/mdx/markdown-component-map.tsx +174 -0
  115. package/src/fuma/mdx/mermaid.tsx +145 -10
  116. package/src/fuma/mdx/toc-base.tsx +5 -5
  117. package/src/fuma/mdx/trophy-card.tsx +2 -2
  118. package/src/fuma/mdx/zia-card.tsx +3 -3
  119. package/src/fuma/mdx/zia-file.tsx +3 -3
  120. package/src/main/ads-alert-dialog.tsx +5 -5
  121. package/src/main/credit/credit-nav-button.tsx +3 -3
  122. package/src/main/credit/credit-overview-client.tsx +15 -7
  123. package/src/main/features.tsx +5 -3
  124. package/src/main/footer.tsx +4 -5
  125. package/src/main/go-to-top.tsx +2 -2
  126. package/src/main/hero-media.tsx +53 -0
  127. package/src/main/hero-section.tsx +36 -0
  128. package/src/main/index.ts +5 -0
  129. package/src/main/info-tooltip.tsx +99 -0
  130. package/src/main/language-detector.tsx +4 -4
  131. package/src/main/pill-select/x-pill-select.tsx +2 -2
  132. package/src/main/pill-select/x-token-input.tsx +2 -2
  133. package/src/main/x-button.tsx +4 -4
@@ -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 { globalLucideIcons as icons } from '@windrun-huaiin/base-ui/components/server';
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 end-2 top-1/2 -translate-y-1/2 text-neutral-600 dark:text-neutral-400',
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
- <icons.X />
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 { globalLucideIcons as icons } from '@windrun-huaiin/base-ui/components/server';
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
- <icons.ExternalLink className="size-3" />
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
- <icons.Star className="size-3" />
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 { globalLucideIcons as icons } from "@windrun-huaiin/base-ui/components/server";
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 <icons.Loader2 className={cn(finalIconClass, 'animate-spin')} />;
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 <icons.ArrowRight className={cn(finalIconClass)} />;
126
+ return <ArrowRightIcon className={cn(finalIconClass)} />;
127
127
  })();
128
128
 
129
129
  const shouldRenderIcon = iconNode !== null && iconNode !== undefined;
@@ -12,4 +12,5 @@ export * from './zia-file';
12
12
  export * from './toc-footer-wrapper';
13
13
  export * from './toc-clerk-portable';
14
14
  export * from './banner';
15
- export * from './suno-embed';
15
+ export * from './suno-embed';
16
+ export * from './markdown-component-map';
@@ -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
+ };
@@ -1,10 +1,11 @@
1
1
  'use client';
2
2
 
3
- import { globalLucideIcons as icons } from '@windrun-huaiin/base-ui/components/server';
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(svg, watermarkText, themeSvgIconColor);
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
- <icons.Mmd className='mr-1 h-4 w-4' />
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
- <icons.Mmd className="h-4 w-4" />
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
- <icons.RefreshCcw className="h-3.5 w-3.5" />
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
- <icons.Download className="h-3.5 w-3.5" />
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
- <icons.X className="h-3.5 w-3.5" />
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 { globalLucideIcons as icons } from '@windrun-huaiin/base-ui/components/server';
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
- <icons.Check/>
72
+ <CheckIcon/>
73
73
  {t('copyMarkdownDone')}
74
74
  </>
75
75
  ) : (
76
76
  <>
77
- <icons.Markdown/>
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
- <icons.GitHub/>
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
- <icons.LastUpdated/>
104
+ <LastUpdatedIcon/>
105
105
  {viewDate}
106
106
  </div>
107
107
  );
@@ -1,10 +1,10 @@
1
1
  'use client';
2
2
 
3
- import { globalLucideIcons as icons } from '@windrun-huaiin/base-ui/components/server';
3
+ import { StarIcon } from '@windrun-huaiin/base-ui/icons';
4
4
  import React from 'react';
5
5
 
6
6
  export function TrophyCard({
7
- icon = <icons.Star />,
7
+ icon = <StarIcon />,
8
8
  title,
9
9
  children,
10
10
  }: {
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { globalLucideIcons as icons } from '@windrun-huaiin/base-ui/components/server';
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 : <icons.CircleSmall />}
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 : <icons.CircleSmall />}
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 { globalLucideIcons as icons } from '@windrun-huaiin/base-ui/components/server';
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 = <icons.File />,
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 ? <icons.FolderOpen /> : <icons.Folder />}
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 { globalLucideIcons as icons } from "@windrun-huaiin/base-ui/components/server";
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
- <icons.Info className="w-5 h-5" />
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
- <icons.X className="w-5 h-5" />
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
- <icons.ImageOff className="w-12 h-12 mb-2" />
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 { globalLucideIcons as icons } from '@windrun-huaiin/base-ui/components/server';
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
- <icons.Gem className="h-3.5 w-3.5" />
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
- <icons.X className="h-6 w-6" />
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">