Further splits have diminishing returns. Remaining large sections (LOGISTICS ~985, TOOLS ~978, BUILDING IDENTIFIER ~606) are tangled feature code with renderers, event handlers, and state — splitting them requires careful function-by-function analysis.
Resources
10Install
npx skillscat add roiez1-60-seconds-hazardous-materials/italy-trip Install via the SkillsCat registry.
Zuckermanía Italy Trip PWA — Working Guide
Comprehensive reference for anyone (including future Claude) working on this repo.
Live: https://italy-trip-psi.vercel.app
Repo: roiez1-60-seconds-hazardous-materials/italy-trip
Owner: Roei (רועי) — Hebrew-first, mobile-first, autonomous-execution-preferred
Family: רועי · עדי · מאיה (18) · עמית (14) · נועם (10) · רומי (5)
Trip dates: Sep 22 – Oct 5, 2026 (14 days)
Critical rules (read first)
api.github.comis BLOCKED in the Claude bash environment. Onlygithub.comover HTTPS works. Push via PAT embedded in URL:https://x-access-token:$PAT@github.com/roiez1-60-seconds-hazardous-materials/italy-trip.git- Vercel domain
vercel.appis also blocked — can't curl-verify deployments from bash. Rely onnode --checkfor syntax. - Never put API keys in code/chat/README — Google/vendors scan public sources and auto-revoke. Keys live only in Vercel env vars.
- Before answering "do we visit X" or any itinerary question: grep the code first, never answer from memory.
- Small stages for destructive ops. Tag stable versions before risky changes:
git tag v{N}-stable && git push origin v{N}-stable. - Complete file rewrites preferred over partial edits when the user sends a long piece of content. For normal dev, use
str_replaceprecisely.
Architecture overview (current: v322)
italy-trip/
├── index.html (6,980 lines — down from 9,159 in v301)
│ └── <script> inline (~6,200 lines)
├── sw.js (service worker, PRECACHE list)
├── manifest.json (PWA manifest)
├── version.json ({"v":"322","ts":...})
├── js/
│ ├── data.js (async JSON loader, exposes window.dataReady)
│ ├── map.js (Mapbox GL — pre-existing, not our split)
│ ├── games.js (kids games: trivia, race, word, catch, memory, Tetris)
│ ├── chat.js (family chat + read tracking + push subscription)
│ ├── translator.js (Gemini text + photo translate via /api/ai, /api/identify)
│ ├── trip-utils.js (audio guide, nav, URL parsing, map utils, countdown)
│ └── data/
│ ├── itinerary.json (DAYS — 14-day itinerary)
│ ├── stickers.json (STICKER_LOCS — 19 GPS sticker locations)
│ ├── notif-tips.json (NOTIF_TIPS — 14 daily push tips)
│ ├── rain-plans.json (RAIN_PLANS — 13 rainy-day alternatives)
│ ├── bases.json (BAS — 4 regional bases)
│ ├── bookings.json (BKS — 11 pre-trip bookings)
│ ├── checklist.json (CHKLIST — 10 packing categories)
│ ├── spotify.json (SPOTIFY — 8 regional playlists)
│ └── ai-qa.json (AI_SYNS + AI_QA — local search synonyms + FAQ)
└── api/ (Vercel serverless functions)
├── ai.js (Gemini chat — Pinocchio AI guide)
├── avatars.js
├── cron/ (scheduled functions)
├── debug.js
├── identify.js (Gemini vision for photo translate/identify)
├── passport.js (passport photo storage via Google Drive)
├── photos.js (Google Drive gallery)
├── push.js (web-push via VAPID, Supabase subscriptions)
├── resolve-location.js (server-side URL resolver — Google Maps/Waze ll=)
├── setup.js (OAuth setup helper)
└── teaser.jsScript load order (index.html around line 17-24)
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest/..."></script>
<script src="https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.js"></script>
<script src="/js/data.js?v=321"></script> <!-- async JSON loader → window.dataReady -->
<script src="/js/map.js?v=301"></script> <!-- Mapbox wrapper -->
<script src="/js/games.js?v=311"></script> <!-- kids games -->
<script src="/js/chat.js?v=318"></script> <!-- family chat + push -->
<script src="/js/translator.js?v=314"></script> <!-- translate -->
<script src="/js/trip-utils.js?v=320"></script> <!-- audio/nav/maps utils -->
<!-- then inline <script> -->All modules are classic scripts (not ES modules). Top-level var/const/function become window globals, cross-script access works at call-time.
Data loader gate (v309+)
js/data.js fetches all 9 JSONs in parallel, sets window.DAYS, window.AI_QA etc., then resolves window.dataReady.
Inline script has this at the top (~line 800) to defer any data-dependent callback:
// Wrap setTimeout/setInterval so data-hungry callbacks wait for dataReady
var _origST = window.setTimeout, _origSI = window.setInterval;
window.setTimeout = function(cb, ms) { ... dataReady.then(cb) ... };
// similar for setIntervalAnd the eager-init at the bottom of the inline script is explicitly gated:
(window.dataReady || Promise.resolve()).then(function(){
rH(); rB(); rI(); updateNotifUI(); renderExpenses(); updateParkingBtn();
});If you add new code that touches DAYS/BAS/etc. at load time (not inside a function), gate it.
Version bumping
Three files must all stay in sync:
| File | What to change |
|---|---|
index.html |
>v322< (footer display) AND BUILD_VERSION = 'v322'.slice(1) |
sw.js |
var CACHE='zuck-v322'; |
version.json |
{"v":"322","ts":<unix-ms>} |
When a js/*.js file changes, also bump its cache-buster in the <script src="...?v=NNN"> tag in index.html. Otherwise, service worker serves the stale cached copy.
sw.js PRECACHE list must include every js/*.js and js/data/*.json file — missing entries break offline.
Quick bump snippet:
NEW=322
sed -i "s/>v$((NEW-1))</>v${NEW}</; s/'v$((NEW-1))'\\.slice/'v${NEW}'.slice/" index.html
sed -i "s/zuck-v$((NEW-1))/zuck-v${NEW}/" sw.js
ts=$(date +%s) && echo "{\"v\":\"${NEW}\",\"ts\":$ts}" > version.jsonDeployment
Vercel auto-deploys on push to main. No deploy hook needed manually (but if ever: prj_78v53y1Zaad2aDr3yTN9oQvQLfLC/PwaDFzAVzw).
Git push pattern (only workable path from Claude bash):
git push origin main
# Behind the scenes the remote URL uses embedded PAT; env var setup in .gitconfig/remotesCold deploy takes ~30-60s. Users need hard refresh to skip cached sw.js.
Stable tags (rollback targets)
v298-stable v301-stable v306-stable v308-stable v309-stable
v311-stable v313-stable v317-stable v320-stableRollback: git reset --hard v{N}-stable && git push -f origin main.
Environment / services
| Service | What it's for | Key |
|---|---|---|
| Vercel | Hosting, serverless | — |
| Supabase | Chat messages, read receipts, push subscriptions, checklist sync | dmgxvcvjzgmorfibvezr project; publishable key in code |
| Gemini API | Pinocchio AI, translator, photo identify | GEMINI_KEY in Vercel env vars |
| Mapbox GL | Interactive maps | NEXT_PUBLIC_MAPBOX_TOKEN in Vercel env |
| VAPID | Web push | VAPID_PUBLIC_KEY + VAPID_PRIVATE_KEY in Vercel env (public key also duplicated client-side in chat.js) |
| Google Drive | Trip photos gallery | OAuth refresh token in GOOGLE_REFRESH_TOKEN Vercel env |
Known issues / gotchas
Waze share_drive URLs
URLs of the form waze.com/ul?a=share_drive&sd=... cannot be resolved without Waze's official partner API. The app detects these upfront and shows a message asking the user to share a location (not drive) or use Google Maps. See v320 commit for history.
Push subscription identity drift
subscribePush in chat.js must use chatUser.name (not gpsUser) as the subscription's user_name, because the server's notify filter uses neq.sender where sender is always chatUser.name. If mismatched, users get self-notifications and others get duplicates. Fixed in v318. Server also does endpoint-based dedup on every subscribe (v313).
Notification toggle persistence
subscribePush checks localStorage.getItem('zuck_notif') === 'true' before subscribing. Users who never explicitly toggled the bell won't auto-subscribe (opt-in). Fix in v316.
Android safe-area
.hdr and floating buttons use max(env(safe-area-inset-*), N) to respect device cutouts. Min left-inset set to 8-16px so Android hole-punch cameras don't obscure icons. iOS unaffected (env is already 0 in portrait). See v322.
Map legend bleeds through modals
#mapLegendBox is position:fixed z-index:9997. Chat modal is z-index:98. When user opens chat from map tab, legend appears over chat. Fix: chat open/close explicitly hides/shows #mapLegendBox. See v318.
Chat badge counting own messages
checkChatBadge must filter by name=neq.<self> so the user's just-sent message doesn't count as unread. See v318.
Common dev workflows
Add a new itinerary entry
Edit js/data/itinerary.json, bump js/data.js V= counter, version bump the 3 files.
Add a new feature that uses DAYS/BAS/BKS
Add code inside a function (not at top level). Function will be called after dataReady resolves via the setTimeout/setInterval gate wrapper.
Add a new module
- Create
js/foo.js - Add
<script src="/js/foo.js?v=322"></script>to index.html before the inline<script> - Add
/js/foo.jsto sw.js PRECACHE - Bump version
Debug production issues
User lacks easy access to Vercel logs. Add a debug flag or error-surfacing via alert() or a toast. Don't ship debug to prod; remove after diagnosed.
User-supplied info kept in memory (not in repo)
- Anthropic API key (for shamur project, different repo) — in Vercel env
- Gemini API key (this repo) — in Vercel env as
GEMINI_KEY - PAT for GitHub push — already embedded in the git remote URL
- Deploy hook URL — rarely needed, Vercel auto-deploys on push
Related projects (separate repos, same user)
- Shamur (
roiez1-.../shamur) — Hebrew journal PWA for wife Adi, atshamur.vercel.app - Iran missile propellants (
roiez1-.../iran-missile-propellants) — bilingual intel dossier, atiran-missile-propellants.vercel.app - IHU chemical weapons (
roiez1-.../ihu-chemical-weapons) - Various HazMat/CBRN tools for Israel Fire & Rescue Services (user's day job)
Session history milestones
- v301 (start of recent major refactor): 9,159 lines inline
- v302-v308: Data → JSON files (8 files)
- v309: Async loader + dataReady gate
- v310: games.js split (515 lines)
- v312: chat.js split (504 lines)
- v313: Endpoint-based push dedup
- v314: translator.js split (95 lines)
- v315: trip-utils.js split (1,017 lines) — biggest win
- v316: Notification toggle fixes
- v317:
/api/resolve-locationfor Google Maps/Waze URLs - v318: Chat badge, map legend z-index, push identity preference
- v320: Removed Waze share_drive API probing (unreachable)
- v321: AI_SYNS + AI_QA → ai-qa.json (final data extraction)
- v322: Android safe-area for bell/chat icons
Current: 6,980 lines in index.html (−23.8% from v301).
Further splits have diminishing returns. Remaining large sections (LOGISTICS ~985, TOOLS ~978, BUILDING IDENTIFIER ~606) are tangled feature code with renderers, event handlers, and state — splitting them requires careful function-by-function analysis.