@windrun-huaiin/third-ui 30.1.0 → 31.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 (91) 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/site-x.js +0 -1
  42. package/dist/fuma/site-x.mjs +0 -1
  43. package/dist/main/calendar/calendar-date-range-input.js +1 -1
  44. package/dist/main/calendar/calendar-date-range-input.mjs +1 -1
  45. package/dist/main/credit/types.d.ts +8 -8
  46. package/dist/main/money-price/index.d.ts +1 -1
  47. package/dist/main/money-price/money-price-button.js +10 -10
  48. package/dist/main/money-price/money-price-button.mjs +10 -10
  49. package/dist/main/money-price/money-price-config-util.d.ts +30 -30
  50. package/dist/main/money-price/money-price-config-util.js +48 -48
  51. package/dist/main/money-price/money-price-config-util.mjs +48 -48
  52. package/dist/main/money-price/money-price-interactive.js +30 -18
  53. package/dist/main/money-price/money-price-interactive.mjs +30 -18
  54. package/dist/main/money-price/money-price-types.d.ts +7 -1
  55. package/dist/main/money-price/money-price-types.js +2 -2
  56. package/dist/main/money-price/money-price-types.mjs +2 -2
  57. package/dist/main/money-price/server.d.ts +1 -1
  58. package/dist/main/pill-select/x-pill-select.js +2 -2
  59. package/dist/main/pill-select/x-pill-select.mjs +2 -2
  60. package/dist/main/server.d.ts +1 -1
  61. package/package.json +13 -7
  62. package/src/ai/ai-prompt-textarea.tsx +6 -6
  63. package/src/clerk/clerk-auth-appearance.ts +16 -0
  64. package/src/clerk/clerk-page-context-generator.tsx +3 -5
  65. package/src/clerk/clerk-page-generator.tsx +9 -8
  66. package/src/clerk/clerk-user-client.tsx +14 -5
  67. package/src/clerk/fingerprint/fingerprint-client.ts +20 -20
  68. package/src/clerk/fingerprint/fingerprint-provider.tsx +11 -11
  69. package/src/clerk/fingerprint/fingerprint-server.ts +17 -17
  70. package/src/clerk/fingerprint/fingerprint-shared.ts +10 -10
  71. package/src/clerk/fingerprint/types.ts +0 -1
  72. package/src/clerk/fingerprint/use-fingerprint.ts +7 -7
  73. package/src/clerk/signin-with-fingerprint-client.tsx +7 -7
  74. package/src/clerk/signup-button-with-fingerprint-client.tsx +7 -5
  75. package/src/clerk/signup-with-fingerprint-client.tsx +7 -7
  76. package/src/fuma/base/custom-home-layout.tsx +4 -4
  77. package/src/fuma/heavy/mermaid.tsx +1 -1
  78. package/src/fuma/site-x.tsx +0 -1
  79. package/src/main/calendar/calendar-date-range-input.tsx +1 -1
  80. package/src/main/credit/types.ts +8 -8
  81. package/src/main/gallery/gallery-mobile-swiper.tsx +0 -1
  82. package/src/main/gallery/gallery-server.tsx +2 -2
  83. package/src/main/money-price/index.ts +2 -0
  84. package/src/main/money-price/money-price-button.tsx +10 -10
  85. package/src/main/money-price/money-price-config-util.ts +49 -49
  86. package/src/main/money-price/money-price-interactive.tsx +40 -20
  87. package/src/main/money-price/money-price-types.ts +21 -14
  88. package/src/main/money-price/server.ts +2 -0
  89. package/src/main/pill-select/x-pill-select.tsx +2 -2
  90. package/src/main/server.ts +3 -1
  91. package/src/styles/third-ui.css +8 -0
@@ -1,29 +1,29 @@
1
1
  /**
2
2
  * Money Price Configuration
3
- * 价格组件配置文件
3
+ * Pricing component configuration.
4
4
  */
5
5
  /**
6
- * 获取当前激活的支付供应商配置
6
+ * Get the currently active payment provider configuration.
7
7
  *
8
- * 🔒 安全设计:
9
- * - util层负责从config中提取激活的provider配置
10
- * - 只返回提取的结果,不暴露任何config结构
11
- * - 调用方(应用层)通过wrapper隐藏config对象
8
+ * Security design:
9
+ * - The utility layer extracts the active provider configuration from the config.
10
+ * - Only the extracted result is returned; the full config structure is not exposed.
11
+ * - Application-level wrappers hide the config object from callers.
12
12
  *
13
- * @param config - MoneyPriceConfig对象(由应用层提供)
14
- * @returns 当前激活的支付供应商配置
13
+ * @param config - MoneyPriceConfig object provided by the application layer.
14
+ * @returns The currently active payment provider configuration.
15
15
  */
16
16
  function getActiveProviderConfigUtil(config) {
17
17
  const provider = config.activeProvider;
18
18
  return config.paymentProviders[provider];
19
19
  }
