New icons for all shit
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m22s

This commit is contained in:
sandrodach committed 2026-10-28 19:01:08 +01:00
1 parent f82ab2fd32
commit 3540b4b2ae
17 files changed
+117 -7

No files matched your search

+20
View File
@@ -96,3 +96,23 @@ Stage 3 alpine:3.21 nur Binary, ca-certificates, tzdata
Die angezeigte Version (Kopf der App) setzt der Build-Parameter `APP_VERSION`: Die CI übergibt bei Git-Tags den Tag-Namen (`v0.2`), sonst zeigt die App die Version aus `frontend/package.json` mit `-dev` (`v0.2-dev`). Umgesetzt in `frontend/vite.config.ts` und `frontend/src/version.ts`.
Die Tests laufen beim Image-Build (Frontend: `npm run test:run`, Backend: `go test -v ./...`); ein Fehler bricht den Build ab. Frontend-Tests liegen unter `frontend/src/**/__tests__/` (Vitest + jsdom + Testing Library, Setup in `frontend/src/test/setup.ts`).
## App-Icons, PWA & Social-Preview (Open Graph)
MindSift verwendet plattformübergreifend ein einheitliches App-Icon-Design, das exakt dem Icon im Kopfbereich der mobilen App entspricht:
- **Design & Geometrie:** Sanft abgerundetes Quadrat (`rx=108` auf 512x512 Canvas bzw. `rounded-xl`), Primärblau (`--accent-primary: #2563eb`) als Hintergrund und zentriertes Lucide Brain Icon in reinem Weiß (`--text-on-accent: #ffffff`).
### Asset-Matrix (`frontend/public/`)
| Datei | Zweck & Zielplattform | Besonderheit |
|---|---|---|
| `icon.svg` | Modernes Standard-Icon & SVG-Favicon | Vektor-Original mit abgerundeten Ecken |
| `icon-192x192.png`, `icon-512x512.png` | PWA-Installation (`purpose: "any"`), Desktop Linux/Windows | Erforderlich für Chromium unter Linux (erzeugt sonst kein `.desktop`-Icon) |
| `icon-maskable-192x192.png`, `icon-maskable-512x512.png`, `icon-maskable.svg` | Android Adaptive Icons (`purpose: "maskable"`) | Full-Bleed Hintergrund (`#2563eb`) ohne transparente Ecken, Brain im 80%-Sicherheitsbereich |
| `apple-touch-icon.png` (180x180) | iOS Safari („Zum Home-Bildschirm“) | Opaker Hintergrund verhindert schwarze Ränder beim automatischen Apple-Squircle-Maskieren |
| `favicon.ico`, `favicon-32x32.png`, `favicon-16x16.png` | Browser-Tabs, Lesezeichen, Chronik | Multi-Resolution-ICO (16/32/48) für alle Desktop-Browser |
| `og-image.png` (1200x630), `og-image-square.png` (512x512) | Link-Vorschau in WhatsApp, Discord, Telegram, X/Twitter | Dunkler `#0b0f19`-Hintergrund mit Icon & Claim; Datei-Größe < 120 KB (WhatsApp-Limit < 300 KB) |
### Dynamische OpenGraph-URL-Injektion
Messenger-Crawler (insbesondere WhatsApp) werten relative Bild-URLs in `og:image` oft nicht korrekt aus. Der Go-SPA-Handler in [`internal/api/server.go`](../internal/api/server.go) ersetzt beim Ausliefern von `index.html` automatisch relative Pfade (`/og-image.png`, `og:url`) durch die absolute Server-Origin (`scheme://host`), ermittelt über `X-Forwarded-Proto` und `X-Forwarded-Host` bzw. `req.Host`.
+3
View File
@@ -97,6 +97,9 @@ In [`frontend/src/index.css`](../frontend/src/index.css) und `tailwind.config.js
- `.checkbox-square`: Kontrollkästchen mit `--radius-checkbox`.
- `.capture-pill`: Spezial-Pille für die Desktop-Schnelleingabeleiste mit `--radius-pill`.
### 🏷️ App-Icon & Branding
Das standardmäßige Marken-Icon von MindSift (`frontend/public/icon.svg`, PWA-Icons und Social-Preview-Bilder) folgt der primären Akzentfarbe `--accent: #2563eb` und `--text-on-accent: #ffffff` mit den typischen leichten Eckenrundungen (`rounded-xl` bzw. `rx=108`). Details zur Asset-Matrix und den Icon-Formaten siehe [architecture.md](architecture.md#app-icons-pwa--social-preview-open-graph).
---
## 🛠️ 5. Ein eigenes Theme erstellen
+28 -2
View File
@@ -9,9 +9,35 @@
<meta name="apple-mobile-web-app-title" content="MindSift" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="description" content="MindSift — Der Kopf frei in drei Sekunden. Instant Capture, Tinder-Style Triage, Workspace & Echtzeit-Listen." />
<link rel="manifest" href="/manifest.json" />
<!-- Favicon & Touch Icons -->
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<link rel="apple-touch-icon" href="/icon.svg" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<!-- Open Graph / WhatsApp / Discord / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:site_name" content="MindSift" />
<meta property="og:url" content="/" />
<meta property="og:title" content="MindSift — Der Kopf frei in drei Sekunden" />
<meta property="og:description" content="Instant Brain-Dump, Tinder-Style Triage, Workspace & Echtzeit-Listen." />
<meta property="og:image" content="/og-image.png" />
<meta property="og:image:secure_url" content="/og-image.png" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="MindSift App Logo & Vorschau" />
<!-- Twitter / X -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="MindSift — Der Kopf frei in drei Sekunden" />
<meta name="twitter:description" content="Instant Brain-Dump, Tinder-Style Triage, Workspace & Echtzeit-Listen." />
<meta name="twitter:image" content="/og-image.png" />
<meta name="twitter:image:alt" content="MindSift App Logo & Vorschau" />
<link rel="manifest" href="/manifest.json" />
<title>MindSift</title>
<script>
window.__deferredPwaPrompt = null;
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+14
View File
@@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
<rect width="512" height="512" fill="#2563eb"/>
<g transform="translate(256, 256) scale(11) translate(-12, -12)" fill="none" stroke="#ffffff" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"/>
<path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"/>
<path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"/>
<path d="M17.599 6.5a3 3 0 0 0 .399-1.375"/>
<path d="M6.003 5.125A3 3 0 0 0 6.401 6.5"/>
<path d="M3.477 10.896a4 4 0 0 1 .585-.396"/>
<path d="M19.938 10.5a4 4 0 0 1 .585.396"/>
<path d="M6 18a4 4 0 0 1-1.967-.516"/>
<path d="M19.967 17.484A4 4 0 0 1 18 18"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 861 B

