diff --git a/.gitignore b/.gitignore
index 6cdafec..cfef653 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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/
diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index eb172cf..2e3b39a 100644
--- a/apps/desktop/package.json
+++ b/apps/desktop/package.json
@@ -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"
diff --git a/apps/desktop/src-tauri/icons/128x128.png b/apps/desktop/src-tauri/icons/128x128.png
index 8c63fb5..fdcfecc 100644
Binary files a/apps/desktop/src-tauri/icons/128x128.png and b/apps/desktop/src-tauri/icons/128x128.png differ
diff --git a/apps/desktop/src-tauri/icons/128x128@2x.png b/apps/desktop/src-tauri/icons/128x128@2x.png
index f308b2e..538a8da 100644
Binary files a/apps/desktop/src-tauri/icons/128x128@2x.png and b/apps/desktop/src-tauri/icons/128x128@2x.png differ
diff --git a/apps/desktop/src-tauri/icons/32x32.png b/apps/desktop/src-tauri/icons/32x32.png
index 5987efe..59ac352 100644
Binary files a/apps/desktop/src-tauri/icons/32x32.png and b/apps/desktop/src-tauri/icons/32x32.png differ
diff --git a/apps/desktop/src-tauri/icons/64x64.png b/apps/desktop/src-tauri/icons/64x64.png
index 7c7a0db..b0dd30b 100644
Binary files a/apps/desktop/src-tauri/icons/64x64.png and b/apps/desktop/src-tauri/icons/64x64.png differ
diff --git a/apps/desktop/src-tauri/icons/icon.icns b/apps/desktop/src-tauri/icons/icon.icns
index f3e77c9..7901ff7 100644
Binary files a/apps/desktop/src-tauri/icons/icon.icns and b/apps/desktop/src-tauri/icons/icon.icns differ
diff --git a/apps/desktop/src-tauri/icons/icon.ico b/apps/desktop/src-tauri/icons/icon.ico
index dc81fe6..2cb2d3c 100644
Binary files a/apps/desktop/src-tauri/icons/icon.ico and b/apps/desktop/src-tauri/icons/icon.ico differ
diff --git a/apps/desktop/src-tauri/icons/icon.png b/apps/desktop/src-tauri/icons/icon.png
index a3c10b7..8e1e558 100644
Binary files a/apps/desktop/src-tauri/icons/icon.png and b/apps/desktop/src-tauri/icons/icon.png differ
diff --git a/apps/mobile/app.config.ts b/apps/mobile/app.config.ts
index d068733..66ffe6b 100644
--- a/apps/mobile/app.config.ts
+++ b/apps/mobile/app.config.ts
@@ -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,
diff --git a/apps/mobile/assets/adaptive-icon.png b/apps/mobile/assets/adaptive-icon.png
new file mode 100644
index 0000000..db6d6cd
Binary files /dev/null and b/apps/mobile/assets/adaptive-icon.png differ
diff --git a/apps/mobile/assets/icon.png b/apps/mobile/assets/icon.png
new file mode 100644
index 0000000..1c835bf
Binary files /dev/null and b/apps/mobile/assets/icon.png differ
diff --git a/apps/mobile/assets/monochrome-icon.png b/apps/mobile/assets/monochrome-icon.png
new file mode 100644
index 0000000..3cb226f
Binary files /dev/null and b/apps/mobile/assets/monochrome-icon.png differ
diff --git a/apps/web/index.html b/apps/web/index.html
index ee8ef8e..50040d2 100644
--- a/apps/web/index.html
+++ b/apps/web/index.html
@@ -5,6 +5,8 @@
+
+
pipu
diff --git a/apps/web/public/apple-touch-icon.png b/apps/web/public/apple-touch-icon.png
new file mode 100644
index 0000000..984ea87
Binary files /dev/null and b/apps/web/public/apple-touch-icon.png differ
diff --git a/apps/web/public/favicon-32.png b/apps/web/public/favicon-32.png
new file mode 100644
index 0000000..59ac352
Binary files /dev/null and b/apps/web/public/favicon-32.png differ
diff --git a/apps/web/public/favicon.svg b/apps/web/public/favicon.svg
index 4c0ef27..a5d254c 100644
--- a/apps/web/public/favicon.svg
+++ b/apps/web/public/favicon.svg
@@ -1 +1,17 @@
-
+
diff --git a/apps/web/src/shared/ui/ui.module.css b/apps/web/src/shared/ui/ui.module.css
index d7c0022..e3e1e5f 100644
--- a/apps/web/src/shared/ui/ui.module.css
+++ b/apps/web/src/shared/ui/ui.module.css
@@ -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 {
diff --git a/docs/design.md b/docs/design.md
index 8da36bc..1a96105 100644
--- a/docs/design.md
+++ b/docs/design.md
@@ -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`.
+После изменения значков установленные приложения нужно пересобрать и обновить.
### Типографика
diff --git a/package.json b/package.json
index 732d909..bf7fe40 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/scripts/generate-icons.mjs b/scripts/generate-icons.mjs
new file mode 100644
index 0000000..73225ca
--- /dev/null
+++ b/scripts/generate-icons.mjs
@@ -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(/', '');
+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, `\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 = '';
+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', `${body}`, 1024,
+ join(root, 'apps/mobile/assets/adaptive-icon.png'));
+const mono = body.replace(/[\s\S]*?<\/defs>/, '').replace(/]*\/>/, '')
+ .replaceAll('url(#note)', '#FFFFFF');
+variant('monochrome', `${mono}`, 1024,
+ join(root, 'apps/mobile/assets/monochrome-icon.png'));
+console.log('Updated desktop, Android, and browser icons.');