@lobehub/lobehub 2.0.0-next.184 → 2.0.0-next.185

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 (73) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/changelog/v1.json +9 -0
  3. package/locales/ar/auth.json +1 -0
  4. package/locales/ar/models.json +40 -0
  5. package/locales/ar/setting.json +1 -0
  6. package/locales/ar/subscription.json +13 -0
  7. package/locales/bg-BG/auth.json +1 -0
  8. package/locales/bg-BG/models.json +22 -0
  9. package/locales/bg-BG/setting.json +1 -0
  10. package/locales/bg-BG/subscription.json +13 -0
  11. package/locales/de-DE/auth.json +1 -0
  12. package/locales/de-DE/models.json +37 -0
  13. package/locales/de-DE/setting.json +1 -0
  14. package/locales/de-DE/subscription.json +13 -0
  15. package/locales/en-US/auth.json +1 -0
  16. package/locales/en-US/setting.json +1 -1
  17. package/locales/es-ES/auth.json +1 -0
  18. package/locales/es-ES/models.json +30 -0
  19. package/locales/es-ES/setting.json +1 -0
  20. package/locales/es-ES/subscription.json +13 -0
  21. package/locales/fa-IR/auth.json +1 -0
  22. package/locales/fa-IR/models.json +31 -0
  23. package/locales/fa-IR/setting.json +1 -0
  24. package/locales/fa-IR/subscription.json +13 -0
  25. package/locales/fr-FR/auth.json +1 -0
  26. package/locales/fr-FR/models.json +17 -0
  27. package/locales/fr-FR/setting.json +1 -0
  28. package/locales/fr-FR/subscription.json +13 -0
  29. package/locales/it-IT/auth.json +1 -0
  30. package/locales/it-IT/models.json +31 -0
  31. package/locales/it-IT/setting.json +1 -0
  32. package/locales/it-IT/subscription.json +13 -0
  33. package/locales/ja-JP/auth.json +1 -0
  34. package/locales/ja-JP/models.json +1 -0
  35. package/locales/ja-JP/setting.json +1 -0
  36. package/locales/ja-JP/subscription.json +13 -0
  37. package/locales/ko-KR/auth.json +1 -0
  38. package/locales/ko-KR/models.json +39 -0
  39. package/locales/ko-KR/setting.json +1 -0
  40. package/locales/ko-KR/subscription.json +13 -0
  41. package/locales/nl-NL/auth.json +1 -0
  42. package/locales/nl-NL/models.json +35 -0
  43. package/locales/nl-NL/setting.json +1 -0
  44. package/locales/nl-NL/subscription.json +13 -0
  45. package/locales/pl-PL/auth.json +1 -0
  46. package/locales/pl-PL/models.json +12 -0
  47. package/locales/pl-PL/setting.json +1 -0
  48. package/locales/pl-PL/subscription.json +13 -0
  49. package/locales/pt-BR/auth.json +1 -0
  50. package/locales/pt-BR/models.json +1 -0
  51. package/locales/pt-BR/setting.json +1 -0
  52. package/locales/pt-BR/subscription.json +13 -0
  53. package/locales/ru-RU/auth.json +1 -0
  54. package/locales/ru-RU/models.json +32 -0
  55. package/locales/ru-RU/setting.json +1 -0
  56. package/locales/ru-RU/subscription.json +13 -0
  57. package/locales/tr-TR/auth.json +1 -0
  58. package/locales/tr-TR/models.json +20 -0
  59. package/locales/tr-TR/setting.json +1 -0
  60. package/locales/tr-TR/subscription.json +13 -0
  61. package/locales/vi-VN/auth.json +1 -0
  62. package/locales/vi-VN/models.json +39 -0
  63. package/locales/vi-VN/setting.json +1 -0
  64. package/locales/vi-VN/subscription.json +13 -0
  65. package/locales/zh-CN/auth.json +1 -0
  66. package/locales/zh-CN/models.json +49 -0
  67. package/locales/zh-CN/setting.json +1 -1
  68. package/locales/zh-TW/auth.json +1 -0
  69. package/locales/zh-TW/models.json +23 -0
  70. package/locales/zh-TW/setting.json +1 -0
  71. package/locales/zh-TW/subscription.json +13 -0
  72. package/package.json +1 -1
  73. package/src/store/image/utils/size.test.ts +245 -0
@@ -147,6 +147,7 @@
147
147
  "loginGuide.f4": "探索強大的外掛功能",
