diff --git a/assets/brand/singlinkvpn-logo.png b/assets/brand/singlinkvpn-logo.png
new file mode 100644
index 0000000..6df59c4
Binary files /dev/null and b/assets/brand/singlinkvpn-logo.png differ
diff --git a/assets/covers/en.png b/assets/covers/en.png
index a808505..a9de22c 100644
Binary files a/assets/covers/en.png and b/assets/covers/en.png differ
diff --git a/assets/covers/en.svg b/assets/covers/en.svg
index d7d8614..8720781 100644
--- a/assets/covers/en.svg
+++ b/assets/covers/en.svg
@@ -3,12 +3,14 @@
+
-
- SingLinkVPN
+
+
+ SingLinkVPN
Free VPN · Open-Source Project
Downloads · Security Audits · No-Logs Evidence
FREE VPNOPEN-SOURCE
diff --git a/assets/covers/ja.png b/assets/covers/ja.png
index 39cf7bc..d1ebb74 100644
Binary files a/assets/covers/ja.png and b/assets/covers/ja.png differ
diff --git a/assets/covers/ja.svg b/assets/covers/ja.svg
index d4f2eca..1303998 100644
--- a/assets/covers/ja.svg
+++ b/assets/covers/ja.svg
@@ -3,12 +3,14 @@
+
-
- SingLinkVPN
+
+
+ SingLinkVPN
無料VPN・オープンソースプロジェクト
ダウンロード・セキュリティ監査・ノーログ検証
FREE VPNOPEN-SOURCE
diff --git a/assets/covers/ko.png b/assets/covers/ko.png
index 9466db0..b15ddbe 100644
Binary files a/assets/covers/ko.png and b/assets/covers/ko.png differ
diff --git a/assets/covers/ko.svg b/assets/covers/ko.svg
index 351bb36..28a4dce 100644
--- a/assets/covers/ko.svg
+++ b/assets/covers/ko.svg
@@ -3,12 +3,14 @@
+
-
- SingLinkVPN
+
+
+ SingLinkVPN
무료 VPN · 오픈소스 프로젝트
다운로드 · 보안 감사 · 노로그 검증
FREE VPNOPEN-SOURCE
diff --git a/assets/covers/ru.png b/assets/covers/ru.png
index 52a4ef7..0ce1643 100644
Binary files a/assets/covers/ru.png and b/assets/covers/ru.png differ
diff --git a/assets/covers/ru.svg b/assets/covers/ru.svg
index a2617df..ee06450 100644
--- a/assets/covers/ru.svg
+++ b/assets/covers/ru.svg
@@ -3,12 +3,14 @@
+
-
- SingLinkVPN
+
+
+ SingLinkVPN
Бесплатный VPN · Открытый проект
Загрузки · Аудит безопасности · No-logs
FREE VPNOPEN-SOURCE
diff --git a/assets/covers/th.png b/assets/covers/th.png
index 9b74784..5d26381 100644
Binary files a/assets/covers/th.png and b/assets/covers/th.png differ
diff --git a/assets/covers/th.svg b/assets/covers/th.svg
index a1a3ec9..6bb1a83 100644
--- a/assets/covers/th.svg
+++ b/assets/covers/th.svg
@@ -3,12 +3,14 @@
+
-
- SingLinkVPN
+
+
+ SingLinkVPN
VPN ฟรี · โครงการโอเพนซอร์ส
ดาวน์โหลด · ตรวจสอบความปลอดภัย · ไม่บันทึกกิจกรรม
FREE VPNOPEN-SOURCE
diff --git a/assets/covers/vi.png b/assets/covers/vi.png
index 9baed5a..1f0ee65 100644
Binary files a/assets/covers/vi.png and b/assets/covers/vi.png differ
diff --git a/assets/covers/vi.svg b/assets/covers/vi.svg
index c125a28..e6b0259 100644
--- a/assets/covers/vi.svg
+++ b/assets/covers/vi.svg
@@ -3,12 +3,14 @@
+
-
- SingLinkVPN
+
+
+ SingLinkVPN
VPN miễn phí · Dự án mã nguồn mở
Tải xuống · Kiểm toán bảo mật · Không lưu nhật ký
FREE VPNOPEN-SOURCE
diff --git a/assets/covers/zh-Hans.png b/assets/covers/zh-Hans.png
index ed857db..db70293 100644
Binary files a/assets/covers/zh-Hans.png and b/assets/covers/zh-Hans.png differ
diff --git a/assets/covers/zh-Hans.svg b/assets/covers/zh-Hans.svg
index 7d80787..9818211 100644
--- a/assets/covers/zh-Hans.svg
+++ b/assets/covers/zh-Hans.svg
@@ -3,12 +3,14 @@
+
-
- SingLinkVPN
+
+
+ SingLinkVPN
免费VPN · 开源项目
全平台下载 · 安全审计 · 无日志验证
FREE VPNOPEN-SOURCE
diff --git a/assets/covers/zh-Hant.png b/assets/covers/zh-Hant.png
index fa2446d..2ebd532 100644
Binary files a/assets/covers/zh-Hant.png and b/assets/covers/zh-Hant.png differ
diff --git a/assets/covers/zh-Hant.svg b/assets/covers/zh-Hant.svg
index 2f55720..a9eb9dd 100644
--- a/assets/covers/zh-Hant.svg
+++ b/assets/covers/zh-Hant.svg
@@ -3,12 +3,14 @@
+
-
- SingLinkVPN
+
+
+ SingLinkVPN
免費VPN · 開源項目
全平台下載 · 安全審計 · 無日誌驗證
FREE VPNOPEN-SOURCE
diff --git a/assets/singlinkvpn-public-project.png b/assets/singlinkvpn-public-project.png
index a808505..a9de22c 100644
Binary files a/assets/singlinkvpn-public-project.png and b/assets/singlinkvpn-public-project.png differ
diff --git a/assets/singlinkvpn-public-project.svg b/assets/singlinkvpn-public-project.svg
index d7d8614..8720781 100644
--- a/assets/singlinkvpn-public-project.svg
+++ b/assets/singlinkvpn-public-project.svg
@@ -3,12 +3,14 @@
+
-
- SingLinkVPN
+
+
+ SingLinkVPN
Free VPN · Open-Source Project
Downloads · Security Audits · No-Logs Evidence
FREE VPNOPEN-SOURCE
diff --git a/scripts/build-covers.mjs b/scripts/build-covers.mjs
index 1b31b78..1ca882e 100644
--- a/scripts/build-covers.mjs
+++ b/scripts/build-covers.mjs
@@ -5,6 +5,7 @@ import sharp from 'sharp';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const output = path.join(root, 'assets/covers');
+const brandLogoPath = path.join(root, 'assets/brand/singlinkvpn-logo.png');
const locales = ['en', 'zh-Hant', 'zh-Hans', 'ja', 'ko', 'vi', 'th', 'ru'];
const escapeXml = (value) => String(value)
@@ -14,7 +15,7 @@ const escapeXml = (value) => String(value)
.replaceAll('"', '"')
.replaceAll("'", ''');
-function coverSvg(copy, locale) {
+function coverSvg(copy, locale, brandLogoDataUri) {
const titleSizes = { en: 62, 'zh-Hant': 72, 'zh-Hans': 72, ja: 46, ko: 58, vi: 50, th: 54, ru: 50 };
const titleSize = titleSizes[locale];
const subtitleSize = copy.coverSubtitle.length > 42 ? 29 : 34;
@@ -23,12 +24,14 @@ function coverSvg(copy, locale) {
+
-
- SingLinkVPN
+
+
+ SingLinkVPN
${escapeXml(copy.coverTitle)}
${escapeXml(copy.coverSubtitle)}
FREE VPNOPEN-SOURCE
@@ -37,9 +40,14 @@ function coverSvg(copy, locale) {
}
await mkdir(output, { recursive: true });
+const brandLogo = await sharp(brandLogoPath)
+ .resize(152, 152, { fit: 'cover' })
+ .png({ quality: 94, compressionLevel: 9 })
+ .toBuffer();
+const brandLogoDataUri = `data:image/png;base64,${brandLogo.toString('base64')}`;
for (const locale of locales) {
const copy = JSON.parse(await readFile(path.join(root, `metadata/locales/${locale}.json`), 'utf8'));
- const svg = coverSvg(copy, locale);
+ const svg = coverSvg(copy, locale, brandLogoDataUri);
const svgPath = path.join(output, `${locale}.svg`);
const pngPath = path.join(output, `${locale}.png`);
await writeFile(svgPath, svg, 'utf8');
diff --git a/test/project.test.mjs b/test/project.test.mjs
index 5c3f642..86bcdb2 100644
--- a/test/project.test.mjs
+++ b/test/project.test.mjs
@@ -26,6 +26,9 @@ test('project record contains stable, scoped facts', async () => {
});
test('all locale records are complete and distinct', async () => {
+ const brandLogo = await sharp(path.join(root, 'assets/brand/singlinkvpn-logo.png')).metadata();
+ assert.equal(brandLogo.width, 512, 'official brand logo width');
+ assert.equal(brandLogo.height, 512, 'official brand logo height');
const titles = new Set();
for (const locale of locales) {
const copy = JSON.parse(await readFile(path.join(root, `metadata/locales/${locale}.json`), 'utf8'));
@@ -39,6 +42,9 @@ test('all locale records are complete and distinct', async () => {
const coverMetadata = await sharp(path.join(root, `assets/covers/${locale}.png`)).metadata();
assert.equal(coverMetadata.width, 1600, `${locale} cover width`);
assert.equal(coverMetadata.height, 900, `${locale} cover height`);
+ const coverSvg = await readFile(path.join(root, `assets/covers/${locale}.svg`), 'utf8');
+ assert.match(coverSvg, /data:image\/png;base64,/, `${locale} cover embeds the official brand logo`);
+ assert.doesNotMatch(coverSvg, /translate\(110 118\)/, `${locale} cover removed the generic shield logo`);
titles.add(copy.title);
}
assert.equal(titles.size, locales.length, 'titles must be localized');