@jant/core 0.3.8 → 0.3.9

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 (44) hide show
  1. package/dist/app.js +7 -4
  2. package/dist/db/schema.js +2 -1
  3. package/dist/lib/image.js +39 -15
  4. package/dist/lib/media-helpers.js +14 -8
  5. package/dist/lib/storage.js +164 -0
  6. package/dist/routes/api/posts.js +12 -7
  7. package/dist/routes/api/timeline.js +3 -2
  8. package/dist/routes/api/upload.js +27 -20
  9. package/dist/routes/dash/media.js +24 -14
  10. package/dist/routes/dash/posts.js +4 -1
  11. package/dist/routes/feed/rss.js +3 -2
  12. package/dist/routes/pages/home.js +3 -2
  13. package/dist/routes/pages/post.js +9 -5
  14. package/dist/services/media.js +7 -5
  15. package/dist/theme/components/PostForm.js +4 -3
  16. package/dist/types.js +32 -0
  17. package/package.json +2 -1
  18. package/src/__tests__/helpers/app.ts +1 -0
  19. package/src/__tests__/helpers/db.ts +10 -0
  20. package/src/app.tsx +8 -7
  21. package/src/db/migrations/0004_add_storage_provider.sql +3 -0
  22. package/src/db/migrations/meta/_journal.json +7 -0
  23. package/src/db/schema.ts +2 -1
  24. package/src/i18n/locales/en.po +67 -67
  25. package/src/i18n/locales/zh-Hans.po +67 -67
  26. package/src/i18n/locales/zh-Hant.po +67 -67
  27. package/src/lib/__tests__/image.test.ts +96 -0
  28. package/src/lib/__tests__/storage.test.ts +162 -0
  29. package/src/lib/image.ts +46 -16
  30. package/src/lib/media-helpers.ts +29 -18
  31. package/src/lib/storage.ts +236 -0
  32. package/src/routes/api/__tests__/posts.test.ts +8 -8
  33. package/src/routes/api/posts.ts +20 -6
  34. package/src/routes/api/timeline.tsx +8 -1
  35. package/src/routes/api/upload.ts +44 -21
  36. package/src/routes/dash/media.tsx +40 -8
  37. package/src/routes/dash/posts.tsx +5 -0
  38. package/src/routes/feed/rss.ts +3 -2
  39. package/src/routes/pages/home.tsx +8 -1
  40. package/src/routes/pages/post.tsx +29 -17
  41. package/src/services/__tests__/media.test.ts +44 -26
  42. package/src/services/media.ts +10 -7
  43. package/src/theme/components/PostForm.tsx +13 -2
  44. package/src/types.ts +41 -1
@@ -44,7 +44,7 @@ msgid "Account"
44
44
  msgstr "帳戶"
45
45
 
46
46
  #. @context: Button to open media picker
47
- #: src/theme/components/PostForm.tsx:178
47
+ #: src/theme/components/PostForm.tsx:189
48
48
  msgid "Add Media"
49
49
  msgstr "添加媒體"
50
50
 
@@ -72,7 +72,7 @@ msgstr ""
72
72
  #. @context: Post type label - article
73
73
  #. @context: Post type option
74
74
  #: src/routes/pages/archive.tsx:28
75
- #: src/theme/components/PostForm.tsx:67
75
+ #: src/theme/components/PostForm.tsx:73
76
76
  #: src/theme/components/TypeBadge.tsx:20
77
77
  msgid "Article"
78
78
  msgstr "文章"
@@ -83,12 +83,12 @@ msgid "Articles"
83
83
  msgstr "文章"
84
84
 
85
85
  #. @context: Hint for image post type media requirement
86
- #: src/theme/components/PostForm.tsx:130
86
+ #: src/theme/components/PostForm.tsx:136
87
87
  msgid "At least 1 image required for image posts."
88
88
  msgstr "至少需要 1 張圖片才能發佈圖片帖子。"
89
89
 
90
90
  #. @context: Button to go back to media list
91
- #: src/routes/dash/media.tsx:245
91
+ #: src/routes/dash/media.tsx:266
92
92
  msgid "Back"
93
93
  msgstr "返回"
94
94
 
@@ -103,7 +103,7 @@ msgstr "返回"
103
103
  #: src/routes/dash/navigation.tsx:182
104
104
  #: src/routes/dash/redirects.tsx:167
