diff --git a/html/map.ejs b/html/map.ejs index 24ee8c64..5b4cd9a5 100644 --- a/html/map.ejs +++ b/html/map.ejs @@ -1,4 +1,3 @@ - @@ -13,7 +12,10 @@ --vh:100vh;--pad:12px;--radius:14px;--fg:#fff;--bg:rgba(0,0,0,.6);--glass:blur(12px); --marker-size:20px;--marker-color:#e53935;--marker-ring:rgba(229,57,53,.35);--ring-width:3px; --panel:rgba(0,0,0,.85);--accent:#0ea5e9;--chip:#111;--chipb:#222;--tip:#888; - --surface:#0b0b0b;--border:#333 + --surface:#0b0b0b;--border:#333; + --sidebar-w:320px; /* default; will be overridden from prefs */ + --sidebar-min:260px; + --sidebar-max:60vw; } *{box-sizing:border-box} html,body{height:100%;margin:0} @@ -77,18 +79,34 @@ .settings textarea{width:100%;min-height:120px;border-radius:10px;border:1px solid var(--border);background:#0a0a0a;color:#eee;padding:8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace} .settings .about{font-size:13px;opacity:.92;line-height:1.5} - /* Desktop layout — simplified & clean */ + /* Desktop layout — with resizable, scrollable sidebar */ .desktop .bar{grid-column:2} - .desktop .app{grid-template-columns:320px 1fr;grid-template-rows:auto 1fr} - .desktop .sidebar{position:fixed;left:0;top:0;bottom:0;width:320px;background:var(--panel);border-right:1px solid var(--border);display:flex;flex-direction:column;gap:12px;padding:12px;z-index:9} + .desktop .app{grid-template-columns:var(--sidebar-w) 1fr;grid-template-rows:auto 1fr} + .desktop .sidebar{ + position:fixed;left:0;top:0;bottom:0; + width:var(--sidebar-w); + min-width:var(--sidebar-min);max-width:var(--sidebar-max); + background:var(--panel);border-right:1px solid var(--border); + display:flex;flex-direction:column;gap:12px;padding:12px;z-index:9; + overflow:auto; /* scrollable */ + } .desktop .sidecard{border:1px solid #222;border-radius:12px;overflow:hidden} .desktop .sidecard header{padding:10px 12px;border-bottom:1px solid #222;font-weight:600;display:flex;justify-content:space-between;align-items:center} - .desktop .sidecard .row{display:flex;gap:8px;align-items:center;padding:8px 12px;border-top:1px solid #111} + .desktop .sidecard .row{display:flex;gap:8px;align-items:center;padding:8px 12px;border-top:1px solid #111;flex-wrap:wrap} .desktop .sidecard .row:first-of-type{border-top:0} .desktop .sidecard .hint{font-size:12px;opacity:.75;padding:8px 12px} .desktop .mapwrap{grid-column:2} - /* Suppress popovers on desktop (sidebar replaces them) */ + /* Suppress popovers and dock on desktop (sidebar replaces them) */ .desktop .menu,.desktop .pins,.desktop .settings{display:none !important} + .desktop .dock{display:none !important} + + /* Sidebar resizer handle */ + .desktop .side-resizer{ + position:fixed; top:0; bottom:0; left:calc(var(--sidebar-w) - 4px); + width:8px; cursor:col-resize; z-index:10; + background:linear-gradient(to right, transparent 0 6px, #ffffff14 6px 7px, transparent 7px 100%); + } + .side-resizer.dragging{background:linear-gradient(to right, transparent 0 6px, #ffffff33 6px 7px, transparent 7px 100%)} /* Pro detail rows subtly toned down */ .pro-only{display:none;} @@ -292,8 +310,8 @@ const accentColorEl=S("#accentColor"), accentResetEl=S("#accentReset"); const customCSSEl=S("#customCSS"), applyCSSEl=S("#applyCSS"), clearCSSEl=S("#clearCSS"); - /* Pro mode */ - const PREFS_KEY="pokemaps_prefs_v6"; + /* Prefs */ + const PREFS_KEY="pokemaps_prefs_v7"; // bump version for new sidebar width const LS_PINS="pokemaps_pins_v1"; const LS_SEARCH="pokemaps_search_hist_v1"; @@ -391,13 +409,15 @@ if(!("geolocation" in navigator)){ alert("Geolocation not supported."); return } if(watchId!==null) return; followBtn && (followBtn.textContent="🛰️ Following"); + const d_follow = document.querySelector("#d_follow"); + d_follow && (d_follow.textContent="Following"); watchId = navigator.geolocation.watchPosition( pos=>{ centerOn(pos.coords.latitude,pos.coords.longitude,{push:false}) }, _=>stopFollow(), {enableHighAccuracy:true,timeout:15000,maximumAge:1000} ) }; - const stopFollow=()=>{ if(watchId!==null){ navigator.geolocation.clearWatch(watchId); watchId=null } ; followBtn && (followBtn.textContent="🛰️ Follow") }; + const stopFollow=()=>{ if(watchId!==null){ navigator.geolocation.clearWatch(watchId); watchId=null } ; followBtn && (followBtn.textContent="🛰️ Follow"); const d_follow=document.querySelector("#d_follow"); d_follow && (d_follow.textContent="Follow") }; const toggleFollow=()=> watchId===null ? startFollow() : stopFollow(); const copyLink=async()=>{ try{ await navigator.clipboard.writeText(appURL(state)); alert("Link copied!") }catch{ alert("Could not copy.") } }; @@ -429,7 +449,6 @@ const renderSuggest=(items, term="")=>{ if(!sug) return; - // keep the sticky head; rebuild the rest const head = sug.querySelector(".head"); sug.innerHTML=""; if(head) sug.appendChild(head); @@ -609,7 +628,7 @@ toggleCoords.onchange=()=>{ prefs.showCoords = toggleCoords.checked; coordsEl.style.display = prefs.showCoords ? "block" : "none"; savePrefs() }; coordFmtEl.onchange=()=>{ prefs.coordFmt = coordFmtEl.value; savePrefs() }; - coordPrecEl.oninput=()=>{ prefs.prec = +coordPrecEl.value; coordPrecVal.textContent=String(prefs.prec); savePrefs() }; + coordPrecEl.oninput=()=>{ prefs.prec = +coordPrecEl.value; coordPrecVal.textContent = String(prefs.prec); savePrefs() }; autoFollowEl.onchange=()=>{ prefs.autoFollow = autoFollowEl.checked; savePrefs() }; shareDeltaEl.onchange=()=>{ prefs.includeDelta = shareDeltaEl.checked; savePrefs() }; confirmDeleteEl.onchange=()=>{ prefs.confirmDelete = confirmDeleteEl.checked; savePrefs() }; @@ -653,11 +672,16 @@ applyMarkerStyle(prefs.markerStyle||"dot"); setAccent(prefs.accent||"#0ea5e9"); applyTheme(prefs.theme||"auto"); - // apply pro mode class on body + // sidebar width + if(prefs.sidebarW){ + const w = clamp(prefs.sidebarW, parseSize(getComputedStyle(document.documentElement).getPropertyValue('--sidebar-min')), window.innerWidth*0.6); + document.documentElement.style.setProperty('--sidebar-w', w + 'px'); + } document.body.classList.toggle("pro-enabled", !!prefs.proMode); } function applyMarkerStyle(style){ markerEl.classList.toggle("crosshair", style==="crosshair") } function setAccent(hex){ document.documentElement.style.setProperty("--accent",hex); themeColorMeta?.setAttribute("content",hex) } + function parseSize(v){ return Math.max(0, parseFloat(String(v).replace(/[^\d.]/g,'')) || 0) } function applyTheme(mode){ if(mode==="dark"){ document.documentElement.style.colorScheme="dark" } @@ -694,10 +718,68 @@ parseURL(); apply(false); const urlHasLat = new URLSearchParams(location.search).has("lat"); if(!urlHasLat){ locate() } - if(prefs.autoFollow) startFollow(); + if(prefs.autoFollow) + startFollow(); - // Desktop sidebar builder (clean, with Pro toggle) - if(isDesktop()) buildDesktopSidebar(); + // Desktop sidebar builder + resizer + if(isDesktop()){ + buildDesktopSidebar(); + setupSidebarResizer(); + } + + function setupSidebarResizer(){ + let handle=document.querySelector(".side-resizer"); + if(!handle){ + handle=document.createElement("div"); + handle.className="side-resizer"; + document.body.appendChild(handle); + } + + const minPx = ()=> parseSize(getComputedStyle(document.documentElement).getPropertyValue('--sidebar-min')); + const maxPx = ()=> Math.min(window.innerWidth*0.6, parseSize(getComputedStyle(document.documentElement).getPropertyValue('--sidebar-max')) || window.innerWidth*0.6); + + let dragging=false, startX=0, startW=0; + + const onDown=(e)=>{ + if(!isDesktop()) return; + dragging=true; + startX = e.clientX ?? (e.touches?.[0]?.clientX || 0); + startW = parseSize(getComputedStyle(document.documentElement).getPropertyValue('--sidebar-w')) || 320; + handle.classList.add("dragging"); + document.addEventListener("pointermove", onMove); + document.addEventListener("pointerup", onUp); + e.preventDefault(); + }; + const onMove=(e)=>{ + if(!dragging) return; + const x = e.clientX ?? (e.touches?.[0]?.clientX || 0); + const raw = startW + (x - startX); + const clamped = clamp(raw, minPx(), maxPx()); + document.documentElement.style.setProperty('--sidebar-w', clamped + 'px'); + }; + const onUp=()=>{ + if(!dragging) return; + dragging=false; + handle.classList.remove("dragging"); + // persist + const w = parseSize(getComputedStyle(document.documentElement).getPropertyValue('--sidebar-w')) || 320; + prefs.sidebarW = Math.round(clamp(w, minPx(), maxPx())); + savePrefs(); + document.removeEventListener("pointermove", onMove); + document.removeEventListener("pointerup", onUp); + }; + + handle.addEventListener("pointerdown", onDown); + addEventListener("resize", ()=>{ + if(!isDesktop()) return; + // ensure width stays in bounds on resize + const w = parseSize(getComputedStyle(document.documentElement).getPropertyValue('--sidebar-w')) || 320; + const clamped = clamp(w, minPx(), maxPx()); + document.documentElement.style.setProperty('--sidebar-w', clamped + 'px'); + }, {passive:true}); + } + + // Desktop sidebar (mirrors mobile features) function buildDesktopSidebar(){ const side=document.createElement("div"); side.className="sidebar"; side.innerHTML=` @@ -718,7 +800,7 @@
-
Tip: / to focus search • 1–4 change layers • S save pin • R reset
+
Tip: / focus • 1–4 layers • S save pin • R reset
@@ -802,15 +884,15 @@ d_style.value=prefs.markerStyle||"dot"; d_ms.value=markerSizeEl.value; d_msv.textContent=d_ms.value+"px"; d_mc.value=markerColorEl.value; - d_mr && (d_mr.checked=markerRingEl.checked); - d_rw && (d_rw.value=ringWidthEl.value, d_rwv.textContent=d_rw.value+"px"); + if(d_mr) d_mr.checked=markerRingEl.checked; + if(d_rw){ d_rw.value=ringWidthEl.value; d_rwv.textContent=d_rw.value+"px" } d_markerv.onchange=()=>{ markerVisibleEl.checked=d_markerv.checked; markerVisibleEl.onchange() }; d_style.onchange=()=>{ markerStyleEl.value=d_style.value; markerStyleEl.onchange() }; d_ms.oninput=()=>{ markerSizeEl.value=d_ms.value; markerSizeEl.oninput(); d_msv.textContent=d_ms.value+"px" }; d_mc.oninput=()=>{ markerColorEl.value=d_mc.value; markerColorEl.oninput() }; - d_mr && (d_mr.onchange=()=>{ markerRingEl.checked=d_mr.checked; markerRingEl.onchange() }); - d_rw && (d_rw.oninput=()=>{ ringWidthEl.value=d_rw.value; ringWidthEl.oninput(); d_rwv.textContent=d_rw.value+"px" }); + if(d_mr) d_mr.onchange=()=>{ markerRingEl.checked=d_mr.checked; markerRingEl.onchange() }; + if(d_rw) d_rw.oninput=()=>{ ringWidthEl.value=d_rw.value; ringWidthEl.oninput(); d_rwv.textContent=d_rw.value+"px" }; // Pins mini const d_save=side.querySelector("#d_save"); @@ -823,7 +905,7 @@ d_pinlistmini.innerHTML=""; const pins=loadPins().slice(0,6); if(!pins.length){ d_pinlistmini.innerHTML='No pins yet.'; return } - pins.forEach((p,idx)=>{ + pins.forEach((p)=>{ const row=document.createElement("div"); row.style.display="flex"; row.style.gap="6px"; row.style.alignItems="center"; row.style.justifyContent="space-between"; const txt=document.createElement("div"); @@ -841,11 +923,11 @@ const d_accent=side.querySelector("#d_accent"), d_accent_reset=side.querySelector("#d_accent_reset"); d_showc.checked=!!prefs.showCoords; d_fmt.value=prefs.coordFmt||"dec"; - d_prec && (d_prec.value=prefs.prec??6, d_precv.textContent=String(prefs.prec??6)); + if(d_prec){ d_prec.value=prefs.prec??6; d_precv.textContent=String(prefs.prec??6) } d_accent.value=prefs.accent||"#0ea5e9"; d_showc.onchange=()=>{ toggleCoords.checked=d_showc.checked; toggleCoords.onchange() }; d_fmt.onchange=()=>{ coordFmtEl.value=d_fmt.value; coordFmtEl.onchange() }; - d_prec && (d_prec.oninput=()=>{ coordPrecEl.value=d_prec.value; coordPrecEl.oninput(); d_precv.textContent=d_prec.value }); + if(d_prec) d_prec.oninput=()=>{ coordPrecEl.value=d_prec.value; coordPrecEl.oninput(); d_precv.textContent=d_prec.value }; d_accent.oninput=()=>{ accentColorEl.value=d_accent.value; accentColorEl.oninput() }; d_accent_reset.onclick=()=>{ accentResetEl.click(); d_accent.value=accentColorEl.value };