Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion public/live.js
Original file line number Diff line number Diff line change
Expand Up @@ -2373,7 +2373,7 @@
? window.makeRoleMarkerSVG(n.role, color, sizePx)
: '<svg width="' + sizePx + '" height="' + sizePx + '" viewBox="0 0 ' + sizePx + ' ' + sizePx +
'"><circle cx="' + (sizePx/2) + '" cy="' + (sizePx/2) + '" r="' + (sizePx/2 - 2) +
'" fill="' + color + '" stroke="#fff" stroke-width="2"/></svg>');
'" fill="' + color + '" stroke="#fff" stroke-width="1"/></svg>');

const icon = L.divIcon({
html: svgHtml,
Expand Down
12 changes: 6 additions & 6 deletions public/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@
let path;
switch (s.shape) {
case 'diamond':
path = `<polygon points="${c},2 ${size-2},${c} ${c},${size-2} 2,${c}" fill="${fillColor}" stroke="#fff" stroke-width="2"/>`;
path = `<polygon points="${c},2 ${size-2},${c} ${c},${size-2} 2,${c}" fill="${fillColor}" stroke="#fff" stroke-width="1"/>`;
break;
case 'square':
path = `<rect x="3" y="3" width="${size-6}" height="${size-6}" fill="${fillColor}" stroke="#fff" stroke-width="2"/>`;
path = `<rect x="3" y="3" width="${size-6}" height="${size-6}" fill="${fillColor}" stroke="#fff" stroke-width="1"/>`;
break;
case 'triangle':
path = `<polygon points="${c},2 ${size-2},${size-2} 2,${size-2}" fill="${fillColor}" stroke="#fff" stroke-width="2"/>`;
path = `<polygon points="${c},2 ${size-2},${size-2} 2,${size-2}" fill="${fillColor}" stroke="#fff" stroke-width="1"/>`;
break;
case 'hexagon': {
// #1293 — pointy-top hexagon for room servers
Expand All @@ -53,7 +53,7 @@
hpts += (c + hr * Math.cos(ha)).toFixed(2) + ',' +
(c + hr * Math.sin(ha)).toFixed(2) + ' ';
}
path = `<polygon points="${hpts.trim()}" fill="${fillColor}" stroke="#fff" stroke-width="2"/>`;
path = `<polygon points="${hpts.trim()}" fill="${fillColor}" stroke="#fff" stroke-width="1"/>`;
break;
}
case 'star': {
Expand All @@ -66,11 +66,11 @@
pts += `${cx + outer * Math.cos(aOuter)},${cy + outer * Math.sin(aOuter)} `;
pts += `${cx + inner * Math.cos(aInner)},${cy + inner * Math.sin(aInner)} `;
}
path = `<polygon points="${pts.trim()}" fill="${fillColor}" stroke="#fff" stroke-width="1.5"/>`;
path = `<polygon points="${pts.trim()}" fill="${fillColor}" stroke="#fff" stroke-width="1"/>`;
break;
}
default: // circle
path = `<circle cx="${c}" cy="${c}" r="${c-2}" fill="${fillColor}" stroke="#fff" stroke-width="2"/>`;
path = `<circle cx="${c}" cy="${c}" r="${c-2}" fill="${fillColor}" stroke="#fff" stroke-width="1"/>`;
}
// If this node is also an observer, add a small star overlay
let obsOverlay = '';
Expand Down
12 changes: 6 additions & 6 deletions public/roles.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,16 @@
switch (shape) {
case 'square':
path = '<rect x="3" y="3" width="' + (size - 6) + '" height="' + (size - 6) +
'" fill="' + fill + '" stroke="#fff" stroke-width="2"/>';
'" fill="' + fill + '" stroke="#fff" stroke-width="1"/>';
break;
case 'triangle':
path = '<polygon points="' + c + ',2 ' + (size - 2) + ',' + (size - 2) +
' 2,' + (size - 2) + '" fill="' + fill + '" stroke="#fff" stroke-width="2"/>';
' 2,' + (size - 2) + '" fill="' + fill + '" stroke="#fff" stroke-width="1"/>';
break;
case 'diamond':
path = '<polygon points="' + c + ',2 ' + (size - 2) + ',' + c + ' ' +
c + ',' + (size - 2) + ' 2,' + c +
'" fill="' + fill + '" stroke="#fff" stroke-width="2"/>';
'" fill="' + fill + '" stroke="#fff" stroke-width="1"/>';
break;
case 'hexagon': {
// Pointy-top hexagon centred at (c,c), inscribed radius ≈ c-1.5
Expand All @@ -121,7 +121,7 @@
(c + r * Math.sin(a)).toFixed(2) + ' ';
}
path = '<polygon points="' + pts.trim() + '" fill="' + fill +
'" stroke="#fff" stroke-width="2"/>';
'" stroke="#fff" stroke-width="1"/>';
break;
}
case 'star': {
Expand All @@ -134,12 +134,12 @@
spts += (cx + inner * Math.cos(aI)) + ',' + (cy + inner * Math.sin(aI)) + ' ';
}
path = '<polygon points="' + spts.trim() + '" fill="' + fill +
'" stroke="#fff" stroke-width="1.5"/>';
'" stroke="#fff" stroke-width="1"/>';
break;
}
default: // circle
path = '<circle cx="' + c + '" cy="' + c + '" r="' + (c - 2) +
'" fill="' + fill + '" stroke="#fff" stroke-width="2"/>';
'" fill="' + fill + '" stroke="#fff" stroke-width="1"/>';
}
return '<svg width="' + size + '" height="' + size +
'" viewBox="0 0 ' + size + ' ' + size +
Expand Down
1 change: 1 addition & 0 deletions test-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ node test-channel-qr-wiring.js
node test-channel-issue-1087.js
node test-analytics-channels-integration.js
node test-observers-headings.js
node test-marker-outline-weight.js
node test-traces.js