105
105
  #: src/theme/components/PageForm.tsx:161
106
- #: src/theme/components/PostForm.tsx:312
106
+ #: src/theme/components/PostForm.tsx:323
107
107
  msgid "Cancel"
108
108
  msgstr "取消"
109
109
 
@@ -115,7 +115,7 @@ msgid "Change Password"
115
115
  msgstr "更改密碼"
116
116
 
117
117
  #. @context: Hint to click image for lightbox
118
- #: src/routes/dash/media.tsx:278
118
+ #: src/routes/dash/media.tsx:299
119
119
  msgid "Click image to view full size"
120
120
  msgstr "點擊圖片以查看完整大小"
121
121
 
@@ -127,7 +127,7 @@ msgid "Collections"
127
127
  msgstr "收藏夾"
128
128
 
129
129
  #. @context: Post form field - assign to collections
130
- #: src/theme/components/PostForm.tsx:261
130
+ #: src/theme/components/PostForm.tsx:272
131
131
  msgid "Collections (optional)"
132
132
  msgstr "收藏(可選)"
133
133
 
@@ -137,7 +137,7 @@ msgid "Color theme"
137
137
  msgstr "顏色主題"
138
138
 
139
139
  #. @context: Setup form submit button
140
- #: src/app.tsx:248
140
+ #: src/app.tsx:251
141
141
  msgid "Complete Setup"
142
142
  msgstr "完成設置"
143
143
 
@@ -147,21 +147,21 @@ msgid "Confirm New Password"
147
147
  msgstr "確認新密碼"
148
148
 
149
149
  #. @context: Password reset form field
150
- #: src/app.tsx:490
150
+ #: src/app.tsx:493
151
151
  msgid "Confirm Password"
152
152
  msgstr "確認密碼"
153
153
 
154
154
  #. @context: Page form field label - content
155
155
  #. @context: Post form field
156
156
  #: src/theme/components/PageForm.tsx:95
157
- #: src/theme/components/PostForm.tsx:103
157
+ #: src/theme/components/PostForm.tsx:109
158
158
  msgid "Content"
159
159
  msgstr "內容"
160
160
 
161
161
  #. @context: Button to copy Markdown to clipboard
162
162
  #. @context: Button to copy URL to clipboard
163
- #: src/routes/dash/media.tsx:316
164
- #: src/routes/dash/media.tsx:353
163
+ #: src/routes/dash/media.tsx:337
164
+ #: src/routes/dash/media.tsx:374
165
165
  msgid "Copy"
166
166
  msgstr "複製"
167
167
 
@@ -186,7 +186,7 @@ msgid "Create Redirect"
186
186
  msgstr "建立重定向"
187
187
 
188
188
  #. @context: Setup page description
189
- #: src/app.tsx:190
189
+ #: src/app.tsx:193
190
190
  msgid "Create your admin account."
191
191
  msgstr "建立您的管理員帳戶。"
192
192
 
@@ -206,7 +206,7 @@ msgid "Current Password"
206
206
  msgstr "當前密碼"
207
207
 
208
208
  #. @context: Post form field
209
- #: src/theme/components/PostForm.tsx:285
209
+ #: src/theme/components/PostForm.tsx:296
210
210
  msgid "Custom Path (optional)"
211
211
  msgstr "自訂路徑(選填)"
212
212
 
@@ -237,7 +237,7 @@ msgid "Delete Collection"
237
237
  msgstr "刪除收藏夾"
238
238
 
239
239
  #. @context: Button to delete media
240
- #: src/routes/dash/media.tsx:364
240
+ #: src/routes/dash/media.tsx:385
241
241
  msgid "Delete Media"
242
242
  msgstr "刪除媒體"
243
243
 
@@ -247,12 +247,12 @@ msgid "Delete Page"
247
247
  msgstr "刪除頁面"
248
248
 
249
249
  #. @context: Warning message before deleting media
250
- #: src/routes/dash/media.tsx:370
250
+ #: src/routes/dash/media.tsx:391
251
251
  msgid "Deleting this media will remove it permanently from storage."
252
252
  msgstr "刪除此媒體將永久從存儲中移除它。"
253
253
 
254
254
  #. @context: Hint shown on signin page when demo credentials are pre-filled
