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:
@@ -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>
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user