echo ""
Expand Down
74 changes: 74 additions & 0 deletions test-marker-outline-weight.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Follow-up to #1293 (PR #1334) — operator feedback: always-on white
* outline at stroke-width=2 was too heavy and dominated the map at
* zoomed-out levels. This test pins the lighter weight.
*
* Acceptance:
* - makeRoleMarkerSVG renders shape strokes with stroke-width <= 1
* (thin, just enough to make shapes distinct on dark/light tiles).
* - The selected/pulse highlight ring still uses a thicker weight
* (>= 2) so the highlight remains visible.
*/
'use strict';

const fs = require('fs');
const path = require('path');

let passed = 0, failed = 0;
function assert(cond, msg) {
if (cond) { passed++; console.log(' ✓ ' + msg); }
else { failed++; console.error(' ✗ ' + msg); }
}

const rolesSrc = fs.readFileSync(path.join(__dirname, 'public', 'roles.js'), 'utf8');
const liveSrc = fs.readFileSync(path.join(__dirname, 'public', 'live.js'), 'utf8');

console.log('\n=== marker outline weight: always-on stroke is thin ===');

const helperMatch = rolesSrc.match(/window\.makeRoleMarkerSVG[\s\S]*?\n\s*\};/);
const helperBlock = helperMatch ? helperMatch[0] : '';
assert(helperBlock.length > 0, 'makeRoleMarkerSVG block located');

// Every stroke-width literal inside the helper must be <= 1.
const widthRe = /stroke-width="([0-9.]+)"/g;
let m, widths = [];
while ((m = widthRe.exec(helperBlock)) !== null) {
widths.push(parseFloat(m[1]));
}
assert(widths.length > 0, 'helper contains stroke-width literals');
const maxW = widths.reduce((a, b) => Math.max(a, b), 0);
assert(maxW <= 1,
'makeRoleMarkerSVG max stroke-width <= 1 (got ' + maxW + ' across ' +
widths.length + ' shapes)');

// live.js inline fallback SVG must also be thin (it can render before
// roles.js loads in degraded scenarios).
const addNodeIdx = liveSrc.indexOf('function addNodeMarker');
const addNodeBody = liveSrc.slice(addNodeIdx, addNodeIdx + 2500);
const fallbackMatch = addNodeBody.match(/stroke="#fff"\s+stroke-width="([0-9.]+)"/);
if (fallbackMatch) {
assert(parseFloat(fallbackMatch[1]) <= 1,
'live.js inline fallback SVG stroke-width <= 1 (got ' + fallbackMatch[1] + ')');
}

console.log('\n=== highlight ring stays visible (weight >= 2) ===');

// The pulseNodeMarker / highlight ring uses ring.setStyle({ weight: N }).
// At least one such setStyle on _highlightRing must use weight >= 2 so
// the selected/highlighted node remains obviously highlighted.
const ringWeightRe = /ringHl\.setStyle\(\s*\{[^}]*weight:\s*([0-9.]+)/g;
let rm, ringWeights = [];
while ((rm = ringWeightRe.exec(liveSrc)) !== null) {
ringWeights.push(parseFloat(rm[1]));
}
assert(ringWeights.length >= 1,
'highlight ring (_highlightRing) sets weight at least once');
const maxRing = ringWeights.reduce((a, b) => Math.max(a, b), 0);
assert(maxRing >= 2,
'highlight ring max weight >= 2 (got ' + maxRing + ') so highlight stays visible');

console.log('\n=== Summary ===');
console.log(` Passed: ${passed}`);
console.log(` Failed: ${failed}`);
if (failed > 0) { console.error('\nmarker-outline-weight FAIL'); process.exit(1); }
console.log('\nmarker-outline-weight PASS');