20
- // 辅助函数:获取特定产品的价格信息
20
+ // Helper: get pricing information for a specific product.
21
21
  function getProductPricing(productKey, billingType, provider, config) {
22
22
  const providerConfig = config.paymentProviders[provider];
23
- // 如果是 onetime 类型,尝试从积分包中获取
23
+ // For one-time billing, try to resolve pricing from credit packs.
24
24
  if (billingType === 'onetime') {
25
25
  const creditPacks = providerConfig.creditPackProducts;
26
- // 直接使用相同的 keyF1->F1, P2->P2, U3->U3
26
+ // Use the same product key directly: F1 -> F1, P2 -> P2, U3 -> U3.
27
27
  if (creditPacks && creditPacks[productKey]) {
28
28
  const pack = creditPacks[productKey];
29
29
  return {
@@ -34,33 +34,33 @@ function getProductPricing(productKey, billingType, provider, config) {
34
34
  };
35
35
  }
36
36
  }
37
- // 否则从订阅产品中获取
37
+ // Otherwise resolve pricing from subscription products.
38
38
  const products = providerConfig.subscriptionProducts || providerConfig.products;
39
39
  if (products && products[productKey] && products[productKey].plans[billingType]) {
40
40
  return products[productKey].plans[billingType];
41
41
  }
42
42
  throw new Error(`Product pricing not found for ${productKey} ${billingType}`);
43
43
  }
44
- // ============ 安全的util函数 - 只接收简单的映射表参数,不暴露任何config细节 ============
44
+ // ============ Safe utility functions: accept only simple mapping inputs and do not expose config internals ============
45
45
  /**
46
- * 根据 priceId 获取对应的积分数量
46
+ * Get the credit amount for a price ID.
47
47
  *
48
- * 🔒 安全设计:
49
- * - util层负责解析config,提取所需数据
50
- * - 只返回查询结果,不暴露任何config结构
51
- * - 调用方(应用层)通过wrapper隐藏config对象
48
+ * Security design:
49
+ * - The utility layer parses the config and extracts only the required data.
50
+ * - Only the query result is returned; the full config structure is not exposed.
51
+ * - Application-level wrappers hide the config object from callers.
52
52
  *
53
- * @param priceId - 查询的价格ID
54
- * @param config - MoneyPriceConfig对象(由应用层提供)
55
- * @returns 对应的积分数量,或null
53
+ * @param priceId - Price ID to query.
54
+ * @param config - MoneyPriceConfig object provided by the application layer.
55
+ * @returns The matching credit amount, or null.
56
56
  */
57
57
  function getCreditsFromPriceIdUtil(priceId, config) {
58
58
  if (!priceId) {
59
59
  return null;
60
60
  }
61
- // 遍历所有支付提供商
61
+ // Iterate through all payment providers.
62
62
  for (const provider of Object.values(config.paymentProviders)) {
63
- // 遍历订阅产品
63
+ // Iterate through subscription products.
64
64
  const subscriptionProducts = (provider.subscriptionProducts || provider.products);
65
65
  if (subscriptionProducts) {
66
66
  for (const product of Object.values(subscriptionProducts)) {
@@ -74,7 +74,7 @@ function getCreditsFromPriceIdUtil(priceId, config) {
74
74
  }
75
75
  }
76
76
  }
77
- // 遍历积分包产品
77
+ // Iterate through credit pack products.
78
78
  const creditPacks = provider.creditPackProducts;
79
79
  if (creditPacks) {
80
80
  for (const pack of Object.values(creditPacks)) {
@@ -88,46 +88,46 @@ function getCreditsFromPriceIdUtil(priceId, config) {
88
88
  return null;
89
89
  }
90
90
  /**
91
- * 根据查询参数获取价格配置
91
+ * Get price configuration by query parameters.
92
92
  *
93
- * 支持三种查询方式:
94
- * 1. priceId 直接查询
95
- * 2. plan + billingType 查询
96
- * 3. plan 查询
93
+ * Supported query modes:
94
+ * 1. Query directly by priceId.
95
+ * 2. Query by plan and billingType.
96
+ * 3. Query by plan.
97
97
  *
98
- * 🔒 安全设计:
99
- * - util层负责解析config,提取和匹配数据
100
- * - 只返回查询结果,不暴露任何config结构
101
- * - 调用方(应用层)通过wrapper隐藏config对象
98
+ * Security design:
99
+ * - The utility layer parses the config and extracts only matching data.
100
+ * - Only the query result is returned; the full config structure is not exposed.
101
+ * - Application-level wrappers hide the config object from callers.
102
102
  *
103
- * @param priceId - 查询的价格ID(可选)
104
- * @param plan - 查询的套餐名称如'P2''U3'(可选)
105
- * @param billingType - 查询的计费类型如'monthly''yearly'(可选)
106
- * @param config - MoneyPriceConfig对象(由应用层提供)
107
- * @returns 匹配的价格配置,包含计算好的元数据(priceNamedescriptioninterval
103
+ * @param priceId - Optional price ID to query.
104
+ * @param plan - Optional plan name, such as 'P2' or 'U3'.
105
+ * @param billingType - Optional billing type, such as 'monthly' or 'yearly'.
106
+ * @param config - MoneyPriceConfig object provided by the application layer.
107
+ * @returns The matching price config with derived metadata: priceName, description, and interval.
108
108
  */
109
109
  function getPriceConfigUtil(priceId, plan, billingType, config) {
110
- // 遍历所有支付提供商
110
+ // Iterate through all payment providers.
111
111
  for (const provider of Object.values(config.paymentProviders)) {
112
- // 遍历订阅产品
112
+ // Iterate through subscription products.
113
113
  const subscriptionProducts = (provider.subscriptionProducts || provider.products);
114
114
  if (subscriptionProducts) {
115
115
  for (const [productKey, product] of Object.entries(subscriptionProducts)) {
116
116
  if (product.plans) {
117
117
  for (const [billingKey, planConfig] of Object.entries(product.plans)) {
118
118
  const plan_config = planConfig;
119
- // 匹配逻辑:按优先级尝试
120
- // 1. priceId精确匹配(优先级最高)
119
+ // Matching order by priority.
120
+ // 1. Exact priceId match with highest priority.
121
121
  if (priceId && plan_config.priceId === priceId) {
122
122
  return Object.assign(Object.assign({}, plan_config), { priceName: `${productKey} ${billingKey}`, description: `${productKey} plan - ${billingKey} billing`, interval: billingKey === 'yearly' ? 'year' : 'month' });
123
123
  }
124
- // 2. planbillingType同时匹配
124
+ // 2. Match by both plan and billingType.
125
125
  if (!priceId && plan && billingType) {
126
126
  if (productKey === plan && billingKey === billingType) {
127
127
  return Object.assign(Object.assign({}, plan_config), { priceName: `${productKey} ${billingKey}`, description: `${productKey} plan - ${billingKey} billing`, interval: billingKey === 'yearly' ? 'year' : 'month' });
128
128
  }
129
129
  }
130
- // 3. plan匹配(billingType为空时)
130
+ // 3. Match by plan when billingType is empty.
131
131
  if (!priceId && !billingType && plan && productKey === plan) {
132
132
  return Object.assign(Object.assign({}, plan_config), { priceName: `${productKey} ${billingKey}`, description: `${productKey} plan - ${billingKey} billing`, interval: billingKey === 'yearly' ? 'year' : 'month' });
133
133
  }
@@ -135,12 +135,12 @@ function getPriceConfigUtil(priceId, plan, billingType, config) {
135
135
  }
136
136
  }
137
137
  }
138
- // 遍历积分包产品
138
+ // Iterate through credit pack products.
139
139
  const creditPacks = provider.creditPackProducts;
140
140
  if (creditPacks) {
141
141
  for (const [packKey, pack] of Object.entries(creditPacks)) {
142
142
  const pack_typed = pack;
143
- // 积分包匹配
143
+ // Credit pack match.
144
144
  if (priceId && pack_typed.priceId === priceId) {
145
145
  return {
146
146
  priceId: pack_typed.priceId,
@@ -152,7 +152,7 @@ function getPriceConfigUtil(priceId, plan, billingType, config) {
152
152
  interval: 'onetime',
153
153
  };
154
154
  }
155
- // plan和onetime匹配
155
+ // Match by plan and one-time billing.
156
156
  if (!priceId && plan && billingType === 'onetime') {
157
157
  if (packKey === plan) {
158
158
  return {
@@ -166,7 +166,7 @@ function getPriceConfigUtil(priceId, plan, billingType, config) {
166
166
  };
167
167
  }
168
168
  }
169
- // plan匹配(billingType为空时也能找到first积分包)
169
+ // Match by plan; also resolves the first credit pack when billingType is empty.
170
170
  if (!priceId && !billingType && plan && packKey === plan) {
171
171
  return {
172
172
  priceId: pack_typed.priceId,
@@ -12,6 +12,9 @@ var moneyPriceConfigUtil = require('./money-price-config-util.js');
12
12
  var moneyPriceTypes = require('./money-price-types.js');
13
13
  var customerPortal = require('./customer-portal.js');
14
14
  var lib = require('@windrun-huaiin/base-ui/lib');
15
+ var animeBeamFrame = require('../anime/anime-beam-frame.js');
16
+ require('animejs');
17
+ require('../anime/anime-404-page.js');
15
18
 
16
19
  const PLAN_KEYS = ['F1', 'P2', 'U3'];
17
20
  function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPortalApiEndpoint, enableClerkModal = false, enabledBillingTypes, enableSubscriptionUpgrade = true, initialBillingType, disableAutoDetectBilling = false, initUserContext, isInitLoading = false, }) {
@@ -21,11 +24,11 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
21
24
  const providerConfig = React.useMemo(() => moneyPriceConfigUtil.getActiveProviderConfigUtil(config), [config]);
22
25
  const billingOptions = React.useMemo(() => {
23
26
  const options = data.billingSwitch.options;
24
- // 如果配置了 enabledBillingTypes,只显示配置的类型
27
+ // If enabledBillingTypes is configured, show only those billing types.
25
28
  if (enabledBillingTypes === null || enabledBillingTypes === void 0 ? void 0 : enabledBillingTypes.length) {
26
29
  return options.filter(option => enabledBillingTypes.includes(option.key));
27
30
  }
28
- // 否则显示所有配置的选项
31
+ // Otherwise show all configured options.
29
32
  return options;
30
33
  }, [data.billingSwitch.options, enabledBillingTypes]);
31
34
  const billingOptionMap = React.useMemo(() => {
@@ -37,11 +40,11 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
37
40
  const defaultBilling = React.useMemo(() => {
38
41
  var _a;
39
42
  const defaultKey = data.billingSwitch.defaultKey;
40
- // 如果默认值在可用选项中,使用默认值
43
+ // Use the default value when it is available.
41
44
  if (billingOptions.some(opt => opt.key === defaultKey)) {
42
45
  return defaultKey;
43
46
  }
44
- // 否则使用第一个可用选项
47
+ // Otherwise use the first available option.
45
48
  return ((_a = billingOptions[0]) === null || _a === void 0 ? void 0 : _a.key) || 'monthly';
46
49
  }, [data.billingSwitch.defaultKey, billingOptions]);
47
50
  const resolvedInitialBilling = React.useMemo(() => {
@@ -53,18 +56,18 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
53
56
  }, [initialBillingType, billingOptions, defaultBilling]);
54
57
  const priceIdsByCycle = React.useMemo(() => {
55
58
  const priceIds = {};
56
- // 为每个可用的计费类型创建价格ID数组
59
+ // Build a price ID list for each available billing type.
57
60
  billingOptions.forEach(option => {
58
61
  priceIds[option.key] = [];
59
62
  if (option.key === 'onetime') {
60
- // 处理积分包产品
63
+ // Handle credit pack products.
61
64
  const creditPacks = providerConfig.creditPackProducts || {};
62
65
  Object.values(creditPacks).forEach((pack) => {
63
66
  priceIds[option.key].push(pack.priceId);
64
67
  });
65
68
  }
66
69
  else {
67
- // 处理订阅产品
70
+ // Handle subscription products.
68
71
  const products = providerConfig.subscriptionProducts || providerConfig.products || {};
69
72
  PLAN_KEYS.forEach(planKey => {
70
73
  const product = products[planKey];
@@ -263,7 +266,7 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
263
266
  userContext,
264
267
  enableSubscriptionUpgrade
265
268
  ]);
266
- // 根据当前计费类型动态选择要显示的 plans
269
+ // Select visible plans dynamically based on the current billing type.
267
270
  const currentPlans = React.useMemo(() => {
268
271
  if (billingType === 'onetime') {
269
272
  return data.creditsPlans || [];
@@ -288,11 +291,11 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
288
291
  const discountBadgeText = React.useMemo(() => {
289
292
  if (!(selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.discountText))
290
293
  return null;
291
- // 对于 onetime 模式,直接显示 discountText,不依赖 discountPercent
294
+ // In one-time mode, show discountText directly without relying on discountPercent.
292
295
  if (billingType === 'onetime') {
293
296
  return selectedBillingOption.discountText;
294
297
  }
295
- // 对于订阅模式,查找 discountPercent 并替换
298
+ // In subscription mode, find discountPercent and interpolate it.
296
299
  let discountPercent = null;
297
300
  const products = providerConfig.subscriptionProducts || providerConfig.products || {};
298
301
  PLAN_KEYS.forEach(planKey => {
@@ -306,7 +309,7 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
306
309
  return null;
307
310
  return selectedBillingOption.discountText.replace('{percent}', String(discountPercent));
308
311
  }, [selectedBillingOption, providerConfig, billingType]);
309
- // 配置移动端BillingTypeButton悬浮样式
312
+ // Configure the mobile floating style for BillingTypeButton.
310
313
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "flex justify-center mb-6 max-md:sticky max-md:top-30 max-md:z-30 max-md:py-2 max-md:bg-transparent", children: jsxRuntime.jsx("div", { className: "inline-flex bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded-full px-2 py-2 sm:px-3 sm:py-3 max-md:w-full max-md:max-w-[340px] max-md:mx-auto shadow-sm", "data-billing-switch": true, children: billingOptions.map(option => {
311
314
  const isActive = option.key === billingType;
312
315
  const buttonClasses = isActive
@@ -315,6 +318,7 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
315
318
  const showBadge = option.key === billingType && !!discountBadgeText;
316
319
  return (jsxRuntime.jsxs("div", { className: "relative flex items-center justify-center mx-1", children: [showBadge && (jsxRuntime.jsx("span", { className: "absolute z-10 left-1/2 -translate-x-1/2 -top-3 sm:-top-4 translate-y-[-50%] px-3 py-0.5 text-[0.625rem] sm:text-xs rounded-md bg-yellow-100 text-yellow-800 font-semibold shadow-sm whitespace-nowrap", children: discountBadgeText })), jsxRuntime.jsx("button", { className: utils.cn('text-sm md:text-base font-medium transition relative text-center z-10 px-2 sm:px-4 py-2 min-w-[100px] sm:min-w-[120px]', buttonClasses), type: "button", "data-billing-button": option.key, onClick: () => setUserSelectedBillingType(option.key), children: option.name })] }, option.key));
317
320
  }) }) }), jsxRuntime.jsx("div", { className: "w-full", children: jsxRuntime.jsx("div", { className: "flex flex-wrap justify-center gap-5 md:gap-6 xl:gap-8 w-full max-w-6xl mx-auto", children: currentPlans.map((plan) => {
321
+ var _a, _b;
318
322
  const planKey = plan.key;
319
323
  if (!PLAN_KEYS.includes(planKey)) {
320
324
  console.warn(`Unknown plan key "${plan.key}" detected in pricing plans`);
@@ -323,13 +327,21 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
323
327
  const pricing = getPricingForPlan(planKey);
324
328
  const showBillingSubtitle = plan.showBillingSubTitle !== false;
325
329
  const hasDiscount = !!pricing.discountPercent && !!pricing.originalAmount;
326
- // 移动端宽度样式
327
- return (jsxRuntime.jsxs("div", { "data-price-plan": planKey, className: utils.cn('flex flex-col bg-white dark:bg-gray-800/60 rounded-2xl border border-gray-300 dark:border-[#7c3aed40] transition p-5 md:p-8 h-full shadow-sm dark:shadow-none w-[85vw] max-w-[360px]', 'md:w-[clamp(280px,32vw,360px)] md:max-w-[360px] md:shrink-0', 'hover:border-2 hover:border-current', 'focus-within:border-2 focus-within:border-current', lib.themeIconColor), style: { minHeight: maxFeaturesCount * (isTouchDevice ? 86 : 100) }, children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-2", children: [jsxRuntime.jsx("span", { className: "text-lg md:text-xl font-bold text-gray-900 dark:text-gray-100", children: plan.title }), plan.titleTags && plan.titleTags.map((tag, i) => (jsxRuntime.jsx("span", { className: "px-2 py-0.5 text-xs rounded bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200 font-semibold align-middle", children: tag }, i)))] }), jsxRuntime.jsxs("div", { className: "flex flex-col items-start w-full", "data-price-container": planKey, children: [jsxRuntime.jsxs("div", { className: "flex items-end gap-2", children: [jsxRuntime.jsx("span", { className: "text-3xl md:text-4xl font-extrabold text-gray-900 dark:text-gray-100", "data-price-value": planKey, children: pricing.amount === 0 ? 'Free' : `${data.currency}${pricing.amount}` }), pricing.amount > 0 && (jsxRuntime.jsx("span", { className: "text-base md:text-lg text-gray-700 dark:text-gray-300 font-medium mb-1", "data-price-unit": planKey, children: (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.unit) || '/month' }))] }), jsxRuntime.jsxs("div", { className: "flex flex-col md:flex-row items-start md:items-center gap-1 md:gap-2 min-h-[28px] mt-1", children: [hasDiscount && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("span", { className: "text-sm md:text-base text-gray-400 line-through", "data-price-original": planKey, children: [data.currency, pricing.originalAmount] }), (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.discountText) && (jsxRuntime.jsx("span", { className: "px-2 py-0.5 text-[11px] md:text-xs rounded bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 font-semibold align-middle", "data-price-discount": planKey, children: selectedBillingOption.discountText.replace('{percent}', String(pricing.discountPercent)) }))] })), jsxRuntime.jsx("div", { className: utils.cn('flex items-center gap-2 text-[11px] md:text-xs', !showBillingSubtitle && 'opacity-0 select-none'), "data-price-subtitle": planKey, children: showBillingSubtitle && billingType === 'onetime' ? (
328
- // OneTime 模式下的特殊处理:普通文本 + 带样式的产品副标题
329
- jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.subTitle) && (jsxRuntime.jsx("span", { className: "text-[11px] md:text-xs text-gray-700 dark:text-gray-300 font-medium", children: selectedBillingOption.subTitle })), plan.subtitle && (jsxRuntime.jsxs("span", { className: "px-2 py-0.5 text-[11px] md:text-xs rounded bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 font-semibold align-middle", children: ["+", plan.subtitle] }))] })) : (
330
- // 其他模式下保持原逻辑
331
- showBillingSubtitle && (jsxRuntime.jsx("span", { className: "text-[11px] md:text-xs text-gray-700 dark:text-gray-300 font-medium", children: (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.subTitle) || '' }))) })] })] }), jsxRuntime.jsx("ul", { className: "flex-1 mb-6 mt-4 text-xs md:text-sm leading-5", children: getFeatureRows(plan).map((feature, i) => (jsxRuntime.jsxs("li", { className: "flex items-start gap-2 mb-2 min-h-[24px] md:min-h-[28px]", "data-feature-item": `${planKey}-${i}`, children: [feature ? (jsxRuntime.jsx("span", { className: "inline-flex items-center justify-center w-5 h-5 rounded-full bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-200 mr-1", children: feature.icon ? jsxRuntime.jsx("span", { children: feature.icon }) : jsxRuntime.jsx("span", { className: "font-bold", children: "\u2713" }) })) : (jsxRuntime.jsx("span", { className: "inline-flex items-center justify-center w-5 h-5 rounded-full mr-1", children: "\u00A0" })), feature && feature.tag && (jsxRuntime.jsx("span", { className: "px-1 py-0.5 text-[6px] rounded bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 font-semibold align-middle", children: feature.tag })), feature ? (jsxRuntime.jsxs("div", { className: "flex-1 text-gray-800 dark:text-gray-200", children: [jsxRuntime.jsx("span", { children: feature.description }), feature.tooltip && (jsxRuntime.jsx("span", { className: "block text-[11px] text-gray-500 dark:text-gray-400 mt-1", children: feature.tooltip }))] })) : (jsxRuntime.jsx("span", { children: "\u00A0" }))] }, i))) }), jsxRuntime.jsx("div", { className: "flex-1" }), jsxRuntime.jsx(moneyPriceButton.MoneyPriceButton, { planKey: planKey, userContext: userContext, billingType: billingType, onAuth: handleAuth, onAction: handleAction, texts: data.buttonTexts, isProcessing: (processingTarget === null || processingTarget === void 0 ? void 0 : processingTarget.plan) === planKey &&
332
- (processingTarget === null || processingTarget === void 0 ? void 0 : processingTarget.billing) === billingType, isAnyProcessing: !!processingTarget, isInitLoading: isInitLoading, enableSubscriptionUpgrade: enableSubscriptionUpgrade })] }, plan.key));
330
+ const hasStrictDiffAnime = Object.prototype.hasOwnProperty.call((_a = plan.strictDiffAnime) !== null && _a !== void 0 ? _a : {}, billingType);
331
+ const animeTone = hasStrictDiffAnime
332
+ ? (_b = plan.strictDiffAnime) === null || _b === void 0 ? void 0 : _b[billingType]
333
+ : plan.animeTone;
334
+ const hasAnimeTone = !!animeTone;
335
+ return (jsxRuntime.jsx(animeBeamFrame.AnimeBeamFrame, { active: false, interactive: hasAnimeTone, tone: animeTone !== null && animeTone !== void 0 ? animeTone : 'theme', radius: 16, className: utils.cn('h-full w-[85vw] max-w-[360px]', 'md:w-[clamp(280px,32vw,360px)] md:max-w-[360px] md:shrink-0'), children: jsxRuntime.jsxs("div", { "data-price-plan": planKey, className: utils.cn('flex flex-col bg-white dark:bg-gray-800/60 rounded-2xl border border-gray-300 dark:border-[#7c3aed40] transition p-5 md:p-8 h-full shadow-sm dark:shadow-none', !hasAnimeTone && [
336
+ 'hover:border-current',
337
+ 'focus-within:border-current',
338
+ lib.themeIconColor,
339
+ ]), style: { minHeight: maxFeaturesCount * (isTouchDevice ? 86 : 100) }, children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-2", children: [jsxRuntime.jsx("span", { className: "text-lg md:text-xl font-bold text-gray-900 dark:text-gray-100", children: plan.title }), plan.titleTags && plan.titleTags.map((tag, i) => (jsxRuntime.jsx("span", { className: "px-2 py-0.5 text-xs rounded bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200 font-semibold align-middle", children: tag }, i)))] }), jsxRuntime.jsxs("div", { className: "flex flex-col items-start w-full", "data-price-container": planKey, children: [jsxRuntime.jsxs("div", { className: "flex items-end gap-2", children: [jsxRuntime.jsx("span", { className: "text-3xl md:text-4xl font-extrabold text-gray-900 dark:text-gray-100", "data-price-value": planKey, children: pricing.amount === 0 ? 'Free' : `${data.currency}${pricing.amount}` }), pricing.amount > 0 && (jsxRuntime.jsx("span", { className: "text-base md:text-lg text-gray-700 dark:text-gray-300 font-medium mb-1", "data-price-unit": planKey, children: (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.unit) || '/month' }))] }), jsxRuntime.jsxs("div", { className: "flex flex-col md:flex-row items-start md:items-center gap-1 md:gap-2 min-h-[28px] mt-1", children: [hasDiscount && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("span", { className: "text-sm md:text-base text-gray-400 line-through", "data-price-original": planKey, children: [data.currency, pricing.originalAmount] }), (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.discountText) && (jsxRuntime.jsx("span", { className: "px-2 py-0.5 text-[11px] md:text-xs rounded bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 font-semibold align-middle", "data-price-discount": planKey, children: selectedBillingOption.discountText.replace('{percent}', String(pricing.discountPercent)) }))] })), jsxRuntime.jsx("div", { className: utils.cn('flex items-center gap-2 text-[11px] md:text-xs', !showBillingSubtitle && 'opacity-0 select-none'), "data-price-subtitle": planKey, children: showBillingSubtitle && billingType === 'onetime' ? (
340
+ // Special one-time mode rendering: plain text plus styled product subtitle.
341
+ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.subTitle) && (jsxRuntime.jsx("span", { className: "text-[11px] md:text-xs text-gray-700 dark:text-gray-300 font-medium", children: selectedBillingOption.subTitle })), plan.subtitle && (jsxRuntime.jsxs("span", { className: "px-2 py-0.5 text-[11px] md:text-xs rounded bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 font-semibold align-middle", children: ["+", plan.subtitle] }))] })) : (
342
+ // Keep the original rendering for other modes.
343
+ showBillingSubtitle && (jsxRuntime.jsx("span", { className: "text-[11px] md:text-xs text-gray-700 dark:text-gray-300 font-medium", children: (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.subTitle) || '' }))) })] })] }), jsxRuntime.jsx("ul", { className: "flex-1 mb-6 mt-4 text-xs md:text-sm leading-5", children: getFeatureRows(plan).map((feature, i) => (jsxRuntime.jsxs("li", { className: "flex items-start gap-2 mb-2 min-h-[24px] md:min-h-[28px]", "data-feature-item": `${planKey}-${i}`, children: [feature ? (jsxRuntime.jsx("span", { className: "inline-flex items-center justify-center w-5 h-5 rounded-full bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-200 mr-1", children: feature.icon ? jsxRuntime.jsx("span", { children: feature.icon }) : jsxRuntime.jsx("span", { className: "font-bold", children: "\u2713" }) })) : (jsxRuntime.jsx("span", { className: "inline-flex items-center justify-center w-5 h-5 rounded-full mr-1", children: "\u00A0" })), feature && feature.tag && (jsxRuntime.jsx("span", { className: "px-1 py-0.5 text-[6px] rounded bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 font-semibold align-middle", children: feature.tag })), feature ? (jsxRuntime.jsxs("div", { className: "flex-1 text-gray-800 dark:text-gray-200", children: [jsxRuntime.jsx("span", { children: feature.description }), feature.tooltip && (jsxRuntime.jsx("span", { className: "block text-[11px] text-gray-500 dark:text-gray-400 mt-1", children: feature.tooltip }))] })) : (jsxRuntime.jsx("span", { children: "\u00A0" }))] }, i))) }), jsxRuntime.jsx("div", { className: "flex-1" }), jsxRuntime.jsx(moneyPriceButton.MoneyPriceButton, { planKey: planKey, userContext: userContext, billingType: billingType, onAuth: handleAuth, onAction: handleAction, texts: data.buttonTexts, isProcessing: (processingTarget === null || processingTarget === void 0 ? void 0 : processingTarget.plan) === planKey &&
344
+ (processingTarget === null || processingTarget === void 0 ? void 0 : processingTarget.billing) === billingType, isAnyProcessing: !!processingTarget, isInitLoading: isInitLoading, enableSubscriptionUpgrade: enableSubscriptionUpgrade })] }) }, `${billingType}-${plan.key}-${animeTone !== null && animeTone !== void 0 ? animeTone : 'none'}`));
333
345
  }) }) })] }));