+3 -3
View File
@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
<rect width="512" height="512" rx="128" fill="#8ab4f8"/>
<g transform="translate(80, 80) scale(14.666)" fill="none" stroke="#0b0f19" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect width="512" height="512" rx="108" fill="#2563eb"/>
<g transform="translate(256, 256) scale(12.5) translate(-12, -12)" fill="none" stroke="#ffffff" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"/>
<path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"/>
<path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"/>
@@ -11,4 +11,4 @@
<path d="M6 18a4 4 0 0 1-1.967-.516"/>
<path d="M19.967 17.484A4 4 0 0 1 18 18"/>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 851 B

After

Width:  |  Height:  |  Size: 872 B

+26 -2
View File
@@ -17,8 +17,32 @@
"purpose": "any"
},
{
"src": "/icon.svg",
"sizes": "192x192 512x512",
"src": "/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icon-maskable-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/icon-maskable-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/icon-maskable.svg",
"sizes": "any",
"type": "image/svg+xml",
"purpose": "maskable"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

+23
View File
@@ -254,6 +254,29 @@ func (s *Server) Router() http.Handler {
w.Header().Set("Pragma", "no-cache")
w.Header().Set("Expires", "0")
}
if path == "index.html" {
if data, err := fs.ReadFile(s.staticFS, "index.html"); err == nil {
scheme := "http"
if req.TLS != nil || req.Header.Get("X-Forwarded-Proto") == "https" {
scheme = "https"
}
host := req.Header.Get("X-Forwarded-Host")
if host == "" {
host = req.Host
}
if host != "" {
origin := scheme + "://" + host
htmlStr := string(data)
htmlStr = strings.ReplaceAll(htmlStr, `content="/og-image.png"`, `content="`+origin+`/og-image.png"`)
htmlStr = strings.ReplaceAll(htmlStr, `property="og:url" content="/"`, `property="og:url" content="`+origin+`/"`)
data = []byte(htmlStr)
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
_, _ = w.Write(data)
return
}
}
fileServer.ServeHTTP(w, req)
})
}