255
- #: src/app.tsx:339
255
+ #: src/app.tsx:342
256
256
  msgid "Demo account pre-filled. Just click Sign In."
257
257
  msgstr "示範帳戶已預填。只需點擊登入。"
258
258
 
@@ -269,7 +269,7 @@ msgid "Display text for the link"
269
269
  msgstr "顯示連結的文字"
270
270
 
271
271
  #. @context: Close media picker button
272
- #: src/theme/components/PostForm.tsx:334
272
+ #: src/theme/components/PostForm.tsx:345
273
273
  msgid "Done"
274
274
  msgstr "完成"
275
275
 
@@ -277,7 +277,7 @@ msgstr "完成"
277
277
  #. @context: Post visibility badge - draft
278
278
  #. @context: Post visibility option
279
279
  #: src/theme/components/PageForm.tsx:132
280
- #: src/theme/components/PostForm.tsx:249
280
+ #: src/theme/components/PostForm.tsx:260
281
281
  #: src/theme/components/VisibilityBadge.tsx:38
282
282
  msgid "Draft"
283
283
  msgstr "草稿"
@@ -288,7 +288,7 @@ msgid "Drafts"
288
288
  msgstr "草稿"
289
289
 
290
290
  #. @context: Source name placeholder
291
- #: src/theme/components/PostForm.tsx:214
291
+ #: src/theme/components/PostForm.tsx:225
292
292
  msgid "e.g. The Verge, John Doe"
293
293
  msgstr "例如:The Verge,約翰·多伊"
294
294
 
@@ -327,25 +327,25 @@ msgid "Edit Page"
327
327
  msgstr "編輯頁面"
328
328
 
329
329
  #. @context: Page heading
330
- #: src/routes/dash/posts.tsx:185
330
+ #: src/routes/dash/posts.tsx:187
331
331
  msgid "Edit Post"
332
332
  msgstr "編輯文章"
333
333
 
334
334
  #. @context: Setup/signin form field - email
335
335
  #. @context: Setup/signin form field - email
336
- #: src/app.tsx:219
337
- #: src/app.tsx:353
336
+ #: src/app.tsx:222
337
+ #: src/app.tsx:356
338
338
  msgid "Email"
339
339
  msgstr "電子郵件"
340
340
 
341
341
  #. @context: Password reset page description
342
- #: src/app.tsx:460
342
+ #: src/app.tsx:463
343
343
  msgid "Enter your new password."
344
344
  msgstr "請輸入您的新密碼。"
345
345
 
346
346
  #. @context: Post visibility badge - featured
347
347
  #. @context: Post visibility option
348
- #: src/theme/components/PostForm.tsx:237
348
+ #: src/theme/components/PostForm.tsx:248
349
349
  #: src/theme/components/VisibilityBadge.tsx:26
350
350
  msgid "Featured"
351
351
  msgstr "精選"
@@ -376,7 +376,7 @@ msgstr "一般設定"
376
376
  #. @context: Post type label - image
377
377
  #. @context: Post type option
378
378
  #: src/routes/pages/archive.tsx:37
379
- #: src/theme/components/PostForm.tsx:76
379
+ #: src/theme/components/PostForm.tsx:82
380
380
  #: src/theme/components/TypeBadge.tsx:29
381
381
  msgid "Image"
382
382
  msgstr "圖片"
@@ -387,12 +387,12 @@ msgid "Images"
387
387
  msgstr "圖片"
388
388
 
389
389
  #. @context: Media upload instructions - auto optimization
390
- #: src/routes/dash/media.tsx:158
390
+ #: src/routes/dash/media.tsx:171
391
391
  msgid "Images are automatically optimized: resized to max 1920px, converted to WebP, and metadata stripped."
392
392
  msgstr "圖片會自動優化:調整大小至最大 1920 像素,轉換為 WebP 格式,並去除元數據。"
393
393
 
394
394
  #. @context: Password reset error heading
395
- #: src/app.tsx:525
395
+ #: src/app.tsx:528
396
396
  msgid "Invalid or Expired Link"
397
397
  msgstr "無效或已過期的連結"
398
398
 
@@ -410,7 +410,7 @@ msgstr "語言"
410
410
  #. @context: Post type label - link
411
411
  #. @context: Post type option
412
412
  #: src/routes/pages/archive.tsx:32
