feat(rmaps): theme support, mobile fix, theme-aware map tiles
Replace all hardcoded dark colors with --rs-* CSS variables in both demo and room modes. Add LIGHT_STYLE (CARTO voyager) tiles and MutationObserver to swap MapLibre styles on theme toggle. Make SVG demo map theme-aware (ocean, continents, graticule, pins). Fix mobile layout with calc(100vh) sizing instead of fixed heights. Remove hardcoded theme: "dark" from mod.ts renderShell calls. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
53af7fc057
commit
1f6b019dbf
|
|
@ -31,6 +31,19 @@ const DARK_STYLE = {
|
||||||
layers: [{ id: "carto", type: "raster", source: "carto" }],
|
layers: [{ id: "carto", type: "raster", source: "carto" }],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const LIGHT_STYLE = {
|
||||||
|
version: 8,
|
||||||
|
sources: {
|
||||||
|
carto: {
|
||||||
|
type: "raster",
|
||||||
|
tiles: ["https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}@2x.png"],
|
||||||
|
tileSize: 256,
|
||||||
|
attribution: '© <a href="https://carto.com/">CARTO</a> © <a href="https://www.openstreetmap.org/copyright">OSM</a>',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
layers: [{ id: "carto", type: "raster", source: "carto" }],
|
||||||
|
};
|
||||||
|
|
||||||
const PARTICIPANT_COLORS = ["#ef4444", "#f59e0b", "#22c55e", "#3b82f6", "#8b5cf6", "#ec4899", "#14b8a6", "#f97316"];
|
const PARTICIPANT_COLORS = ["#ef4444", "#f59e0b", "#22c55e", "#3b82f6", "#8b5cf6", "#ec4899", "#14b8a6", "#f97316"];
|
||||||
const EMOJIS = ["\u{1F9ED}", "\u{1F30D}", "\u{1F680}", "\u{1F308}", "\u{2B50}", "\u{1F525}", "\u{1F33F}", "\u{1F30A}", "\u{26A1}", "\u{1F48E}"];
|
const EMOJIS = ["\u{1F9ED}", "\u{1F30D}", "\u{1F680}", "\u{1F308}", "\u{2B50}", "\u{1F525}", "\u{1F33F}", "\u{1F30A}", "\u{26A1}", "\u{1F48E}"];
|
||||||
|
|
||||||
|
|
@ -74,6 +87,13 @@ class FolkMapViewer extends HTMLElement {
|
||||||
private watchId: number | null = null;
|
private watchId: number | null = null;
|
||||||
private pushManager: MapPushManager | null = null;
|
private pushManager: MapPushManager | null = null;
|
||||||
private thumbnailTimer: ReturnType<typeof setTimeout> | null = null;
|
private thumbnailTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
private _themeObserver: MutationObserver | null = null;
|
||||||
|
|
||||||
|
private isDarkTheme(): boolean {
|
||||||
|
const theme = document.documentElement.getAttribute("data-theme");
|
||||||
|
if (theme) return theme === "dark";
|
||||||
|
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
@ -99,6 +119,10 @@ class FolkMapViewer extends HTMLElement {
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
this.leaveRoom();
|
this.leaveRoom();
|
||||||
|
if (this._themeObserver) {
|
||||||
|
this._themeObserver.disconnect();
|
||||||
|
this._themeObserver = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── User profile ────────────────────────────────────────────
|
// ─── User profile ────────────────────────────────────────────
|
||||||
|
|
@ -139,6 +163,10 @@ class FolkMapViewer extends HTMLElement {
|
||||||
this.view = "map";
|
this.view = "map";
|
||||||
this.room = "cosmolocal-providers";
|
this.room = "cosmolocal-providers";
|
||||||
this.syncStatus = "connected";
|
this.syncStatus = "connected";
|
||||||
|
// Re-render demo on theme change
|
||||||
|
this._themeObserver = new MutationObserver(() => this.renderDemo());
|
||||||
|
this._themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
|
||||||
|
|
||||||
this.providers = [
|
this.providers = [
|
||||||
{
|
{
|
||||||
name: "Radiant Hall Press", city: "Pittsburgh", country: "USA",
|
name: "Radiant Hall Press", city: "Pittsburgh", country: "USA",
|
||||||
|
|
@ -195,6 +223,18 @@ class FolkMapViewer extends HTMLElement {
|
||||||
private renderDemo() {
|
private renderDemo() {
|
||||||
const W = 900;
|
const W = 900;
|
||||||
const H = 460;
|
const H = 460;
|
||||||
|
const dark = this.isDarkTheme();
|
||||||
|
|
||||||
|
// Theme-aware SVG colors (can't use CSS vars in SVG fill/stroke)
|
||||||
|
const oceanStop1 = dark ? "#0f1b33" : "#d4e5f7";
|
||||||
|
const oceanStop2 = dark ? "#060d1a" : "#e8f0f8";
|
||||||
|
const pinStroke = dark ? "#0f172a" : "#f5f5f0";
|
||||||
|
const continentFill = dark ? "#162236" : "#c8d8c0";
|
||||||
|
const continentStroke = dark ? "#1e3050" : "#a0b898";
|
||||||
|
const graticuleLine = dark ? "#1a2744" : "#c0d0e0";
|
||||||
|
const graticuleStrong = dark ? "#1e3050" : "#a8b8c8";
|
||||||
|
const cityColor = dark ? "#64748b" : "#6b7280";
|
||||||
|
const coordColor = dark ? "#4a5568" : "#6b7280";
|
||||||
|
|
||||||
const px = (lng: number) => ((lng + 180) / 360) * W;
|
const px = (lng: number) => ((lng + 180) / 360) * W;
|
||||||
const py = (lat: number) => ((90 - lat) / 180) * H;
|
const py = (lat: number) => ((90 - lat) / 180) * H;
|
||||||
|
|
@ -253,10 +293,10 @@ class FolkMapViewer extends HTMLElement {
|
||||||
<animate attributeName="opacity" values="0.5;0;0.5" dur="3s" repeatCount="indefinite" />
|
<animate attributeName="opacity" values="0.5;0;0.5" dur="3s" repeatCount="indefinite" />
|
||||||
</circle>
|
</circle>
|
||||||
${isSelected ? `<circle cx="${x}" cy="${y}" r="18" fill="none" stroke="${p.color}" stroke-width="2" opacity="0.6" />` : ""}
|
${isSelected ? `<circle cx="${x}" cy="${y}" r="18" fill="none" stroke="${p.color}" stroke-width="2" opacity="0.6" />` : ""}
|
||||||
<path d="M${x},${y - 2} c0,-7 -6,-12 -6,-16 a6,6 0 1,1 12,0 c0,4 -6,9 -6,16z" fill="${p.color}" stroke="#0f172a" stroke-width="0.8" opacity="0.92" />
|
<path d="M${x},${y - 2} c0,-7 -6,-12 -6,-16 a6,6 0 1,1 12,0 c0,4 -6,9 -6,16z" fill="${p.color}" stroke="${pinStroke}" stroke-width="0.8" opacity="0.92" />
|
||||||
<circle cx="${x}" cy="${y - 14}" r="2.5" fill="#fff" opacity="0.85" />
|
<circle cx="${x}" cy="${y - 14}" r="2.5" fill="#fff" opacity="0.85" />
|
||||||
<text x="${x + lx}" y="${y + ly}" fill="${p.color}" font-size="10" font-weight="600" font-family="system-ui,sans-serif" opacity="0.9">${this.esc(p.name)}</text>
|
<text x="${x + lx}" y="${y + ly}" fill="${p.color}" font-size="10" font-weight="600" font-family="system-ui,sans-serif" opacity="0.9">${this.esc(p.name)}</text>
|
||||||
<text x="${x + lx}" y="${y + ly + 12}" fill="#64748b" font-size="8.5" font-family="system-ui,sans-serif">${this.esc(p.city)}, ${this.esc(p.country)}</text>
|
<text x="${x + lx}" y="${y + ly + 12}" fill="${cityColor}" font-size="8.5" font-family="system-ui,sans-serif">${this.esc(p.city)}, ${this.esc(p.country)}</text>
|
||||||
</g>
|
</g>
|
||||||
`;
|
`;
|
||||||
}).join("");
|
}).join("");
|
||||||
|
|
@ -266,8 +306,8 @@ class FolkMapViewer extends HTMLElement {
|
||||||
<div class="legend-item ${this.selectedProvider === i ? "selected" : ""}" data-legend="${i}">
|
<div class="legend-item ${this.selectedProvider === i ? "selected" : ""}" data-legend="${i}">
|
||||||
<div style="width:10px;height:10px;border-radius:50%;background:${p.color};flex-shrink:0;box-shadow:0 0 6px ${p.color}40;"></div>
|
<div style="width:10px;height:10px;border-radius:50%;background:${p.color};flex-shrink:0;box-shadow:0 0 6px ${p.color}40;"></div>
|
||||||
<div style="flex:1;min-width:0">
|
<div style="flex:1;min-width:0">
|
||||||
<span style="font-weight:600;font-size:13px;color:#e2e8f0;">${this.esc(p.name)}</span>
|
<span style="font-weight:600;font-size:13px;color:var(--rs-text-primary);">${this.esc(p.name)}</span>
|
||||||
<span style="font-size:12px;color:#64748b;margin-left:8px;">${this.esc(p.city)}, ${this.esc(p.country)}</span>
|
<span style="font-size:12px;color:var(--rs-text-muted);margin-left:8px;">${this.esc(p.city)}, ${this.esc(p.country)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join("");
|
`).join("");
|
||||||
|
|
@ -281,16 +321,16 @@ class FolkMapViewer extends HTMLElement {
|
||||||
<div class="detail-header">
|
<div class="detail-header">
|
||||||
<div style="width:14px;height:14px;border-radius:50%;background:${sp.color};flex-shrink:0;box-shadow:0 0 8px ${sp.color}60;"></div>
|
<div style="width:14px;height:14px;border-radius:50%;background:${sp.color};flex-shrink:0;box-shadow:0 0 8px ${sp.color}60;"></div>
|
||||||
<div style="flex:1">
|
<div style="flex:1">
|
||||||
<div style="font-size:15px;font-weight:600;color:#e2e8f0;">${this.esc(sp.name)}</div>
|
<div style="font-size:15px;font-weight:600;color:var(--rs-text-primary);">${this.esc(sp.name)}</div>
|
||||||
<div style="font-size:12px;color:#94a3b8;">${this.esc(sp.city)}, ${this.esc(sp.country)}</div>
|
<div style="font-size:12px;color:var(--rs-text-secondary);">${this.esc(sp.city)}, ${this.esc(sp.country)}</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="detail-close" id="detail-close">\u2715</button>
|
<button class="detail-close" id="detail-close">\u2715</button>
|
||||||
</div>
|
</div>
|
||||||
<p style="font-size:13px;color:#94a3b8;line-height:1.5;margin:10px 0;">${this.esc(sp.desc)}</p>
|
<p style="font-size:13px;color:var(--rs-text-secondary);line-height:1.5;margin:10px 0;">${this.esc(sp.desc)}</p>
|
||||||
<div class="detail-tags">
|
<div class="detail-tags">
|
||||||
${sp.specialties.map(s => `<span class="detail-tag" style="border-color:${sp.color}40;color:${sp.color}">${this.esc(s)}</span>`).join("")}
|
${sp.specialties.map(s => `<span class="detail-tag" style="border-color:${sp.color}40;color:${sp.color}">${this.esc(s)}</span>`).join("")}
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size:11px;color:#4a5568;margin-top:10px;font-family:monospace;">
|
<div style="font-size:11px;color:var(--rs-text-muted);margin-top:10px;font-family:monospace;">
|
||||||
${sp.lat.toFixed(4)}\u00B0N, ${Math.abs(sp.lng).toFixed(4)}\u00B0${sp.lng >= 0 ? "E" : "W"}
|
${sp.lat.toFixed(4)}\u00B0N, ${Math.abs(sp.lng).toFixed(4)}\u00B0${sp.lng >= 0 ? "E" : "W"}
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:8px;margin-top:12px;">
|
<div style="display:flex;gap:8px;margin-top:12px;">
|
||||||
|
|
@ -301,15 +341,15 @@ class FolkMapViewer extends HTMLElement {
|
||||||
|
|
||||||
this.shadow.innerHTML = `
|
this.shadow.innerHTML = `
|
||||||
<style>
|
<style>
|
||||||
:host { display: block; font-family: system-ui, -apple-system, sans-serif; color: #e0e0e0; }
|
:host { display: block; font-family: system-ui, -apple-system, sans-serif; color: var(--rs-text-primary); }
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
.demo-nav {
|
.demo-nav {
|
||||||
display: flex; gap: 8px; margin-bottom: 16px; align-items: center; min-height: 36px; flex-wrap: wrap;
|
display: flex; gap: 8px; margin-bottom: 16px; align-items: center; min-height: 36px; flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
.demo-nav__title { font-size: 15px; font-weight: 600; flex: 1; color: #e2e8f0; min-width: 140px; }
|
.demo-nav__title { font-size: 15px; font-weight: 600; flex: 1; color: var(--rs-text-primary); min-width: 140px; }
|
||||||
.demo-nav__badge {
|
.demo-nav__badge {
|
||||||
display: inline-flex; align-items: center; gap: 5px;
|
display: inline-flex; align-items: center; gap: 5px;
|
||||||
font-size: 11px; color: #94a3b8; background: rgba(16,185,129,0.1);
|
font-size: 11px; color: var(--rs-text-secondary); background: rgba(16,185,129,0.1);
|
||||||
border: 1px solid rgba(16,185,129,0.2); border-radius: 20px; padding: 3px 10px;
|
border: 1px solid rgba(16,185,129,0.2); border-radius: 20px; padding: 3px 10px;
|
||||||
}
|
}
|
||||||
.demo-nav__badge .dot { width: 6px; height: 6px; border-radius: 50%; background: #22c55e; }
|
.demo-nav__badge .dot { width: 6px; height: 6px; border-radius: 50%; background: #22c55e; }
|
||||||
|
|
@ -319,29 +359,29 @@ class FolkMapViewer extends HTMLElement {
|
||||||
display: flex; gap: 4px; align-items: center;
|
display: flex; gap: 4px; align-items: center;
|
||||||
}
|
}
|
||||||
.zoom-btn {
|
.zoom-btn {
|
||||||
width: 28px; height: 28px; border-radius: 6px; border: 1px solid #333;
|
width: 28px; height: 28px; border-radius: 6px; border: 1px solid var(--rs-border);
|
||||||
background: #16161e; color: #94a3b8; cursor: pointer; font-size: 16px;
|
background: var(--rs-bg-surface); color: var(--rs-text-secondary); cursor: pointer; font-size: 16px;
|
||||||
display: flex; align-items: center; justify-content: center; transition: all 0.15s;
|
display: flex; align-items: center; justify-content: center; transition: all 0.15s;
|
||||||
}
|
}
|
||||||
.zoom-btn:hover { border-color: #555; color: #e2e8f0; }
|
.zoom-btn:hover { border-color: var(--rs-border-strong); color: var(--rs-text-primary); }
|
||||||
.zoom-label { font-size: 10px; color: #4a5568; font-variant-numeric: tabular-nums; min-width: 32px; text-align: center; }
|
.zoom-label { font-size: 10px; color: var(--rs-text-muted); font-variant-numeric: tabular-nums; min-width: 32px; text-align: center; }
|
||||||
|
|
||||||
.search-bar { display: flex; gap: 8px; margin-bottom: 12px; align-items: center; }
|
.search-bar { display: flex; gap: 8px; margin-bottom: 12px; align-items: center; }
|
||||||
.search-input {
|
.search-input {
|
||||||
flex: 1; border: 1px solid #1e293b; border-radius: 8px; padding: 8px 12px;
|
flex: 1; border: 1px solid var(--rs-border); border-radius: 8px; padding: 8px 12px;
|
||||||
background: #0c1221; color: #e0e0e0; font-size: 13px; outline: none;
|
background: var(--rs-input-bg); color: var(--rs-text-primary); font-size: 13px; outline: none;
|
||||||
}
|
}
|
||||||
.search-input:focus { border-color: #6366f1; }
|
.search-input:focus { border-color: #6366f1; }
|
||||||
.search-input::placeholder { color: #4a5568; }
|
.search-input::placeholder { color: var(--rs-text-muted); }
|
||||||
.geo-btn {
|
.geo-btn {
|
||||||
padding: 8px 14px; border-radius: 8px; border: 1px solid #1e293b;
|
padding: 8px 14px; border-radius: 8px; border: 1px solid var(--rs-border);
|
||||||
background: #0c1221; color: #94a3b8; cursor: pointer; font-size: 12px; white-space: nowrap;
|
background: var(--rs-input-bg); color: var(--rs-text-secondary); cursor: pointer; font-size: 12px; white-space: nowrap;
|
||||||
}
|
}
|
||||||
.geo-btn:hover { border-color: #334155; color: #e2e8f0; }
|
.geo-btn:hover { border-color: var(--rs-border-strong); color: var(--rs-text-primary); }
|
||||||
.geo-btn.active { border-color: #22c55e; color: #22c55e; }
|
.geo-btn.active { border-color: #22c55e; color: #22c55e; }
|
||||||
|
|
||||||
.map-wrap {
|
.map-wrap {
|
||||||
width: 100%; border-radius: 12px; background: #0c1221; border: 1px solid #1e293b;
|
width: 100%; border-radius: 12px; background: var(--rs-bg-surface-sunken); border: 1px solid var(--rs-border);
|
||||||
overflow: hidden; position: relative; cursor: grab;
|
overflow: hidden; position: relative; cursor: grab;
|
||||||
}
|
}
|
||||||
.map-wrap.dragging { cursor: grabbing; }
|
.map-wrap.dragging { cursor: grabbing; }
|
||||||
|
|
@ -351,22 +391,22 @@ class FolkMapViewer extends HTMLElement {
|
||||||
.pin-group { cursor: pointer; }
|
.pin-group { cursor: pointer; }
|
||||||
.tooltip {
|
.tooltip {
|
||||||
position: absolute; pointer-events: none; opacity: 0; transition: opacity 0.15s;
|
position: absolute; pointer-events: none; opacity: 0; transition: opacity 0.15s;
|
||||||
background: #1e293b; border: 1px solid #334155; border-radius: 8px;
|
background: var(--rs-bg-surface); border: 1px solid var(--rs-border-strong); border-radius: 8px;
|
||||||
padding: 8px 12px; font-size: 12px; color: #e2e8f0; white-space: nowrap;
|
padding: 8px 12px; font-size: 12px; color: var(--rs-text-primary); white-space: nowrap;
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.4); z-index: 10;
|
box-shadow: 0 4px 12px rgba(0,0,0,0.4); z-index: 10;
|
||||||
}
|
}
|
||||||
.tooltip.visible { opacity: 1; }
|
.tooltip.visible { opacity: 1; }
|
||||||
.tooltip strong { display: block; margin-bottom: 2px; }
|
.tooltip strong { display: block; margin-bottom: 2px; }
|
||||||
.tooltip .city { color: #94a3b8; font-size: 11px; }
|
.tooltip .city { color: var(--rs-text-secondary); font-size: 11px; }
|
||||||
.tooltip .coords { color: #64748b; font-size: 10px; font-family: monospace; }
|
.tooltip .coords { color: var(--rs-text-muted); font-size: 10px; font-family: monospace; }
|
||||||
|
|
||||||
/* Legend */
|
/* Legend */
|
||||||
.legend {
|
.legend {
|
||||||
background: rgba(15,23,42,0.6); border: 1px solid #1e293b; border-radius: 10px;
|
background: var(--rs-glass-bg); border: 1px solid var(--rs-border); border-radius: 10px;
|
||||||
padding: 16px; margin-top: 16px;
|
padding: 16px; margin-top: 16px;
|
||||||
}
|
}
|
||||||
.legend-title {
|
.legend-title {
|
||||||
font-size: 12px; font-weight: 600; color: #94a3b8;
|
font-size: 12px; font-weight: 600; color: var(--rs-text-secondary);
|
||||||
text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 8px;
|
text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
.legend-item {
|
.legend-item {
|
||||||
|
|
@ -378,16 +418,16 @@ class FolkMapViewer extends HTMLElement {
|
||||||
|
|
||||||
/* Detail panel */
|
/* Detail panel */
|
||||||
.detail-panel {
|
.detail-panel {
|
||||||
background: #1a1a2e; border: 1px solid #334155; border-radius: 10px;
|
background: var(--rs-bg-surface); border: 1px solid var(--rs-border-strong); border-radius: 10px;
|
||||||
padding: 16px; margin-top: 12px;
|
padding: 16px; margin-top: 12px;
|
||||||
}
|
}
|
||||||
.detail-header { display: flex; align-items: center; gap: 10px; }
|
.detail-header { display: flex; align-items: center; gap: 10px; }
|
||||||
.detail-close { background: none; border: none; color: #64748b; font-size: 16px; cursor: pointer; padding: 4px; }
|
.detail-close { background: none; border: none; color: var(--rs-text-muted); font-size: 16px; cursor: pointer; padding: 4px; }
|
||||||
.detail-close:hover { color: #e2e8f0; }
|
.detail-close:hover { color: var(--rs-text-primary); }
|
||||||
.detail-tags { display: flex; gap: 6px; flex-wrap: wrap; }
|
.detail-tags { display: flex; gap: 6px; flex-wrap: wrap; }
|
||||||
.detail-tag {
|
.detail-tag {
|
||||||
font-size: 10px; padding: 3px 8px; border-radius: 10px;
|
font-size: 10px; padding: 3px 8px; border-radius: 10px;
|
||||||
border: 1px solid #333; font-weight: 500;
|
border: 1px solid var(--rs-border); font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Feature highlights row */
|
/* Feature highlights row */
|
||||||
|
|
@ -396,12 +436,12 @@ class FolkMapViewer extends HTMLElement {
|
||||||
gap: 10px; margin-top: 16px;
|
gap: 10px; margin-top: 16px;
|
||||||
}
|
}
|
||||||
.feat {
|
.feat {
|
||||||
background: rgba(15,23,42,0.4); border: 1px solid #1e293b; border-radius: 10px;
|
background: var(--rs-glass-bg); border: 1px solid var(--rs-border); border-radius: 10px;
|
||||||
padding: 12px; text-align: center;
|
padding: 12px; text-align: center;
|
||||||
}
|
}
|
||||||
.feat-icon { font-size: 20px; margin-bottom: 4px; }
|
.feat-icon { font-size: 20px; margin-bottom: 4px; }
|
||||||
.feat-label { font-size: 12px; font-weight: 600; color: #e2e8f0; }
|
.feat-label { font-size: 12px; font-weight: 600; color: var(--rs-text-primary); }
|
||||||
.feat-desc { font-size: 10.5px; color: #64748b; margin-top: 2px; line-height: 1.4; }
|
.feat-desc { font-size: 10.5px; color: var(--rs-text-muted); margin-top: 2px; line-height: 1.4; }
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.features { grid-template-columns: repeat(2, 1fr); }
|
.features { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
|
@ -430,8 +470,8 @@ class FolkMapViewer extends HTMLElement {
|
||||||
<svg class="map-svg" id="map-svg" viewBox="${this.vbX} ${this.vbY} ${this.vbW} ${this.vbH}" xmlns="http://www.w3.org/2000/svg">
|
<svg class="map-svg" id="map-svg" viewBox="${this.vbX} ${this.vbY} ${this.vbW} ${this.vbH}" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
<radialGradient id="ocean" cx="50%" cy="40%" r="70%">
|
<radialGradient id="ocean" cx="50%" cy="40%" r="70%">
|
||||||
<stop offset="0%" stop-color="#0f1b33" />
|
<stop offset="0%" stop-color="${oceanStop1}" />
|
||||||
<stop offset="100%" stop-color="#060d1a" />
|
<stop offset="100%" stop-color="${oceanStop2}" />
|
||||||
</radialGradient>
|
</radialGradient>
|
||||||
</defs>
|
</defs>
|
||||||
|
|
||||||
|
|
@ -439,10 +479,10 @@ class FolkMapViewer extends HTMLElement {
|
||||||
<rect x="-200" y="-200" width="${W + 400}" height="${H + 400}" fill="url(#ocean)" />
|
<rect x="-200" y="-200" width="${W + 400}" height="${H + 400}" fill="url(#ocean)" />
|
||||||
|
|
||||||
<!-- Graticule -->
|
<!-- Graticule -->
|
||||||
${this.graticule(W, H)}
|
${this.graticule(W, H, graticuleLine, graticuleStrong)}
|
||||||
|
|
||||||
<!-- Continents -->
|
<!-- Continents -->
|
||||||
${this.continents(W, H)}
|
${this.continents(W, H, continentFill, continentStroke)}
|
||||||
|
|
||||||
<!-- Connection arcs -->
|
<!-- Connection arcs -->
|
||||||
${arcs}
|
${arcs}
|
||||||
|
|
@ -525,34 +565,31 @@ class FolkMapViewer extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generate SVG graticule lines */
|
/** Generate SVG graticule lines */
|
||||||
private graticule(W: number, H: number): string {
|
private graticule(W: number, H: number, lineColor: string, strongColor: string): string {
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
for (let lat = -60; lat <= 60; lat += 30) {
|
for (let lat = -60; lat <= 60; lat += 30) {
|
||||||
const y = ((90 - lat) / 180) * H;
|
const y = ((90 - lat) / 180) * H;
|
||||||
lines.push(`<line x1="-200" y1="${y}" x2="${W + 200}" y2="${y}" stroke="#1a2744" stroke-width="0.5" stroke-dasharray="3,5" />`);
|
lines.push(`<line x1="-200" y1="${y}" x2="${W + 200}" y2="${y}" stroke="${lineColor}" stroke-width="0.5" stroke-dasharray="3,5" />`);
|
||||||
}
|
}
|
||||||
for (let lng = -150; lng <= 180; lng += 30) {
|
for (let lng = -150; lng <= 180; lng += 30) {
|
||||||
const x = ((lng + 180) / 360) * W;
|
const x = ((lng + 180) / 360) * W;
|
||||||
lines.push(`<line x1="${x}" y1="-200" x2="${x}" y2="${H + 200}" stroke="#1a2744" stroke-width="0.5" stroke-dasharray="3,5" />`);
|
lines.push(`<line x1="${x}" y1="-200" x2="${x}" y2="${H + 200}" stroke="${lineColor}" stroke-width="0.5" stroke-dasharray="3,5" />`);
|
||||||
}
|
}
|
||||||
const eq = ((90 - 0) / 180) * H;
|
const eq = ((90 - 0) / 180) * H;
|
||||||
const pm = ((0 + 180) / 360) * W;
|
const pm = ((0 + 180) / 360) * W;
|
||||||
lines.push(`<line x1="-200" y1="${eq}" x2="${W + 200}" y2="${eq}" stroke="#1e3050" stroke-width="0.7" stroke-dasharray="4,3" />`);
|
lines.push(`<line x1="-200" y1="${eq}" x2="${W + 200}" y2="${eq}" stroke="${strongColor}" stroke-width="0.7" stroke-dasharray="4,3" />`);
|
||||||
lines.push(`<line x1="${pm}" y1="-200" x2="${pm}" y2="${H + 200}" stroke="#1e3050" stroke-width="0.7" stroke-dasharray="4,3" />`);
|
lines.push(`<line x1="${pm}" y1="-200" x2="${pm}" y2="${H + 200}" stroke="${strongColor}" stroke-width="0.7" stroke-dasharray="4,3" />`);
|
||||||
return lines.join("\n");
|
return lines.join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Simplified continent outlines using equirectangular projection */
|
/** Simplified continent outlines using equirectangular projection */
|
||||||
private continents(W: number, H: number): string {
|
private continents(W: number, H: number, fill: string, stroke: string): string {
|
||||||
const p = (lat: number, lng: number) => {
|
const p = (lat: number, lng: number) => {
|
||||||
const x = ((lng + 180) / 360) * W;
|
const x = ((lng + 180) / 360) * W;
|
||||||
const y = ((90 - lat) / 180) * H;
|
const y = ((90 - lat) / 180) * H;
|
||||||
return `${x.toFixed(1)},${y.toFixed(1)}`;
|
return `${x.toFixed(1)},${y.toFixed(1)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fill = "#162236";
|
|
||||||
const stroke = "#1e3050";
|
|
||||||
|
|
||||||
const continents = [
|
const continents = [
|
||||||
// North America
|
// North America
|
||||||
`M${p(50, -130)} L${p(60, -130)} L${p(65, -120)} L${p(70, -100)} L${p(72, -80)}
|
`M${p(50, -130)} L${p(60, -130)} L${p(65, -120)} L${p(70, -100)} L${p(72, -80)}
|
||||||
|
|
@ -926,6 +963,10 @@ class FolkMapViewer extends HTMLElement {
|
||||||
this.map.remove();
|
this.map.remove();
|
||||||
this.map = null;
|
this.map = null;
|
||||||
}
|
}
|
||||||
|
if (this._themeObserver) {
|
||||||
|
this._themeObserver.disconnect();
|
||||||
|
this._themeObserver = null;
|
||||||
|
}
|
||||||
if (this.thumbnailTimer) clearTimeout(this.thumbnailTimer);
|
if (this.thumbnailTimer) clearTimeout(this.thumbnailTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -953,7 +994,7 @@ class FolkMapViewer extends HTMLElement {
|
||||||
|
|
||||||
this.map = new (window as any).maplibregl.Map({
|
this.map = new (window as any).maplibregl.Map({
|
||||||
container,
|
container,
|
||||||
style: DARK_STYLE,
|
style: this.isDarkTheme() ? DARK_STYLE : LIGHT_STYLE,
|
||||||
center: [0, 20],
|
center: [0, 20],
|
||||||
zoom: 2,
|
zoom: 2,
|
||||||
preserveDrawingBuffer: true,
|
preserveDrawingBuffer: true,
|
||||||
|
|
@ -965,6 +1006,13 @@ class FolkMapViewer extends HTMLElement {
|
||||||
trackUserLocation: false,
|
trackUserLocation: false,
|
||||||
}), "top-right");
|
}), "top-right");
|
||||||
|
|
||||||
|
// Theme observer — swap map tiles on toggle
|
||||||
|
this._themeObserver = new MutationObserver(() => {
|
||||||
|
this.map?.setStyle(this.isDarkTheme() ? DARK_STYLE : LIGHT_STYLE);
|
||||||
|
this.updateMarkerTheme();
|
||||||
|
});
|
||||||
|
this._themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
|
||||||
|
|
||||||
// Debounced thumbnail capture on moveend
|
// Debounced thumbnail capture on moveend
|
||||||
this.map.on("moveend", () => {
|
this.map.on("moveend", () => {
|
||||||
if (this.thumbnailTimer) clearTimeout(this.thumbnailTimer);
|
if (this.thumbnailTimer) clearTimeout(this.thumbnailTimer);
|
||||||
|
|
@ -1034,11 +1082,14 @@ class FolkMapViewer extends HTMLElement {
|
||||||
if (this.participantMarkers.has(id)) {
|
if (this.participantMarkers.has(id)) {
|
||||||
this.participantMarkers.get(id).setLngLat(lngLat);
|
this.participantMarkers.get(id).setLngLat(lngLat);
|
||||||
} else {
|
} else {
|
||||||
|
const dark = this.isDarkTheme();
|
||||||
|
const markerBg = dark ? '#1a1a2e' : '#fafaf7';
|
||||||
|
const textShadow = dark ? 'rgba(0,0,0,0.8)' : 'rgba(0,0,0,0.3)';
|
||||||
const el = document.createElement("div");
|
const el = document.createElement("div");
|
||||||
el.className = "participant-marker";
|
el.className = "participant-marker";
|
||||||
el.style.cssText = `
|
el.style.cssText = `
|
||||||
width: 36px; height: 36px; border-radius: 50%;
|
width: 36px; height: 36px; border-radius: 50%;
|
||||||
border: 3px solid ${p.color}; background: #1a1a2e;
|
border: 3px solid ${p.color}; background: ${markerBg};
|
||||||
display: flex; align-items: center; justify-content: center;
|
display: flex; align-items: center; justify-content: center;
|
||||||
font-size: 18px; cursor: pointer; position: relative;
|
font-size: 18px; cursor: pointer; position: relative;
|
||||||
box-shadow: 0 0 8px ${p.color}60;
|
box-shadow: 0 0 8px ${p.color}60;
|
||||||
|
|
@ -1048,10 +1099,11 @@ class FolkMapViewer extends HTMLElement {
|
||||||
|
|
||||||
// Name label below
|
// Name label below
|
||||||
const label = document.createElement("div");
|
const label = document.createElement("div");
|
||||||
|
label.className = "marker-label";
|
||||||
label.style.cssText = `
|
label.style.cssText = `
|
||||||
position: absolute; bottom: -18px; left: 50%; transform: translateX(-50%);
|
position: absolute; bottom: -18px; left: 50%; transform: translateX(-50%);
|
||||||
font-size: 10px; color: ${p.color}; font-weight: 600;
|
font-size: 10px; color: ${p.color}; font-weight: 600;
|
||||||
white-space: nowrap; text-shadow: 0 1px 3px rgba(0,0,0,0.8);
|
white-space: nowrap; text-shadow: 0 1px 3px ${textShadow};
|
||||||
font-family: system-ui, sans-serif;
|
font-family: system-ui, sans-serif;
|
||||||
`;
|
`;
|
||||||
label.textContent = p.name;
|
label.textContent = p.name;
|
||||||
|
|
@ -1129,6 +1181,19 @@ class FolkMapViewer extends HTMLElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateMarkerTheme() {
|
||||||
|
const dark = this.isDarkTheme();
|
||||||
|
const markerBg = dark ? '#1a1a2e' : '#fafaf7';
|
||||||
|
const textShadow = dark ? 'rgba(0,0,0,0.8)' : 'rgba(0,0,0,0.3)';
|
||||||
|
for (const marker of this.participantMarkers.values()) {
|
||||||
|
const el = marker.getElement?.();
|
||||||
|
if (!el) continue;
|
||||||
|
el.style.background = markerBg;
|
||||||
|
const label = el.querySelector('.marker-label') as HTMLElement | null;
|
||||||
|
if (label) label.style.textShadow = `0 1px 3px ${textShadow}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Location sharing ────────────────────────────────────────
|
// ─── Location sharing ────────────────────────────────────────
|
||||||
|
|
||||||
private toggleLocationSharing() {
|
private toggleLocationSharing() {
|
||||||
|
|
@ -1247,13 +1312,13 @@ class FolkMapViewer extends HTMLElement {
|
||||||
private render() {
|
private render() {
|
||||||
this.shadow.innerHTML = `
|
this.shadow.innerHTML = `
|
||||||
<style>
|
<style>
|
||||||
:host { display: block; font-family: system-ui, -apple-system, sans-serif; color: #e0e0e0; }
|
:host { display: block; font-family: system-ui, -apple-system, sans-serif; color: var(--rs-text-primary); }
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
|
|
||||||
.rapp-nav { display: flex; gap: 8px; margin-bottom: 16px; align-items: center; min-height: 36px; }
|
.rapp-nav { display: flex; gap: 8px; margin-bottom: 16px; align-items: center; min-height: 36px; }
|
||||||
.rapp-nav__back { padding: 4px 10px; border-radius: 6px; border: 1px solid rgba(255,255,255,0.1); background: transparent; color: #94a3b8; cursor: pointer; font-size: 13px; }
|
.rapp-nav__back { padding: 4px 10px; border-radius: 6px; border: 1px solid var(--rs-border); background: transparent; color: var(--rs-text-secondary); cursor: pointer; font-size: 13px; }
|
||||||
.rapp-nav__back:hover { color: #e2e8f0; border-color: rgba(255,255,255,0.2); }
|
.rapp-nav__back:hover { color: var(--rs-text-primary); border-color: var(--rs-border-strong); }
|
||||||
.rapp-nav__title { font-size: 15px; font-weight: 600; flex: 1; color: #e2e8f0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
.rapp-nav__title { font-size: 15px; font-weight: 600; flex: 1; color: var(--rs-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
|
||||||
.status-dot {
|
.status-dot {
|
||||||
width: 8px; height: 8px; border-radius: 50%; display: inline-block;
|
width: 8px; height: 8px; border-radius: 50%; display: inline-block;
|
||||||
|
|
@ -1265,17 +1330,18 @@ class FolkMapViewer extends HTMLElement {
|
||||||
.rapp-nav__btn:hover { background: #6366f1; }
|
.rapp-nav__btn:hover { background: #6366f1; }
|
||||||
|
|
||||||
.room-card {
|
.room-card {
|
||||||
background: #1e1e2e; border: 1px solid #333; border-radius: 10px;
|
background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 10px;
|
||||||
padding: 16px; margin-bottom: 12px; cursor: pointer; transition: border-color 0.2s;
|
padding: 16px; margin-bottom: 12px; cursor: pointer; transition: border-color 0.2s;
|
||||||
display: flex; align-items: center; gap: 12px;
|
display: flex; align-items: center; gap: 12px;
|
||||||
}
|
}
|
||||||
.room-card:hover { border-color: #555; }
|
.room-card:hover { border-color: var(--rs-border-strong); }
|
||||||
.room-icon { font-size: 24px; }
|
.room-icon { font-size: 24px; }
|
||||||
.room-name { font-size: 15px; font-weight: 600; }
|
.room-name { font-size: 15px; font-weight: 600; }
|
||||||
|
|
||||||
.map-container {
|
.map-container {
|
||||||
width: 100%; height: 500px; border-radius: 10px;
|
width: 100%; height: calc(100vh - 220px); min-height: 300px; max-height: 700px;
|
||||||
background: #1a1a2e; border: 1px solid #333;
|
border-radius: 10px;
|
||||||
|
background: var(--rs-bg-surface-sunken); border: 1px solid var(--rs-border);
|
||||||
position: relative; overflow: hidden;
|
position: relative; overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1285,11 +1351,11 @@ class FolkMapViewer extends HTMLElement {
|
||||||
.map-main { flex: 1; min-width: 0; }
|
.map-main { flex: 1; min-width: 0; }
|
||||||
.map-sidebar {
|
.map-sidebar {
|
||||||
width: 220px; flex-shrink: 0;
|
width: 220px; flex-shrink: 0;
|
||||||
background: rgba(15,23,42,0.6); border: 1px solid #1e293b; border-radius: 10px;
|
background: var(--rs-glass-bg); border: 1px solid var(--rs-border); border-radius: 10px;
|
||||||
padding: 12px; max-height: 560px; overflow-y: auto;
|
padding: 12px; max-height: 560px; overflow-y: auto;
|
||||||
}
|
}
|
||||||
.sidebar-title {
|
.sidebar-title {
|
||||||
font-size: 11px; font-weight: 600; color: #94a3b8;
|
font-size: 11px; font-weight: 600; color: var(--rs-text-secondary);
|
||||||
text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 8px;
|
text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1297,10 +1363,10 @@ class FolkMapViewer extends HTMLElement {
|
||||||
display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap;
|
display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
.ctrl-btn {
|
.ctrl-btn {
|
||||||
padding: 8px 16px; border-radius: 8px; border: 1px solid #444;
|
padding: 8px 16px; border-radius: 8px; border: 1px solid var(--rs-border);
|
||||||
background: #1e1e2e; color: #ccc; cursor: pointer; font-size: 13px;
|
background: var(--rs-bg-surface); color: var(--rs-text-secondary); cursor: pointer; font-size: 13px;
|
||||||
}
|
}
|
||||||
.ctrl-btn:hover { border-color: #666; }
|
.ctrl-btn:hover { border-color: var(--rs-border-strong); }
|
||||||
.ctrl-btn.sharing {
|
.ctrl-btn.sharing {
|
||||||
border-color: #22c55e; color: #22c55e; animation: pulse 2s infinite;
|
border-color: #22c55e; color: #22c55e; animation: pulse 2s infinite;
|
||||||
}
|
}
|
||||||
|
|
@ -1310,21 +1376,21 @@ class FolkMapViewer extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
.share-link {
|
.share-link {
|
||||||
background: #1e1e2e; border: 1px solid #333; border-radius: 8px;
|
background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 8px;
|
||||||
padding: 12px; margin-top: 12px; font-family: monospace; font-size: 12px;
|
padding: 12px; margin-top: 12px; font-family: monospace; font-size: 12px;
|
||||||
color: #aaa; display: flex; align-items: center; gap: 8px;
|
color: var(--rs-text-secondary); display: flex; align-items: center; gap: 8px;
|
||||||
}
|
}
|
||||||
.share-link span { flex: 1; overflow: hidden; text-overflow: ellipsis; }
|
.share-link span { flex: 1; overflow: hidden; text-overflow: ellipsis; }
|
||||||
.copy-btn {
|
.copy-btn {
|
||||||
padding: 4px 10px; border-radius: 4px; border: 1px solid #444;
|
padding: 4px 10px; border-radius: 4px; border: 1px solid var(--rs-border);
|
||||||
background: #2a2a3e; color: #ccc; cursor: pointer; font-size: 11px;
|
background: var(--rs-btn-secondary-bg); color: var(--rs-text-secondary); cursor: pointer; font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty { text-align: center; color: #666; padding: 40px; }
|
.empty { text-align: center; color: var(--rs-text-muted); padding: 40px; }
|
||||||
|
|
||||||
/* Section labels */
|
/* Section labels */
|
||||||
.section-label {
|
.section-label {
|
||||||
font-size: 12px; font-weight: 600; color: #94a3b8;
|
font-size: 12px; font-weight: 600; color: var(--rs-text-secondary);
|
||||||
text-transform: uppercase; letter-spacing: 0.06em; margin: 20px 0 10px;
|
text-transform: uppercase; letter-spacing: 0.06em; margin: 20px 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1334,34 +1400,34 @@ class FolkMapViewer extends HTMLElement {
|
||||||
gap: 10px; margin-bottom: 16px;
|
gap: 10px; margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
.history-card {
|
.history-card {
|
||||||
background: #1e1e2e; border: 1px solid #333; border-radius: 10px;
|
background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 10px;
|
||||||
overflow: hidden; cursor: pointer; transition: border-color 0.2s;
|
overflow: hidden; cursor: pointer; transition: border-color 0.2s;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.history-card:hover { border-color: #555; }
|
.history-card:hover { border-color: var(--rs-border-strong); }
|
||||||
.history-thumb {
|
.history-thumb {
|
||||||
width: 100%; height: 90px; object-fit: cover; display: block;
|
width: 100%; height: 90px; object-fit: cover; display: block;
|
||||||
background: #0c1221;
|
background: var(--rs-bg-surface-sunken);
|
||||||
}
|
}
|
||||||
.history-thumb-placeholder {
|
.history-thumb-placeholder {
|
||||||
width: 100%; height: 90px; display: flex; align-items: center; justify-content: center;
|
width: 100%; height: 90px; display: flex; align-items: center; justify-content: center;
|
||||||
background: #0c1221; font-size: 32px;
|
background: var(--rs-bg-surface-sunken); font-size: 32px;
|
||||||
}
|
}
|
||||||
.history-info {
|
.history-info {
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
}
|
}
|
||||||
.history-name {
|
.history-name {
|
||||||
font-size: 13px; font-weight: 600; color: #e2e8f0;
|
font-size: 13px; font-weight: 600; color: var(--rs-text-primary);
|
||||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||||
}
|
}
|
||||||
.history-time {
|
.history-time {
|
||||||
font-size: 10px; color: #64748b; margin-top: 2px;
|
font-size: 10px; color: var(--rs-text-muted); margin-top: 2px;
|
||||||
}
|
}
|
||||||
.ping-btn {
|
.ping-btn {
|
||||||
position: absolute; top: 6px; right: 6px;
|
position: absolute; top: 6px; right: 6px;
|
||||||
width: 26px; height: 26px; border-radius: 50%;
|
width: 26px; height: 26px; border-radius: 50%;
|
||||||
background: rgba(15,23,42,0.8); border: 1px solid #333;
|
background: var(--rs-glass-bg); border: 1px solid var(--rs-border);
|
||||||
color: #94a3b8; cursor: pointer; font-size: 13px;
|
color: var(--rs-text-secondary); cursor: pointer; font-size: 13px;
|
||||||
display: flex; align-items: center; justify-content: center;
|
display: flex; align-items: center; justify-content: center;
|
||||||
opacity: 0; transition: opacity 0.15s;
|
opacity: 0; transition: opacity 0.15s;
|
||||||
}
|
}
|
||||||
|
|
@ -1369,7 +1435,7 @@ class FolkMapViewer extends HTMLElement {
|
||||||
.ping-btn:hover { border-color: #6366f1; color: #818cf8; }
|
.ping-btn:hover { border-color: #6366f1; color: #818cf8; }
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.map-container { height: 350px; }
|
.map-container { height: calc(100vh - 160px); min-height: 250px; max-height: none; }
|
||||||
.map-layout { flex-direction: column; }
|
.map-layout { flex-direction: column; }
|
||||||
.map-sidebar { width: 100%; max-height: 200px; }
|
.map-sidebar { width: 100%; max-height: 200px; }
|
||||||
.room-history-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
|
.room-history-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
|
||||||
|
|
@ -1408,7 +1474,7 @@ class FolkMapViewer extends HTMLElement {
|
||||||
<div class="rapp-nav">
|
<div class="rapp-nav">
|
||||||
<span class="rapp-nav__title">Map Rooms</span>
|
<span class="rapp-nav__title">Map Rooms</span>
|
||||||
<span class="status-dot ${this.syncStatus === "connected" ? "status-connected" : "status-disconnected"}"></span>
|
<span class="status-dot ${this.syncStatus === "connected" ? "status-connected" : "status-disconnected"}"></span>
|
||||||
<span style="font-size:12px;color:#888;margin-right:12px">${this.syncStatus === "connected" ? "Sync online" : "Sync offline"}</span>
|
<span style="font-size:12px;color:var(--rs-text-muted);margin-right:12px">${this.syncStatus === "connected" ? "Sync online" : "Sync offline"}</span>
|
||||||
<button class="rapp-nav__btn" id="create-room">+ New Room</button>
|
<button class="rapp-nav__btn" id="create-room">+ New Room</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
/* Maps module — dark theme */
|
/* Maps module — responsive layout */
|
||||||
folk-map-viewer {
|
folk-map-viewer {
|
||||||
display: block;
|
display: block;
|
||||||
min-height: 400px;
|
min-height: 0;
|
||||||
padding: 20px;
|
padding: 16px;
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
folk-map-viewer { padding: 8px; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,6 @@ routes.get("/", (c) => {
|
||||||
moduleId: "rmaps",
|
moduleId: "rmaps",
|
||||||
spaceSlug: space,
|
spaceSlug: space,
|
||||||
modules: getModuleInfoList(),
|
modules: getModuleInfoList(),
|
||||||
theme: "dark",
|
|
||||||
body: `<folk-map-viewer space="${space}"></folk-map-viewer>`,
|
body: `<folk-map-viewer space="${space}"></folk-map-viewer>`,
|
||||||
scripts: `<script type="module" src="/modules/rmaps/folk-map-viewer.js"></script>`,
|
scripts: `<script type="module" src="/modules/rmaps/folk-map-viewer.js"></script>`,
|
||||||
styles: `<link rel="stylesheet" href="/modules/rmaps/maps.css">`,
|
styles: `<link rel="stylesheet" href="/modules/rmaps/maps.css">`,
|
||||||
|
|
@ -157,7 +156,6 @@ routes.get("/:room", (c) => {
|
||||||
moduleId: "rmaps",
|
moduleId: "rmaps",
|
||||||
spaceSlug: space,
|
spaceSlug: space,
|
||||||
modules: getModuleInfoList(),
|
modules: getModuleInfoList(),
|
||||||
theme: "dark",
|
|
||||||
styles: `<link rel="stylesheet" href="/modules/rmaps/maps.css">`,
|
styles: `<link rel="stylesheet" href="/modules/rmaps/maps.css">`,
|
||||||
body: `<folk-map-viewer space="${space}" room="${room}"></folk-map-viewer>`,
|
body: `<folk-map-viewer space="${space}" room="${room}"></folk-map-viewer>`,
|
||||||
scripts: `<script type="module" src="/modules/rmaps/folk-map-viewer.js"></script>`,
|
scripts: `<script type="module" src="/modules/rmaps/folk-map-viewer.js"></script>`,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue