feat(branding): add unified Pipu Music app icons
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 2m30s
ci / ts / ts (packages + web) (push) Successful in 3m29s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 2m10s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 56s
ci / desktop / rust (desktop proxy) (push) Successful in 3m15s
No files matched your search
@@ -41,3 +41,6 @@ apps/desktop/src-tauri/target/
|
||||
apps/desktop/src-tauri/gen/
|
||||
apps/desktop/src-tauri/tauri.release.json
|
||||
apps/desktop/release/
|
||||
|
||||
# Intermediate platform icon renders (final assets live under apps/).
|
||||
/artifacts/icon-build/
|
||||
@@ -10,7 +10,7 @@
|
||||
"bundle:windows": "node inject-version.mjs && tauri build --config src-tauri/tauri.release.json --bundles nsis",
|
||||
"bundle:windows:cross": "node inject-version.mjs && tauri build --config src-tauri/tauri.release.json --runner cargo-xwin --target x86_64-pc-windows-msvc --bundles nsis",
|
||||
"tauri": "tauri",
|
||||
"icons": "tauri icon ../web/public/favicon.svg"
|
||||
"icons": "node ../../scripts/generate-icons.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^2.9.0"
|
||||
|
||||
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 46 KiB |
@@ -9,6 +9,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
|
||||
name: 'Pipu Music',
|
||||
slug: 'pipu-music',
|
||||
scheme: 'pipu',
|
||||
icon: './assets/icon.png',
|
||||
version,
|
||||
orientation: 'portrait',
|
||||
userInterfaceStyle: 'automatic',
|
||||
@@ -17,6 +18,11 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
|
||||
newArchEnabled: false,
|
||||
backgroundColor: '#05070D',
|
||||
android: {
|
||||
adaptiveIcon: {
|
||||
foregroundImage: './assets/adaptive-icon.png',
|
||||
monochromeImage: './assets/monochrome-icon.png',
|
||||
backgroundColor: '#05070D',
|
||||
},
|
||||
package: 'app.pipu.music',
|
||||
versionCode,
|
||||
edgeToEdgeEnabled: true,
|
||||
|
||||
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
@@ -5,6 +5,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<meta name="color-scheme" content="dark light" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180" />
|
||||
<title>pipu</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
@@ -1 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><defs><radialGradient id="g" cx="35%" cy="30%" r="75%"><stop offset="0" stop-color="#9CC6FF"/><stop offset=".55" stop-color="#2F80FF"/><stop offset="1" stop-color="#3A1FA8"/></radialGradient></defs><circle cx="16" cy="16" r="14" fill="url(#g)"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
|
||||
<defs>
|
||||
<radialGradient id="orb" cx="28%" cy="18%" r="95%">
|
||||
<stop stop-color="#9CC6FF"/>
|
||||
<stop offset=".42" stop-color="#2F80FF"/>
|
||||
<stop offset=".78" stop-color="#6354E8"/>
|
||||
<stop offset="1" stop-color="#3A1FA8"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="note" x1="0" y1="0" x2=".3" y2="1">
|
||||
<stop stop-color="#FFFFFF"/>
|
||||
<stop offset="1" stop-color="#DFEBFF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle cx="256" cy="256" r="240" fill="url(#orb)"/>
|
||||
<path d="M208 348V164H270C356 164 356 280 270 280H208" fill="none" stroke="url(#note)" stroke-width="44" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<ellipse cx="181" cy="348" rx="49" ry="36" fill="url(#note)" transform="rotate(-18 181 348)"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 312 B After Width: | Height: | Size: 840 B |
@@ -21,9 +21,10 @@
|
||||
}
|
||||
|
||||
.logoDot {
|
||||
display: inline-block;
|
||||
flex: none;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 35% 30%, #9cc6ff, #2f80ff 55%, #3a1fa8);
|
||||
box-shadow: 0 0 18px rgba(47, 128, 255, 0.55);
|
||||
background: url('/favicon.svg') center / contain no-repeat;
|
||||
}
|
||||
|
||||
.button {
|
||||
|
||||
@@ -43,7 +43,13 @@
|
||||
| `overlay` | `rgba(3,5,10,.66)` | `rgba(20,30,60,.32)` | Подложка диалогов |
|
||||
| `glass` | `rgba(12,16,28,.78)` | `rgba(255,255,255,.84)` | Стеклянные карточки (вход, диалоги) + `backdrop-filter: blur(28px) saturate(1.3)` |
|
||||
|
||||
Палитра волны по умолчанию: `#2F80FF`, `#7C5CFF`, `#35D3FF`. Логотип — радиальный градиент `#9CC6FF → #2F80FF → #3A1FA8`.
|
||||
Палитра волны по умолчанию: `#2F80FF`, `#7C5CFF`, `#35D3FF`. Логотип — светлая «p»-нота на сфере с радиальным градиентом `#9CC6FF → #2F80FF → #6354E8 → #3A1FA8`.
|
||||
|
||||
Исходник значка — `apps/web/public/favicon.svg`; он же используется в шапке веб-приложения.
|
||||
Команда `pnpm icons` пересобирает PNG/ICO/ICNS для desktop, favicon PNG, Apple touch icon
|
||||
и Android-ресурсы в `apps/mobile/assets/`. Android использует отдельные адаптивный цветной
|
||||
и монохромный слои с отступами для масок лаунчера; фон — `#05070D`.
|
||||
После изменения значков установленные приложения нужно пересобрать и обновить.
|
||||
|
||||
### Типографика
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"node": ">=22"
|
||||
},
|
||||
"scripts": {
|
||||
"icons": "node scripts/generate-icons.mjs",
|
||||
"build": "turbo run build",
|
||||
"lint": "turbo run lint",
|
||||
"typecheck": "turbo run typecheck",
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { createRequire } from 'node:module';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
// All platform assets derive from the editable web SVG. Requires workspace dependencies.
|
||||
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const desktop = join(root, 'apps/desktop');
|
||||
const require = createRequire(join(desktop, 'package.json'));
|
||||
const cli = require.resolve('@tauri-apps/cli/tauri.js');
|
||||
const scratch = join(root, 'artifacts/icon-build');
|
||||
const source = join(root, 'apps/web/public/favicon.svg');
|
||||
const svg = readFileSync(source, 'utf8');
|
||||
const body = svg.replace(/<svg[^>]*>/, '').replace('</svg>', '');
|
||||
mkdirSync(scratch, { recursive: true });
|
||||
|
||||
function render(input, output, sizes = []) {
|
||||
mkdirSync(output, { recursive: true });
|
||||
const result = spawnSync(process.execPath, [cli, 'icon', input, '--output', output,
|
||||
...sizes.flatMap(size => ['--png', String(size)])], { cwd: desktop, stdio: 'inherit' });
|
||||
if (result.error) throw result.error;
|
||||
if (result.status !== 0) throw new Error(`Icon generation failed: ${input}`);
|
||||
}
|
||||
|
||||
function variant(name, contents, size, destination) {
|
||||
const input = join(scratch, `${name}.svg`);
|
||||
const output = join(scratch, name);
|
||||
writeFileSync(input, `<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">${contents}</svg>\n`);
|
||||
render(input, output, [size]);
|
||||
mkdirSync(dirname(destination), { recursive: true });
|
||||
copyFileSync(join(output, `${size}x${size}.png`), destination);
|
||||
}
|
||||
|
||||
const native = join(scratch, 'desktop');
|
||||
render(source, native);
|
||||
for (const name of ['32x32.png', '64x64.png', '128x128.png', '128x128@2x.png', 'icon.png', 'icon.ico', 'icon.icns']) {
|
||||
copyFileSync(join(native, name), join(desktop, 'src-tauri/icons', name));
|
||||
}
|
||||
copyFileSync(join(native, '32x32.png'), join(root, 'apps/web/public/favicon-32.png'));
|
||||
const background = '<rect width="512" height="512" fill="#05070D"/>';
|
||||
variant('touch', background + body, 180, join(root, 'apps/web/public/apple-touch-icon.png'));
|
||||
variant('mobile', background + body, 1024, join(root, 'apps/mobile/assets/icon.png'));
|
||||
// The complete orb fits inside Android's central 66/108 safe-zone circle.
|
||||
variant('adaptive', `<g transform="translate(92.16 92.16) scale(.64)">${body}</g>`, 1024,
|
||||
join(root, 'apps/mobile/assets/adaptive-icon.png'));
|
||||
const mono = body.replace(/<defs>[\s\S]*?<\/defs>/, '').replace(/<circle[^>]*\/>/, '')
|
||||
.replaceAll('url(#note)', '#FFFFFF');
|
||||
variant('monochrome', `<g transform="translate(51.2 51.2) scale(.8)">${mono}</g>`, 1024,
|
||||
join(root, 'apps/mobile/assets/monochrome-icon.png'));
|
||||
console.log('Updated desktop, Android, and browser icons.');
|
||||