413
- #: src/theme/components/PostForm.tsx:70
413
+ #: src/theme/components/PostForm.tsx:76
414
414
  #: src/theme/components/TypeBadge.tsx:24
415
415
  msgid "Link"
416
416
  msgstr "連結"
@@ -428,7 +428,7 @@ msgid "Load more"
428
428
  msgstr "載入更多"
429
429
 
430
430
  #. @context: Loading state for media picker
431
- #: src/theme/components/PostForm.tsx:345
431
+ #: src/theme/components/PostForm.tsx:356
432
432
  msgid "Loading..."
433
433
  msgstr "載入中..."
434
434
 
@@ -438,15 +438,15 @@ msgid "Lowercase letters, numbers, and hyphens only"
438
438
  msgstr "僅限小寫字母、數字和連字符"
439
439
 
440
440
  #. @context: Media detail section - Markdown snippet
441
- #: src/routes/dash/media.tsx:334
441
+ #: src/routes/dash/media.tsx:355
442
442
  msgid "Markdown"
443
443
  msgstr "Markdown"
444
444
 
445
445
  #. @context: Dashboard navigation - media library
446
446
  #. @context: Media main heading
447
447
  #. @context: Post form field - media attachments
448
- #: src/routes/dash/media.tsx:138
449
- #: src/theme/components/PostForm.tsx:121
448
+ #: src/routes/dash/media.tsx:151
449
+ #: src/theme/components/PostForm.tsx:127
450
450
  #: src/theme/layouts/DashLayout.tsx:105
451
451
  msgid "Media"
452
452
  msgstr "媒體"
@@ -500,7 +500,7 @@ msgstr "新頁面"
500
500
 
501
501
  #. @context: Password form field
502
502
  #. @context: Password reset form field
503
- #: src/app.tsx:474
503
+ #: src/app.tsx:477
504
504
  #: src/routes/dash/settings.tsx:417
505
505
  msgid "New Password"
506
506
  msgstr "新密碼"
@@ -536,7 +536,7 @@ msgid "No collections yet."
536
536
  msgstr "尚未有任何收藏。"
537
537
 
538
538
  #. @context: Empty state message when no media exists
539
- #: src/routes/dash/media.tsx:173
539
+ #: src/routes/dash/media.tsx:186
540
540
  msgid "No media uploaded yet."
541
541
  msgstr "尚未上傳任何媒體。"
542
542
 
@@ -583,7 +583,7 @@ msgstr "未找到結果。"
583
583
  #. @context: Post type label - note
584
584
  #. @context: Post type option
585
585
  #: src/routes/pages/archive.tsx:27
586
- #: src/theme/components/PostForm.tsx:64
586
+ #: src/theme/components/PostForm.tsx:70
587
587
  #: src/theme/components/TypeBadge.tsx:19
588
588
  msgid "Note"
589
589
  msgstr "備註"
@@ -628,8 +628,8 @@ msgstr "頁面"
628
628
 
629
629
  #. @context: Setup/signin form field - password
630
630
  #. @context: Setup/signin form field - password
631
- #: src/app.tsx:234
632
- #: src/app.tsx:362
631
+ #: src/app.tsx:237
632
+ #: src/app.tsx:365
633
633
  msgid "Password"
634
634
  msgstr "密碼"
635
635
 
@@ -645,7 +645,7 @@ msgstr "路徑(例如 /archive)或完整網址(例如 https://example.com
645
645
 
646
646
  #. @context: Link to individual post in thread
647
647
  #. @context: Link to permanent URL of post
648
- #: src/routes/pages/post.tsx:52
648
+ #: src/routes/pages/post.tsx:56
649
649
  #: src/theme/components/ThreadView.tsx:68
650
650
  msgid "Permalink"
651
651
  msgstr "永久鏈接"
@@ -656,7 +656,7 @@ msgid "Post"
656
656
  msgstr "文章"
657
657
 
658
658
  #. @context: Post title placeholder
659
- #: src/theme/components/PostForm.tsx:93
659
+ #: src/theme/components/PostForm.tsx:99
660
660
  msgid "Post title..."
661
661
  msgstr "文章標題..."
662
662
 
@@ -673,7 +673,7 @@ msgid "Posts in Collection ({count})"
673
673
  msgstr "收藏中的帖子 ({count})"
674
674
 
675
675
  #. @context: Media detail section - preview