148
148
  "loginGuide.title": "登入後,您可以:",
149
149
  "loginOrSignup": "登入 / 註冊",
150
+ "profile.account": "帳戶",
150
151
  "profile.authorizations.actions.revoke": "取消授權",
151
152
  "profile.authorizations.revoke.description": "取消授權後,該工具將無法存取您的資料。如需再次使用,需重新授權。",
152
153
  "profile.authorizations.revoke.title": "確認取消授權 {{name}}?",
@@ -223,7 +223,30 @@
223
223
  "alibaba/qwen-3-235b.description": "Qwen3 是 Qwen 系列的最新一代,提供完整的密集與 MoE 模型組合。透過大規模訓練,在推理、指令遵循、代理能力與多語言支援方面實現突破。",
224
224
  "alibaba/qwen-3-30b.description": "Qwen3 是 Qwen 系列的最新一代,提供完整的密集與 MoE 模型組合。透過大規模訓練,在推理、指令遵循、代理能力與多語言支援方面實現突破。",
225
225
  "alibaba/qwen-3-32b.description": "Qwen3 是 Qwen 系列的最新一代,提供完整的密集與 MoE 模型組合。透過大規模訓練,在推理、指令遵循、代理能力與多語言支援方面實現突破。",
226
+ "alibaba/qwen3-coder.description": "Qwen3-Coder-480B-A35B-Instruct 是 Qwen 最具代理能力的程式模型,在代理式編碼、瀏覽器操作及其他核心編碼任務上表現優異,達到與 Claude Sonnet 同級的成果。",
227
+ "amazon/nova-lite.description": "一款極低成本的多模態模型,能以極快速度處理圖像、影片與文字輸入。",
228
+ "amazon/nova-micro.description": "一款僅支援文字的模型,提供超低延遲與極低成本的運算效能。",
229
+ "amazon/nova-pro.description": "一款功能強大的多模態模型,在準確性、速度與成本之間達到最佳平衡,適用於各類任務。",
226
230
  "amazon/titan-embed-text-v2.description": "Amazon Titan Text Embeddings V2 是一個輕量級、高效的多語言嵌入模型,支援 1024、512 和 256 維度。",
231
+ "anthropic.claude-3-5-sonnet-20240620-v1:0.description": "Claude 3.5 Sonnet 提升了業界標準,在多項評估中超越競爭對手與 Claude 3 Opus,同時維持中階速度與成本。",
232
+ "anthropic.claude-3-5-sonnet-20241022-v2:0.description": "Claude 3.5 Sonnet 提升了業界標準,在多項評估中超越競爭對手與 Claude 3 Opus,同時維持中階速度與成本。",
233
+ "anthropic.claude-3-haiku-20240307-v1:0.description": "Claude 3 Haiku 是 Anthropic 速度最快、體積最小的模型,能即時回應簡單查詢,提供流暢自然的 AI 體驗,並支援圖像輸入與 200K 的上下文視窗。",
234
+ "anthropic.claude-3-opus-20240229-v1:0.description": "Claude 3 Opus 是 Anthropic 最強大的 AI 模型,在處理高度複雜任務時展現最先進的效能,具備開放式提示與新穎情境的流暢應對能力,並支援圖像輸入與 200K 的上下文視窗。",
235
+ "anthropic.claude-3-sonnet-20240229-v1:0.description": "Claude 3 Sonnet 在智慧與速度之間取得平衡,適用於企業級工作負載,提供高性價比與可靠的大規模部署能力,並支援圖像輸入與 200K 的上下文視窗。",
236
+ "anthropic.claude-instant-v1.description": "一款快速、經濟且功能強大的模型,適用於日常對話、文字分析、摘要與文件問答。",
237
+ "anthropic.claude-v2.description": "一款功能全面的模型,涵蓋複雜對話、創意生成與精確指令執行等任務。",
238
+ "anthropic.claude-v2:1.description": "Claude 2 的升級版,具備雙倍上下文視窗,並在長文件與檢索增強生成(RAG)任務中提升可靠性、降低幻覺率與提升基於證據的準確性。",
239
+ "anthropic/claude-3-haiku.description": "Claude 3 Haiku 是 Anthropic 速度最快的模型,專為處理長提示的企業級工作負載設計,能快速分析大型文件如季報、合約或法律案件,成本僅為同類模型的一半。",
240
+ "anthropic/claude-3-opus.description": "Claude 3 Opus 是 Anthropic 最智慧的模型,在處理高度複雜任務時展現市場領先的效能,能流暢應對開放式提示與新穎情境,具備類人理解能力。",
241
+ "anthropic/claude-3.5-haiku.description": "Claude 3.5 Haiku 提升了速度、程式碼準確性與工具使用能力,適用於對速度與工具互動有高要求的場景。",
242
+ "anthropic/claude-3.5-sonnet.description": "Claude 3.5 Sonnet 是 Sonnet 系列中快速且高效的模型,具備更佳的程式與推理能力,部分版本已逐步由 Sonnet 3.7 或更新版本取代。",
243
+ "anthropic/claude-3.7-sonnet.description": "Claude 3.7 Sonnet 是升級版的 Sonnet 模型,具備更強的推理與程式能力,適用於企業級複雜任務。",
244
+ "anthropic/claude-haiku-4.5.description": "Claude Haiku 4.5 是 Anthropic 的高效能快速模型,在保持高準確度的同時提供極低延遲。",
245
+ "anthropic/claude-opus-4.1.description": "Opus 4.1 是 Anthropic 的高階模型,針對程式設計、複雜推理與長時間任務進行最佳化。",
246
+ "anthropic/claude-opus-4.5.description": "Claude Opus 4.5 是 Anthropic 的旗艦模型,結合頂尖智慧與可擴展效能,適用於複雜且高品質的推理任務。",
247
+ "anthropic/claude-opus-4.description": "Opus 4 是 Anthropic 為複雜任務與企業應用設計的旗艦模型。",
248
+ "anthropic/claude-sonnet-4.5.description": "Claude Sonnet 4.5 是 Anthropic 最新的混合推理模型,針對複雜推理與程式設計進行最佳化。",
249
+ "anthropic/claude-sonnet-4.description": "Claude Sonnet 4 是 Anthropic 的混合推理模型,具備思考與非思考能力的結合。",
227
250
  "gemini-flash-latest.description": "Gemini Flash 最新版本",