334
346
  }
335
347
 
@@ -10,6 +10,9 @@ import { getActiveProviderConfigUtil, getProductPricing } from './money-price-co
10
10
  import { UserState } from './money-price-types.mjs';
11
11
  import { redirectToCustomerPortal } from './customer-portal.mjs';
12
12
  import { themeButtonGradientClass, themeButtonGradientHoverClass, themeIconColor } from '@windrun-huaiin/base-ui/lib';
13
+ import { AnimeBeamFrame } from '../anime/anime-beam-frame.mjs';
14
+ import 'animejs';
15
+ import '../anime/anime-404-page.mjs';
13
16
 
14
17
  const PLAN_KEYS = ['F1', 'P2', 'U3'];
15
18
  function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPortalApiEndpoint, enableClerkModal = false, enabledBillingTypes, enableSubscriptionUpgrade = true, initialBillingType, disableAutoDetectBilling = false, initUserContext, isInitLoading = false, }) {
@@ -19,11 +22,11 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
19
22
  const providerConfig = useMemo(() => getActiveProviderConfigUtil(config), [config]);
20
23
  const billingOptions = useMemo(() => {
21
24
  const options = data.billingSwitch.options;
22
- // 如果配置了 enabledBillingTypes,只显示配置的类型
25
+ // If enabledBillingTypes is configured, show only those billing types.
23
26
  if (enabledBillingTypes === null || enabledBillingTypes === void 0 ? void 0 : enabledBillingTypes.length) {
24
27
  return options.filter(option => enabledBillingTypes.includes(option.key));
25
28
  }
26
- // 否则显示所有配置的选项
29
+ // Otherwise show all configured options.
27
30
  return options;
28
31
  }, [data.billingSwitch.options, enabledBillingTypes]);
29
32
  const billingOptionMap = useMemo(() => {
@@ -35,11 +38,11 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
35
38
  const defaultBilling = useMemo(() => {
36
39
  var _a;
37
40
  const defaultKey = data.billingSwitch.defaultKey;
38
- // 如果默认值在可用选项中,使用默认值
41
+ // Use the default value when it is available.
39
42
  if (billingOptions.some(opt => opt.key === defaultKey)) {
40
43
  return defaultKey;
41
44
  }
42
- // 否则使用第一个可用选项
45
+ // Otherwise use the first available option.
43
46
  return ((_a = billingOptions[0]) === null || _a === void 0 ? void 0 : _a.key) || 'monthly';
44
47
  }, [data.billingSwitch.defaultKey, billingOptions]);
45
48
  const resolvedInitialBilling = useMemo(() => {
@@ -51,18 +54,18 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
51
54
  }, [initialBillingType, billingOptions, defaultBilling]);
52
55
  const priceIdsByCycle = useMemo(() => {
53
56
  const priceIds = {};
54
- // 为每个可用的计费类型创建价格ID数组
57
+ // Build a price ID list for each available billing type.
55
58
  billingOptions.forEach(option => {
56
59
  priceIds[option.key] = [];
57
60
  if (option.key === 'onetime') {
58
- // 处理积分包产品
61
+ // Handle credit pack products.
59
62
  const creditPacks = providerConfig.creditPackProducts || {};
60
63
  Object.values(creditPacks).forEach((pack) => {
61
64
  priceIds[option.key].push(pack.priceId);
62
65
  });
63
66
  }
64
67
  else {
65
- // 处理订阅产品
68
+ // Handle subscription products.
66
69
  const products = providerConfig.subscriptionProducts || providerConfig.products || {};
67
70
  PLAN_KEYS.forEach(planKey => {
68
71
  const product = products[planKey];
@@ -261,7 +264,7 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
261
264
  userContext,
262
265
  enableSubscriptionUpgrade
263
266
  ]);
264
- // 根据当前计费类型动态选择要显示的 plans
267
+ // Select visible plans dynamically based on the current billing type.
265
268
  const currentPlans = useMemo(() => {
266
269
  if (billingType === 'onetime') {
267
270
  return data.creditsPlans || [];
@@ -286,11 +289,11 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
286
289
  const discountBadgeText = useMemo(() => {
287
290
  if (!(selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.discountText))
288
291
  return null;
289
- // 对于 onetime 模式,直接显示 discountText,不依赖 discountPercent
292
+ // In one-time mode, show discountText directly without relying on discountPercent.
290
293
  if (billingType === 'onetime') {
291
294
  return selectedBillingOption.discountText;
292
295
  }
293
- // 对于订阅模式,查找 discountPercent 并替换
296
+ // In subscription mode, find discountPercent and interpolate it.
294
297
  let discountPercent = null;
295
298
  const products = providerConfig.subscriptionProducts || providerConfig.products || {};
296
299
  PLAN_KEYS.forEach(planKey => {
@@ -304,7 +307,7 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
304
307
  return null;
305
308
  return selectedBillingOption.discountText.replace('{percent}', String(discountPercent));
306
309
  }, [selectedBillingOption, providerConfig, billingType]);
307
- // 配置移动端BillingTypeButton悬浮样式
310
+ // Configure the mobile floating style for BillingTypeButton.
308
311
  return (jsxs(Fragment, { children: [jsx("div", { className: "flex justify-center mb-6 max-md:sticky max-md:top-30 max-md:z-30 max-md:py-2 max-md:bg-transparent", children: jsx("div", { className: "inline-flex bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded-full px-2 py-2 sm:px-3 sm:py-3 max-md:w-full max-md:max-w-[340px] max-md:mx-auto shadow-sm", "data-billing-switch": true, children: billingOptions.map(option => {
309
312
  const isActive = option.key === billingType;
310
313
  const buttonClasses = isActive
@@ -313,6 +316,7 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
313
316
  const showBadge = option.key === billingType && !!discountBadgeText;
314
317
  return (jsxs("div", { className: "relative flex items-center justify-center mx-1", children: [showBadge && (jsx("span", { className: "absolute z-10 left-1/2 -translate-x-1/2 -top-3 sm:-top-4 translate-y-[-50%] px-3 py-0.5 text-[0.625rem] sm:text-xs rounded-md bg-yellow-100 text-yellow-800 font-semibold shadow-sm whitespace-nowrap", children: discountBadgeText })), jsx("button", { className: cn('text-sm md:text-base font-medium transition relative text-center z-10 px-2 sm:px-4 py-2 min-w-[100px] sm:min-w-[120px]', buttonClasses), type: "button", "data-billing-button": option.key, onClick: () => setUserSelectedBillingType(option.key), children: option.name })] }, option.key));
315
318
  }) }) }), jsx("div", { className: "w-full", children: jsx("div", { className: "flex flex-wrap justify-center gap-5 md:gap-6 xl:gap-8 w-full max-w-6xl mx-auto", children: currentPlans.map((plan) => {
319
+ var _a, _b;
316
320
  const planKey = plan.key;
317
321
  if (!PLAN_KEYS.includes(planKey)) {
318
322
  console.warn(`Unknown plan key "${plan.key}" detected in pricing plans`);
@@ -321,13 +325,21 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
321
325
  const pricing = getPricingForPlan(planKey);
322
326
  const showBillingSubtitle = plan.showBillingSubTitle !== false;
323
327
  const hasDiscount = !!pricing.discountPercent && !!pricing.originalAmount;
324
- // 移动端宽度样式
325
- return (jsxs("div", { "data-price-plan": planKey, className: cn('flex flex-col bg-white dark:bg-gray-800/60 rounded-2xl border border-gray-300 dark:border-[#7c3aed40] transition p-5 md:p-8 h-full shadow-sm dark:shadow-none w-[85vw] max-w-[360px]', 'md:w-[clamp(280px,32vw,360px)] md:max-w-[360px] md:shrink-0', 'hover:border-2 hover:border-current', 'focus-within:border-2 focus-within:border-current', themeIconColor), style: { minHeight: maxFeaturesCount * (isTouchDevice ? 86 : 100) }, children: [jsxs("div", { className: "flex items-center gap-2 mb-2", children: [jsx("span", { className: "text-lg md:text-xl font-bold text-gray-900 dark:text-gray-100", children: plan.title }), plan.titleTags && plan.titleTags.map((tag, i) => (jsx("span", { className: "px-2 py-0.5 text-xs rounded bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200 font-semibold align-middle", children: tag }, i)))] }), jsxs("div", { className: "flex flex-col items-start w-full", "data-price-container": planKey, children: [jsxs("div", { className: "flex items-end gap-2", children: [jsx("span", { className: "text-3xl md:text-4xl font-extrabold text-gray-900 dark:text-gray-100", "data-price-value": planKey, children: pricing.amount === 0 ? 'Free' : `${data.currency}${pricing.amount}` }), pricing.amount > 0 && (jsx("span", { className: "text-base md:text-lg text-gray-700 dark:text-gray-300 font-medium mb-1", "data-price-unit": planKey, children: (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.unit) || '/month' }))] }), jsxs("div", { className: "flex flex-col md:flex-row items-start md:items-center gap-1 md:gap-2 min-h-[28px] mt-1", children: [hasDiscount && (jsxs(Fragment, { children: [jsxs("span", { className: "text-sm md:text-base text-gray-400 line-through", "data-price-original": planKey, children: [data.currency, pricing.originalAmount] }), (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.discountText) && (jsx("span", { className: "px-2 py-0.5 text-[11px] md:text-xs rounded bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 font-semibold align-middle", "data-price-discount": planKey, children: selectedBillingOption.discountText.replace('{percent}', String(pricing.discountPercent)) }))] })), jsx("div", { className: cn('flex items-center gap-2 text-[11px] md:text-xs', !showBillingSubtitle && 'opacity-0 select-none'), "data-price-subtitle": planKey, children: showBillingSubtitle && billingType === 'onetime' ? (
326
- // OneTime 模式下的特殊处理:普通文本 + 带样式的产品副标题
327
- jsxs(Fragment, { children: [(selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.subTitle) && (jsx("span", { className: "text-[11px] md:text-xs text-gray-700 dark:text-gray-300 font-medium", children: selectedBillingOption.subTitle })), plan.subtitle && (jsxs("span", { className: "px-2 py-0.5 text-[11px] md:text-xs rounded bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 font-semibold align-middle", children: ["+", plan.subtitle] }))] })) : (
328
- // 其他模式下保持原逻辑
329
- showBillingSubtitle && (jsx("span", { className: "text-[11px] md:text-xs text-gray-700 dark:text-gray-300 font-medium", children: (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.subTitle) || '' }))) })] })] }), jsx("ul", { className: "flex-1 mb-6 mt-4 text-xs md:text-sm leading-5", children: getFeatureRows(plan).map((feature, i) => (jsxs("li", { className: "flex items-start gap-2 mb-2 min-h-[24px] md:min-h-[28px]", "data-feature-item": `${planKey}-${i}`, children: [feature ? (jsx("span", { className: "inline-flex items-center justify-center w-5 h-5 rounded-full bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-200 mr-1", children: feature.icon ? jsx("span", { children: feature.icon }) : jsx("span", { className: "font-bold", children: "\u2713" }) })) : (jsx("span", { className: "inline-flex items-center justify-center w-5 h-5 rounded-full mr-1", children: "\u00A0" })), feature && feature.tag && (jsx("span", { className: "px-1 py-0.5 text-[6px] rounded bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 font-semibold align-middle", children: feature.tag })), feature ? (jsxs("div", { className: "flex-1 text-gray-800 dark:text-gray-200", children: [jsx("span", { children: feature.description }), feature.tooltip && (jsx("span", { className: "block text-[11px] text-gray-500 dark:text-gray-400 mt-1", children: feature.tooltip }))] })) : (jsx("span", { children: "\u00A0" }))] }, i))) }), jsx("div", { className: "flex-1" }), jsx(MoneyPriceButton, { planKey: planKey, userContext: userContext, billingType: billingType, onAuth: handleAuth, onAction: handleAction, texts: data.buttonTexts, isProcessing: (processingTarget === null || processingTarget === void 0 ? void 0 : processingTarget.plan) === planKey &&
330
- (processingTarget === null || processingTarget === void 0 ? void 0 : processingTarget.billing) === billingType, isAnyProcessing: !!processingTarget, isInitLoading: isInitLoading, enableSubscriptionUpgrade: enableSubscriptionUpgrade })] }, plan.key));
328
+ const hasStrictDiffAnime = Object.prototype.hasOwnProperty.call((_a = plan.strictDiffAnime) !== null && _a !== void 0 ? _a : {}, billingType);
329
+ const animeTone = hasStrictDiffAnime
330
+ ? (_b = plan.strictDiffAnime) === null || _b === void 0 ? void 0 : _b[billingType]
331
+ : plan.animeTone;
332
+ const hasAnimeTone = !!animeTone;
333
+ return (jsx(AnimeBeamFrame, { active: false, interactive: hasAnimeTone, tone: animeTone !== null && animeTone !== void 0 ? animeTone : 'theme', radius: 16, className: cn('h-full w-[85vw] max-w-[360px]', 'md:w-[clamp(280px,32vw,360px)] md:max-w-[360px] md:shrink-0'), children: jsxs("div", { "data-price-plan": planKey, className: cn('flex flex-col bg-white dark:bg-gray-800/60 rounded-2xl border border-gray-300 dark:border-[#7c3aed40] transition p-5 md:p-8 h-full shadow-sm dark:shadow-none', !hasAnimeTone && [
334
+ 'hover:border-current',
335
+ 'focus-within:border-current',
336
+ themeIconColor,
337
+ ]), style: { minHeight: maxFeaturesCount * (isTouchDevice ? 86 : 100) }, children: [jsxs("div", { className: "flex items-center gap-2 mb-2", children: [jsx("span", { className: "text-lg md:text-xl font-bold text-gray-900 dark:text-gray-100", children: plan.title }), plan.titleTags && plan.titleTags.map((tag, i) => (jsx("span", { className: "px-2 py-0.5 text-xs rounded bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200 font-semibold align-middle", children: tag }, i)))] }), jsxs("div", { className: "flex flex-col items-start w-full", "data-price-container": planKey, children: [jsxs("div", { className: "flex items-end gap-2", children: [jsx("span", { className: "text-3xl md:text-4xl font-extrabold text-gray-900 dark:text-gray-100", "data-price-value": planKey, children: pricing.amount === 0 ? 'Free' : `${data.currency}${pricing.amount}` }), pricing.amount > 0 && (jsx("span", { className: "text-base md:text-lg text-gray-700 dark:text-gray-300 font-medium mb-1", "data-price-unit": planKey, children: (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.unit) || '/month' }))] }), jsxs("div", { className: "flex flex-col md:flex-row items-start md:items-center gap-1 md:gap-2 min-h-[28px] mt-1", children: [hasDiscount && (jsxs(Fragment, { children: [jsxs("span", { className: "text-sm md:text-base text-gray-400 line-through", "data-price-original": planKey, children: [data.currency, pricing.originalAmount] }), (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.discountText) && (jsx("span", { className: "px-2 py-0.5 text-[11px] md:text-xs rounded bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 font-semibold align-middle", "data-price-discount": planKey, children: selectedBillingOption.discountText.replace('{percent}', String(pricing.discountPercent)) }))] })), jsx("div", { className: cn('flex items-center gap-2 text-[11px] md:text-xs', !showBillingSubtitle && 'opacity-0 select-none'), "data-price-subtitle": planKey, children: showBillingSubtitle && billingType === 'onetime' ? (
338
+ // Special one-time mode rendering: plain text plus styled product subtitle.
339
+ jsxs(Fragment, { children: [(selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.subTitle) && (jsx("span", { className: "text-[11px] md:text-xs text-gray-700 dark:text-gray-300 font-medium", children: selectedBillingOption.subTitle })), plan.subtitle && (jsxs("span", { className: "px-2 py-0.5 text-[11px] md:text-xs rounded bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 font-semibold align-middle", children: ["+", plan.subtitle] }))] })) : (
340
+ // Keep the original rendering for other modes.
341
+ showBillingSubtitle && (jsx("span", { className: "text-[11px] md:text-xs text-gray-700 dark:text-gray-300 font-medium", children: (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.subTitle) || '' }))) })] })] }), jsx("ul", { className: "flex-1 mb-6 mt-4 text-xs md:text-sm leading-5", children: getFeatureRows(plan).map((feature, i) => (jsxs("li", { className: "flex items-start gap-2 mb-2 min-h-[24px] md:min-h-[28px]", "data-feature-item": `${planKey}-${i}`, children: [feature ? (jsx("span", { className: "inline-flex items-center justify-center w-5 h-5 rounded-full bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-200 mr-1", children: feature.icon ? jsx("span", { children: feature.icon }) : jsx("span", { className: "font-bold", children: "\u2713" }) })) : (jsx("span", { className: "inline-flex items-center justify-center w-5 h-5 rounded-full mr-1", children: "\u00A0" })), feature && feature.tag && (jsx("span", { className: "px-1 py-0.5 text-[6px] rounded bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 font-semibold align-middle", children: feature.tag })), feature ? (jsxs("div", { className: "flex-1 text-gray-800 dark:text-gray-200", children: [jsx("span", { children: feature.description }), feature.tooltip && (jsx("span", { className: "block text-[11px] text-gray-500 dark:text-gray-400 mt-1", children: feature.tooltip }))] })) : (jsx("span", { children: "\u00A0" }))] }, i))) }), jsx("div", { className: "flex-1" }), jsx(MoneyPriceButton, { planKey: planKey, userContext: userContext, billingType: billingType, onAuth: handleAuth, onAction: handleAction, texts: data.buttonTexts, isProcessing: (processingTarget === null || processingTarget === void 0 ? void 0 : processingTarget.plan) === planKey &&
342
+ (processingTarget === null || processingTarget === void 0 ? void 0 : processingTarget.billing) === billingType, isAnyProcessing: !!processingTarget, isInitLoading: isInitLoading, enableSubscriptionUpgrade: enableSubscriptionUpgrade })] }) }, `${billingType}-${plan.key}-${animeTone !== null && animeTone !== void 0 ? animeTone : 'none'}`));
331
343
  }) }) })] }));
332
344
  }
333
345
 
@@ -1,7 +1,7 @@
1
1
  import type { XCredit, XSubscription, XUser } from '../../clerk/fingerprint/types';
2
2
  /**
3
3
  * Money Price Component Types
4
- * 价格组件类型定义
4
+ * Pricing component type definitions.
5
5
  */
6
6
  export declare enum UserState {
7
7
  Anonymous = "anonymous",
@@ -117,6 +117,8 @@ export interface MoneyPriceButtonProps {
117
117
  isInitLoading: boolean;
118
118
  enableSubscriptionUpgrade?: boolean;
119
119
  }
120
+ export type MoneyPriceAnimeTone = 'theme' | 'rainbow' | 'mono' | 'warm' | 'cool';
121
+ export type MoneyPriceStrictDiffAnime = Record<string, MoneyPriceAnimeTone | null | undefined>;
120
122
  export interface MoneyPriceData {
121
123
  title: string;
122
124
  subtitle: string;
@@ -133,6 +135,8 @@ export interface MoneyPriceData {
133
135
  subscriptionPlans: Array<{
134
136
  key: string;
135
137
  title: string;
138
+ animeTone?: MoneyPriceAnimeTone;
139
+ strictDiffAnime?: MoneyPriceStrictDiffAnime;
136
140
  showBillingSubTitle?: boolean;
137
141
  titleTags?: string[];
138
142
  features?: Array<{
@@ -146,6 +150,8 @@ export interface MoneyPriceData {
146
150
  key: string;
147
151
  title: string;
148
152
  subtitle?: string;
153
+ animeTone?: MoneyPriceAnimeTone;
154
+ strictDiffAnime?: MoneyPriceStrictDiffAnime;
149
155
  showBillingSubTitle?: boolean;
150
156
  titleTags?: string[];
151
157
  features?: Array<{
@@ -2,9 +2,9 @@
2
2
 
3
3
  /**
4
4
  * Money Price Component Types
5
- * 价格组件类型定义
5
+ * Pricing component type definitions.
6
6
  */
7
- // 用户状态枚举
7
+ // User status enum.
8
8
  exports.UserState = void 0;
9
9
  (function (UserState) {
10
10
  UserState["Anonymous"] = "anonymous";
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Money Price Component Types
3
- * 价格组件类型定义
3
+ * Pricing component type definitions.
4
4
  */
5
- // 用户状态枚举
5
+ // User status enum.
6
6
  var UserState;
7
7
  (function (UserState) {
8
8
  UserState["Anonymous"] = "anonymous";
@@ -1,5 +1,5 @@
1
1
  export { MoneyPrice } from './money-price';
2
2
  export { getActiveProviderConfigUtil, getCreditsFromPriceIdUtil, getPriceConfigUtil, } from './money-price-config-util';
3
3
  export { buildMoneyPriceData } from './money-price-data';
4
- export type { MoneyPriceConfig, MoneyPriceProps, MoneyPriceInteractiveProps, MoneyPriceButtonProps, MoneyPriceData, InitUserContext, PaymentProvider, PaymentProviderConfig, EnhancePricePlan, SubscriptionProductConfig, CreditPackProductConfig, UserContext, } from './money-price-types';
4
+ export type { MoneyPriceConfig, MoneyPriceProps, MoneyPriceInteractiveProps, MoneyPriceButtonProps, MoneyPriceData, MoneyPriceAnimeTone, MoneyPriceStrictDiffAnime, InitUserContext, PaymentProvider, PaymentProviderConfig, EnhancePricePlan, SubscriptionProductConfig, CreditPackProductConfig, UserContext, } from './money-price-types';
5
5
  export { UserState } from './money-price-types';
@@ -26,7 +26,7 @@ function XPillSelect(props) {
26
26
  const isAllSelected = props.mode === 'multiple' &&
27
27
  allOptionValues.length > 0 &&
28
28
  allOptionValues.every((value) => selectedValues.includes(value));
29
- const aggregatedSelectedLabel = isAllSelected ? (allSelectedLabel === null || allSelectedLabel === void 0 ? void 0 : allSelectedLabel.trim()) || '全部' : null;
29
+ const aggregatedSelectedLabel = isAllSelected ? (allSelectedLabel === null || allSelectedLabel === void 0 ? void 0 : allSelectedLabel.trim()) || 'All' : null;
30
30
  const hasVisiblePillLimit = props.mode === 'multiple' && typeof maxVisiblePills === 'number' && maxVisiblePills >= 0;
31
31
  const visibleSelectedValues = aggregatedSelectedLabel || !hasVisiblePillLimit
32
32
  ? selectedValues
@@ -126,7 +126,7 @@ function XPillSelect(props) {
126
126
  event.stopPropagation();
127
127
  removeValue(selectedValue);
128
128
  }, disabled: disabled, className: utils.cn('inline-flex max-w-full items-center rounded-full font-semibold transition', compact ? 'gap-1 px-2.5 py-0.5 text-[11px]' : 'gap-1.5 px-3 py-1 text-xs', lib.themeBgColor, lib.themeIconColor, 'hover:brightness-95 dark:hover:brightness-110', disabled && 'cursor-not-allowed opacity-60'), title: optionLabel, children: jsxRuntime.jsx("span", { className: utils.cn('truncate', maxPillWidthClassName), children: optionLabel }) }, selectedValue));
129
- }), hiddenSelectedCount > 0 ? (jsxRuntime.jsxs("span", { className: utils.cn('inline-flex max-w-full items-center rounded-full font-semibold transition', compact ? 'px-2.5 py-0.5 text-[11px]' : 'px-3 py-1 text-xs', 'bg-neutral-200 text-neutral-700 dark:bg-neutral-800 dark:text-white'), title: `还有 ${hiddenSelectedCount} 项未展开`, children: ["+", hiddenSelectedCount] })) : null] }))) : (jsxRuntime.jsx("span", { className: utils.cn(compact ? 'text-xs' : 'text-sm', 'text-slate-500 dark:text-slate-400'), children: emptyLabel })) }), jsxRuntime.jsx(icons.ChevronDownIcon, { className: utils.cn(compact ? 'h-3.5 w-3.5' : 'h-4 w-4', 'shrink-0 text-slate-500 transition-transform dark:text-slate-400', open && 'rotate-180') })] }), open ? (jsxRuntime.jsxs("div", { role: "listbox", "aria-multiselectable": props.mode === 'multiple' ? true : undefined, className: utils.cn('absolute left-0 right-0 top-[calc(100%+0.375rem)] z-50 rounded-3xl border border-black/10 bg-neutral-100 shadow-xl dark:border-white/10 dark:bg-neutral-900', compact ? 'space-y-2.5 p-3' : 'space-y-3 p-4', open && lib.themeBorderColor), children: [inputEnabled ? (jsxRuntime.jsx("input", { value: draftValue, onChange: (event) => setDraftValue(event.target.value.replaceAll(',', '')), onKeyDown: (event) => {
129
+ }), hiddenSelectedCount > 0 ? (jsxRuntime.jsxs("span", { className: utils.cn('inline-flex max-w-full items-center rounded-full font-semibold transition', compact ? 'px-2.5 py-0.5 text-[11px]' : 'px-3 py-1 text-xs', 'bg-neutral-200 text-neutral-700 dark:bg-neutral-800 dark:text-white'), title: `${hiddenSelectedCount} items remain unexpanded`, children: ["+", hiddenSelectedCount] })) : null] }))) : (jsxRuntime.jsx("span", { className: utils.cn(compact ? 'text-xs' : 'text-sm', 'text-slate-500 dark:text-slate-400'), children: emptyLabel })) }), jsxRuntime.jsx(icons.ChevronDownIcon, { className: utils.cn(compact ? 'h-3.5 w-3.5' : 'h-4 w-4', 'shrink-0 text-slate-500 transition-transform dark:text-slate-400', open && 'rotate-180') })] }), open ? (jsxRuntime.jsxs("div", { role: "listbox", "aria-multiselectable": props.mode === 'multiple' ? true : undefined, className: utils.cn('absolute left-0 right-0 top-[calc(100%+0.375rem)] z-50 rounded-3xl border border-black/10 bg-neutral-100 shadow-xl dark:border-white/10 dark:bg-neutral-900', compact ? 'space-y-2.5 p-3' : 'space-y-3 p-4', open && lib.themeBorderColor), children: [inputEnabled ? (jsxRuntime.jsx("input", { value: draftValue, onChange: (event) => setDraftValue(event.target.value.replaceAll(',', '')), onKeyDown: (event) => {
130
130
  if (event.key !== 'Enter') {
131
131
  return;
132
132
  }