@windrun-huaiin/third-ui 30.1.0 → 31.0.1

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 (103) hide show
  1. package/README.md +109 -143
  2. package/dist/ai/ai-prompt-textarea.js +5 -5
  3. package/dist/ai/ai-prompt-textarea.mjs +5 -5
  4. package/dist/clerk/clerk-auth-appearance.d.ts +13 -0
  5. package/dist/clerk/clerk-auth-appearance.js +19 -0
  6. package/dist/clerk/clerk-auth-appearance.mjs +15 -0
  7. package/dist/clerk/clerk-auth-modal-appearance.d.ts +12 -0
  8. package/dist/clerk/clerk-auth-modal-appearance.js +17 -0
  9. package/dist/clerk/clerk-auth-modal-appearance.mjs +14 -0
  10. package/dist/clerk/clerk-page-context-generator.js +3 -3
  11. package/dist/clerk/clerk-page-context-generator.mjs +3 -3
  12. package/dist/clerk/clerk-page-generator.js +4 -4
  13. package/dist/clerk/clerk-page-generator.mjs +4 -4
  14. package/dist/clerk/clerk-user-client.js +2 -1
  15. package/dist/clerk/clerk-user-client.mjs +2 -1
  16. package/dist/clerk/fingerprint/fingerprint-client.d.ts +10 -10
  17. package/dist/clerk/fingerprint/fingerprint-client.js +20 -20
  18. package/dist/clerk/fingerprint/fingerprint-client.mjs +20 -20
  19. package/dist/clerk/fingerprint/fingerprint-provider.d.ts +3 -3
  20. package/dist/clerk/fingerprint/fingerprint-provider.js +8 -8
  21. package/dist/clerk/fingerprint/fingerprint-provider.mjs +8 -8
  22. package/dist/clerk/fingerprint/fingerprint-server.d.ts +12 -12
  23. package/dist/clerk/fingerprint/fingerprint-server.js +17 -17
  24. package/dist/clerk/fingerprint/fingerprint-server.mjs +17 -17
  25. package/dist/clerk/fingerprint/fingerprint-shared.d.ts +3 -3
  26. package/dist/clerk/fingerprint/fingerprint-shared.js +10 -10
  27. package/dist/clerk/fingerprint/fingerprint-shared.mjs +10 -10
  28. package/dist/clerk/fingerprint/types.d.ts +0 -1
  29. package/dist/clerk/fingerprint/use-fingerprint.js +7 -7
  30. package/dist/clerk/fingerprint/use-fingerprint.mjs +7 -7
  31. package/dist/clerk/signin-with-fingerprint-client.d.ts +2 -2
  32. package/dist/clerk/signin-with-fingerprint-client.js +7 -6
  33. package/dist/clerk/signin-with-fingerprint-client.mjs +7 -6
  34. package/dist/clerk/signup-button-with-fingerprint-client.js +6 -4
  35. package/dist/clerk/signup-button-with-fingerprint-client.mjs +6 -4
  36. package/dist/clerk/signup-with-fingerprint-client.d.ts +2 -2
  37. package/dist/clerk/signup-with-fingerprint-client.js +7 -6
  38. package/dist/clerk/signup-with-fingerprint-client.mjs +7 -6
  39. package/dist/fuma/heavy/mermaid.js +1 -1
  40. package/dist/fuma/heavy/mermaid.mjs +1 -1
  41. package/dist/fuma/mdx/fuma-github-info.d.ts +1 -2
  42. package/dist/fuma/mdx/fuma-github-info.js +3 -6
  43. package/dist/fuma/mdx/fuma-github-info.mjs +3 -6
  44. package/dist/fuma/site-x.js +0 -1
  45. package/dist/fuma/site-x.mjs +0 -1
  46. package/dist/main/calendar/calendar-date-range-input.js +1 -1
  47. package/dist/main/calendar/calendar-date-range-input.mjs +1 -1
  48. package/dist/main/credit/credit-overview-nav-client.d.ts +12 -0
  49. package/dist/main/credit/credit-overview-nav-client.js +65 -0
  50. package/dist/main/credit/credit-overview-nav-client.mjs +63 -0
  51. package/dist/main/credit/index.d.ts +2 -0
  52. package/dist/main/credit/index.js +2 -0
  53. package/dist/main/credit/index.mjs +1 -0
  54. package/dist/main/credit/types.d.ts +8 -8
  55. package/dist/main/money-price/index.d.ts +1 -1
  56. package/dist/main/money-price/money-price-button.js +10 -10
  57. package/dist/main/money-price/money-price-button.mjs +10 -10
  58. package/dist/main/money-price/money-price-config-util.d.ts +30 -30
  59. package/dist/main/money-price/money-price-config-util.js +48 -48
  60. package/dist/main/money-price/money-price-config-util.mjs +48 -48
  61. package/dist/main/money-price/money-price-interactive.js +30 -18
  62. package/dist/main/money-price/money-price-interactive.mjs +30 -18
  63. package/dist/main/money-price/money-price-types.d.ts +7 -1
  64. package/dist/main/money-price/money-price-types.js +2 -2
  65. package/dist/main/money-price/money-price-types.mjs +2 -2
  66. package/dist/main/money-price/server.d.ts +1 -1
  67. package/dist/main/pill-select/x-pill-select.js +2 -2
  68. package/dist/main/pill-select/x-pill-select.mjs +2 -2
  69. package/dist/main/server.d.ts +1 -1
  70. package/package.json +13 -7
  71. package/src/ai/ai-prompt-textarea.tsx +6 -6
  72. package/src/clerk/clerk-auth-appearance.ts +16 -0
  73. package/src/clerk/clerk-page-context-generator.tsx +3 -5
  74. package/src/clerk/clerk-page-generator.tsx +9 -8
  75. package/src/clerk/clerk-user-client.tsx +14 -5
  76. package/src/clerk/fingerprint/fingerprint-client.ts +20 -20
  77. package/src/clerk/fingerprint/fingerprint-provider.tsx +11 -11
  78. package/src/clerk/fingerprint/fingerprint-server.ts +17 -17
  79. package/src/clerk/fingerprint/fingerprint-shared.ts +10 -10
  80. package/src/clerk/fingerprint/types.ts +0 -1
  81. package/src/clerk/fingerprint/use-fingerprint.ts +7 -7
  82. package/src/clerk/signin-with-fingerprint-client.tsx +7 -7
  83. package/src/clerk/signup-button-with-fingerprint-client.tsx +7 -5
  84. package/src/clerk/signup-with-fingerprint-client.tsx +7 -7
  85. package/src/fuma/base/custom-home-layout.tsx +4 -4
  86. package/src/fuma/heavy/mermaid.tsx +1 -1
  87. package/src/fuma/mdx/fuma-github-info.tsx +3 -8
  88. package/src/fuma/site-x.tsx +0 -1
  89. package/src/main/calendar/calendar-date-range-input.tsx +1 -1
  90. package/src/main/credit/credit-overview-nav-client.tsx +95 -0
  91. package/src/main/credit/index.ts +5 -0
  92. package/src/main/credit/types.ts +8 -8
  93. package/src/main/gallery/gallery-mobile-swiper.tsx +0 -1
  94. package/src/main/gallery/gallery-server.tsx +2 -2
  95. package/src/main/money-price/index.ts +2 -0
  96. package/src/main/money-price/money-price-button.tsx +10 -10
  97. package/src/main/money-price/money-price-config-util.ts +49 -49
  98. package/src/main/money-price/money-price-interactive.tsx +40 -20
  99. package/src/main/money-price/money-price-types.ts +21 -14
  100. package/src/main/money-price/server.ts +2 -0
  101. package/src/main/pill-select/x-pill-select.tsx +2 -2
  102. package/src/main/server.ts +3 -1
  103. package/src/styles/third-ui.css +8 -0