676
- #: src/routes/dash/media.tsx:257
676
+ #: src/routes/dash/media.tsx:278
677
677
  msgid "Preview"
678
678
  msgstr "預覽"
679
679
 
@@ -685,7 +685,7 @@ msgid "Previous"
685
685
  msgstr "上一頁"
686
686
 
687
687
  #. @context: Upload status - processing
688
- #: src/routes/dash/media.tsx:106
688
+ #: src/routes/dash/media.tsx:119
689
689
  msgid "Processing..."
690
690
  msgstr "處理中..."
691
691
 
@@ -695,7 +695,7 @@ msgid "Profile"
695
695
  msgstr "個人資料"
696
696
 
697
697
  #. @context: Button to publish new post
698
- #: src/theme/components/PostForm.tsx:306
698
+ #: src/theme/components/PostForm.tsx:317
699
699
  msgid "Publish"
700
700
  msgstr "發佈"
701
701
 
@@ -722,7 +722,7 @@ msgid "Quiet"
722
722
  msgstr "安靜"
723
723
 
724
724
  #. @context: Post visibility option
725
- #: src/theme/components/PostForm.tsx:231
725
+ #: src/theme/components/PostForm.tsx:242
726
726
  msgid "Quiet (normal)"
727
727
  msgstr "安靜(正常)"
728
728
 
@@ -730,7 +730,7 @@ msgstr "安靜(正常)"
730
730
  #. @context: Post type label - quote
731
731
  #. @context: Post type option
732
732
  #: src/routes/pages/archive.tsx:33
733
- #: src/theme/components/PostForm.tsx:73
733
+ #: src/theme/components/PostForm.tsx:79
734
734
  #: src/theme/components/TypeBadge.tsx:25
735
735
  msgid "Quote"
736
736
  msgstr "引用"
@@ -750,14 +750,14 @@ msgstr "重定向"
750
750
  #. @context: Button to remove post from collection
751
751
  #. @context: Remove media attachment button
752
752
  #: src/routes/dash/collections.tsx:257
753
- #: src/theme/components/PostForm.tsx:161
753
+ #: src/theme/components/PostForm.tsx:172
754
754
  msgid "Remove"
755
755
  msgstr "移除"
756
756
 
757
757
  #. @context: Password reset form submit button
758
758
  #. @context: Password reset page heading
759
- #: src/app.tsx:454
760
- #: src/app.tsx:505
759
+ #: src/app.tsx:457
760
+ #: src/app.tsx:508
761
761
  msgid "Reset Password"
762
762
  msgstr "重設密碼"
763
763
 
@@ -789,7 +789,7 @@ msgid "Search posts..."
789
789
  msgstr "搜尋帖子..."
790
790
 
791
791
  #. @context: Media picker dialog title
792
- #: src/theme/components/PostForm.tsx:324
792
+ #: src/theme/components/PostForm.tsx:335
793
793
  msgid "Select Media"
794
794
  msgstr "選擇媒體"
795
795
 
@@ -812,8 +812,8 @@ msgstr "顯示 {remainingCount} 個更多 {0}"
812
812
 
813
813
  #. @context: Sign in form submit button
814
814
  #. @context: Sign in page heading
815
- #: src/app.tsx:330
816
- #: src/app.tsx:375
815
+ #: src/app.tsx:333
816
+ #: src/app.tsx:378
817
817
  msgid "Sign In"
818
818
  msgstr "登入"
819
819
 
@@ -840,12 +840,12 @@ msgid "Slug"
840
840
  msgstr "縮略名"
841
841
 
842
842
  #. @context: Post form field - name of the source website or author
843
- #: src/theme/components/PostForm.tsx:204
843
+ #: src/theme/components/PostForm.tsx:215
844
844
  msgid "Source Name (optional)"
845
845
  msgstr "來源名稱(選填)"
846
846
 
847
847
  #. @context: Post form field
848
- #: src/theme/components/PostForm.tsx:188
848
+ #: src/theme/components/PostForm.tsx:199
849
849
  msgid "Source URL (optional)"
850
850
  msgstr "來源網址(選填)"
851
851
 
@@ -870,7 +870,7 @@ msgid "The URL path for this page. Use lowercase letters, numbers, and hyphens."
870
870
  msgstr "此頁面的 URL 路徑。使用小寫字母、數字和連字符。"
871
871
 