228
251
  "gemini-flash-lite-latest.description": "Gemini Flash-Lite 最新版本",
229
252
  "gemini-pro-latest.description": "Gemini Pro 最新版本",
@@ -215,6 +215,7 @@
215
215
  "settingAgent.name.placeholder": "請輸入助手名稱",
216
216
  "settingAgent.name.title": "名稱",
217
217
  "settingAgent.prompt.placeholder": "輸入助手設定,按 / 開啟指令選單",
218
+ "settingAgent.prompt.templatePlaceholder": "#### 目標\n描述此代理的主要目的與目標。\n\n#### 技能\n- 列出主要能力\n- 以及專業知識領域\n\n#### 工作流程\n1. 步驟式流程\n2. 代理執行任務的方式\n3. 與使用者的預期互動方式\n\n#### 限制\n- 必須遵守的重要限制\n- 行為準則",
218
219
  "settingAgent.prompt.title": "助手設定",
219
220
  "settingAgent.submit": "更新助手資訊",
220
221
  "settingAgent.tag.desc": "助手標籤將會在助手社群中顯示",
@@ -271,15 +271,28 @@
271
271
  "referral.edit.hint": "支援 2-8 位英文字母、數字或底線",
272
272
  "referral.edit.placeholder": "輸入推薦碼",
273
273
  "referral.edit.save": "儲存",
274
+ "referral.errors.alreadyBound": "您已綁定邀請碼",
275
+ "referral.errors.backfillExpired": "補填期限已過。註冊後超過 3 天無法補填",
274
276
  "referral.errors.codeExists": "此推薦碼已被使用,請選擇其他碼",
277
+ "referral.errors.invalidCode": "邀請碼不存在,請確認後再試一次",
275
278
  "referral.errors.invalidFormat": "推薦碼格式錯誤,請輸入 2-8 位英文字母、數字或底線",
279
+ "referral.errors.selfReferral": "您不能使用自己的邀請碼",
276
280
  "referral.errors.updateFailed": "更新失敗,請稍後再試",
277
281
  "referral.inviteCode.description": "分享您的專屬推薦碼,邀請朋友註冊",
278
282
  "referral.inviteCode.title": "我的推薦碼",
279
283
  "referral.inviteLink.description": "複製連結並分享給朋友,完成註冊即可獲得獎勵",
280
284
  "referral.inviteLink.title": "推薦連結",
