diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts
index ec6f9038f..c65abfdde 100644
--- a/composeApp/build.gradle.kts
+++ b/composeApp/build.gradle.kts
@@ -28,6 +28,10 @@ kotlin {
implementation(projects.core.domain)
implementation(projects.core.presentation)
+ // Coil SVG decoder registered in App() via SingletonImageLoaderFactory
+ implementation(libs.coil3.compose)
+ implementation(libs.coil3.svg)
+
implementation(projects.feature.apps.domain)
implementation(projects.feature.apps.data)
implementation(projects.feature.apps.presentation)
diff --git a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/Main.kt b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/Main.kt
index 3091b8508..a0c34969c 100644
--- a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/Main.kt
+++ b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/Main.kt
@@ -33,6 +33,16 @@ import zed.rainxch.githubstore.app.whatsnew.WhatsNewViewModel
@Composable
fun App(deepLinkUri: String? = null) {
+ // Wire Coil's singleton ImageLoader with the SVG decoder so README
+ // images that point to .svg URLs (shields.io badges, diagrams,
+ // hero images) render natively instead of failing silently.
+ coil3.compose.setSingletonImageLoaderFactory { context ->
+ coil3.ImageLoader
+ .Builder(context)
+ .components { add(coil3.svg.SvgDecoder.Factory()) }
+ .build()
+ }
+
val viewModel: MainViewModel = koinViewModel()
val state by viewModel.state.collectAsStateWithLifecycle()
diff --git a/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/applyThemeAwareImages.kt b/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/applyThemeAwareImages.kt
new file mode 100644
index 000000000..9105f380c
--- /dev/null
+++ b/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/applyThemeAwareImages.kt
@@ -0,0 +1,50 @@
+package zed.rainxch.core.domain.util
+
+fun applyThemeAwareImages(
+ content: String,
+ isDark: Boolean,
+): String {
+ if (content.isEmpty()) return content
+ var processed = content
+
+ // Strip markdown-image entries whose URL has `#gh-dark-mode-only`
+ // or `#gh-light-mode-only` and we're on the OTHER theme. GitHub's
+ // own renderer hides these on the mismatched theme; emulating that
+ // keeps READMEs from showing both light + dark variants stacked.
+ processed = processed.replace(
+ Regex("""!\[([^\]]*)\]\(([^)]*?)#gh-(dark|light)-mode-only([^)]*)\)"""),
+ ) { match ->
+ val mode = match.groupValues[3]
+ if ((isDark && mode == "light") || (!isDark && mode == "dark")) {
+ "" // drop entirely; alt text would just be noise here
+ } else {
+ // Strip the fragment so the URL is clean for Coil's cache key.
+ val alt = match.groupValues[1]
+ val urlBase = match.groupValues[2]
+ val trailing = match.groupValues[4]
+ ""
+ }
+ }
+
+ // Same for raw HTML
tags.
+ processed = processed.replace(
+ Regex(
+ """
]*?)src\s*=\s*(["'])([^"']*?)#gh-(dark|light)-mode-only([^"']*?)\2([^>]*?)>""",
+ RegexOption.IGNORE_CASE,
+ ),
+ ) { match ->
+ val mode = match.groupValues[4]
+ if ((isDark && mode == "light") || (!isDark && mode == "dark")) {
+ ""
+ } else {
+ val pre = match.groupValues[1]
+ val quote = match.groupValues[2]
+ val urlBase = match.groupValues[3]
+ val trailing = match.groupValues[5]
+ val post = match.groupValues[6]
+ "
"
+ }
+ }
+
+ return processed
+}
diff --git a/core/presentation/src/commonMain/composeResources/files/whatsnew/18.json b/core/presentation/src/commonMain/composeResources/files/whatsnew/18.json
index 11f2dc565..056d701f3 100644
--- a/core/presentation/src/commonMain/composeResources/files/whatsnew/18.json
+++ b/core/presentation/src/commonMain/composeResources/files/whatsnew/18.json
@@ -31,7 +31,8 @@
"Manually linking apps — sorted by installer source (F-Droid / Obtainium first, Play Store and system updates last) with a chip showing each app's source.",
"Manual link now suggests matching GitHub repos automatically — pick an app, get ranked candidates, tap to link. Manual URL entry still available.",
"GitHub-style alert callouts in README and release notes — Note, Tip, Important, Warning, Caution now render as tinted cards with icons instead of literal text.",
- "Emoji shortcodes in README and release notes — :rocket: now renders as 🚀, :tada: as 🎉, and ~250 others. Common dev/status icons covered out of the box."
+ "Emoji shortcodes in README and release notes — :rocket: now renders as 🚀, :tada: as 🎉, and ~250 others. Common dev/status icons covered out of the box.",
+ "SVG images in README and release notes now render natively (diagrams, hero images). Theme-only images respect light/dark — no more both-variants-stacked. Browser-like User-Agent fixes hotlink-blocked badges from common CDNs."
]
}
]
diff --git a/core/presentation/src/commonMain/composeResources/files/whatsnew/ar/18.json b/core/presentation/src/commonMain/composeResources/files/whatsnew/ar/18.json
index 20184799b..a0c7e70a3 100644
--- a/core/presentation/src/commonMain/composeResources/files/whatsnew/ar/18.json
+++ b/core/presentation/src/commonMain/composeResources/files/whatsnew/ar/18.json
@@ -31,7 +31,8 @@
"عند الربط اليدوي للتطبيقات — يتم الفرز حسب مصدر التثبيت (F-Droid / Obtainium أولاً، Play Store وتحديثات النظام أخيراً) مع شارة تُظهر مصدر كل تطبيق.",
"الربط اليدوي يقترح الآن مستودعات GitHub المطابقة تلقائياً — اختر تطبيقاً، احصل على مرشحين مرتبين، انقر للربط. الإدخال اليدوي للعنوان لا يزال متاحاً.",
"تنبيهات GitHub في README وملاحظات الإصدار — Note وTip وImportant وWarning وCaution تظهر الآن كبطاقات ملونة مع أيقونات بدلاً من نص حرفي.",
- "اختصارات الإيموجي في README وملاحظات الإصدار — :rocket: تظهر الآن كـ 🚀، :tada: كـ 🎉، و~250 اختصاراً آخر."
+ "اختصارات الإيموجي في README وملاحظات الإصدار — :rocket: تظهر الآن كـ 🚀، :tada: كـ 🎉، و~250 اختصاراً آخر.",
+ "صور SVG في README وملاحظات الإصدار تظهر الآن أصلياً (مخططات، صور رئيسية). الصور الخاصة بسمة معينة تحترم الفاتح/الداكن. User-Agent يشبه المتصفح يصلح شارات CDN المحجوبة."
]
}
]
diff --git a/core/presentation/src/commonMain/composeResources/files/whatsnew/bn/18.json b/core/presentation/src/commonMain/composeResources/files/whatsnew/bn/18.json
index b4c301faa..8e928289f 100644
--- a/core/presentation/src/commonMain/composeResources/files/whatsnew/bn/18.json
+++ b/core/presentation/src/commonMain/composeResources/files/whatsnew/bn/18.json
@@ -31,7 +31,8 @@
"ম্যানুয়াল লিঙ্কে — ইনস্টলার উৎস অনুযায়ী সাজানো (F-Droid / Obtainium প্রথমে, Play Store ও সিস্টেম আপডেট শেষে), প্রতিটি অ্যাপের উৎস দেখাতে চিপ যুক্ত।",
"ম্যানুয়াল লিঙ্ক এখন স্বয়ংক্রিয়ভাবে মিলে যাওয়া GitHub রিপো সাজেস্ট করে — অ্যাপ বেছে নিন, র্যাংকড প্রার্থী পান, লিঙ্ক করতে ট্যাপ করুন। ম্যানুয়াল URL এন্ট্রি এখনও উপলব্ধ।",
"README ও রিলিজ নোটে GitHub-স্টাইল অ্যালার্ট কলআউট — Note, Tip, Important, Warning, Caution এখন আইকনসহ রঙিন কার্ড হিসেবে দেখায়, আগে শুধু টেক্সট ছিল।",
- "README ও রিলিজ নোটে ইমোজি শর্টকোড — :rocket: এখন 🚀, :tada: এখন 🎉, এবং আরও ~250টি।"
+ "README ও রিলিজ নোটে ইমোজি শর্টকোড — :rocket: এখন 🚀, :tada: এখন 🎉, এবং আরও ~250টি।",
+ "README ও রিলিজ নোটে SVG ছবি এখন নেটিভভাবে রেন্ডার হয় (ডায়াগ্রাম, হিরো ইমেজ)। থিম-নির্দিষ্ট ছবি লাইট/ডার্ক মেনে চলে। ব্রাউজারের মতো User-Agent CDN-ব্লকড ব্যাজ ঠিক করে।"
]
}
]
diff --git a/core/presentation/src/commonMain/composeResources/files/whatsnew/es/18.json b/core/presentation/src/commonMain/composeResources/files/whatsnew/es/18.json
index 22ddbec88..fae413eab 100644
--- a/core/presentation/src/commonMain/composeResources/files/whatsnew/es/18.json
+++ b/core/presentation/src/commonMain/composeResources/files/whatsnew/es/18.json
@@ -31,7 +31,8 @@
"Al vincular apps manualmente — orden por origen del instalador (F-Droid / Obtainium primero, Play Store y actualizaciones del sistema al final) con un chip que muestra el origen de cada app.",
"El enlace manual ahora sugiere repos de GitHub coincidentes automáticamente — elige una app, obtén candidatos clasificados, toca para vincular. Sigue disponible la entrada manual de URL.",
"Llamadas de alerta estilo GitHub en README y notas de versión — Note, Tip, Important, Warning, Caution se muestran como tarjetas tintadas con iconos en vez de texto literal.",
- "Atajos de emoji en README y notas de versión — :rocket: ahora se ve como 🚀, :tada: como 🎉, y ~250 más."
+ "Atajos de emoji en README y notas de versión — :rocket: ahora se ve como 🚀, :tada: como 🎉, y ~250 más.",
+ "Imágenes SVG en README y notas de versión ahora se renderizan nativamente (diagramas, hero). Imágenes de tema único respetan claro/oscuro. User-Agent de navegador desbloquea badges de CDN."
]
}
]
diff --git a/core/presentation/src/commonMain/composeResources/files/whatsnew/fr/18.json b/core/presentation/src/commonMain/composeResources/files/whatsnew/fr/18.json
index df9f5575b..b263d5b51 100644
--- a/core/presentation/src/commonMain/composeResources/files/whatsnew/fr/18.json
+++ b/core/presentation/src/commonMain/composeResources/files/whatsnew/fr/18.json
@@ -31,7 +31,8 @@
"Lors du lien manuel — tri par source d'installation (F-Droid / Obtainium d'abord, Play Store et mises à jour système en dernier) avec une puce indiquant la source de chaque app.",
"Le lien manuel suggère désormais automatiquement les dépôts GitHub correspondants — choisis une app, obtiens des candidats classés, touche pour lier. Saisie d'URL manuelle toujours disponible.",
"Encadrés d'alerte style GitHub dans README et notes de version — Note, Tip, Important, Warning, Caution s'affichent en cartes teintées avec icônes plutôt qu'en texte brut.",
- "Raccourcis emoji dans README et notes de version — :rocket: s'affiche en 🚀, :tada: en 🎉, et ~250 autres."
+ "Raccourcis emoji dans README et notes de version — :rocket: s'affiche en 🚀, :tada: en 🎉, et ~250 autres.",
+ "Les images SVG dans README et notes de version s'affichent maintenant nativement (diagrammes, héros). Les images mono-thème respectent clair/sombre. User-Agent navigateur débloque les badges CDN."
]
}
]
diff --git a/core/presentation/src/commonMain/composeResources/files/whatsnew/hi/18.json b/core/presentation/src/commonMain/composeResources/files/whatsnew/hi/18.json
index c1025a48b..f5e0fd1b2 100644
--- a/core/presentation/src/commonMain/composeResources/files/whatsnew/hi/18.json
+++ b/core/presentation/src/commonMain/composeResources/files/whatsnew/hi/18.json
@@ -31,7 +31,8 @@
"मैन्युअल लिंक में — इंस्टॉलर स्रोत के अनुसार क्रम (F-Droid / Obtainium पहले, Play Store और सिस्टम अपडेट अंत में), हर ऐप के स्रोत को दिखाने वाली चिप के साथ।",
"मैन्युअल लिंक अब अपने आप मेल खाते GitHub रिपो सुझाता है — कोई ऐप चुनें, रैंक किए हुए उम्मीदवार पाएं, लिंक करने के लिए टैप करें। मैन्युअल URL एंट्री अब भी उपलब्ध है।",
"README और रिलीज़ नोट्स में GitHub-शैली अलर्ट कॉलआउट — Note, Tip, Important, Warning, Caution अब आइकन के साथ रंगीन कार्ड के रूप में दिखेंगे, पहले सिर्फ टेक्स्ट थे।",
- "README और रिलीज़ नोट्स में इमोजी शॉर्टकोड — :rocket: अब 🚀 दिखेगा, :tada: 🎉, और ~250 अन्य।"
+ "README और रिलीज़ नोट्स में इमोजी शॉर्टकोड — :rocket: अब 🚀 दिखेगा, :tada: 🎉, और ~250 अन्य।",
+ "README और रिलीज़ नोट्स में SVG इमेज अब नेटिव रूप से रेंडर होती हैं (डायग्राम, हीरो)। थीम-विशिष्ट इमेज लाइट/डार्क का सम्मान करती हैं। ब्राउज़र-समान User-Agent CDN-ब्लॉक्ड बैज ठीक करता है।"
]
}
]
diff --git a/core/presentation/src/commonMain/composeResources/files/whatsnew/it/18.json b/core/presentation/src/commonMain/composeResources/files/whatsnew/it/18.json
index b5d35cfe4..e1d80ab03 100644
--- a/core/presentation/src/commonMain/composeResources/files/whatsnew/it/18.json
+++ b/core/presentation/src/commonMain/composeResources/files/whatsnew/it/18.json
@@ -31,7 +31,8 @@
"Collegamento manuale di app — ordinate per origine di installazione (F-Droid / Obtainium per primi, Play Store e aggiornamenti di sistema per ultimi) con un chip che mostra l'origine di ogni app.",
"Il collegamento manuale ora suggerisce automaticamente repo GitHub corrispondenti — scegli un'app, ottieni candidati classificati, tocca per collegare. L'inserimento manuale dell'URL resta disponibile.",
"Avvisi stile GitHub in README e note di rilascio — Note, Tip, Important, Warning, Caution ora si vedono come card colorate con icone invece di testo letterale.",
- "Codici emoji in README e note di rilascio — :rocket: ora appare come 🚀, :tada: come 🎉, e ~250 altri."
+ "Codici emoji in README e note di rilascio — :rocket: ora appare come 🚀, :tada: come 🎉, e ~250 altri.",
+ "Le immagini SVG in README e note di rilascio ora si renderizzano nativamente (diagrammi, hero). Le immagini per tema rispettano chiaro/scuro. User-Agent da browser sblocca i badge CDN."
]
}
]
diff --git a/core/presentation/src/commonMain/composeResources/files/whatsnew/ja/18.json b/core/presentation/src/commonMain/composeResources/files/whatsnew/ja/18.json
index 48d5a43f1..b65b68391 100644
--- a/core/presentation/src/commonMain/composeResources/files/whatsnew/ja/18.json
+++ b/core/presentation/src/commonMain/composeResources/files/whatsnew/ja/18.json
@@ -31,7 +31,8 @@
"手動リンク — インストーラ別に並び替え(F-Droid / Obtainium が先、Play ストアやシステム更新は最後)し、各アプリの入手元をチップで表示。",
"手動リンクが一致する GitHub リポを自動提案 — アプリを選ぶとランク付き候補が出る。タップでリンク。手動 URL 入力も引き続き利用可能。",
"README やリリースノートで GitHub スタイルのアラートが表示されるように — Note、Tip、Important、Warning、Caution がアイコン付きの色付きカードでレンダリングされます。",
- "README とリリースノートで絵文字ショートコードに対応 — :rocket: は 🚀、:tada: は 🎉、合計約 250 種類。"
+ "README とリリースノートで絵文字ショートコードに対応 — :rocket: は 🚀、:tada: は 🎉、合計約 250 種類。",
+ "README とリリースノートの SVG 画像をネイティブに描画(図、ヒーロー画像)。テーマ専用画像はライト/ダークを尊重。ブラウザ風 User-Agent で CDN ホットリンクブロックを回避。"
]
}
]
diff --git a/core/presentation/src/commonMain/composeResources/files/whatsnew/ko/18.json b/core/presentation/src/commonMain/composeResources/files/whatsnew/ko/18.json
index a15b93755..0c22c4a42 100644
--- a/core/presentation/src/commonMain/composeResources/files/whatsnew/ko/18.json
+++ b/core/presentation/src/commonMain/composeResources/files/whatsnew/ko/18.json
@@ -31,7 +31,8 @@
"수동 연결 — 설치 출처별 정렬(F-Droid / Obtainium 먼저, Play 스토어와 시스템 업데이트는 마지막)에 각 앱의 출처를 보여주는 칩 표시.",
"수동 연결이 일치하는 GitHub 리포를 자동으로 제안합니다 — 앱 선택 후 순위 후보를 받고 탭하여 연결. 수동 URL 입력도 그대로 사용 가능.",
"README와 릴리스 노트에서 GitHub 스타일 알림 카드 — Note, Tip, Important, Warning, Caution이 아이콘과 함께 색상 카드로 표시됩니다.",
- "README와 릴리스 노트에서 이모지 단축코드 지원 — :rocket: 은 🚀, :tada: 는 🎉, 총 약 250개."
+ "README와 릴리스 노트에서 이모지 단축코드 지원 — :rocket: 은 🚀, :tada: 는 🎉, 총 약 250개.",
+ "README와 릴리스 노트의 SVG 이미지가 네이티브로 렌더링됩니다 (다이어그램, 히어로). 테마 전용 이미지는 라이트/다크를 따릅니다. 브라우저 같은 User-Agent로 CDN 핫링크 차단 우회."
]
}
]
diff --git a/core/presentation/src/commonMain/composeResources/files/whatsnew/pl/18.json b/core/presentation/src/commonMain/composeResources/files/whatsnew/pl/18.json
index 47d59719d..11913ca1e 100644
--- a/core/presentation/src/commonMain/composeResources/files/whatsnew/pl/18.json
+++ b/core/presentation/src/commonMain/composeResources/files/whatsnew/pl/18.json
@@ -31,7 +31,8 @@
"Ręczne powiązywanie aplikacji — sortowane wg źródła instalatora (F-Droid / Obtainium na początku, Play Store i aktualizacje systemu na końcu) z chipem pokazującym źródło każdej aplikacji.",
"Ręczne powiązanie automatycznie proponuje pasujące repozytoria GitHub — wybierz aplikację, otrzymaj uszeregowanych kandydatów, dotknij, aby powiązać. Ręczne wpisanie URL nadal dostępne.",
"Wyróżnienia w stylu GitHub w README i notatkach wydania — Note, Tip, Important, Warning, Caution renderują się teraz jako kolorowe karty z ikonami zamiast surowego tekstu.",
- "Skróty emoji w README i notatkach wydania — :rocket: pokazuje się jako 🚀, :tada: jako 🎉, łącznie ~250 skrótów."
+ "Skróty emoji w README i notatkach wydania — :rocket: pokazuje się jako 🚀, :tada: jako 🎉, łącznie ~250 skrótów.",
+ "Obrazy SVG w README i notatkach wydania renderują się teraz natywnie (diagramy, hero). Obrazy tylko-jasny/tylko-ciemny respektują motyw. Przeglądarkowy User-Agent odblokuje badge'e CDN."
]
}
]
diff --git a/core/presentation/src/commonMain/composeResources/files/whatsnew/ru/18.json b/core/presentation/src/commonMain/composeResources/files/whatsnew/ru/18.json
index 4427f2833..252646edb 100644
--- a/core/presentation/src/commonMain/composeResources/files/whatsnew/ru/18.json
+++ b/core/presentation/src/commonMain/composeResources/files/whatsnew/ru/18.json
@@ -31,7 +31,8 @@
"Ручная привязка приложений — сортировка по источнику установки (F-Droid / Obtainium первыми, Play Маркет и системные обновления — последними), с шильдиком источника для каждого приложения.",
"Ручная привязка теперь автоматически предлагает подходящие GitHub-репозитории — выберите приложение, получите ранжированных кандидатов, нажмите для привязки. Ручной ввод URL по-прежнему доступен.",
"Выделения в стиле GitHub в README и заметках о выпуске — Note, Tip, Important, Warning, Caution теперь отображаются цветными карточками с иконками вместо обычного текста.",
- "Короткие коды эмодзи в README и заметках о выпуске — :rocket: теперь 🚀, :tada: — 🎉, всего около 250 кодов."
+ "Короткие коды эмодзи в README и заметках о выпуске — :rocket: теперь 🚀, :tada: — 🎉, всего около 250 кодов.",
+ "SVG-изображения в README и заметках о выпуске рендерятся нативно (диаграммы, hero). Тематические изображения учитывают светлую/тёмную тему. Браузерный User-Agent обходит блокировку хотлинков CDN."
]
}
]
diff --git a/core/presentation/src/commonMain/composeResources/files/whatsnew/tr/18.json b/core/presentation/src/commonMain/composeResources/files/whatsnew/tr/18.json
index 35900c2be..84a8d89bb 100644
--- a/core/presentation/src/commonMain/composeResources/files/whatsnew/tr/18.json
+++ b/core/presentation/src/commonMain/composeResources/files/whatsnew/tr/18.json
@@ -31,7 +31,8 @@
"Uygulamayı manuel bağlarken — yükleyici kaynağına göre sıralı (önce F-Droid / Obtainium, en sonda Play Store ve sistem güncellemeleri); her uygulamanın kaynağını gösteren çip eklendi.",
"Manuel bağlama artık eşleşen GitHub depolarını otomatik öneriyor — uygulamayı seçin, sıralı adaylar alın, bağlamak için dokunun. URL'yi manuel girme seçeneği de duruyor.",
"README ve sürüm notlarında GitHub tarzı uyarı kutuları — Note, Tip, Important, Warning, Caution artık düz metin yerine ikonlu renkli kartlar olarak görünüyor.",
- "README ve sürüm notlarında emoji kısayolları — :rocket: artık 🚀 olarak, :tada: 🎉 olarak ve ~250 başka kısayol."
+ "README ve sürüm notlarında emoji kısayolları — :rocket: artık 🚀 olarak, :tada: 🎉 olarak ve ~250 başka kısayol.",
+ "README ve sürüm notlarındaki SVG resimleri artık doğrudan render ediliyor (diyagram, hero). Tema özel resimler açık/koyu temaya saygı duyuyor. Tarayıcı benzeri User-Agent CDN hotlink engelini aşıyor."
]
}
]
diff --git a/core/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/18.json b/core/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/18.json
index 5d52f23e9..c7914091a 100644
--- a/core/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/18.json
+++ b/core/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/18.json
@@ -31,7 +31,8 @@
"手动链接应用时 — 按安装来源排序(F-Droid / Obtainium 在前,Play 商店与系统更新在后),并通过标签显示每个应用的来源。",
"手动链接现可自动推荐匹配的 GitHub 仓库 — 选择应用,获得排序后的候选项,点击即可链接。仍支持手动输入 URL。",
"README 与发布说明中的 GitHub 风格警示框 — Note、Tip、Important、Warning、Caution 现在以带图标的彩色卡片呈现,而不是字面文本。",
- "README 与发布说明中的 emoji 简写 — :rocket: 现在显示为 🚀,:tada: 为 🎉,共约 250 种。"
+ "README 与发布说明中的 emoji 简写 — :rocket: 现在显示为 🚀,:tada: 为 🎉,共约 250 种。",
+ "README 与发布说明中的 SVG 图片现可原生渲染(示意图、主图)。主题专属图片遵循浅色/深色。浏览器风 User-Agent 绕过 CDN 防盗链。"
]
}
]
diff --git a/feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt b/feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt
index fc11eed7b..23c5720e0 100644
--- a/feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt
+++ b/feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt
@@ -42,7 +42,11 @@ fun preprocessMarkdown(
(lower.contains("/badge") && isSvgUrl(lower))
}
- fun shouldSkipImage(url: String): Boolean = isSvgUrl(url) || isBadgeUrl(url)
+ // SVGs are no longer skipped wholesale — Coil's SVG decoder now
+ // handles them (registered in the App composable). Only known badge
+ // / shields services stay skipped because they're noise even when
+ // rendered correctly (status badges = clutter on small screens).
+ fun shouldSkipImage(url: String): Boolean = isBadgeUrl(url)
fun resolveUrl(path: String): String {
val trimmed = path.trim()
diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.kt
index 47b4374fe..507af9c3d 100644
--- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.kt
+++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.kt
@@ -101,12 +101,17 @@ fun LazyListScope.about(
}
item {
- val displayContent =
+ val raw =
if (translationState.isShowingTranslation && translationState.translatedText != null) {
translationState.translatedText
} else {
readmeMarkdown
}
+ val isDark = androidx.compose.foundation.isSystemInDarkTheme()
+ val displayContent =
+ androidx.compose.runtime.remember(raw, isDark) {
+ zed.rainxch.core.domain.util.applyThemeAwareImages(raw, isDark)
+ }
AnimatedContent(
targetState = displayContent,
diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt
index bee576527..8b84b33fa 100644
--- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt
+++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt
@@ -142,12 +142,17 @@ private fun ExpandableMarkdownContent(
isExpanded: Boolean,
onToggleExpanded: () -> Unit,
) {
- val displayContent =
+ val raw =
if (translationState.isShowingTranslation && translationState.translatedText != null) {
translationState.translatedText
} else {
release.description ?: stringResource(Res.string.no_release_notes)
}
+ val isDark = androidx.compose.foundation.isSystemInDarkTheme()
+ val displayContent =
+ remember(raw, isDark) {
+ zed.rainxch.core.domain.util.applyThemeAwareImages(raw, isDark)
+ }
val density = LocalDensity.current
val colors = rememberMarkdownColors()
diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/utils/MarkdownImageTransformer.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/utils/MarkdownImageTransformer.kt
index 8dc2b42c1..2c8594d62 100644
--- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/utils/MarkdownImageTransformer.kt
+++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/utils/MarkdownImageTransformer.kt
@@ -6,16 +6,31 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.ContentScale
+import coil3.compose.LocalPlatformContext
import coil3.compose.rememberAsyncImagePainter
+import coil3.network.NetworkHeaders
+import coil3.network.httpHeaders
+import coil3.request.ImageRequest
import com.mikepenz.markdown.model.ImageData
import com.mikepenz.markdown.model.ImageTransformer
object MarkdownImageTransformer : ImageTransformer {
+ private const val BROWSER_UA =
+ "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) " +
+ "Chrome/126.0.0.0 Mobile Safari/537.36 GitHubStore/1.8"
+
+ private val networkHeaders =
+ NetworkHeaders.Builder()
+ .add("User-Agent", BROWSER_UA)
+ .add(
+ "Accept",
+ "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
+ )
+ .build()
+
@Composable
override fun transform(link: String): ImageData? {
- if (link.isBlank()) {
- return null
- }
+ if (link.isBlank()) return null
val normalizedLink =
if (link.contains("github.com") && link.contains("/blob/")) {
@@ -26,13 +41,6 @@ object MarkdownImageTransformer : ImageTransformer {
link
}
- if (normalizedLink.endsWith(".svg", ignoreCase = true) ||
- normalizedLink.contains(".svg?", ignoreCase = true) ||
- normalizedLink.contains(".svg#", ignoreCase = true)
- ) {
- return null
- }
-
if (!normalizedLink.startsWith("http://") &&
!normalizedLink.startsWith("https://") &&
!normalizedLink.startsWith("data:")
@@ -40,10 +48,14 @@ object MarkdownImageTransformer : ImageTransformer {
return null
}
- val painter =
- rememberAsyncImagePainter(
- model = normalizedLink,
- )
+ val context = LocalPlatformContext.current
+ val request =
+ ImageRequest.Builder(context)
+ .data(normalizedLink)
+ .httpHeaders(networkHeaders)
+ .build()
+
+ val painter = rememberAsyncImagePainter(model = request)
return ImageData(
painter = painter,
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 4ae87ff7c..107e2bc5d 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -40,6 +40,7 @@ moko = "0.20.1"
buildkonfig = "0.17.1"
markdownRenderer = "0.39.2"
landscapist = "2.9.5"
+coil3 = "3.3.0"
shizuku = "13.1.5"
dhizuku = "2.5.4"
hidden-api = "4.4.0"
@@ -110,6 +111,9 @@ sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sql
# Image loading
landscapist-image = { module = "com.github.skydoves:landscapist-coil3", version.ref = "landscapist" }
landscapist-core = { module = "com.github.skydoves:landscapist-core", version.ref = "landscapist" }
+coil3-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil3" }
+coil3-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil3" }
+coil3-svg = { module = "io.coil-kt.coil3:coil-svg", version.ref = "coil3" }
#Permission handling
moko-permissions = { module = "dev.icerock.moko:permissions", version.ref = "moko" }
@@ -211,5 +215,7 @@ ktor-common = [
landscapist = [
"landscapist-core",
- "landscapist-image"
+ "landscapist-image",
+ "coil3-compose",
+ "coil3-svg",
]
\ No newline at end of file