@@ -109,7 +109,7 @@ function useFingerprintStatusTranslations(): FingerprintStatusTranslations {
109
109
 
110
110
  /**
111
111
  * Fingerprint Provider Component
112
- * 为应用提供fingerprint和匿名用户管理功能
112
+ * Provides fingerprint and anonymous user management for the application.
113
113
  */
114
114
  export function FingerprintProvider({
115
115
  children,
@@ -137,7 +137,7 @@ export function useFingerprintContext(): FingerprintContextType {
137
137
 
138
138
  /**
139
139
  * Safe hook to use fingerprint context - returns null if no provider
140
- * 安全版本的fingerprint context hook - 如果没有Provider则返回null
140
+ * Returns null when no provider is available.
141
141
  */
142
142
  export function useFingerprintContextSafe(): FingerprintContextType | null {
143
143
  const context = useContext(FingerprintContext);
@@ -162,7 +162,7 @@ export function withFingerprint<P extends object>(
162
162
  }
163
163
 
164
164
  /**
165
- * 组件:显示用户状态和积分信息(用于调试)
165
+ * Component for displaying user status and credit information for debugging.
166
166
  */
167
167
  export function FingerprintStatus() {
168
168
  const translations = useFingerprintStatusTranslations();
@@ -400,7 +400,7 @@ export function FingerprintStatus() {
400
400
 
401
401
  return (
402
402
  <>
403
- {/* 灯泡按钮 */}
403
+ {/* Lightbulb button */}
404
404
  {!isOpen && (
405
405
  <button
406
406
  onClick={handleToggle}
@@ -417,7 +417,7 @@ export function FingerprintStatus() {
417
417
  </button>
418
418
  )}
419
419
 
420
- {/* 面板 */}
420
+ {/* Panel */}
421
421
  {isOpen && (
422
422
  <>
423
423
  <div onClick={handleBackdropClick} className="fixed inset-0 z-9998 bg-black/60 backdrop-blur-sm" />
@@ -622,9 +622,9 @@ export function FingerprintStatus() {
622
622
  }
623
623
 
624
624
 
625
- /* ==================== 新增辅助组件 ==================== */
625
+ /* ==================== Helper Components ==================== */
626
626
 
627
- // 标题行:左侧图标+标题,右侧信息(右对齐)
627
+ // Header row with icon and title on the left, right-aligned metadata on the right.
628
628
  function PanelHeader({ icon, title, rightInfo }: { icon: React.ReactNode; title: string; rightInfo: React.ReactNode }) {
629
629
  return (
630
630
  <div className="flex items-center justify-between mb-3">
@@ -643,7 +643,7 @@ function PanelHeader({ icon, title, rightInfo }: { icon: React.ReactNode; title:
643
643
  );
644
644
  }
645
645
 
646
- // 复用:普通 PanelSection 支持右侧信息
646
+ // Reusable PanelSection with optional right-side metadata.
647
647
  interface PanelSectionProps {
648
648
  icon: React.ReactNode;
649
649
  title: string;
@@ -727,19 +727,19 @@ function StatusTag({
727
727
  const normalized = value.toLowerCase();
728
728
 
729
729
  const colorMap: Record<string, string> = {
730
- // 绿色:正常/活跃
730
+ // Green: normal or active.
731
731
  registered: 'bg-green-100 text-green-700 dark:bg-green-500/20 dark:text-green-300',
732
732
  active: 'bg-green-100 text-green-700 dark:bg-green-500/20 dark:text-green-300',
733
733
  trialing: 'bg-green-100 text-green-700 dark:bg-green-500/20 dark:text-green-300',
734
734
 
735
- // 灰色:失效/删除
735
+ // Gray: inactive or deleted.
736
736
  canceled: 'bg-slate-100 text-slate-600 dark:bg-slate-700 dark:text-slate-400',
737
737
  frozen: 'bg-slate-100 text-slate-600 dark:bg-slate-700 dark:text-slate-400',
738
738
  deleted: 'bg-slate-100 text-slate-600 dark:bg-slate-700 dark:text-slate-400',
739
739
  expired: 'bg-slate-100 text-slate-600 dark:bg-slate-700 dark:text-slate-400',
740
740
  past_due: 'bg-slate-100 text-slate-600 dark:bg-slate-700 dark:text-slate-400',
741
741
 
742
- // 橙色:待处理/异常
742
+ // Amber: pending or exceptional states.
743
743
  pending: 'bg-amber-100 text-amber-700 dark:bg-amber-500/20 dark:text-amber-300',
744
744
  failed: 'bg-amber-100 text-amber-700 dark:bg-amber-500/20 dark:text-amber-300',
745
745
  unpaid: 'bg-amber-100 text-amber-700 dark:bg-amber-500/20 dark:text-amber-300',
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Fingerprint Server Utilities
3
- * 服务端专用的指纹ID提取和验证逻辑
4
- * 可以安全地在服务端使用,不依赖浏览器API或FingerprintJS
3
+ * Server-only fingerprint ID extraction and validation logic.
4
+ * Safe for server usage without browser APIs or FingerprintJS.
5
5
  */
6
6
 
7
7
  import {
@@ -11,16 +11,16 @@ import {
11
11
  } from './fingerprint-shared';
12
12
 
13
13
  /**
14
- * 从请求中提取fingerprint ID
15
- * 优先级:header > cookie > query参数
16
- * 可以安全地在服务端使用
14
+ * Extract fingerprint ID from a request.
15
+ * Priority: header > cookie > query parameter.
16
+ * Safe to use on the server.
17
17
  */
18
18
  export function extractFingerprintId(
19
19
  headers: Headers | Record<string, string>,
20
20
  cookies?: Record<string, string>,
21
21
  query?: Record<string, string | undefined>
22
22
  ): string | null {
23
- // 1. header中获取
23
+ // 1. Read from header.
24
24
  const headerValue = headers instanceof Headers
25
25
  ? headers.get(FINGERPRINT_HEADER_NAME)
26
26
  : headers[FINGERPRINT_HEADER_NAME];
@@ -29,7 +29,7 @@ export function extractFingerprintId(
29
29
  return headerValue;
30
30
  }
31
31
 
32
- // 2. cookie中获取
32
+ // 2. Read from cookie.
33
33
  if (cookies) {
34
34
  const cookieValue = cookies[FINGERPRINT_COOKIE_NAME];
35
35
  if (cookieValue && isValidFingerprintId(cookieValue)) {
@@ -37,7 +37,7 @@ export function extractFingerprintId(
37
37
  }
38
38
  }
39
39
 
40
- // 3. query参数中获取
40
+ // 3. Read from query parameters.
41
41
  if (query) {
42
42
  const queryValue = query.fingerprint_id || query.fp_id;
43
43
  if (queryValue && isValidFingerprintId(queryValue)) {
@@ -49,22 +49,22 @@ export function extractFingerprintId(
49
49
  }
50
50
 
51
51
  /**
52
- * 生成服务端降级fingerprint ID
53
- * 当客户端无法生成fingerprint时使用
54
- * 可以安全地在服务端使用
52
+ * Generate a server-side fallback fingerprint ID.
53
+ * Used when the client cannot generate a fingerprint.
54
+ * Safe to use on the server.
55
55
  */
56
56
  export function generateServerFingerprintId(): string {
57
57
  return `fp_server_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
58
58
  }
59
59
 
60
60
  /**
61
- * Next.js Request对象中提取fingerprint ID
62
- * 便捷方法,适用于Next.js API路由
61
+ * Extract fingerprint ID from a Next.js Request object.
62
+ * Convenience helper for Next.js API routes.
63
63
  */
64
64
  export function extractFingerprintFromNextRequest(request: Request): string | null {
65
65
  const headers = request.headers;
66
66
 
67
- // 尝试从cookies获取(需要解析cookie header
67
+ // Try cookies by parsing the cookie header.
68
68
  const cookieHeader = headers.get('cookie');
69
69
  const cookies: Record<string, string> = {};
70
70
 
@@ -77,7 +77,7 @@ export function extractFingerprintFromNextRequest(request: Request): string | nu
77
77
  });
78
78
  }
79
79
 
80
- // 尝试从URL query参数获取
80
+ // Try URL query parameters.
81
81
  const url = new URL(request.url);
82
82
  const query: Record<string, string> = {};
83
83
  url.searchParams.forEach((value, key) => {
@@ -93,8 +93,8 @@ type NextCookiesLike = {
93
93
  };
94
94
 
95
95
  /**
96
- * Next.js runtime提供的headers/cookies实例里提取fingerprint ID
97
- * App Router服务端组件和Server Actions直接复用
96
+ * Extract fingerprint ID from Next.js runtime headers/cookies stores.
97
+ * Reusable in App Router server components and Server Actions.
98
98
  */
99
99
  export function extractFingerprintFromNextStores(params: {
100
100
  headers: NextHeadersLike;
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Fingerprint Shared Utilities
3
- * 客户端和服务端共享的常量、类型和验证逻辑
3
+ * Shared constants, types, and validation logic for client and server.
4
4
  */
5
5
 
6
- // Fingerprint ID的存储键和header
6
+ // Storage keys and header names for fingerprint IDs.
7
7
  export const FINGERPRINT_STORAGE_KEY = '__x_fingerprint_id';
8
8
  export const FINGERPRINT_DEBUG_OVERRIDE_STORAGE_KEY = '__x_fingerprint_debug_override';
9
9
  export const FINGERPRINT_HEADER_NAME = 'x-fingerprint-id-v8';
@@ -15,16 +15,16 @@ export const FINGERPRINT_FIRST_TOUCH_HEADER = 'x-first-touch';
15
15
  export const FINGERPRINT_DEBUG_PREFIX = 'fp_test_dbg_';
16
16
 
17
17
  /**
18
- * 验证fingerprint ID格式
19
- * 可以在客户端和服务端使用
18
+ * Validate fingerprint ID format.
19
+ * Safe to use on both client and server.
20
20
  */
21
21
  export function isValidFingerprintId(fingerprintId: string): boolean {
22
22
  if (!fingerprintId) return false;
23
- // 支持多种格式:
24
- // - fp_ + FingerprintJS visitorId (变长字符串)
25
- // - fp_fallback_ + 时间戳_随机字符串 (客户端降级方案)
26
- // - fp_server_ + 时间戳_随机字符串 (服务端降级)
27
- // - fp_test_dbg_ + 时间戳_随机字符串 (调试并发测试)
23
+ // Supported formats:
24
+ // - fp_ + FingerprintJS visitorId with variable length.
25
+ // - fp_fallback_ + timestamp + random string for client fallback.
26
+ // - fp_server_ + timestamp + random string for server fallback.
27
+ // - fp_test_dbg_ + timestamp + random string for debug concurrency tests.
28
28
  return /^fp(_fallback|_server|_test_dbg)?_[a-zA-Z0-9_]+$/.test(fingerprintId);
29
29
  }
30
30
 
@@ -36,7 +36,7 @@ export function isDebugFingerprintId(fingerprintId: string | null | undefined):
36
36
  return fingerprintId.startsWith(FINGERPRINT_DEBUG_PREFIX);
37
37
  }
38
38
 
39
- // 常量导出
39
+ // Exported constants.
40
40
  export const FINGERPRINT_CONSTANTS = {
41
41
  STORAGE_KEY: FINGERPRINT_STORAGE_KEY,
42
42
  DEBUG_OVERRIDE_STORAGE_KEY: FINGERPRINT_DEBUG_OVERRIDE_STORAGE_KEY,
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * Fingerprint System Types
3
- * 指纹识别系统的类型定义
4
3
  */
5
4
 
6
5
  export interface XUser {
@@ -21,7 +21,7 @@ import { FINGERPRINT_SOURCE_REFER, isDebugFingerprintId, isValidFingerprintId }
21
21
  * Accepts configuration to customize API endpoint and behavior
22
22
  */
23
23
  export function useFingerprint(config: FingerprintConfig): UseFingerprintResult {
24
- // 服务端渲染检查
24
+ // Server-side rendering guard.
25
25
  if (typeof window === 'undefined') {
26
26
  return {
27
27
  fingerprintId: null,
@@ -51,7 +51,7 @@ export function useFingerprint(config: FingerprintConfig): UseFingerprintResult
51
51
  }, []);
52
52
 
53
53
  /**
54
- * 第一阶段:初始化fingerprint ID
54
+ * Phase 1: initialize fingerprint ID.
55
55
  */
56
56
  const initializeFingerprintId = useCallback(async () => {
57
57
  try {
@@ -68,7 +68,7 @@ export function useFingerprint(config: FingerprintConfig): UseFingerprintResult
68
68
  }, []);
69
69
 
70
70
  /**
71
- * 第二阶段:初始化匿名用户
71
+ * Phase 2: initialize anonymous user.
72
72
  */
73
73
  const initializeAnonymousUser = useCallback(async () => {
74
74
  if (!fingerprintId) {
@@ -140,7 +140,7 @@ export function useFingerprint(config: FingerprintConfig): UseFingerprintResult
140
140
  }, [fingerprintId, config.apiEndpoint, isInitialized]);
141
141
 
142
142
  /**
143
- * 刷新用户数据 - 使用POST请求(后端支持upsert逻辑)
143
+ * Refresh user data with a POST request; the backend supports upsert semantics.
144
144
  */
145
145
  const refreshUserData = useCallback(async () => {
146
146
  if (!fingerprintId) {
@@ -180,7 +180,7 @@ export function useFingerprint(config: FingerprintConfig): UseFingerprintResult
180
180
  }
181
181
  }, [fingerprintId, config.apiEndpoint]);
182
182
 
183
- // 第一阶段:页面加载完成后生成指纹ID
183
+ // Phase 1: generate fingerprint ID after page load.
184
184
  useEffect(() => {
185
185
  const initFingerprint = async () => {
186
186
  setIsLoading(true);
@@ -191,11 +191,11 @@ export function useFingerprint(config: FingerprintConfig): UseFingerprintResult
191
191
  initFingerprint();
192
192
  }, [initializeFingerprintId]);
193
193
 
194
- // 第二阶段:有指纹ID后直接初始化用户(后端支持upsert逻辑)
194
+ // Phase 2: initialize the user once a fingerprint ID is available; the backend supports upsert semantics.
195
195
  useEffect(() => {
196
196
  if (!fingerprintId || isInitialized || isLoading || error || config.autoInitialize === false) return;
197
197
 
198
- // 直接使用 POST 请求,后端会处理查询-不存在则创建的逻辑
198
+ // Use POST directly; the backend handles lookup and create-if-missing behavior.
199
199
  initializeAnonymousUser();
200
200
  }, [fingerprintId, isInitialized, isLoading, error, initializeAnonymousUser, config.autoInitialize]);
201
201
 
@@ -2,17 +2,18 @@
2
2
 
3
3
  import { SignIn } from '@clerk/nextjs';
4
4
  import { useEffect } from 'react';
5
+ import { clerkAuthPageAppearance } from './clerk-auth-appearance';
5
6
  import { useFingerprintContextSafe } from './fingerprint/fingerprint-provider';
6
7
 
7
8
  /**
8
9
  * SignIn component with fingerprint awareness
9
- * 如果没有FingerprintProvider,会优雅降级为普通SignIn组件
10
- * 如果有FingerprintProvider,会处理fingerprint相关逻辑
10
+ * Falls back to the standard SignIn component when FingerprintProvider is absent.
11
+ * Handles fingerprint-related metadata when FingerprintProvider is available.
11
12
  */
12
13
  export function SignInWithFingerprint() {
13
14
  const fingerprintContext = useFingerprintContextSafe();
14
15
 
15
- // 如果没有fingerprint context,使用默认值
16
+ // Use defaults when fingerprint context is unavailable.
16
17
  const {
17
18
  fingerprintId = null,
18
19
  xUser = null,
@@ -20,19 +21,18 @@ export function SignInWithFingerprint() {
20
21
  initializeAnonymousUser = async () => {}
21
22
  } = fingerprintContext || {};
22
23
 
23
- // 准备传递给Clerkmetadata,包含匿名用户信息
24
+ // Prepare Clerk metadata with anonymous user information.
24
25
  const unsafeMetadata = {
25
26
  user_id: xUser?.userId || null,
26
27
  fingerprint_id: fingerprintId || null,
27
28
  };
28
29
 
29
- // 确保匿名用户已初始化
30
+ // Ensure the anonymous user has been initialized.
30
31
  useEffect(() => {
31
32
  if (!isInitialized && fingerprintId) {
32
33
  initializeAnonymousUser();
33
34
  }
34
35
  }, [fingerprintId, isInitialized, initializeAnonymousUser]);
35
36
 
36
- return <SignIn unsafeMetadata={unsafeMetadata} />;
37
+ return <SignIn appearance={clerkAuthPageAppearance} unsafeMetadata={unsafeMetadata} />;
37
38
  }
38
-
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { SignUpButton, useClerk } from '@clerk/nextjs';
4
4
  import { useEffect } from 'react';
5
+ import { clerkAuthModalAppearance } from './clerk-auth-appearance';
5
6
  import { useFingerprintContextSafe } from './fingerprint/fingerprint-provider';
6
7
 
7
8
  interface SignUpButtonWithFingerprintProps {
@@ -15,7 +16,7 @@ export function SignUpButtonWithFingerprint({
15
16
  }: SignUpButtonWithFingerprintProps) {
16
17
  if (mode === 'redirect') {
17
18
  return (
18
- // 重定向模式则直接跳转到自定义注册页面
19
+ // Redirect mode navigates directly to the custom sign-up page.
19
20
  <SignUpButton>
20
21
  <button
21
22
  className="w-16 sm:w-20 h-8 sm:h-9 px-1.5 sm:px-2 border border-gray-300 rounded-full hover:bg-gray-100 dark:border-gray-600 dark:hover:bg-gray-800 text-center text-xs sm:text-sm whitespace-nowrap"
@@ -26,10 +27,10 @@ export function SignUpButtonWithFingerprint({
26
27
  )
27
28
  }
28
29
 
29
- // 弹框模式则需要自定义注册按钮
30
+ // Modal mode requires a custom sign-up button.
30
31
  const fingerprintContext = useFingerprintContextSafe();
31
32
 
32
- // 如果没有fingerprint context,使用默认值
33
+ // Use defaults when fingerprint context is unavailable.
33
34
  const {
34
35
  fingerprintId = null,
35
36
  xUser = null,
@@ -43,7 +44,7 @@ export function SignUpButtonWithFingerprint({
43
44
  fingerprint_id: fingerprintId || null,
44
45
  };
45
46
 
46
- // 确保匿名用户已初始化
47
+ // Ensure the anonymous user has been initialized.
47
48
  useEffect(() => {
48
49
  if (!isInitialized && fingerprintId) {
49
50
  initializeAnonymousUser();
@@ -55,6 +56,7 @@ export function SignUpButtonWithFingerprint({
55
56
 
56
57
  const handleClick = () => {
57
58
  openSignUp({
59
+ appearance: clerkAuthModalAppearance,
58
60
  unsafeMetadata,
59
61
  });
60
62
  };
@@ -67,4 +69,4 @@ export function SignUpButtonWithFingerprint({
67
69
  {signUp}
68
70
  </button>
69
71
  );
70
- }
72
+ }
@@ -2,17 +2,18 @@
2
2
 
3
3
  import { SignUp } from '@clerk/nextjs';
4
4
  import { useEffect } from 'react';
5
+ import { clerkAuthPageAppearance } from './clerk-auth-appearance';
5
6
  import { useFingerprintContextSafe } from './fingerprint/fingerprint-provider';
6
7
 
7
8
  /**
8
9
  * SignUp component with fingerprint awareness
9
- * 如果没有FingerprintProvider,会优雅降级为普通SignUp组件
10
- * 如果有FingerprintProvider,会处理fingerprint相关逻辑
10
+ * Falls back to the standard SignUp component when FingerprintProvider is absent.
11
+ * Handles fingerprint-related metadata when FingerprintProvider is available.
11
12
  */
12
13
  export function SignUpWithFingerprint() {
13
14
  const fingerprintContext = useFingerprintContextSafe();
14
15
 
15
- // 如果没有fingerprint context,使用默认值
16
+ // Use defaults when fingerprint context is unavailable.
16
17
  const {
17
18
  fingerprintId = null,
18
19
  xUser = null,
@@ -20,19 +21,18 @@ export function SignUpWithFingerprint() {
20
21
  initializeAnonymousUser = async () => {}
21
22
  } = fingerprintContext || {};
22
23
 
23
- // 准备传递给Clerkmetadata,包含匿名用户信息
24
+ // Prepare Clerk metadata with anonymous user information.
24
25
  const unsafeMetadata = {
25
26
  user_id: xUser?.userId || null,
26
27
  fingerprint_id: fingerprintId || null,
27
28
  };
28
29
 
29
- // 确保匿名用户已初始化
30
+ // Ensure the anonymous user has been initialized.
30
31
  useEffect(() => {
31
32
  if (!isInitialized && fingerprintId) {
32
33
  initializeAnonymousUser();
33
34
  }
34
35
  }, [fingerprintId, isInitialized, initializeAnonymousUser]);
35
36
 
36
- return <SignUp unsafeMetadata={unsafeMetadata} />;
37
+ return <SignUp appearance={clerkAuthPageAppearance} unsafeMetadata={unsafeMetadata} />;
37
38
  }
38
-
@@ -11,10 +11,10 @@ import {
11
11
  type MobileMenuAction,
12
12
  } from './custom-header';
13
13
 
14
- // - bannerHeight/headerHeight 换成你项目期望的 rem 值即可(如果没有 Banner 就把 bannerHeight 设成 0)。
15
- // - layoutStyle 同时把变量传给 HomeLayout main 元素,这样内容整体会往下错开,不需要 has-banner/no-banner class。
16
- // - CustomHomeHeader 直接接受 HomeLayout 的各类 props(linksnavsearchTogglethemeSwitchi18n 等),内部会复用 Fumadocs 原本的导航功能。
17
- // - Banner 部分仍然可以用你现有的 FumaBannerSuit(或者任何自定义 Banner 组件),因为 Header 是固定定位、z-index 也处理好了,开关只影响 bannerHeight
14
+ // - Set bannerHeight/headerHeight to the rem values expected by the project. Use bannerHeight = 0 when there is no banner.
15
+ // - layoutStyle passes the variables to HomeLayout's main element, offsetting content without has-banner/no-banner classes.
16
+ // - CustomHomeHeader accepts HomeLayout props such as links, nav, searchToggle, themeSwitch, and i18n, then reuses Fumadocs navigation behavior.
17
+ // - The banner can still use FumaBannerSuit or any custom banner component. Header positioning and z-index are already handled; the toggle only affects bannerHeight.
18
18
 
19
19
  export interface CustomHomeLayoutProps {
20
20
  locale: string;
@@ -179,7 +179,7 @@ export function Mermaid({ chart, title, watermarkEnabled, watermarkText, handDra
179
179
  // prevent browser-level zoom (touchpad pinch/shortcut) from taking effect when the dialog is open
180
180
  useEffect(() => {
181
181
  if (!open) return;
182
- // 初次打开时,默认放大到 400%
182
+ // Init default zoom out 400%
183
183
  resetTransform();
184
184
  const onGlobalWheel = (ev: WheelEvent) => {
185
185
  if (ev.ctrlKey || ev.metaKey) {
@@ -6,7 +6,6 @@ import { ExternalLinkIcon, StarIcon } from '@windrun-huaiin/base-ui/icons';
6
6
  interface FumaGithubInfoProps {
7
7
  owner: string;
8
8
  repo: string;
9
- token?: string;
10
9
  className?: string;
11
10
  }
12
11
 
@@ -16,7 +15,7 @@ interface GitHubRepoData {
16
15
  }
17
16
 
18
17
  // Loading state component
19
- function GitHubInfoSkeleton({ owner, repo, className }: Pick<FumaGithubInfoProps, 'owner' | 'repo' | 'className'>) {
18
+ function GitHubInfoSkeleton({ className }: Pick<FumaGithubInfoProps, 'owner' | 'repo' | 'className'>) {
20
19
  return (
21
20
  <div className={`flex flex-col gap-1.5 p-2 rounded-lg text-sm text-fd-foreground/80 lg:flex-row lg:items-center animate-pulse ${className}`}>
22
21
  <div className="flex items-center gap-2">
@@ -114,7 +113,7 @@ function humanizeNumber(num: number): string {
114
113
  * - 🎨 Three states: loading, success, error
115
114
  * - 💯 Not affected by network issues causing page crashes
116
115
  */
117
- export function FumaGithubInfo({ owner, repo, token, className }: FumaGithubInfoProps) {
116
+ export function FumaGithubInfo({ owner, repo, className }: FumaGithubInfoProps) {
118
117
  const [data, setData] = useState<GitHubRepoData | null>(null);
119
118
  const [loading, setLoading] = useState(true);
120
119
  const [error, setError] = useState<string | null>(null);
@@ -133,10 +132,6 @@ export function FumaGithubInfo({ owner, repo, token, className }: FumaGithubInfo
133
132
  'Accept': 'application/vnd.github.v3+json',
134
133
  });
135
134
 
136
- if (token) {
137
- headers.set('Authorization', `Bearer ${token}`);
138
- }
139
-
140
135
  const response = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
141
136
  signal: controller.signal,
142
137
  headers,
@@ -170,7 +165,7 @@ export function FumaGithubInfo({ owner, repo, token, className }: FumaGithubInfo
170
165
  };
171
166
 
172
167
  fetchRepoData();
173
- }, [owner, repo, token]);
168
+ }, [owner, repo]);
174
169
 
175
170
  // Loading state
176
171
  if (loading) {
@@ -10,7 +10,6 @@ export type SiteXProps = Omit<HTMLAttributes<HTMLSpanElement>, 'type'> & {
10
10
  };
11
11
 
12
12
  export async function SiteX({ locale, type, namespace, tKey, className, ...props }: SiteXProps) {
13
- // 默认命名空间和key
14
13
  let ns = namespace;
15
14
  let key = tKey;
16
15
  if (!ns) {
@@ -29,7 +29,7 @@ export type CalendarDateRangeInputProps = {
29
29
 
30
30
  type DateRangeInputPressKey = 'clear';
31
31
 
32
- const DEFAULT_PLACEHOLDER = '滑动窗口日期';
32
+ const DEFAULT_PLACEHOLDER = 'Slide pick Date';
33
33
  const DEFAULT_RANGE_DAYS = 7;
34
34
  const CLEAR_PRESS_FEEDBACK_MS = 180;
35
35
 
@@ -0,0 +1,95 @@
1
+ 'use client';
2
+
3
+ import { useAuth } from '@clerk/nextjs';
4
+ import { useEffect, useState } from 'react';
5
+ import { CreditNavButton } from './credit-nav-button';
6
+ import { CreditOverviewClient, type CreditOverviewTranslations } from './credit-overview-client';
7
+ import type { CreditOverviewData } from './types';
8
+
9
+ export interface CreditOverviewPayload {
10
+ data: CreditOverviewData;
11
+ totalLabel: string;
12
+ translations: CreditOverviewTranslations;
13
+ }
14
+
15
+ export interface CreditOverviewNavClientProps {
16
+ locale: string;
17
+ endpoint: string;
18
+ }
19
+
20
+ function buildCreditOverviewUrl(endpoint: string, locale: string) {
21
+ const url = new URL(endpoint, window.location.origin);
22
+ url.searchParams.set('locale', locale);
23
+ return url.toString();
24
+ }
25
+
26
+ export function CreditOverviewNavClient({
27
+ locale,
28
+ endpoint,
29
+ }: CreditOverviewNavClientProps) {
30
+ const { isLoaded, isSignedIn, userId } = useAuth();
31
+ const [payload, setPayload] = useState<CreditOverviewPayload | null>(null);
32
+
33
+ useEffect(() => {
34
+ if (!isLoaded) {
35
+ return;
36
+ }
37
+
38
+ if (!isSignedIn) {
39
+ setPayload(null);
40
+ return;
41
+ }
42
+
43
+ const controller = new AbortController();
44
+
45
+ async function loadCreditOverview() {
46
+ try {
47
+ const response = await fetch(buildCreditOverviewUrl(endpoint, locale), {
48
+ credentials: 'same-origin',
49
+ signal: controller.signal,
50
+ });
51
+
52
+ if (!response.ok) {
53
+ if (!controller.signal.aborted) {
54
+ setPayload(null);
55
+ }
56
+ return;
57
+ }
58
+
59
+ const nextPayload = (await response.json()) as CreditOverviewPayload | null;
60
+ if (!controller.signal.aborted) {
61
+ setPayload(nextPayload);
62
+ }
63
+ } catch (error) {
64
+ if (!controller.signal.aborted) {
65
+ setPayload(null);
66
+ console.warn('[CreditOverviewNavClient] Failed to load credit overview', error);
67
+ }
68
+ }
69
+ }
70
+
71
+ loadCreditOverview();
72
+
73
+ return () => {
74
+ controller.abort();
75
+ };
76
+ }, [endpoint, isLoaded, isSignedIn, locale, userId]);
77
+
78
+ if (!payload) {
79
+ return null;
80
+ }
81
+
82
+ return (
83
+ <CreditNavButton
84
+ locale={locale}
85
+ totalBalance={payload.data.totalBalance}
86
+ totalLabel={payload.totalLabel}
87
+ >
88
+ <CreditOverviewClient
89
+ locale={locale}
90
+ data={payload.data}
91
+ translations={payload.translations}
92
+ />
93
+ </CreditNavButton>
94
+ );
95
+ }
@@ -1,8 +1,13 @@
1
1
  'use client';
2
2
 
3
3
  export { CreditOverviewClient } from './credit-overview-client';
4
+ export { CreditOverviewNavClient } from './credit-overview-nav-client';
4
5
  export { CreditNavButton } from './credit-nav-button';
5
6
  export type { CreditOverviewTranslations } from './credit-overview-client';
7
+ export type {
8
+ CreditOverviewNavClientProps,
9
+ CreditOverviewPayload,
10
+ } from './credit-overview-nav-client';
6
11
  export type {
7
12
  CreditOverviewData,
8
13
  CreditBucket,