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');