@windrun-huaiin/third-ui 26.0.0 → 28.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 (210) hide show
  1. package/dist/ai/ai-prompt-textarea.d.ts +72 -0
  2. package/dist/ai/ai-prompt-textarea.js +114 -0
  3. package/dist/ai/ai-prompt-textarea.mjs +112 -0
  4. package/dist/ai/index.d.ts +1 -0
  5. package/dist/ai/index.js +2 -0
  6. package/dist/ai/index.mjs +1 -0
  7. package/dist/clerk/clerk-provider-client.js +0 -1
  8. package/dist/clerk/clerk-provider-client.mjs +0 -1
  9. package/dist/clerk/fingerprint/fingerprint-client.js +0 -4
  10. package/dist/clerk/fingerprint/fingerprint-client.mjs +0 -4
  11. package/dist/clerk/fingerprint/use-fingerprint.js +0 -6
  12. package/dist/clerk/fingerprint/use-fingerprint.mjs +0 -6
  13. package/dist/clerk/signin-with-fingerprint-client.js +0 -9
  14. package/dist/clerk/signin-with-fingerprint-client.mjs +0 -9
  15. package/dist/clerk/signup-button-with-fingerprint-client.js +0 -16
  16. package/dist/clerk/signup-button-with-fingerprint-client.mjs +0 -16
  17. package/dist/clerk/signup-with-fingerprint-client.js +0 -9
  18. package/dist/clerk/signup-with-fingerprint-client.mjs +0 -9
  19. package/dist/fuma/base/custom-header.js +10 -8
  20. package/dist/fuma/base/custom-header.mjs +10 -8
  21. package/dist/fuma/base/custom-home-layout.d.ts +0 -4
  22. package/dist/fuma/base/docs-root-provider.d.ts +19 -0
  23. package/dist/fuma/base/docs-root-provider.js +17 -0
  24. package/dist/fuma/base/docs-root-provider.mjs +15 -0
  25. package/dist/fuma/base/index.d.ts +5 -0
  26. package/dist/fuma/base/index.js +16 -7
  27. package/dist/fuma/base/index.mjs +5 -1
  28. package/dist/fuma/base/nav-config.d.ts +10 -0
  29. package/dist/fuma/base/nav-config.js +32 -0
  30. package/dist/fuma/base/nav-config.mjs +28 -0
  31. package/dist/fuma/base/site-docs-layout.d.ts +11 -0
  32. package/dist/fuma/base/site-docs-layout.js +15 -0
  33. package/dist/fuma/base/site-docs-layout.mjs +13 -0
  34. package/dist/fuma/base/site-home-layout.d.ts +24 -0
  35. package/dist/fuma/base/site-home-layout.js +16 -0
  36. package/dist/fuma/base/site-home-layout.mjs +14 -0
  37. package/dist/fuma/base/site-layout-shared.d.ts +89 -0
  38. package/dist/fuma/base/site-layout-shared.js +48 -0
  39. package/dist/fuma/base/site-layout-shared.mjs +42 -0
  40. package/dist/fuma/base/site-layout.d.ts +4 -116
  41. package/dist/fuma/base/site-layout.js +2 -2
  42. package/dist/fuma/base/site-layout.mjs +2 -2
  43. package/dist/fuma/fuma-page-genarator.d.ts +1 -1
  44. package/dist/fuma/fuma-page-genarator.js +65 -10
  45. package/dist/fuma/fuma-page-genarator.mjs +61 -6
  46. package/dist/fuma/llm-copy-handler.js +0 -9
  47. package/dist/fuma/llm-copy-handler.mjs +0 -9
  48. package/dist/fuma/mdx/index.d.ts +0 -1
  49. package/dist/fuma/mdx/index.js +0 -2
  50. package/dist/fuma/mdx/index.mjs +0 -1
  51. package/dist/fuma/mdx/suno-embed.js +3 -1
  52. package/dist/fuma/mdx/suno-embed.mjs +3 -1
  53. package/dist/fuma/mdx/toc-base.js +0 -1
  54. package/dist/fuma/mdx/toc-base.mjs +0 -1
  55. package/dist/fuma/server/features/widgets.js +5 -1
  56. package/dist/fuma/server/features/widgets.mjs +5 -1
  57. package/dist/fuma/server/llm-copy-handler.d.ts +2 -0
  58. package/dist/fuma/server/llm-copy-handler.js +7 -0
  59. package/dist/fuma/server/llm-copy-handler.mjs +1 -0
  60. package/dist/fuma/server/page-generator.d.ts +2 -0
  61. package/dist/fuma/server/page-generator.js +7 -0
  62. package/dist/fuma/server/page-generator.mjs +1 -0
  63. package/dist/lib/seo-metadata.js +3 -3
  64. package/dist/lib/seo-metadata.mjs +1 -1
  65. package/dist/lib/seo-util.js +4 -4
  66. package/dist/lib/seo-util.mjs +1 -1
  67. package/dist/lib/site-docs-helper.d.ts +51 -0
  68. package/dist/lib/site-docs-helper.js +68 -0
  69. package/dist/lib/site-docs-helper.mjs +66 -0
  70. package/dist/main/alert-dialog/index.js +14 -0
  71. package/dist/main/alert-dialog/index.mjs +5 -0
  72. package/dist/main/buttons/gradient-button.d.ts +20 -0
  73. package/dist/main/buttons/gradient-button.js +88 -0
  74. package/dist/main/buttons/gradient-button.mjs +86 -0
  75. package/dist/main/buttons/index.d.ts +3 -0
  76. package/dist/main/buttons/index.js +12 -0
  77. package/dist/main/buttons/index.mjs +4 -0
  78. package/dist/main/buttons/x-button.d.ts +39 -0
  79. package/dist/main/buttons/x-button.js +92 -0
  80. package/dist/main/buttons/x-button.mjs +90 -0
  81. package/dist/main/buttons/x-toggle-button.d.ts +32 -0
  82. package/dist/main/buttons/x-toggle-button.js +95 -0
  83. package/dist/main/buttons/x-toggle-button.mjs +74 -0
  84. package/dist/main/credit/credit-nav-button.js +25 -1
  85. package/dist/main/credit/credit-nav-button.mjs +25 -1
  86. package/dist/main/credit/credit-overview-client.js +3 -2
  87. package/dist/main/credit/credit-overview-client.mjs +3 -2
  88. package/dist/main/credit/index.d.ts +4 -0
  89. package/dist/main/credit/index.js +10 -0
  90. package/dist/main/credit/index.mjs +3 -0
  91. package/dist/main/credit/server.d.ts +2 -0
  92. package/dist/main/credit/server.js +7 -0
  93. package/dist/main/credit/server.mjs +1 -0
  94. package/dist/main/cta.js +4 -2
  95. package/dist/main/cta.mjs +4 -2
  96. package/dist/main/footer.js +3 -3
  97. package/dist/main/footer.mjs +1 -1
  98. package/dist/main/hero/index.d.ts +2 -0
  99. package/dist/main/hero/index.js +10 -0
  100. package/dist/main/hero/index.mjs +3 -0
  101. package/dist/main/home/server.d.ts +7 -0
  102. package/dist/main/home/server.js +19 -0
  103. package/dist/main/home/server.mjs +7 -0
  104. package/dist/main/index.d.ts +0 -15
  105. package/dist/main/index.js +0 -43
  106. package/dist/main/index.mjs +0 -21
  107. package/dist/main/loading/index.d.ts +1 -0
  108. package/dist/main/loading/index.js +9 -0
  109. package/dist/main/loading/index.mjs +2 -0
  110. package/dist/main/loading-frame/index.d.ts +1 -0
  111. package/dist/main/loading-frame/index.js +9 -0
  112. package/dist/main/loading-frame/index.mjs +2 -0
  113. package/dist/main/money-price/index.d.ts +4 -0
  114. package/dist/main/money-price/index.js +15 -0
  115. package/dist/main/money-price/index.mjs +4 -0
  116. package/dist/main/money-price/money-price-button.d.ts +1 -1
  117. package/dist/main/money-price/money-price-button.js +12 -9
  118. package/dist/main/money-price/money-price-button.mjs +12 -9
  119. package/dist/main/money-price/money-price-interactive.d.ts +1 -1
  120. package/dist/main/money-price/money-price-interactive.js +22 -25
  121. package/dist/main/money-price/money-price-interactive.mjs +22 -25
  122. package/dist/main/money-price/money-price-types.d.ts +2 -0
  123. package/dist/main/money-price/server.d.ts +5 -0
  124. package/dist/main/money-price/server.js +18 -0
  125. package/dist/main/money-price/server.mjs +4 -0
  126. package/package.json +94 -4
  127. package/src/ai/index.ts +1 -0
  128. package/src/clerk/clerk-provider-client.tsx +1 -3
  129. package/src/clerk/fingerprint/fingerprint-client.ts +0 -4
  130. package/src/clerk/fingerprint/use-fingerprint.ts +0 -6
  131. package/src/clerk/signin-with-fingerprint-client.tsx +0 -10
  132. package/src/clerk/signup-button-with-fingerprint-client.tsx +0 -17
  133. package/src/clerk/signup-with-fingerprint-client.tsx +0 -10
  134. package/src/fuma/base/custom-header.tsx +12 -8
  135. package/src/fuma/base/custom-home-layout.tsx +3 -6
  136. package/src/fuma/base/docs-root-provider.tsx +58 -0
  137. package/src/fuma/base/index.ts +5 -0
  138. package/src/fuma/base/nav-config.ts +81 -0
  139. package/src/fuma/base/site-docs-layout.tsx +35 -0
  140. package/src/fuma/base/site-home-layout.tsx +78 -0
  141. package/src/fuma/base/site-layout-shared.tsx +190 -0
  142. package/src/fuma/base/site-layout.tsx +4 -289
  143. package/src/fuma/fuma-banner-suit.tsx +1 -1
  144. package/src/fuma/fuma-page-genarator.tsx +61 -8
  145. package/src/fuma/llm-copy-handler.ts +0 -11
  146. package/src/fuma/mdx/index.ts +0 -1
  147. package/src/fuma/mdx/suno-embed.tsx +1 -1
  148. package/src/fuma/mdx/toc-base.tsx +0 -1
  149. package/src/fuma/mdx/toc-footer-wrapper.tsx +2 -2
  150. package/src/fuma/server/features/widgets.tsx +1 -1
  151. package/src/fuma/server/llm-copy-handler.ts +2 -0
  152. package/src/fuma/server/page-generator.ts +2 -0
  153. package/src/lib/seo-metadata.ts +1 -1
  154. package/src/lib/seo-util.ts +2 -2
  155. package/src/lib/server.ts +1 -1
  156. package/src/{fuma/mdx → main/buttons}/gradient-button.tsx +10 -21
  157. package/src/main/buttons/index.ts +5 -0
  158. package/src/main/{x-button.tsx → buttons/x-button.tsx} +28 -42
  159. package/src/main/credit/credit-nav-button.tsx +36 -3
  160. package/src/main/credit/credit-overview-client.tsx +1 -1
  161. package/src/main/credit/index.ts +11 -0
  162. package/src/main/credit/server.ts +7 -0
  163. package/src/main/cta.tsx +1 -1
  164. package/src/main/footer.tsx +1 -2
  165. package/src/main/hero/index.ts +4 -0
  166. package/src/main/home/server.ts +7 -0
  167. package/src/main/index.ts +1 -20
  168. package/src/main/language-detector.tsx +0 -1
  169. package/src/main/loading/index.ts +3 -0
  170. package/src/main/loading-frame/index.ts +3 -0
  171. package/src/main/money-price/index.ts +18 -0
  172. package/src/main/money-price/money-price-button.tsx +17 -9
  173. package/src/main/money-price/money-price-interactive.tsx +30 -25
  174. package/src/main/money-price/money-price-types.ts +2 -0
  175. package/src/main/money-price/server.ts +22 -0
  176. package/dist/fuma/mdx/features.d.ts +0 -8
  177. package/dist/fuma/mdx/features.js +0 -92
  178. package/dist/fuma/mdx/features.mjs +0 -85
  179. package/dist/fuma/mdx/image-grid.d.ts +0 -6
  180. package/dist/fuma/mdx/image-grid.js +0 -17
  181. package/dist/fuma/mdx/image-grid.mjs +0 -15
  182. package/dist/fuma/mdx/image-zoom.d.ts +0 -22
  183. package/dist/fuma/mdx/image-zoom.js +0 -39
  184. package/dist/fuma/mdx/image-zoom.mjs +0 -37
  185. package/dist/fuma/mdx/markdown-component-map.d.ts +0 -3
  186. package/dist/fuma/mdx/markdown-component-map.js +0 -79
  187. package/dist/fuma/mdx/markdown-component-map.mjs +0 -77
  188. package/dist/fuma/mdx/math.d.ts +0 -17
  189. package/dist/fuma/mdx/math.js +0 -60
  190. package/dist/fuma/mdx/math.mjs +0 -57
  191. package/dist/fuma/mdx/mermaid.d.ts +0 -13
  192. package/dist/fuma/mdx/mermaid.js +0 -360
  193. package/dist/fuma/mdx/mermaid.mjs +0 -358
  194. package/dist/fuma/mdx/site-mdx-components.d.ts +0 -13
  195. package/dist/fuma/mdx/site-mdx-components.js +0 -19
  196. package/dist/fuma/mdx/site-mdx-components.mjs +0 -17
  197. package/dist/fuma/mdx/site-mdx-presets.d.ts +0 -13
  198. package/dist/fuma/mdx/site-mdx-presets.js +0 -49
  199. package/dist/fuma/mdx/site-mdx-presets.mjs +0 -45
  200. package/dist/fuma/server/optional-features.d.ts +0 -6
  201. package/dist/fuma/server/optional-features.js +0 -17
  202. package/dist/fuma/server/optional-features.mjs +0 -6
  203. package/dist/fuma/server/site-mdx-components.d.ts +0 -13
  204. package/dist/fuma/server/site-mdx-components.js +0 -18
  205. package/dist/fuma/server/site-mdx-components.mjs +0 -16
  206. package/dist/fuma/server/site-mdx-presets.d.ts +0 -195
  207. package/dist/fuma/server/site-mdx-presets.js +0 -55
  208. package/dist/fuma/server/site-mdx-presets.mjs +0 -52
  209. /package/src/{main → ai}/ai-prompt-textarea.tsx +0 -0
  210. /package/src/main/{x-toggle-button.tsx → buttons/x-toggle-button.tsx} +0 -0