285
+ "referral.rules.backfill.alreadyBound": "您已綁定邀請碼",
286
+ "referral.rules.backfill.description": "忘記輸入邀請碼?您可以在註冊後 3 天內補填",
287
+ "referral.rules.backfill.expiredTip": "補填期限已過。註冊後超過 3 天無法補填",
288
+ "referral.rules.backfill.link": "補填邀請碼",
289
+ "referral.rules.backfill.placeholder": "請輸入邀請碼",
290
+ "referral.rules.backfill.submit": "確認綁定",
291
+ "referral.rules.backfill.success": "邀請碼綁定成功",
292
+ "referral.rules.backfill.title": "補填邀請碼",
281
293
  "referral.rules.description": "了解推薦獎勵計畫規則",
282
294
  "referral.rules.expiry": "點數有效期:用戶 100 天未活躍後,推薦點數將被清除",
295
+ "referral.rules.missedCode": "錯過邀請碼:您可以在註冊後 3 天內<0>補填</0>",
283
296
  "referral.rules.priority": "點數使用優先順序:免費點數 → 訂閱點數 → 推薦點數 → 儲值點數",
284
297
  "referral.rules.registration": "註冊方式:受邀者透過推薦連結註冊或在註冊頁輸入推薦碼",
285
298
  "referral.rules.reward": "獎勵:推薦人與受邀人各獲得 {{reward}}M 點數",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.184",
3
+ "version": "2.0.0-next.185",
4
4
  "description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -0,0 +1,245 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { adaptSizeToRatio, parseRatio } from './size';
