···
padding: 0.125rem 0.5rem;
188
-
background: rgba(251, 146, 60, 0.2);
190
-
border: 1px solid #fb923c;
191
+
margin-left: 0.25rem;
···
const MAX_FAIL_COUNT = 3;
let lastProxiesState = null;
350
+
// Predefined color palette for labels
351
+
const labelColors = [
352
+
{ color: '#a78bfa', bg: 'rgba(167, 139, 250, 0.2)' }, // purple
353
+
{ color: '#f472b6', bg: 'rgba(244, 114, 182, 0.2)' }, // pink
354
+
{ color: '#facc15', bg: 'rgba(250, 204, 21, 0.2)' }, // yellow
355
+
{ color: '#60a5fa', bg: 'rgba(96, 165, 250, 0.2)' }, // blue
356
+
{ color: '#f87171', bg: 'rgba(248, 113, 113, 0.2)' }, // red
357
+
{ color: '#38bdf8', bg: 'rgba(56, 189, 248, 0.2)' }, // sky
358
+
{ color: '#c084fc', bg: 'rgba(192, 132, 252, 0.2)' }, // violet
359
+
{ color: '#fb7185', bg: 'rgba(251, 113, 133, 0.2)' }, // rose
362
+
// Hash string to index
363
+
function stringToColorIndex(str) {
365
+
for (let i = 0; i < str.length; i++) {
366
+
hash = str.charCodeAt(i) + ((hash << 5) - hash);
368
+
return Math.abs(hash) % labelColors.length;
371
+
// Get label color and styles
372
+
function getLabelStyle(label) {
373
+
const trimmedLabel = label.trim();
374
+
if (trimmedLabel === 'prod') {
377
+
bgColor: 'rgba(34, 197, 94, 0.2)',
378
+
borderColor: '#22c55e'
382
+
if (trimmedLabel === 'dev') {
385
+
bgColor: 'rgba(251, 146, 60, 0.2)',
386
+
borderColor: '#fb923c'
390
+
const colorIndex = stringToColorIndex(trimmedLabel);
391
+
const colorScheme = labelColors[colorIndex];
393
+
color: colorScheme.color,
394
+
bgColor: colorScheme.bg,
395
+
borderColor: colorScheme.color
async function fetchStats() {
···
const subdomain = proxy.conf?.subdomain || 'unknown';
const url = `https://${subdomain}.bore.dunkirk.sh`;
430
-
// Parse label from proxy name (format: subdomain[label])
478
+
// Parse labels from proxy name (format: subdomain[label1,label2])
const labelMatch = proxy.name.match(/\[([^\]]+)\]$/);
432
-
const label = labelMatch ? labelMatch[1] : null;
433
-
const displayName = label ? proxy.name.replace(/\[[^\]]+\]$/, '') : proxy.name;
480
+
const labels = labelMatch ? labelMatch[1].split(',') : [];
481
+
const displayName = labels.length > 0 ? proxy.name.replace(/\[[^\]]+\]$/, '') : proxy.name;
483
+
const labelHtml = labels.map(label => {
484
+
const trimmedLabel = label.trim();
485
+
const style = getLabelStyle(trimmedLabel);
486
+
return `<span class="tunnel-label" style="color: ${style.color}; background: ${style.bgColor}; border-color: ${style.borderColor};">${trimmedLabel}</span>`;
<div class="tunnel" data-tunnel="${proxy.name}">
<div class="tunnel-info">
<div class="tunnel-name">
${displayName || 'unnamed'}
440
-
${label ? `<span class="tunnel-label">${label}</span>` : ''}
<a href="${url}" target="_blank">${url}</a>
···
html += '<div class="offline-tunnels">';
html += '<div style="color: #8b949e; font-size: 0.85rem; margin-bottom: 0.75rem;">recently disconnected</div>';
html += offlineTunnels.map(proxy => {
460
-
// Parse label from proxy name (format: subdomain[label])
514
+
// Parse labels from proxy name (format: subdomain[label1,label2])
const labelMatch = proxy.name.match(/\[([^\]]+)\]$/);
462
-
const label = labelMatch ? labelMatch[1] : null;
463
-
const displayName = label ? proxy.name.replace(/\[[^\]]+\]$/, '') : proxy.name;
516
+
const labels = labelMatch ? labelMatch[1].split(',').map(l => l.trim()) : [];
517
+
const displayName = labels.length > 0 ? proxy.name.replace(/\[[^\]]+\]$/, '') : proxy.name;
518
+
const labelStr = labels.length > 0 ? ` [${labels.join(', ')}]` : '';
<div class="offline-tunnel" data-tunnel="${proxy.name}">
468
-
<span class="offline-tunnel-name">${displayName || 'unnamed'}${label ? ` [${label}]` : ''}</span>
523
+
<span class="offline-tunnel-name">${displayName || 'unnamed'}${labelStr}</span>
<span class="offline-tunnel-stats">in: <span data-traffic-in="${proxy.name}">0 B</span> • out: <span data-traffic-out="${proxy.name}">0 B</span></span>
···
const url = `https://${subdomain}.bore.dunkirk.sh`;
<div class="offline-tunnel" data-tunnel="${proxy.name}">
478
-
<span class="offline-tunnel-name">${displayName || 'unnamed'}${label ? ` [${label}]` : ''} → ${url}</span>
533
+
<span class="offline-tunnel-name">${displayName || 'unnamed'}${labelStr} → ${url}</span>
<span class="offline-tunnel-stats">in: <span data-traffic-in="${proxy.name}">0 B</span> • out: <span data-traffic-out="${proxy.name}">0 B</span></span>