@@ -0,0 +1,2 @@
1
+ export { createFumaPage } from '../fuma-page-genarator';
2
+ export type { FumaPageTocRenderMode } from '../fuma-page-genarator';
@@ -1,6 +1,6 @@
1
1
  import type { Metadata } from 'next';
2
2
  import { getTranslations } from 'next-intl/server';
3
- import { getAsNeededLocalizedUrl } from '@windrun-huaiin/lib';
3
+ import { getAsNeededLocalizedUrl } from '@windrun-huaiin/lib/utils';
4
4
 
5
5
  export interface CreateLocalizedSiteMetadataOptions {
6
6
  locale: string;
@@ -1,7 +1,7 @@
1
1
  import type { MetadataRoute } from 'next';
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
- import { getAsNeededLocalizedUrl } from '@windrun-huaiin/lib';
4
+ import { getAsNeededLocalizedUrl } from '@windrun-huaiin/lib/utils';
5
5
 
6
6
  /**
7
7
  * Generate robots.txt content
@@ -139,4 +139,4 @@ export function createSitemapHandler(
139
139
  (sitemapHandler as any).dynamic = 'force-static';
140
140
 
141
141
  return sitemapHandler;
142
- }
142
+ }
package/src/lib/server.ts CHANGED
@@ -3,4 +3,4 @@
3
3
  export * from './clerk-intl';
4
4
  export * from './fuma-schema-check-util';
5
5
  export * from './seo-util';
6
- export * from './t-intl';
6
+ export * from './t-intl';
@@ -23,12 +23,9 @@ export interface GradientButtonProps {
23
23
  disabled?: boolean;
24
24
  className?: string;
25
25
  iconClassName?: string;
26
- // for Link
27
26
  href?: string;
28
27
  openInNewTab?: boolean;
29
28
  preserveReferrer?: boolean;
30
-
31
- // for click
32
29
  onClick?: () => void | Promise<void>;
33
30
  loadingText?: React.ReactNode;
34
31
  preventDoubleClick?: boolean;
@@ -52,7 +49,7 @@ export function GradientButton({
52
49
  variant = 'default',
53
50
  }: GradientButtonProps) {
54
51
  const [isLoading, setIsLoading] = useState(false);
55
- const actualLoadingText = loadingText || title?.toString().trim() || 'Loading...'
52
+ const actualLoadingText = loadingText || title?.toString().trim() || 'Loading...';
56
53
 
57
54
  const defaultIconClass = "h-4 w-4";
58
55
  const finalIconClass = cn(
@@ -60,14 +57,13 @@ export function GradientButton({
60
57
  iconClassName || defaultIconClass
61
58
  );
62
59
 
63
- // set justify class according to alignment
64
60
  const getAlignmentClass = () => {
65
61
  switch (align) {
66
62
  case 'center':
67
63
  return 'justify-center';
68
64
  case 'right':
69
65
  return 'justify-end';
70
- default: // 'left'
66
+ default:
71
67
  return 'justify-start';
72
68
  }
73
69
  };
@@ -80,11 +76,11 @@ export function GradientButton({
80
76
 
81
77
  if (onClick) {
82
78
  e.preventDefault();
83
-
79
+
84
80
  if (preventDoubleClick) {
85
81
  setIsLoading(true);
86
82
  }
87
-
83
+
88
84
  try {
89
85
  await onClick();
90
86
  } catch (error) {
@@ -98,10 +94,7 @@ export function GradientButton({
98
94
  };
99
95
 
100
96
  const isDisabled = disabled || isLoading;
101
-
102
97
  const displayTitle = isLoading ? actualLoadingText : title;
103
-
104
- // icon
105
98
  const iconProvided = icon !== undefined;
106
99
 
107
100
  const iconNode = (() => {
@@ -147,8 +140,6 @@ export function GradientButton({
147
140
  ? 'justify-center'
148
141
  : 'justify-start';
149
142
 
150
- // Base styles extracted from Button component + size="lg" (h-11 px-8)
151
- // Removed [&_svg] constraints
152
143
  const baseButtonStyles = "inline-flex items-center gap-2 whitespace-nowrap h-11 px-8 ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50";
153
144
  const variantClassName = variant === 'soft'
154
145
  ? cn(
@@ -163,11 +154,11 @@ export function GradientButton({
163
154
  themeIconColor,
164
155
  'border border-neutral-200 shadow-sm hover:shadow-md hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-800'
165
156
  )
166
- : cn(
167
- themeButtonGradientClass,
168
- themeButtonGradientHoverClass,
169
- 'text-white shadow-lg hover:shadow-xl'
170
- );
157
+ : cn(
158
+ themeButtonGradientClass,
159
+ themeButtonGradientHoverClass,
160
+ 'text-white shadow-lg hover:shadow-xl'
161
+ );
171
162
 
172
163
  const buttonClassName = cn(
173
164
  baseButtonStyles,
@@ -181,7 +172,6 @@ export function GradientButton({
181
172
  return (
182
173
  <div className={`flex flex-row gap-3 ${getAlignmentClass()}`}>
183
174
  {onClick ? (
184
- // for click
185
175
  <button
186
176
  type="button"
187
177
  className={buttonClassName}
@@ -191,7 +181,6 @@ export function GradientButton({
191
181
  {buttonContent}
192
182
  </button>
193
183
  ) : (
194
- // for Link
195
184
  <Link
196
185
  href={href || "#"}
197
186
  className={cn(buttonClassName, "no-underline hover:no-underline")}
@@ -204,4 +193,4 @@ export function GradientButton({
204
193
  )}
205
194
  </div>
206
195
  );
207
- }
196
+ }
@@ -0,0 +1,5 @@
1
+ 'use client';
2
+
3
+ export * from './gradient-button';
4
+ export * from './x-button';
5
+ export * from './x-toggle-button';
@@ -7,7 +7,6 @@ import { cn } from '@windrun-huaiin/lib/utils'
7
7
 
8
8
  type XButtonVariant = 'default' | 'soft' | 'subtle'
9
9
 
10
- // base button config
11
10
  interface BaseButtonConfig {
12
11
  icon: ReactNode
13
12
  text: string
@@ -15,7 +14,6 @@ interface BaseButtonConfig {
15
14
  disabled?: boolean
16
15
  }
17
16
 
18
- // menu item config
19
17
  interface MenuItemConfig extends BaseButtonConfig {
20
18
  tag?: {
21
19
  text: string
@@ -24,7 +22,6 @@ interface MenuItemConfig extends BaseButtonConfig {
24
22
  splitTopBorder?: boolean
25
23
  }
26
24
 
27
- // single button config
28
25
  interface SingleButtonProps {
29
26
  type: 'single'
30
27
  button: BaseButtonConfig
@@ -35,7 +32,6 @@ interface SingleButtonProps {
35
32
  variant?: XButtonVariant
36
33
  }
37
34
 
38
- // split button config
39
35
  interface SplitButtonProps {
40
36
  type: 'split'
41
37
  mainButton: BaseButtonConfig
@@ -77,7 +73,6 @@ export function XButton(props: xButtonProps) {
77
73
  return icon;
78
74
  };
79
75
 
80
- // click outside to close menu
81
76
  useEffect(() => {
82
77
  if (props.type === 'split') {
83
78
  const handleClickOutside = (event: MouseEvent) => {
@@ -96,7 +91,6 @@ export function XButton(props: xButtonProps) {
96
91
  }
97
92
  }, [menuOpen, props.type])
98
93
 
99
- // handle button click
100
94
  const handleButtonClick = async (onClick: () => void | Promise<void>) => {
101
95
  if (isLoading) return
102
96
 
@@ -110,7 +104,6 @@ export function XButton(props: xButtonProps) {
110
104
  }
111
105
  }
112
106
 
113
- // base style class
114
107
  const baseButtonClass = "flex items-center justify-center gap-2 px-4 py-2 text-sm font-semibold transition-colors"
115
108
  const singleButtonVariantClass = variant === 'soft'
116
109
  ? cn(
@@ -125,7 +118,7 @@ export function XButton(props: xButtonProps) {
125
118
  themeIconColor,
126
119
  "border border-neutral-200 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-800"
127
120
  )
128
- : "bg-neutral-200 dark:bg-neutral-800 text-neutral-700 dark:text-white hover:bg-neutral-300 dark:hover:bg-neutral-700"
121
+ : "bg-neutral-200 dark:bg-neutral-800 text-neutral-700 dark:text-white hover:bg-neutral-300 dark:hover:bg-neutral-700"
129
122
  const splitMainButtonVariantClass = variant === 'soft'
130
123
  ? cn(
131
124
  "bg-transparent hover:bg-black/5 dark:hover:bg-white/5",
@@ -136,7 +129,7 @@ export function XButton(props: xButtonProps) {
136
129
  "bg-transparent hover:bg-neutral-50 dark:hover:bg-neutral-800",
137
130
  themeIconColor
138
131
  )
139
- : "bg-neutral-200 dark:bg-neutral-800 text-neutral-700 dark:text-white hover:bg-neutral-300 dark:hover:bg-neutral-700"
132
+ : "bg-neutral-200 dark:bg-neutral-800 text-neutral-700 dark:text-white hover:bg-neutral-300 dark:hover:bg-neutral-700"
140
133
  const splitDropdownVariantClass = variant === 'soft'
141
134
  ? cn(
142
135
  "bg-transparent hover:bg-black/5 dark:hover:bg-white/5 sm:border-l",
@@ -149,13 +142,12 @@ export function XButton(props: xButtonProps) {
149
142
  themeIconColor,
150
143
  "border-neutral-200 dark:border-neutral-800"
151
144
  )
152
- : "bg-neutral-200 dark:bg-neutral-800 text-neutral-700 dark:text-white hover:bg-neutral-300 dark:hover:bg-neutral-700 sm:border-l sm:border-neutral-300 sm:dark:border-neutral-700"
145
+ : "bg-neutral-200 dark:bg-neutral-800 text-neutral-700 dark:text-white hover:bg-neutral-300 dark:hover:bg-neutral-700 sm:border-l sm:border-neutral-300 sm:dark:border-neutral-700"
153
146
  const disabledClass = "opacity-60 cursor-not-allowed"
154
147
 
155
148
  if (props.type === 'single') {
156
149
  const { button, loadingText, minWidth = 'min-w-[110px]', className = '' } = props
157
150
  const isDisabled = button.disabled || isLoading
158
- // loadingText: props.loadingText > button.text > 'Loading...'
159
151
  const actualLoadingText = loadingText || button.text?.trim() || 'Loading...'
160
152
 
161
153
  return (
@@ -188,36 +180,28 @@ export function XButton(props: xButtonProps) {
188
180
  )
189
181
  }
190
182
 
191
- // Split button
192
183
  const { mainButton, menuItems, loadingText, menuWidth = 'w-full sm:w-40', className = '', mainButtonClassName = '', dropdownButtonClassName = '' } = props
193
184
  const isMainDisabled = mainButton.disabled || isLoading
194
- // loadingText prioty:props.loadingText > mainButton.text > 'Loading...'
195
185
  const actualLoadingText = loadingText || mainButton.text?.trim() || 'Loading...'
196
186
 
197
187
  return (
198
188
  <div className={cn(
199
189
  "relative flex flex-row items-stretch w-full sm:w-auto rounded-full gap-0",
200
190
  menuOpen && "z-90",
201
- variant === 'soft'
202
- ? cn(themeBgColor, themeBorderColor, "border")
203
- : variant === 'subtle'
204
- ? cn(themeMainBgColor, "border border-neutral-200 dark:border-neutral-800")
205
- : "bg-neutral-200 dark:bg-neutral-800",
206
191
  className
207
192
  )}>
208
- {/* left main button */}
209
193
  <button