872
872
  #. @context: Password reset error description
873
- #: src/app.tsx:533
873
+ #: src/app.tsx:536
874
874
  msgid "This password reset link is invalid or has expired. Please generate a new one."
875
875
  msgstr "此密碼重設連結無效或已過期。請生成一個新的連結。"
876
876
 
@@ -904,7 +904,7 @@ msgid "Title"
904
904
  msgstr "標題"
905
905
 
906
906
  #. @context: Post form field
907
- #: src/theme/components/PostForm.tsx:84
907
+ #: src/theme/components/PostForm.tsx:90
908
908
  msgid "Title (optional)"
909
909
  msgstr "標題(選填)"
910
910
 
@@ -916,13 +916,13 @@ msgstr "到路徑"
916
916
  #. @context: Post form field - post type
917
917
  #. @context: Redirect form field
918
918
  #: src/routes/dash/redirects.tsx:141
919
- #: src/theme/components/PostForm.tsx:57
919
+ #: src/theme/components/PostForm.tsx:63
920
920
  msgid "Type"
921
921
  msgstr "類型"
922
922
 
923
923
  #. @context: Post visibility badge - unlisted
924
924
  #. @context: Post visibility option
925
- #: src/theme/components/PostForm.tsx:243
925
+ #: src/theme/components/PostForm.tsx:254
926
926
  #: src/theme/components/VisibilityBadge.tsx:34
927
927
  msgid "Unlisted"
928
928
  msgstr "不公開"
@@ -935,7 +935,7 @@ msgid "Untitled"
935
935
  msgstr "無標題"
936
936
 
937
937
  #. @context: Button to update existing post
938
- #: src/theme/components/PostForm.tsx:302
938
+ #: src/theme/components/PostForm.tsx:313
939
939
  msgid "Update"
940
940
  msgstr "更新"
941
941
 
@@ -950,23 +950,23 @@ msgid "Update Page"
950
950
  msgstr "更新頁面"
951
951
 
952
952
  #. @context: Button to upload media file
953
- #: src/routes/dash/media.tsx:114
953
+ #: src/routes/dash/media.tsx:127
954
954
  msgid "Upload"
955
955
  msgstr "上傳"
956
956
 
957
957
  #. @context: Upload error message
958
- #: src/routes/dash/media.tsx:118
958
+ #: src/routes/dash/media.tsx:131
959
959
  msgid "Upload failed. Please try again."
960
960
  msgstr "上傳失敗。請再試一次。"
961
961
 
962
962
  #. @context: Upload status - uploading
963
- #: src/routes/dash/media.tsx:110
963
+ #: src/routes/dash/media.tsx:123
964
964
  msgid "Uploading..."
965
965
  msgstr "上傳中..."
966
966
 
967
967
  #. @context: Media detail section - URL
968
968
  #. @context: Navigation link form field
969
- #: src/routes/dash/media.tsx:297
969
+ #: src/routes/dash/media.tsx:318
970
970
  #: src/routes/dash/navigation.tsx:148
971
971
  msgid "URL"
972
972
  msgstr "網址"
@@ -977,7 +977,7 @@ msgid "URL-safe identifier (lowercase, numbers, hyphens)"
977
977
  msgstr "網址安全識別碼(小寫、數字、連字符)"
978
978
 
979
979
  #. @context: Media URL helper text
980
- #: src/routes/dash/media.tsx:323
980
+ #: src/routes/dash/media.tsx:344
981
981
  msgid "Use this URL to embed the media in your posts."
982
982
  msgstr "使用此 URL 將媒體嵌入到您的帖子中。"
983
983
 
@@ -1004,17 +1004,17 @@ msgid "View Site"
1004
1004
  msgstr "查看網站"
1005
1005
 
1006
1006
  #. @context: Post form field
1007
- #: src/theme/components/PostForm.tsx:224
1007
+ #: src/theme/components/PostForm.tsx:235
1008
1008
  msgid "Visibility"
1009
1009
  msgstr "可見性"
1010
1010
 
1011
1011
  #. @context: Setup page welcome heading
1012
- #: src/app.tsx:184
1012
+ #: src/app.tsx:187
1013
1013
  msgid "Welcome to Jant"
1014
1014
  msgstr "歡迎來到 Jant"
1015
1015
 
1016
1016
  #. @context: Post content placeholder
