feat(pwa): favicon, maskable icon, splash screens, runtime cache, safe-area insets

- Add favicon.ico, favicon-32x32.png, favicon-16x16.png
- Add dedicated maskable-512x512.png with safe-zone padding for Android adaptive icons
- Add iOS splash screens for 5 common device sizes (excluded from SW precache)
- Add NetworkFirst runtime caching for stable API routes (excludes volatile endpoints)
- Add safe-top inset to all view headers for PWA standalone mode
- Bump version to 0.2.2

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
kuannnn
2026-03-27 22:16:38 +08:00
parent 5207d2b0dd
commit 9c2158961c
19 changed files with 37 additions and 9 deletions
+1 -1
View File
@@ -36,7 +36,7 @@ export function AdapterSettingsSection({ adapter, onBack }: { adapter: string; o
return (
<div className="flex flex-col h-full bg-bg">
<div className="flex items-center px-4 py-3 border-b border-border gap-2">
<div className="flex items-center px-4 py-3 border-b border-border gap-2 safe-top">
<button onClick={onBack} className="text-text-dim hover:text-text"><ChevronLeft className="w-5 h-5" /></button>
<AdapterIcon adapterId={adapter} size={20} />
<span className="font-medium text-text font-mono tracking-wide">{brand.displayName} Settings</span>
+1 -1
View File
@@ -477,7 +477,7 @@ export function ChatView({
}
return (
<div className="flex flex-col h-dvh bg-bg relative overflow-hidden">
<div className="flex flex-col h-dvh bg-bg relative overflow-hidden safe-top">
{/* Header — auto-hides when scrolling up to view history */}
<div className={`flex items-center gap-2 px-4 py-3 border-b border-border shrink-0 transition-all duration-200 ${headerHidden ? 'max-h-0 py-0 overflow-hidden opacity-0 border-b-0' : 'max-h-16 opacity-100'}`}>
<Button variant="ghost" size="icon" onClick={onBack}>
+1 -1
View File
@@ -142,7 +142,7 @@ export function NewChatView({
return (
<div className="flex flex-col h-screen bg-bg">
{/* Header */}
<div className="flex items-center gap-2 px-4 py-3 border-b border-border shrink-0">
<div className="flex items-center gap-2 px-4 py-3 border-b border-border shrink-0 safe-top">
<Button variant="ghost" size="icon" onClick={onBack}>
<ChevronLeft className="w-5 h-5" />
</Button>
+1 -1
View File
@@ -42,7 +42,7 @@ export function SavedInstructionsView({ onBack }: { onBack: () => void }) {
return (
<div className="flex flex-col h-full bg-bg">
{/* Header */}
<div className="flex items-center px-4 py-3 border-b border-border">
<div className="flex items-center px-4 py-3 border-b border-border safe-top">
<button
onClick={onBack}
className="text-text-dim hover:text-text mr-2"
+1 -1
View File
@@ -228,7 +228,7 @@ export function SessionsView({
// --- Projects list (default view) ---
return (
<div className="min-h-screen bg-bg flex flex-col">
<div className="flex items-center justify-between px-4 py-3 border-b border-border shrink-0">
<div className="flex items-center justify-between px-4 py-3 border-b border-border shrink-0 safe-top">
<span className="flex items-center gap-1.5 text-lg font-medium text-text font-mono tracking-wider">
<svg width="20" height="15" viewBox="0 0 8 6" style={{ imageRendering: 'pixelated' }} className="text-accent">
<rect x="4" y="0" width="4" height="1" fill="currentColor"/>
+1 -1
View File
@@ -55,7 +55,7 @@ export function SettingsView({ onBack }: { onBack: () => void }) {
return (
<div className="flex flex-col h-full bg-bg">
{/* Header */}
<div className="flex items-center px-4 py-3 border-b border-border">
<div className="flex items-center px-4 py-3 border-b border-border safe-top">
<button onClick={onBack} className="text-text-dim hover:text-text mr-2"><ChevronLeft className="w-5 h-5" /></button>
<span className="text-lg font-medium text-text font-mono tracking-wide">Settings</span>
</div>
+19
View File
@@ -1,11 +1,30 @@
/// <reference lib="webworker" />
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { NetworkFirst } from 'workbox-strategies';
declare const self: ServiceWorkerGlobalScope;
// Precache static assets (injected by vite-plugin-pwa at build time)
precacheAndRoute(self.__WB_MANIFEST);
// Cache stable API responses — show last-known data when offline.
// Exclude volatile real-time endpoints (messages, active sessions, reviews).
registerRoute(
({ url }) => {
const p = url.pathname;
if (!p.startsWith('/api/')) return false;
if (p.includes('/messages')) return false;
if (p.startsWith('/api/active-sessions')) return false;
if (p.startsWith('/api/reviews')) return false;
return true;
},
new NetworkFirst({
cacheName: 'api-cache',
networkTimeoutSeconds: 5,
})
);
// Push notification handler — server already filters by clientCount,
// so we always show the notification if one is received.
self.addEventListener('push', (event) => {