Skip to content

Commit b97476f

Browse files
minor fix
1 parent 1cb4022 commit b97476f

File tree

1 file changed

+27
-12
lines changed

1 file changed

+27
-12
lines changed

git_diff/templates/index.html

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,12 +1157,12 @@
11571157
} else {
11581158
const {l,oN,nN} = seg;
11591159
const sign = l.type==='add'?'+':l.type==='del'?'-':' ';
1160-
const content = JSON.stringify(l.content);
1161-
h += `<div class="dl ${l.type}">
1160+
// Use data-copy attribute — never put user content inside onclick
1161+
h += `<div class="dl ${l.type}" data-copy="${EA(l.content)}">
11621162
<div class="ln">${oN}</div>
11631163
<div class="ln">${nN}</div>
11641164
<div class="lc"><span class="sign">${sign}</span>${EH(l.content)}</div>
1165-
<button class="clb" onclick="copyText(${content})" title="Copy line">⎘</button>
1165+
<button class="clb" title="Copy line">⎘</button>
11661166
</div>`;
11671167
}
11681168
}
@@ -1236,11 +1236,8 @@
12361236
const inner = _id(id+'-inner');
12371237
if (!inner) return;
12381238
const lines = [];
1239-
inner.querySelectorAll('.dl').forEach(row => {
1240-
const lc = row.querySelector('.lc');
1241-
if (!lc) return;
1242-
const sign = lc.querySelector('.sign')?.textContent || '';
1243-
const text = lc.textContent.replace(sign, '');
1239+
inner.querySelectorAll('.dl.add[data-copy], .dl.del[data-copy]').forEach(row => {
1240+
const text = row.dataset.copy || '';
12441241
if (row.classList.contains('add')) lines.push('+' + text);
12451242
else if (row.classList.contains('del')) lines.push('-' + text);
12461243
});
@@ -1320,13 +1317,15 @@
13201317
]);
13211318
const lines = fc.content.split('\n');
13221319
const isLarge = lines.length > 500;
1320+
// Store file content for copy-all (safe - not in HTML attribute)
1321+
window._fileCopyContent = fc.content;
13231322
let h = backBtn('showOverview()');
13241323
h += `<div style="background:var(--bg2);border:1px solid var(--border);border-radius:8px;padding:9px 12px;margin-bottom:10px;display:flex;align-items:center;gap:8px">
13251324
${fileIcon(path)}<span style="font-size:13px;font-weight:500">${E(path)}</span>
13261325
<span style="margin-left:auto;display:flex;gap:8px;align-items:center">
13271326
<span style="font-size:11px;color:var(--text2)">${lines.length} lines</span>
13281327
<button class="btn sm" onclick="showBlame('${ea(path)}')">Blame</button>
1329-
<button class="btn sm" onclick="copyText(${JSON.stringify(fc.content)})">Copy all</button>
1328+
<button class="btn sm" onclick="copyText(window._fileCopyContent)">Copy all</button>
13301329
</span>
13311330
</div>
13321331
<div class="ctabs">
@@ -1346,7 +1345,7 @@
13461345
h += renderSegs(fileDataMap[id].segs, 0, CHUNK) + sentinel(id);
13471346
} else {
13481347
lines.forEach((l,i) => {
1349-
h += `<div class="dl ctx"><div class="ln" style="width:50px">${i+1}</div><div class="lc">${EH(l)}</div><button class="clb" onclick="copyText(${JSON.stringify(l)})" title="Copy line">⎘</button></div>`;
1348+
h += `<div class="dl ctx" data-copy="${EA(l)}"><div class="ln" style="width:50px">${i+1}</div><div class="lc">${EH(l)}</div><button class="clb" title="Copy line">⎘</button></div>`;
13501349
});
13511350
}
13521351
h += `</div></div></div></div>
@@ -1492,6 +1491,8 @@
14921491
function E(s) { if(s==null)return'';return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
14931492
function EH(s) { if(s==null)return'';return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
14941493
function ea(s) { return String(s||'').replace(/\\/g,'\\\\').replace(/'/g,"\\'"); }
1494+
// EA: escape for HTML attribute value (double-quotes) — safe for data-* attributes
1495+
function EA(s) { if(s==null)return'';return String(s).replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
14951496

14961497
function notify(msg, type='') {
14971498
const el = _id('notif');
@@ -1500,11 +1501,25 @@
15001501
}
15011502

15021503
async function copyText(text) {
1503-
try { await navigator.clipboard.writeText(text); }
1504-
catch(e) { const ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta); }
1504+
try { await navigator.clipboard.writeText(String(text ?? '')); }
1505+
catch(e) { const ta=document.createElement('textarea');ta.value=String(text??'');ta.style.cssText='position:fixed;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta); }
15051506
notify('📋 Copied!','ok');
15061507
}
15071508

1509+
// ================================================================
1510+
// DELEGATED EVENT: copy-line buttons (.clb)
1511+
// Reads from data-copy on the parent .dl row — never uses inline onclick
1512+
// ================================================================
1513+
document.addEventListener('click', e => {
1514+
const btn = e.target.closest('.clb');
1515+
if (!btn) return;
1516+
e.stopPropagation();
1517+
const row = btn.closest('.dl');
1518+
if (row && row.dataset.copy !== undefined) {
1519+
copyText(row.dataset.copy);
1520+
}
1521+
});
1522+
15081523
const AV_COLORS=['#1e88e5','#43a047','#e53935','#8e24aa','#f4511e','#00897b','#d81b60','#3949ab','#c0ca33','#00acc1'];
15091524
function avatarColor(name) { if(!name)return'#555';let h=0;for(let i=0;i<name.length;i++)h=(h*31+name.charCodeAt(i))&0xffffffff;return AV_COLORS[Math.abs(h)%AV_COLORS.length]; }
15101525
function avatarInit(name) { if(!name)return'?';return name.trim().split(/\s+/).map(w=>w[0]).join('').slice(0,2).toUpperCase(); }

0 commit comments

Comments
 (0)