From 9c2158961c04f316b551cf4ac06088dfea92cb3c Mon Sep 17 00:00:00 2001 From: kuannnn Date: Fri, 27 Mar 2026 22:16:38 +0800 Subject: [PATCH] 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) --- index.html | 9 +++++++++ package.json | 2 +- public/favicon-16x16.png | Bin 0 -> 404 bytes public/favicon-32x32.png | Bin 0 -> 671 bytes public/favicon.ico | Bin 0 -> 4286 bytes public/maskable-512x512.png | Bin 0 -> 2012 bytes public/splash/apple-splash-1170x2532.png | Bin 0 -> 14504 bytes public/splash/apple-splash-1179x2556.png | Bin 0 -> 14636 bytes public/splash/apple-splash-1290x2796.png | Bin 0 -> 15222 bytes public/splash/apple-splash-2048x2732.png | Bin 0 -> 22789 bytes public/splash/apple-splash-750x1334.png | Bin 0 -> 6076 bytes src/components/AdapterSettingsSection.tsx | 2 +- src/components/ChatView.tsx | 2 +- src/components/NewChatView.tsx | 2 +- src/components/SavedInstructionsView.tsx | 2 +- src/components/SessionsView.tsx | 2 +- src/components/SettingsView.tsx | 2 +- src/sw.ts | 19 +++++++++++++++++++ vite.config.ts | 4 ++-- 19 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 public/favicon-16x16.png create mode 100644 public/favicon-32x32.png create mode 100644 public/favicon.ico create mode 100644 public/maskable-512x512.png create mode 100644 public/splash/apple-splash-1170x2532.png create mode 100644 public/splash/apple-splash-1179x2556.png create mode 100644 public/splash/apple-splash-1290x2796.png create mode 100644 public/splash/apple-splash-2048x2732.png create mode 100644 public/splash/apple-splash-750x1334.png diff --git a/index.html b/index.html index 8f75138..d31c65c 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,15 @@ + + + + + + + + + diff --git a/package.json b/package.json index 1c090b3..83a729e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kuannnn/clawtap", - "version": "0.2.1", + "version": "0.2.2", "description": "Mobile UI for AI coding assistants. Real-time sync with Claude Code, Codex CLI, and Gemini CLI via tmux.", "type": "module", "bin": { diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..7d3754e5b76ade22e43f2521f386fb66128bb2b6 GIT binary patch literal 404 zcmV;F0c-w=P)yjt$MN&9*rO4k3y}K}TMf-lNxGgO>_wpY1)q-_PU6<7WUuO6L+d2G*I6E>QP< z@7$EqxdP6Bl{w4&X7?>i2t!JzyTHqT=^0WwddV^Xk?}*kx`|!R&J5e-Eb(e?wm?=g z*rhavFel1}FtJN%WF~a>>a?$Q!wm`K%h@>TGunH-XrOZg|1!%4L_5)PAA0BP|7mQdGdoNAmXa}p9 z3I&7a8~xiS29Gc3FEw2-PW&Lp>S+l8_qxI7{qwZgY(aFxLF}#pFl@e}I^JS}d5JJu yjAiLWfcifKbyKO}!Loz^wwQk-yTFa_dlx@Y(pv*RFfMTb0000*}OMK+|(&vyyTat@ODY<^f-}~L~`{DO} zJWlXWo|M@~U>*Rs0H4cM1NZ`BL#g^ppa~!Vq?A)&y#wYh*)&OII9XCkeYXH_#O46= zV7&!ATgvPc;Nb|(J1V)Z_tSP=_XRMO0stT;gp^|uh-^9lrf#KY4j{5tdgg7V4&Wpm ze+X7H(;U|9mY@zaf1TDz+13O zK5kGzS3-2y#6)poZ{h|-E`yWL-~@TbR~L|r(*v0U$i->KR~K-CJWjzwR>$&ruq4sD=j+6B4^9ae{o;nf_Yq zs<}ad+{(@F-fGB6E%^>40`yaEc@}5pJeaP87a4_+(l@;hoar3S>?FDS0RXN2-`K@5 z0E_`4&i9|Ko$Za@RM0yI=-sF%K;i~EAH>ySn>1tL&2-1nOUK`8w5mt#IepCg0(2lc zFIeXS4kEo9QGdNNP;X)c{WSUfE}6OFxmvun&%x8pVMkrzcc7n+PxJj>Z4g~3g3DCu z5nKfTQ%}oW2x=CH?X&=l5!)7s^|Szh4FPyk`X7P$5u2BlYTU7aCY&q*Bb5M5&eS;RyN`BZ`yXku$jv_T7q*qpz>)~V#$EMsh&j9l!H41Rmiy$K_c z=KO+ekmCG(iF0An5R+m*gM&0AR2`i25^;@8@=b{~0;i)P88Nr4>j-tAdbFl!m3ZT4 ztOaqu#K=FBy{4T{D)pv~HT-*+^M-%E99-wbNq5a<@I_TqaGyPGx(vSm%)fiS>dM!f zzxo|m6Sc^1EsI(^Z`;vrL7N{~IS(R%tz8h< zS1@pU^MOy^2UgCjcZveBy!dI~z4-^()4NSmX zdO`GGucl*b#6$m;L=WinpHXU1`%g%~NIGjVB<&v>`hz}G5pzf!EpXY4bm%j-c!R$P KA|BxE6Za=jHszfF literal 0 HcmV?d00001 diff --git a/public/maskable-512x512.png b/public/maskable-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..78c3ef72e0ae02d2ea6f2962302f113a90271ff4 GIT binary patch literal 2012 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&t&wwUqN(AjOvC?e4eewDCF5X!!|1Ex1A5@p7W0D7jK+X2&zfHgVslD4a&;Hhn_w463 zFp`n)D)}3{i|5<*Br}r{c)N5@Cx7GNd16<4?#A`pZyY=+gTe~DWM4fQ<_dVAm@O?fx<8Ey_t1{|8u69f45_1U}#wN)f%X?LHsrw1H**0+d%fE z9BBpyhgrEmS&z-RK!MC{yg;MQZewI%SaQ=0sMusw@n~p_rj5~@GFlXj7L23S#Au~C z+A0`rBaOBgN88V%9fZ;D(rD*#w0At(J09&FkM@p7d&i@_`5``OQZxIKli;bEo1l3e zHE{o@Os{vmAU zAW;nRT215EJN6$}W?S#8;k^I6c)t-t{(-GS&@bpgy5;#728Ls6p4Z)uzFv0c+qJpx z{_rvW*cyX9lF@aW;7bws6wkZN%TJijO3=H;DX`D)KB}c|xmS*^|VczdAEI*2K zkAGodu-if7G-x~Rsp($E2Fceu{rw-<$J{ow1#;GbvkJa|!N^x6Deh0L^v!2@@OSNV zJKKlR5KF&5zlA5;l4SejcZ90d(gqq#|)RIwN9MSPTP$L?CD_fWvV2 z?5GEER`SjYMvH>c zqF}Tr0BuDW=HoM?9vtn%(xngkCwG4zD{zGx1KZDN|77<_7KQ_}Ktt+lGJ#V*qs-B8 h7)=DDnSuC{fVK3Z&Q-R9E#;sh#naW#Wt~$(696jM)CK?m literal 0 HcmV?d00001 diff --git a/public/splash/apple-splash-1179x2556.png b/public/splash/apple-splash-1179x2556.png new file mode 100644 index 0000000000000000000000000000000000000000..2336e58e336ac27b2af22279e0f676ca35d67a95 GIT binary patch literal 14636 zcmeAS@N?(olHy`uVBq!ia0y~yV42Op!1;%R2`F-Eb9n}kVoUONcVYMsf(!O8pUl9Z zz~JfP7*a9k&Gn6(K+XY!QsMvqugk|QnGkbfYw>$ypdOB>{mekB;>imjxk^Tsfx%(w zVxY*1q7tA$XdfQ~!-7*685tNtEUg$A8noQm85jbdjw&7vjnT9*no~xLg3*F;w3-;L zG)G$nqiv+o7UO99d9;Ht+Fcs$JdXB`M|;Pkz2ni|@o4XOw0At(J09&FkM@p7d&ku3 z9Un+N-1R;IrDjkAshIvaR7fUwnT(&w<~C;K?y61Rb&NphXP6vZ`WW&__hu zxsqq)91Qk%XplmXy}52NJHv;riK`UiU{$zvEkn~I)$Um5vszbMV{ zLkDE*2a)5Odgn15I6gd5D#U9H3_IFsoL(?OmE1CsaD36(FVdQ&MBb@0L1D=kN^Mx literal 0 HcmV?d00001 diff --git a/public/splash/apple-splash-1290x2796.png b/public/splash/apple-splash-1290x2796.png new file mode 100644 index 0000000000000000000000000000000000000000..1d7cbd3c613fc9b4e92fac0d236564c1bfecf1c4 GIT binary patch literal 15222 zcmeAS@N?(olHy`uVBq!ia0y~yUytz-z0#<8$%$G z($5Ig4i>zb4CJ4X11fU>%WpakALJ^>BLs(=S%D?y{QCqVHK;N;0jdEe47FlE zz;-!1!-x4%=l`%M6-AK#dh zwZ8jPmgxX{A!vS-0bE^EyN(7Yj0e7JpWE3oGVJK5VS@M?G41*nHU_)fRqrkfFUYoD zrq1x;W(+8b96+s)25L7sRvq88XPG&}fpC!D8uNE-ORi#KsF_N8A6B@(-s$iEL-=}` zk3U00`I9P8GYcGVL#`MJ=})cn%?CQaA#ok3fB@zw3$`d<1_o()&{_a!)HzT)QI0l8 zM?+vV1V%$(Gz3ONU^E0qLtr!nMnhmU1V{*h7xBMK6@cw^28E3JSFWd&!0T(jfQEN3 zfrop+9ph2K(EtIa2#?Xk0GjL=%^jm918A4RXsI|_Dvp+lqZwheMi^}?jy8=(Ys1mn gaI`igy*A8pzpM9jVL;N+U5Y(xb~0328c z-~bMi0Q>{%035(U5J22y8Gr*gNCLZxcKPs;P_-wZbz25zcD}9hzKq?b!?Rt zY*7ImBmwAwbpQ_FAPK-vvJSuj90UP}5&44@LKrx7do#8K&jKJ!BYjo<5!|wh0bv?$ zIZSRDt{AQut~e|i(LxM8*W0P3L|-k_8O;M(o%UC2YZ?M^uWgy9ee-u> zDOBjA9&ygt=SlvuHCyz#@`;-thM7{RUvQPT+E0nrb&q!qPU&a;)g_*I(bmuTP7>uNTM5#{~32su`_kL9$O<{Ku)E?pJPOBo2BFh?$+ z_EyNW%yy{k+6Z^#e8zGvV2K$VB!LjIV6RRU2DT)5e1HQuNCJo#tOIZW2T1^NlXUkNUyd9g9`wnls$9+2GD^d03CpV zDg*dLj0IH)h>h@x@QLt=m@|nJHJCSofp7)T0T{@L03CnEz-q?91Qc=Ix+D-tu_bxCyD}=j^>2zD<0ox&CP@ zMh26=lC}&C4{C$h7&s;yF*wYa!pLxF2@ivSS}H?>fhIFUi>D-mg3l=ihQv`Nqv0}| z7)CS6Xvr{I9FA5KqjlqGgJ5tsl-`(DG1#Q+?mK>O*Z0pGw`Y8`Z|N_dI!K#$8?L`# z{J@=i{o}Fa@BaSzAyjv+dcVJswlXO43=WjZ zE>>r#Xn}5U0d*?3-Kd9bGy~h3LO=}jLGh8 -
+
{brand.displayName} Settings diff --git a/src/components/ChatView.tsx b/src/components/ChatView.tsx index f1408fe..576270d 100644 --- a/src/components/ChatView.tsx +++ b/src/components/ChatView.tsx @@ -477,7 +477,7 @@ export function ChatView({ } return ( -
+
{/* Header — auto-hides when scrolling up to view history */}
diff --git a/src/components/SavedInstructionsView.tsx b/src/components/SavedInstructionsView.tsx index a676fe6..b1a79ca 100644 --- a/src/components/SavedInstructionsView.tsx +++ b/src/components/SavedInstructionsView.tsx @@ -42,7 +42,7 @@ export function SavedInstructionsView({ onBack }: { onBack: () => void }) { return (
{/* Header */} -
+
Settings
diff --git a/src/sw.ts b/src/sw.ts index 6e544ba..d9934e6 100644 --- a/src/sw.ts +++ b/src/sw.ts @@ -1,11 +1,30 @@ /// 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) => { diff --git a/vite.config.ts b/vite.config.ts index 115b422..2af6a16 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -26,7 +26,7 @@ export default defineConfig({ icons: [ { src: '/pwa-192x192.png', sizes: '192x192', type: 'image/png' }, { src: '/pwa-512x512.png', sizes: '512x512', type: 'image/png' }, - { src: '/pwa-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' }, + { src: '/maskable-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' }, ], shortcuts: [ { @@ -39,7 +39,7 @@ export default defineConfig({ categories: ['developer-tools', 'productivity'], }, injectManifest: { - globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'], + globPatterns: ['**/*.{js,css,html,ico,svg,woff2}', '*.png', 'mascot/*.png'], }, }), ],