1017
- #: src/theme/components/PostForm.tsx:108
1017
+ #: src/theme/components/PostForm.tsx:114
1018
1018
  msgid "What's on your mind?"
1019
1019
  msgstr "你在想什麼?"
1020
1020
 
@@ -1024,6 +1024,6 @@ msgid "What's this collection about?"
1024
1024
  msgstr "這個收藏是關於什麼的?"
1025
1025
 
1026
1026
  #. @context: Setup form field - user name
1027
- #: src/app.tsx:204
1027
+ #: src/app.tsx:207
1028
1028
  msgid "Your Name"
1029
1029
  msgstr "您的姓名"
@@ -0,0 +1,96 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { getMediaUrl, getImageUrl, getPublicUrlForProvider } from "../image.js";
3
+
4
+ describe("getPublicUrlForProvider", () => {
5
+ it("returns r2PublicUrl for r2 provider", () => {
6
+ const result = getPublicUrlForProvider(
7
+ "r2",
8
+ "https://r2.example.com",
9
+ "https://s3.example.com",
10
+ );
11
+ expect(result).toBe("https://r2.example.com");
12
+ });
13
+
14
+ it("returns s3PublicUrl for s3 provider", () => {
15
+ const result = getPublicUrlForProvider(
16
+ "s3",
17
+ "https://r2.example.com",
18
+ "https://s3.example.com",
19
+ );
20
+ expect(result).toBe("https://s3.example.com");
21
+ });
22
+
23
+ it("returns undefined when r2 provider has no r2PublicUrl", () => {
24
+ const result = getPublicUrlForProvider(
25
+ "r2",
26
+ undefined,
27
+ "https://s3.example.com",
28
+ );
29
+ expect(result).toBeUndefined();
30
+ });
31
+
32
+ it("returns undefined when s3 provider has no s3PublicUrl", () => {
33
+ const result = getPublicUrlForProvider(
34
+ "s3",
35
+ "https://r2.example.com",
36
+ undefined,
37
+ );
38
+ expect(result).toBeUndefined();
39
+ });
40
+
41
+ it("defaults to r2PublicUrl for unknown providers", () => {
42
+ const result = getPublicUrlForProvider(
43
+ "unknown",
44
+ "https://r2.example.com",
45
+ "https://s3.example.com",
46
+ );
47
+ expect(result).toBe("https://r2.example.com");
48
+ });
49
+ });
50
+
51
+ describe("getMediaUrl", () => {
52
+ it("returns local proxy URL when no publicUrl provided", () => {
53
+ const result = getMediaUrl(
54
+ "01902a9f-1a2b-7c3d",
55
+ "media/2025/01/01902a9f-1a2b-7c3d.webp",
56
+ );
57
+ expect(result).toBe("/media/01902a9f-1a2b-7c3d.webp");
58
+ });
59
+
60
+ it("returns CDN URL when publicUrl is provided", () => {
61
+ const result = getMediaUrl(
62
+ "01902a9f-1a2b-7c3d",
63
+ "media/2025/01/01902a9f-1a2b-7c3d.webp",
64
+ "https://cdn.example.com",
65
+ );
66
+ expect(result).toBe(
67
+ "https://cdn.example.com/media/2025/01/01902a9f-1a2b-7c3d.webp",
68
+ );
69
+ });
70
+ });
71
+
72
+ describe("getImageUrl", () => {
73
+ it("returns original URL when no transform URL provided", () => {
74
+ const result = getImageUrl("/media/test.jpg", undefined, { width: 200 });
75
+ expect(result).toBe("/media/test.jpg");
76
+ });
77
+
78
+ it("returns transformed URL with options", () => {
79
+ const result = getImageUrl(
80
+ "/media/test.jpg",
81
+ "https://example.com/cdn-cgi/image",
82
+ { width: 200, quality: 80, format: "auto" },
83
+ );
84
+ expect(result).toBe(
85
+ "https://example.com/cdn-cgi/image/width=200,quality=80,format=auto//media/test.jpg",
86
+ );
87
+ });
88
+
89
+ it("returns original URL when no options provided", () => {
90
+ const result = getImageUrl(
91
+ "/media/test.jpg",
92
+ "https://example.com/cdn-cgi/image",
93
+ );
94
+ expect(result).toBe("/media/test.jpg");
95
+ });
96
+ });