4
+
5
+ describe('size utils', () => {
6
+ describe('parseRatio', () => {
7
+ it('should parse valid ratio string correctly', () => {
8
+ expect(parseRatio('16:9')).toBe(16 / 9);
9
+ expect(parseRatio('4:3')).toBe(4 / 3);
10
+ expect(parseRatio('1:1')).toBe(1);
11
+ expect(parseRatio('21:9')).toBe(21 / 9);
12
+ });
13
+
14
+ it('should handle square ratio correctly', () => {
15
+ expect(parseRatio('1:1')).toBe(1);
16
+ expect(parseRatio('100:100')).toBe(1);
17
+ expect(parseRatio('512:512')).toBe(1);
18
+ });
19
+
20
+ it('should handle wide ratios correctly', () => {
21
+ expect(parseRatio('16:9')).toBeCloseTo(16 / 9, 6);
22
+ expect(parseRatio('2:1')).toBe(2);
23
+ expect(parseRatio('3:1')).toBe(3);
24
+ });
25
+
26
+ it('should handle tall ratios correctly', () => {
27
+ expect(parseRatio('9:16')).toBeCloseTo(0.5625, 4);
28
+ expect(parseRatio('1:2')).toBe(0.5);
29
+ expect(parseRatio('3:4')).toBe(0.75);
30
+ });
31
+
32
+ it('should handle decimal values in ratio string', () => {
33
+ expect(parseRatio('1.5:1')).toBe(1.5);
34
+ expect(parseRatio('16.5:9.5')).toBeCloseTo(16.5 / 9.5, 6);
35
+ });
36
+
37
+ it('should return 1 for invalid ratio string formats', () => {
38
+ expect(parseRatio('16')).toBe(1); // Missing colon
39
+ expect(parseRatio('16-9')).toBe(1); // Wrong separator
40
+ expect(parseRatio('16:9:1')).toBe(1); // Too many parts
41
+ expect(parseRatio('')).toBe(1); // Empty string
42
+ expect(parseRatio(':')).toBe(1); // Only separator
43
+ });
44
+
45
+ it('should return 1 for invalid input types', () => {
46
+ // @ts-expect-error - testing runtime behavior with invalid types
47
+ expect(parseRatio(null)).toBe(1);
48
+ // @ts-expect-error - testing runtime behavior with invalid types
49
+ expect(parseRatio(undefined)).toBe(1);
50
+ // @ts-expect-error - testing runtime behavior with invalid types
51
+ expect(parseRatio(123)).toBe(1);
52
+ // @ts-expect-error - testing runtime behavior with invalid types
53
+ expect(parseRatio({})).toBe(1);
54
+ });
55
+
56
+ it('should return 1 for non-numeric ratio parts', () => {
57
+ expect(parseRatio('abc:def')).toBe(1);
58
+ expect(parseRatio('16:abc')).toBe(1);
59
+ expect(parseRatio('abc:9')).toBe(1);
60
+ expect(parseRatio('NaN:9')).toBe(1);
61
+ expect(parseRatio('16:NaN')).toBe(1);
62
+ });
63
+
64
+ it('should return 1 for zero or negative values', () => {
65
+ expect(parseRatio('0:9')).toBe(1);
66
+ expect(parseRatio('16:0')).toBe(1);
67
+ expect(parseRatio('0:0')).toBe(1);
68
+ expect(parseRatio('-16:9')).toBe(1);
69
+ expect(parseRatio('16:-9')).toBe(1);
70
+ expect(parseRatio('-16:-9')).toBe(1);
71
+ });
72
+
73
+ it('should return 1 for Infinity values', () => {
74
+ expect(parseRatio('Infinity:9')).toBe(1);
75
+ expect(parseRatio('16:Infinity')).toBe(1);
76
+ expect(parseRatio('Infinity:Infinity')).toBe(1);
77
+ });
78
+ });
79
+
80
+ describe('adaptSizeToRatio', () => {
81
+ describe('valid inputs', () => {
82
+ it('should keep width and adjust height when target ratio is wider', () => {
83
+ // 16:9 (1.777) is wider than 4:3 (1.333)
84
+ const result = adaptSizeToRatio(16 / 9, 800, 600);
85
+ expect(result).toEqual({ width: 800, height: 450 });
86
+ });
87
+
88
+ it('should keep height and adjust width when target ratio is taller', () => {
89
+ // 4:3 (1.333) is taller than 16:9 (1.777)
90
+ const result = adaptSizeToRatio(4 / 3, 800, 450);
91
+ expect(result).toEqual({ width: 600, height: 450 });
92
+ });
93
+
94
+ it('should maintain dimensions when ratio matches current ratio', () => {
95
+ // 16:9 matches 1920:1080
96
+ const result = adaptSizeToRatio(16 / 9, 1920, 1080);
97
+ expect(result).toEqual({ width: 1920, height: 1080 });
98
+ });
99
+
100
+ it('should handle square ratio (1:1) correctly', () => {
101
+ // Current ratio: 800/600 = 1.333, target ratio: 1
102
+ // Since 1 < 1.333, keeps height (600), adjusts width to 600
103
+ const result = adaptSizeToRatio(1, 800, 600);
104
+ expect(result).toEqual({ width: 600, height: 600 });
105
+
106
+ // Current ratio: 600/800 = 0.75, target ratio: 1
107
+ // Since 1 > 0.75, keeps width (600), adjusts height to 600
108
+ const result2 = adaptSizeToRatio(1, 600, 800);
109
+ expect(result2).toEqual({ width: 600, height: 600 });
110
+ });
111
+
112
+ it('should round dimensions to nearest integer', () => {
113
+ // Current ratio: 1000/800 = 1.25, target ratio: 1.5
114
+ // Since 1.5 > 1.25, keeps width (1000), adjusts height to 1000/1.5 = 666.67 -> 667
115
+ const result = adaptSizeToRatio(1.5, 1000, 800);
116
+ expect(result.width).toBe(1000);
117
+ expect(result.height).toBe(667);
118
+ expect(Number.isInteger(result.width)).toBe(true);
119
+ expect(Number.isInteger(result.height)).toBe(true);
120
+ });
121
+
122
+ it('should handle very wide ratios correctly', () => {
123
+ const result = adaptSizeToRatio(3, 1200, 800);
124
+ expect(result).toEqual({ width: 1200, height: 400 });
125
+ });
126
+
127
+ it('should handle very tall ratios correctly', () => {
128
+ const result = adaptSizeToRatio(1 / 3, 1200, 800);
129
+ expect(result).toEqual({ width: 267, height: 800 });
130
+ });
131
+
132
+ it('should handle small dimensions', () => {
133
+ const result = adaptSizeToRatio(16 / 9, 320, 240);
134
+ expect(result).toEqual({ width: 320, height: 180 });
135
+ });
136
+
137
+ it('should handle large dimensions', () => {
138
+ const result = adaptSizeToRatio(16 / 9, 3840, 2880);
139
+ expect(result).toEqual({ width: 3840, height: 2160 });
140
+ });
141
+
142
+ it('should handle decimal ratio values', () => {
143
+ const result = adaptSizeToRatio(1.5, 900, 600);
144
+ expect(result).toEqual({ width: 900, height: 600 });
145
+ });
146
+ });
147
+
148
+ describe('error handling - invalid ratio', () => {
149
+ it('should throw error for zero ratio', () => {
150
+ expect(() => adaptSizeToRatio(0, 800, 600)).toThrow(
151
+ 'Invalid ratio: must be a positive finite number',
152
+ );
153
+ });
154
+
155
+ it('should throw error for negative ratio', () => {
156
+ expect(() => adaptSizeToRatio(-1.5, 800, 600)).toThrow(
157
+ 'Invalid ratio: must be a positive finite number',
158
+ );
159
+ });
160
+
161
+ it('should throw error for NaN ratio', () => {
162
+ expect(() => adaptSizeToRatio(NaN, 800, 600)).toThrow(
163
+ 'Invalid ratio: must be a positive finite number',
164
+ );
165
+ });
166
+
167
+ it('should throw error for Infinity ratio', () => {
168
+ expect(() => adaptSizeToRatio(Infinity, 800, 600)).toThrow(
169
+ 'Invalid ratio: must be a positive finite number',
170
+ );
171
+ expect(() => adaptSizeToRatio(-Infinity, 800, 600)).toThrow(
172
+ 'Invalid ratio: must be a positive finite number',
173
+ );
174
+ });
175
+ });
176
+
177
+ describe('error handling - invalid defaultWidth', () => {
178
+ it('should throw error for zero defaultWidth', () => {
179
+ expect(() => adaptSizeToRatio(16 / 9, 0, 600)).toThrow(
180
+ 'Invalid defaultWidth: must be a positive finite number',
181
+ );
182
+ });
183
+
184
+ it('should throw error for negative defaultWidth', () => {
185
+ expect(() => adaptSizeToRatio(16 / 9, -800, 600)).toThrow(
186
+ 'Invalid defaultWidth: must be a positive finite number',
187
+ );
188
+ });
189
+
190
+ it('should throw error for NaN defaultWidth', () => {
191
+ expect(() => adaptSizeToRatio(16 / 9, NaN, 600)).toThrow(
192
+ 'Invalid defaultWidth: must be a positive finite number',
193
+ );
194
+ });
195
+
196
+ it('should throw error for Infinity defaultWidth', () => {
197
+ expect(() => adaptSizeToRatio(16 / 9, Infinity, 600)).toThrow(
198
+ 'Invalid defaultWidth: must be a positive finite number',
199
+ );
200
+ });
201
+ });
202
+
203
+ describe('error handling - invalid defaultHeight', () => {
204
+ it('should throw error for zero defaultHeight', () => {
205
+ expect(() => adaptSizeToRatio(16 / 9, 800, 0)).toThrow(
206
+ 'Invalid defaultHeight: must be a positive finite number',
207
+ );
208
+ });
209
+
210
+ it('should throw error for negative defaultHeight', () => {
211
+ expect(() => adaptSizeToRatio(16 / 9, 800, -600)).toThrow(
212
+ 'Invalid defaultHeight: must be a positive finite number',
213
+ );
214
+ });
215
+
216
+ it('should throw error for NaN defaultHeight', () => {
217
+ expect(() => adaptSizeToRatio(16 / 9, 800, NaN)).toThrow(
218
+ 'Invalid defaultHeight: must be a positive finite number',
219
+ );
220
+ });
221
+
222
+ it('should throw error for Infinity defaultHeight', () => {
223
+ expect(() => adaptSizeToRatio(16 / 9, 800, Infinity)).toThrow(
224
+ 'Invalid defaultHeight: must be a positive finite number',
225
+ );
226
+ });
227
+ });
228
+
229
+ describe('error handling - multiple invalid parameters', () => {
230
+ it('should validate ratio first before other parameters', () => {
231
+ // When multiple params are invalid, ratio is checked first
232
+ expect(() => adaptSizeToRatio(0, 0, 0)).toThrow(
233
+ 'Invalid ratio: must be a positive finite number',
234
+ );
235
+ });
236
+
237
+ it('should validate defaultWidth before defaultHeight', () => {
238
+ // When ratio is valid but width and height are invalid
239
+ expect(() => adaptSizeToRatio(1.5, 0, 0)).toThrow(
240
+ 'Invalid defaultWidth: must be a positive finite number',
241
+ );
242
+ });
243
+ });
244
+ });
245
+ });