diff --git a/src/skin-market-web/src/App.tsx b/src/skin-market-web/src/App.tsx
index bbba50e89..097fefb0e 100644
--- a/src/skin-market-web/src/App.tsx
+++ b/src/skin-market-web/src/App.tsx
@@ -1,5 +1,6 @@
import {
ArrowClockwise,
+ ArrowSquareOut,
GithubLogo,
GlobeSimple,
Moon,
@@ -16,6 +17,7 @@ import { AdminPage } from './AdminPage';
import { CatalogPage } from './CatalogPage';
import { DetailPage } from './DetailPage';
import { useI18n } from './i18n';
+import { BITFUN_HOME_URL } from './links';
import { adminPath, parseMarketRoute, submissionsPath } from './router';
import { SubmissionsPage } from './SubmissionsPage';
import { useTheme } from './theme';
@@ -241,6 +243,10 @@ export default function App() {
diff --git a/src/skin-market-web/src/CatalogPage.tsx b/src/skin-market-web/src/CatalogPage.tsx
index 9012e19c8..be9676fac 100644
--- a/src/skin-market-web/src/CatalogPage.tsx
+++ b/src/skin-market-web/src/CatalogPage.tsx
@@ -1,5 +1,6 @@
import {
ArrowRight,
+ ArrowSquareOut,
DownloadSimple,
MagnifyingGlass,
} from '@phosphor-icons/react';
@@ -12,6 +13,7 @@ import {
import { skinMarketApi, SkinMarketApiError } from './api';
import { formatCompactNumber, formatMarketDate } from './format';
import type { Locale, Translate } from './i18n';
+import { BITFUN_RELEASES_URL } from './links';
import { PosterImage } from './PosterImage';
import type {
AppearanceListingSummary,
@@ -160,10 +162,13 @@ export function CatalogPage({
diff --git a/src/skin-market-web/src/DetailPage.tsx b/src/skin-market-web/src/DetailPage.tsx
index 7ddc5e23b..f31fd1f28 100644
--- a/src/skin-market-web/src/DetailPage.tsx
+++ b/src/skin-market-web/src/DetailPage.tsx
@@ -15,6 +15,7 @@ import {
shortHash,
} from './format';
import type { Locale, Translate } from './i18n';
+import { BITFUN_RELEASES_URL } from './links';
import { PosterImage } from './PosterImage';
import type {
AppearanceListingDetail,
@@ -141,7 +142,13 @@ export function DetailPage({ catalogSearch, locale, onNavigate, slug, t }: Detai
) : null}
diff --git a/src/skin-market-web/src/i18n.ts b/src/skin-market-web/src/i18n.ts
index 5a9648abc..8a6932728 100644
--- a/src/skin-market-web/src/i18n.ts
+++ b/src/skin-market-web/src/i18n.ts
@@ -83,6 +83,7 @@ const messages = {
notFoundBody: 'The link may be outdated, or the release may no longer be public.',
backToCatalog: 'Browse appearances',
footerNote: 'Reviewed packages. Local installation. Your appearance stays on your device.',
+ bitfunHome: 'BitFun website',
refresh: 'Refresh',
submissionsTitle: 'My submissions',
submissionsIntro: 'Track appearance packages submitted from BitFun Agent and respond to review feedback.',
@@ -200,6 +201,7 @@ const messages = {
notFoundBody: '链接可能已经失效,或者该版本不再公开。',
backToCatalog: '浏览外观',
footerNote: '人工审核安装包,本机安装,外观资源只保存在你的设备中。',
+ bitfunHome: 'BitFun 官网',
refresh: '刷新',
submissionsTitle: '我的投稿',
submissionsIntro: '查看通过 BitFun Agent 投稿的外观包,并跟进审核意见。',
diff --git a/src/skin-market-web/src/links.test.ts b/src/skin-market-web/src/links.test.ts
new file mode 100644
index 000000000..159b8c02f
--- /dev/null
+++ b/src/skin-market-web/src/links.test.ts
@@ -0,0 +1,11 @@
+import { describe, expect, it } from 'vitest';
+import { BITFUN_HOME_URL, BITFUN_RELEASES_URL } from './links';
+
+describe('Skin Market external links', () => {
+ it('uses the official BitFun website and GitHub release pages', () => {
+ expect(BITFUN_HOME_URL).toBe('https://openbitfun.com/');
+ expect(BITFUN_RELEASES_URL).toBe('https://github.com/GCWing/BitFun/releases');
+ expect(new URL(BITFUN_HOME_URL).protocol).toBe('https:');
+ expect(new URL(BITFUN_RELEASES_URL).protocol).toBe('https:');
+ });
+});
diff --git a/src/skin-market-web/src/links.ts b/src/skin-market-web/src/links.ts
new file mode 100644
index 000000000..2e8e34896
--- /dev/null
+++ b/src/skin-market-web/src/links.ts
@@ -0,0 +1,2 @@
+export const BITFUN_HOME_URL = 'https://openbitfun.com/';
+export const BITFUN_RELEASES_URL = 'https://github.com/GCWing/BitFun/releases';
diff --git a/src/skin-market-web/src/styles.css b/src/skin-market-web/src/styles.css
index 9c775a3c3..05e5db4d7 100644
--- a/src/skin-market-web/src/styles.css
+++ b/src/skin-market-web/src/styles.css
@@ -452,13 +452,25 @@ img {
color: var(--accent);
}
+.install-note > a {
+ color: inherit;
+ text-decoration: none;
+}
+
.install-note strong {
- display: block;
+ display: flex;
+ width: fit-content;
+ align-items: center;
+ gap: 6px;
margin-bottom: 7px;
color: var(--text);
font-size: 15px;
}
+.install-note > a:hover strong {
+ color: var(--accent-strong);
+}
+
.install-note p {
margin: 0;
font-size: 14px;
@@ -1048,20 +1060,29 @@ img {
color: var(--accent);
}
-.desktop-guidance p {
+.desktop-guidance a {
display: flex;
- margin: 0;
flex-direction: column;
gap: 5px;
+ color: inherit;
font-size: 12px;
line-height: 1.5;
+ text-decoration: none;
}
.desktop-guidance strong {
+ display: flex;
+ width: fit-content;
+ align-items: center;
+ gap: 5px;
color: var(--text);
font-size: 13px;
}
+.desktop-guidance a:hover strong {
+ color: var(--accent-strong);
+}
+
.detail-fact-strip {
display: grid;
grid-template-columns: 0.55fr 0.55fr 1.4fr;
@@ -1768,12 +1789,27 @@ img {
}
.site-footer__inner p {
+ flex: 1;
max-width: 620px;
margin: 0;
text-align: right;
line-height: 1.55;
}
+.site-footer__link {
+ display: inline-flex;
+ flex: 0 0 auto;
+ align-items: center;
+ gap: 6px;
+ color: var(--text-soft);
+ font-weight: 650;
+ text-decoration: none;
+}
+
+.site-footer__link:hover {
+ color: var(--accent-strong);
+}
+
@media (prefers-reduced-motion: no-preference) {
.skeleton::after {
animation: skeleton-sweep 1.45s ease-in-out infinite;
diff --git a/src/web-ui/src/infrastructure/config/components/AppearanceConfig.appearance.ts b/src/web-ui/src/infrastructure/config/components/AppearanceConfig.appearance.ts
index f658298f1..0c85a628c 100644
--- a/src/web-ui/src/infrastructure/config/components/AppearanceConfig.appearance.ts
+++ b/src/web-ui/src/infrastructure/config/components/AppearanceConfig.appearance.ts
@@ -19,6 +19,7 @@ export const appearanceConfigAppearanceDescriptor: AppearanceSurfaceDescriptor =
{ id: 'marketDetail' }, { id: 'marketDetailPreview' }, { id: 'marketDetailBody' },
{ id: 'marketWarning' }, { id: 'marketReleaseList' }, { id: 'marketRelease' },
{ id: 'marketActions' }, { id: 'marketNav' }, { id: 'marketWorkflow' },
+ { id: 'marketManualSubmit' },
{ id: 'marketSubmissionList' }, { id: 'marketSubmission' },
{ id: 'marketReviewLayout' }, { id: 'marketReviewQueue' },
{ id: 'marketReviewDetail' }, { id: 'marketReviewActions' },