Skip to content

Commit 669e5c4

Browse files
Replace HTML string interpolation with safe DOM construction
Tower path popups and deadzone suggestion icons/popups used template literals with backend values interpolated into HTML. Now uses createElement/textContent per CLAUDE.md rules. Fixes meshtastic#50, meshtastic#51. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a4d1394 commit 669e5c4

File tree

1 file changed

+26
-21
lines changed

1 file changed

+26
-21
lines changed

src/store.ts

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,19 @@ const useStore = defineStore("store", {
217217
losText = path.has_los ? "Yes" : "No";
218218
}
219219
const distText = path.distance_km !== null ? `${path.distance_km.toFixed(1)} km` : "pending";
220-
polyline.bindPopup(`<b>Path Loss:</b> ${lossText}<br><b>LOS:</b> ${losText}<br><b>Distance:</b> ${distText}`);
220+
221+
const popupEl = document.createElement("div");
222+
const addLine = (label: string, value: string) => {
223+
const b = document.createElement("b");
224+
b.textContent = `${label}: `;
225+
popupEl.appendChild(b);
226+
popupEl.appendChild(document.createTextNode(value));
227+
popupEl.appendChild(document.createElement("br"));
228+
};
229+
addLine("Path Loss", lossText);
230+
addLine("LOS", losText);
231+
addLine("Distance", distText);
232+
polyline.bindPopup(popupEl);
221233

222234
polyline.addTo(this.map);
223235
this.towerPathLayers.push(polyline);
@@ -277,21 +289,12 @@ const useStore = defineStore("store", {
277289
this.deadzoneLayer = layer;
278290

279291
for (const suggestion of this.deadzoneAnalysis.suggestions) {
292+
const iconEl = document.createElement("div");
293+
iconEl.style.cssText =
294+
"background:#0d6efd;color:white;border-radius:50%;width:24px;height:24px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:bold;border:2px solid white;box-shadow:0 1px 4px rgba(0,0,0,0.4)";
295+
iconEl.textContent = String(suggestion.priority_rank);
280296
const icon = L.divIcon({
281-
html: `<div style="
282-
background: #0d6efd;
283-
color: white;
284-
border-radius: 50%;
285-
width: 24px;
286-
height: 24px;
287-
display: flex;
288-
align-items: center;
289-
justify-content: center;
290-
font-size: 12px;
291-
font-weight: bold;
292-
border: 2px solid white;
293-
box-shadow: 0 1px 4px rgba(0,0,0,0.4);
294-
">${suggestion.priority_rank}</div>`,
297+
html: iconEl.outerHTML,
295298
iconSize: [24, 24],
296299
iconAnchor: [12, 12],
297300
className: "",
@@ -316,14 +319,16 @@ const useStore = defineStore("store", {
316319
hr.style.margin = "4px 0";
317320
popupEl.appendChild(hr);
318321

319-
const coverage = document.createElement("span");
320-
coverage.innerHTML = `<b>Est. coverage:</b> ${suggestion.estimated_coverage_km2.toFixed(1)} km&sup2;`;
321-
popupEl.appendChild(coverage);
322+
const coverageLabel = document.createElement("b");
323+
coverageLabel.textContent = "Est. coverage: ";
324+
popupEl.appendChild(coverageLabel);
325+
popupEl.appendChild(document.createTextNode(`${suggestion.estimated_coverage_km2.toFixed(1)} km\u00B2`));
322326
popupEl.appendChild(document.createElement("br"));
323327

324-
const location = document.createElement("span");
325-
location.innerHTML = `<b>Location:</b> ${suggestion.lat.toFixed(4)}, ${suggestion.lon.toFixed(4)}`;
326-
popupEl.appendChild(location);
328+
const locationLabel = document.createElement("b");
329+
locationLabel.textContent = "Location: ";
330+
popupEl.appendChild(locationLabel);
331+
popupEl.appendChild(document.createTextNode(`${suggestion.lat.toFixed(4)}, ${suggestion.lon.toFixed(4)}`));
327332
popupEl.appendChild(document.createElement("br"));
328333

329334
const btn = document.createElement("button");

0 commit comments

Comments
 (0)