210
194
  onClick={() => handleButtonClick(mainButton.onClick)}
211
195
  disabled={isMainDisabled}
212
196
  className={cn(
213
- "min-w-0 flex-1",
197
+ "flex-1 min-w-0 sm:min-w-[100px] sm:flex-initial rounded-l-full",
214
198
  baseButtonClass,
215
199
  splitMainButtonVariantClass,
216
- "rounded-l-full rounded-r-none",
217
200
  isMainDisabled && disabledClass,
218
201
  mainButtonClassName
219
202
  )}
220
203
  onMouseDown={e => { if (e.button === 2) e.preventDefault() }}
204
+ title={mainButton.text}
221
205
  >
222
206
  {isLoading ? (
223
207
  <>
@@ -227,52 +211,54 @@ export function XButton(props: xButtonProps) {
227
211
  ) : (
228
212
  <>
229
213
  {renderIcon(mainButton.icon)}
230
- <span className="min-w-0 truncate">{mainButton.text}</span>
214
+ <span>{mainButton.text}</span>
231
215
  </>
232
216
  )}
233
217
  </button>
234
218
 
235
- {/* right dropdown button */}
236
219
  <button
237
220
  type="button"
221
+ onClick={() => setMenuOpen(!menuOpen)}
222
+ disabled={isLoading}
238
223
  className={cn(
239
- "flex h-full w-9 shrink-0 items-center justify-center px-0 py-1.5 cursor-pointer transition rounded-r-full rounded-l-none border-l sm:w-10",
224
+ "w-12 rounded-r-full",
225
+ baseButtonClass,
240
226
  splitDropdownVariantClass,
227
+ isLoading && disabledClass,
241
228
  dropdownButtonClassName
242
229
  )}
243
- onClick={e => { e.stopPropagation(); setMenuOpen(v => !v) }}
244
- aria-label="More actions"
245
- aria-expanded={menuOpen}
230
+ aria-label="Open menu"
246
231
  >
247
- <ChevronDownIcon className={chevronIconClass} />
232
+ <ChevronDownIcon className={cn(chevronIconClass, menuOpen && "rotate-180", "transition-transform")} />
248
233
  </button>
249
234
 
250
- {/* dropdown menu */}
251
235
  {menuOpen && (
252
236
  <div
253
237
  ref={menuRef}
254
- className={`absolute right-0 top-full ${menuWidth} bg-white dark:bg-neutral-800 text-neutral-800 dark:text-white text-sm rounded-xl shadow-lg z-100 border border-neutral-200 dark:border-neutral-700 overflow-hidden animate-fade-in`}
238
+ className={cn(
239
+ "absolute top-full right-0 mt-2 bg-white dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 rounded-lg shadow-lg z-50 overflow-hidden",
240
+ menuWidth
241
+ )}
255
242
  >
256
243
  {menuItems.map((item, index) => (
257
244
  <button
258
245
  key={index}
246
+ type="button"
259
247
  onClick={() => {
260
- handleButtonClick(item.onClick)
261
248
  setMenuOpen(false)
249
+ handleButtonClick(item.onClick)
262
250
  }}
263
- disabled={item.disabled}
264
- className={`flex items-center w-full px-4 py-3 transition hover:bg-neutral-300 dark:hover:bg-neutral-600 text-left relative ${item.disabled ? disabledClass : ''}`}
265
- style={item.splitTopBorder ? { borderTop: '1px solid #AC62FD' } : undefined}
251
+ disabled={item.disabled || isLoading}
252
+ className={cn(
253
+ "w-full flex items-center gap-2 px-3 py-2 text-sm text-left hover:bg-neutral-100 dark:hover:bg-neutral-700 transition-colors",
254
+ item.disabled && disabledClass,
255
+ item.splitTopBorder && "border-t border-neutral-200 dark:border-neutral-700"
256
+ )}
266
257
  >
267
- <span className="flex items-center">
268
- {item.icon}
269
- <span>{item.text}</span>
270
- </span>
258
+ {renderIcon(item.icon)}
259
+ <span className="flex-1">{item.text}</span>
271
260
  {item.tag && (
272
- <span
273
- className="absolute right-3 top-1 text-[10px] font-semibold"
274
- style={{ color: item.tag.color || '#A855F7', pointerEvents: 'none' }}
275
- >
261
+ <span className={cn("px-1.5 py-0.5 text-xs rounded", item.tag.color || "bg-blue-100 text-blue-800")}>
276
262
  {item.tag.text}
277
263
  </span>
278
264
  )}
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { cn } from '@windrun-huaiin/lib/utils';
4
4
  import { GemIcon, XIcon } from '@windrun-huaiin/base-ui/icons';
5
+ import { themeMainBgColor } from '@windrun-huaiin/base-ui/lib';
5
6
  import {
6
7
  AlertDialog,
7
8
  AlertDialogContent,
@@ -21,7 +22,9 @@ import {
21
22
  useState,
22
23
  } from 'react';
23
24
  import { MoneyPriceInteractive } from '../money-price/money-price-interactive';
24
- import type { MoneyPriceData } from '../money-price/money-price-types';
25
+ import { getActiveProviderConfigUtil } from '../money-price/money-price-config-util';
26
+ import type { MoneyPriceData, SubscriptionProductConfig } from '../money-price/money-price-types';
27
+ import { dialogThemedOverlayClass } from '../alert-dialog/dialog-styles';
25
28
  import type { CreditPricingContext, PricingModalMode } from './types';
26
29
 
27
30
  interface CreditNavButtonProps {
@@ -172,6 +175,30 @@ export function CreditNavButton({
172
175
  );
173
176
 
174
177
  const isOnetimeModal = pricingModal.mode === 'onetime';
178
+ const modalInitialBillingType = useMemo(() => {
179
+ if (isOnetimeModal) {
180
+ return 'onetime';
181
+ }
182
+
183
+ const pricingContext = pricingModal.pricingContext;
184
+ const priceId = pricingContext?.initUserContext?.xSubscription?.priceId;
185
+ if (!pricingContext || !priceId) {
186
+ return undefined;
187
+ }
188
+
189
+ const providerConfig = getActiveProviderConfigUtil(pricingContext.moneyPriceConfig);
190
+ const products: Record<string, SubscriptionProductConfig> =
191
+ providerConfig.subscriptionProducts || providerConfig.products || {};
192
+ for (const product of Object.values(products)) {
193
+ for (const [billingType, plan] of Object.entries(product.plans)) {
194
+ if (plan.priceId === priceId) {
195
+ return billingType;
196
+ }
197
+ }
198
+ }
199
+
200
+ return undefined;
201
+ }, [isOnetimeModal, pricingModal.pricingContext]);
175
202
 
176
203
  return (
177
204
  <CreditNavPopoverContext.Provider value={contextValue}>
@@ -225,7 +252,13 @@ export function CreditNavButton({
225
252
  }))
226
253
  }
227
254
  >
228
- <AlertDialogContent className="mt-5 sm:mt-6 md:mt-10 lg:mt-15 w-[95vw] max-w-[1200px] overflow-hidden border border-slate-200 bg-white p-0 shadow-[0_32px_90px_rgba(15,23,42,0.25)] ring-1 ring-black/5 dark:border-white/12 dark:bg-[#0f1222] dark:shadow-[0_40px_120px_rgba(0,0,0,0.6)] dark:ring-white/10">
255
+ <AlertDialogContent
256
+ className={cn(
257
+ 'mt-5 sm:mt-6 md:mt-10 lg:mt-15 w-[95vw] max-w-[1200px] overflow-hidden border border-slate-200 p-0 shadow-[0_32px_90px_rgba(15,23,42,0.25)] ring-1 ring-black/5 dark:border-white/12 dark:shadow-[0_40px_120px_rgba(0,0,0,0.6)] dark:ring-white/10',
258
+ themeMainBgColor,
259
+ )}
260
+ overlayClassName={dialogThemedOverlayClass}
261
+ >
229
262
  <AlertDialogHeader className="flex flex-row items-center justify-between border-b border-slate-200 px-6 pt-4 pb-1 dark:border-slate-800">
230
263
  <AlertDialogTitle asChild>
231
264
  <div className="flex flex-wrap items-baseline gap-3 text-slate-900 dark:text-white">
@@ -256,7 +289,7 @@ export function CreditNavButton({
256
289
  checkoutApiEndpoint={pricingModal.pricingContext.checkoutApiEndpoint}
257
290
  customerPortalApiEndpoint={pricingModal.pricingContext.customerPortalApiEndpoint}
258
291
  enableSubscriptionUpgrade={pricingModal.pricingContext.enableSubscriptionUpgrade}
259
- initialBillingType={isOnetimeModal ? 'onetime' : undefined}
292
+ initialBillingType={modalInitialBillingType}
260
293
  disableAutoDetectBilling={isOnetimeModal}
261
294
  initUserContext={pricingModal.pricingContext.initUserContext}
262
295
  />
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useClerk } from '@clerk/nextjs';
4
- import { GradientButton } from '@third-ui/fuma/mdx/gradient-button';
4
+ import { GradientButton } from '../buttons';
5
5
  import {
6
6
  BellIcon,
7
7
  ChevronDownIcon,
@@ -0,0 +1,11 @@
1
+ 'use client';
2
+
3
+ export { CreditOverviewClient } from './credit-overview-client';
4
+ export { CreditNavButton } from './credit-nav-button';
5
+ export type { CreditOverviewTranslations } from './credit-overview-client';
6
+ export type {
7
+ CreditOverviewData,
8
+ CreditBucket,
9
+ CreditBucketStatus,
10
+ SubscriptionInfo,
11
+ } from './types';
@@ -0,0 +1,7 @@
1
+ export { CreditOverview } from './credit-overview';
2
+ export type {
3
+ CreditOverviewData,
4
+ CreditBucket,
5
+ CreditBucketStatus,
6
+ SubscriptionInfo,
7
+ } from './types';
package/src/main/cta.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import { getTranslations } from 'next-intl/server';
2
- import { GradientButton } from "@third-ui/fuma/mdx/gradient-button";
2
+ import { GradientButton } from "./buttons";
3
3
  import { cn } from '@windrun-huaiin/lib/utils';
4
4
  import { themeIconColor } from '@windrun-huaiin/base-ui/lib';
5
5
  import { richText } from './rich-text-expert';
@@ -3,9 +3,8 @@ import { MailIcon, ReceiptTextIcon, ShieldUserIcon } from '@windrun-huaiin/base-
3
3
  import Link from "next/link";
4
4
  import { FooterEmail } from './footer-email';
5
5
  import { safeT } from '../lib/t-intl';
6
- import { getAsNeededLocalizedUrl } from '@windrun-huaiin/lib';
6
+ import { cn, getAsNeededLocalizedUrl } from '@windrun-huaiin/lib/utils';
7
7
  import { themeIconColor } from '@windrun-huaiin/base-ui/lib';
8
- import { cn } from '@windrun-huaiin/lib';
9
8
 
10
9
  interface FooterData {
11
10
  terms: string;
@@ -0,0 +1,4 @@
1
+ 'use client';
2
+
3
+ export { HeroMedia } from '../hero-media';
4
+ export { HeroSection } from '../hero-section';
@@ -0,0 +1,7 @@
1
+ export * from '../gallery/gallery-server';
2
+ export * from '../usage';
3
+ export * from '../features';
4
+ export * from '../tips';
5
+ export * from '../faq';
6
+ export * from '../seo-content';
7
+ export * from '../cta';
package/src/main/index.ts CHANGED
@@ -1,28 +1,9 @@
1
1
  'use client';
2
2
 
3
- // Main application Client components
3
+ // Lightweight common client components.
4
4
  export * from './go-to-top';
5
5
  export * from './loading';
6
6
  export * from './nprogress-bar';
7
- export * from './alert-dialog';
8
- export * from './x-button'
9
- export * from './x-toggle-button'
10
- export * from './ai-prompt-textarea'
11
7
  export * from './rich-text-expert'
12
8
  export * from './faq-interactive'
13
- export * from './price-plan-interactive'
14
- export * from './gallery/gallery-interactive'
15
- export * from './delayed-img'
16
- export * from './snake-loading-frame'
17
- export * from './pill-select'
18
- // Money Price Client Components
19
- export { MoneyPriceInteractive } from './money-price/money-price-interactive';
20
- export { MoneyPriceButton } from './money-price/money-price-button';
21
-
22
- export { CreditOverviewClient } from './credit/credit-overview-client';
23
- export { CreditNavButton } from './credit/credit-nav-button';
24
-
25
- export { HeroMedia } from './hero-media';
26
- export { HeroSection } from './hero-section';
27
-
28
9
  export { InfoTooltip } from './info-tooltip';
@@ -76,7 +76,6 @@ export function LanguageDetector({ i18nConfig }: LanguageDetectorProps) {
76
76
 
77
77
  // Use the automatic closing time from the configuration
78
78
  const timer = setTimeout(() => {
79
- console.log('[LanguageDetector] Auto closing after timeout')
80
79
  setShow(false)
81
80
  // Save the rejected state when the automatic closing occurs
82
81
  savePreference(browserLang, 'rejected')
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+
3
+ export { Loading, getLoadingCycleDurationMs } from '../loading';
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+
3
+ export { SnakeLoadingFrame, SnakeLoadingPreview } from '../snake-loading-frame';
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+
3
+ export { MoneyPriceInteractive } from './money-price-interactive';
4
+ export { MoneyPriceButton } from './money-price-button';
5
+ export type {
6
+ MoneyPriceConfig,
7
+ MoneyPriceData,
8
+ InitUserContext,
9
+ MoneyPriceInteractiveProps,
10
+ MoneyPriceButtonProps,
11
+ PaymentProvider,
12
+ PaymentProviderConfig,
13
+ EnhancePricePlan,
14
+ SubscriptionProductConfig,
15
+ CreditPackProductConfig,
16
+ UserContext,
17
+ } from './money-price-types';
18
+ export { UserState } from './money-price-types';
@@ -14,22 +14,25 @@ export function MoneyPriceButton({
14
14
  onAction,
15
15
  texts,
16
16
  isProcessing = false,
17
+ isAnyProcessing = false,
17
18
  isInitLoading = false,
18
19
  enableSubscriptionUpgrade = true
19
20
  }: MoneyPriceButtonProps) {
21
+ const [isLoading, setIsLoading] = useState(false);
20
22
 
21
23
  if (isInitLoading) {
22
24
  return (
23
25
  <div
24
- className="w-full h-11 md:h-12 mt-4 md:mt-auto rounded-full bg-transparent"
26
+ className="relative w-full h-11 md:h-12 mt-4 md:mt-auto overflow-hidden rounded-full bg-gray-100/70 dark:bg-gray-800/40 animate-pulse transition-opacity duration-300 ease-out"
25
27
  aria-hidden="true"
26
28
  data-plan-button-placeholder={planKey}
27
- />
29
+ >
30
+ <div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/50 to-transparent dark:via-white/10" />
31
+ </div>
28
32
  );
29
33
  }
30
34
 
31
35
  const { isAuthenticated, subscriptionStatus } = userContext;
32
- const [isLoading, setIsLoading] = useState(false);
33
36
  const subscriptionBilling = userContext.subscriptionType;
34
37
  const planTier = planKey;
35
38
  const planBilling = billingType;
@@ -224,19 +227,24 @@ export function MoneyPriceButton({
224
227
 
225
228
  if (config.hidden) return null;
226
229
 
230
+ const hasActiveSubscription =
231
+ subscriptionStatus === UserState.ProUser ||
232
+ subscriptionStatus === UserState.UltraUser;
233
+
227
234
  if (
228
235
  !enableSubscriptionUpgrade &&
229
236
  billingType !== 'onetime' &&
237
+ hasActiveSubscription &&
230
238
  config.text === texts.upgrade &&
231
239
  typeof config.onClick === 'function'
232
240
  ) {
233
241
  return null;
234
242
  }
235
243
 
236
- const isBusy = isLoading || isProcessing;
237
- const isDisabled = config.disabled || isBusy;
238
- const displayText = isBusy ? 'Processing...' : config.text;
239
- const isDisabledByConfigOnly = config.disabled && !isBusy;
244
+ const isCurrentButtonBusy = isLoading || isProcessing;
245
+ const isDisabled = config.disabled || isCurrentButtonBusy || isAnyProcessing;
246
+ const displayText = isCurrentButtonBusy ? 'Processing...' : config.text;
247
+ const isDisabledByConfigOnly = config.disabled && !isCurrentButtonBusy && !isAnyProcessing;
240
248
 
241
249
  const handleClick = async (e: React.MouseEvent) => {
242
250
  if (isDisabled) {
@@ -265,9 +273,9 @@ export function MoneyPriceButton({
265
273
  isDisabledByConfigOnly
266
274
  ? 'bg-gray-400 cursor-not-allowed'
267
275
  : themeButtonGradientClass,
268
- !isDisabledByConfigOnly && !isBusy &&
276
+ !isDisabledByConfigOnly && !isCurrentButtonBusy && !isAnyProcessing &&
269
277
  themeButtonGradientHoverClass,
270
- isBusy && !isDisabledByConfigOnly && 'opacity-70 cursor-not-allowed'
278
+ (isCurrentButtonBusy || isAnyProcessing) && !isDisabledByConfigOnly && 'opacity-70 cursor-not-allowed'
271
279
  );
272
280
 
273
281
  return (