<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Paste Editor • Navigator + Bring In</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.32.9/ace.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<style>
:root{--bg:#0f172a;--panel:#111827;--text:#e5e7eb;--muted:#94a3b8;--border:#243244;--accent:#60a5fa}
*{box-sizing:border-box}
html,body{height:100%}
html{scroll-behavior:auto}
body{margin:0;background:var(--bg);color:var(--text);font:14px/1.5 ui-sans-serif,system-ui,Segoe UI,Inter,Roboto,sans-serif;display:flex;flex-direction:column;overscroll-behavior-y:none}
body.no-scroll{overflow:hidden}
.bar{display:flex;gap:.75rem;align-items:center;padding:.6rem 1rem;background:var(--panel);border-bottom:1px solid var(--border)}
.bar select{background:#0b1220;color:var(--text);border:1px solid var(--border);border-radius:6px;padding:.35rem .5rem}
.bar .btn{margin-left:.5rem}
.hint{margin-left:auto;color:var(--muted);font-size:.9rem}
.toolbar{display:flex;gap:.5rem;flex-wrap:wrap;align-items:center;padding:.5rem 1rem;background:#0b1220;border-bottom:1px solid var(--border)}
.find-mode,.find-input,.btn{border:1px solid var(--border);background:#0b1220;color:var(--text);border-radius:6px}
.find-mode{padding:.4rem .5rem}
.find-input{padding:.4rem .6rem;min-width:250px}
.btn{padding:.45rem .7rem;cursor:pointer}
.btn[disabled]{opacity:.5;cursor:not-allowed}
.btn-accent{border-color:var(--accent); background:rgba(96,165,250,.12)}
.count{color:var(--muted);margin-left:.25rem;font-size:.9rem}
.badge{margin-left:.75rem;color:#cbd5e1;font-size:.85rem}
.pane{flex:1;min-height:0}
#editor{width:100%;height:100%}
.ace_selected_block{position:absolute;background:rgba(80,160,255,.18);border:1px solid rgba(80,160,255,.35)}
/* Overlay */
.overlay{position:fixed;inset:0;display:none;align-items:center;justify-content:center;z-index:9999;background:rgba(0,0,0,.55);backdrop-filter:saturate(120%) blur(2px)}
.overlay.open{display:flex}
.sheet{background:#0b1220;border:1px solid var(--border);border-radius:12px;box-shadow:0 10px 40px rgba(0,0,0,.35);width:min(960px,94vw);max-height:88vh;display:flex;flex-direction:column}
.sheet-head{display:flex;align-items:center;justify-content:space-between;padding:.8rem 1rem;border-bottom:1px solid var(--border)}
.sheet-title{font-weight:600}
.sheet-close{background:transparent;border:1px solid var(--border);border-radius:8px;color:var(--text);width:34px;height:34px;cursor:pointer}
.sheet-body{padding:1rem;overflow:auto}
.sheet-body textarea{width:100%;height:50vh;min-height:200px;background:#0a101b;color:var(--text);border:1px solid var(--border);border-radius:10px;padding:.75rem;resize:vertical}
.sheet-foot{display:flex;gap:.5rem;flex-wrap:wrap;justify-content:flex-end;padding:1rem;border-top:1px solid var(--border)}
.sheet-foot .btn{min-width:120px}
.btn-ghost{background:transparent}
.btn-primary{background:#0f172a;border-color:var(--accent)}
.btn-primary:hover{background:#132036}
</style>
</head>
<body>
<div class="bar">
<strong>Paste Editor</strong>
<label>Mode:
<select id="modeSel" title="Syntax mode">
<option value="javascript">JavaScript</option>
<option value="typescript">TypeScript</option>
<option value="php">PHP</option>
<option value="python">Python</option>
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="json">JSON</option>
<option value="sql">SQL</option>
<option value="markdown">Markdown</option>
<option value="sh">Shell</option>
<option value="text">Plain Text</option>
</select>
</label>
<button id="bringBtn" class="btn btn-accent" type="button" title="Analyze snippet and locate">Bring In</button>
<span id="detectedBadge" class="badge"></span>
<div class="hint">Paste (Ctrl/Cmd-V) • Ctrl/Cmd-F finds • Esc clears search</div>
</div>
<div class="toolbar">
<select id="findMode" class="find-mode" title="Search mode">
<option value="normal">Normal text</option>
<option value="function">Function</option>
<option value="tag">Tag (HTML)</option>
<option value="variable">Variable</option>
<option value="attribute">Attribute / ID</option>
</select>
<input id="findInput" class="find-input" placeholder="Query (blank = browse all in current mode)" />
<button class="btn" id="prevBtn" type="button" title="Previous (Shift+Enter)">▲</button>
<button class="btn" id="nextBtn" type="button" title="Next (Enter)">▼</button>
<button class="btn" id="clearBtn" type="button" title="Clear search">Clear</button>
<span class="count" id="countHint"></span>
</div>
<div class="pane"><div id="editor">
// Paste code or HTML here.
// Modes:
// • Function: blank = all functions; type to filter.
// • Tag: blank = browse <div>; type tag name (e.g., span).
// • Variable: blank = all declarations; type to filter.
// • Attribute: blank = elements with id; filters: #id, .class, [attr], [attr=value], [attr*=substr].
</div></div>
<!-- Overlay -->
<div id="overlay" class="overlay" aria-hidden="true">
<div class="sheet" role="dialog" aria-modal="true" aria-labelledby="sheetTitle">
<div class="sheet-head">
<div id="sheetTitle" class="sheet-title">Bring In</div>
<button id="sheetClose" class="sheet-close" type="button" aria-label="Close">×</button>
</div>
<div class="sheet-body">
<textarea id="bringText" placeholder="Paste a snippet to analyze (editor content will NOT change unless you press Paste/Replace)… Examples: • function saveUser() { … } • let userName = '…' • <div id="hero" class="card">… • #hero or .card or [data-role=nav]"></textarea>
</div>
<div class="sheet-foot">
<button id="locateBtn" class="btn btn-primary" type="button" title="Analyze and jump (Ctrl/Cmd+Enter)">Locate</button>
<button id="pasteBtn" class="btn btn-primary" type="button" title="Insert at cursor">Paste at Cursor</button>
<button id="replaceBtn" class="btn btn-primary" type="button" title="Replace selection or current highlighted block">Replace Block/Selection</button>
<button id="cancelBtn" class="btn btn-ghost" type="button">Close</button>
</div>
</div>
</div>
<script>
/* ================== ACE setup ================== */
const editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.session.setMode("ace/mode/javascript"); // default
editor.setOptions({
wrap:true, tabSize:2, useSoftTabs:true, showPrintMargin:false,
enableBasicAutocompletion:true, enableLiveAutocompletion:true,
highlightActiveLine:true, highlightGutterLine:true, fontSize:14
});
const modeSel = document.getElementById("modeSel");
modeSel.addEventListener("change", e => editor.session.setMode("ace/mode/" + e.target.value));
/* ================== Bring In overlay ================== */
const overlay = document.getElementById('overlay');
const bringBtn = document.getElementById('bringBtn');
const closeBtn = document.getElementById('sheetClose');
const cancelBtn = document.getElementById('cancelBtn');
const locateBtn = document.getElementById('locateBtn');
const pasteBtn = document.getElementById('pasteBtn');
const replaceBtn = document.getElementById('replaceBtn');
const bringText = document.getElementById('bringText');
const detectedBadge = document.getElementById('detectedBadge');
function lockScroll(lock){
document.body.classList.toggle('no-scroll', !!lock);
}
function openOverlay(){
overlay.classList.add('open');
overlay.setAttribute('aria-hidden','false');
lockScroll(true);
// avoid page jumping: focus after frame
requestAnimationFrame(()=>bringText.focus());
}
function closeOverlay(){
overlay.classList.remove('open');
overlay.setAttribute('aria-hidden','true');
lockScroll(false);
editor.focus();
}
bringBtn.addEventListener('click', openOverlay);
closeBtn.addEventListener('click', closeOverlay);
cancelBtn.addEventListener('click', closeOverlay);
overlay.addEventListener('click', (e) => { if (e.target === overlay) closeOverlay(); });
overlay.addEventListener('keydown', (e) => {
if (e.key === 'Escape') { e.preventDefault(); closeOverlay(); }
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'enter') { e.preventDefault(); locateBtn.click(); }
});
/* ================== Navigator state ================== */
const findInput = document.getElementById('findInput');
const findMode = document.getElementById('findMode');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const clearBtn = document.getElementById('clearBtn');
const countHint = document.getElementById('countHint');
let term = '';
let matches = [];
let idx = -1;
let cache = { function:null, tag:null, variable:null, attribute:null };
let lastBlockRange = null; // store highlighted block to support replace
editor._blockMarkers = [];
function clearHighlights(){
if (editor._blockMarkers.length) {
editor._blockMarkers.forEach(id => editor.session.removeMarker(id));
editor._blockMarkers = [];
}
lastBlockRange = null;
}
function selectBlock(m){
if (!m) return;
clearHighlights();
const Range = ace.require('ace/range').Range;
const range = new Range(m.startLine, m.startColumn || 0, m.endLine || m.startLine, m.endColumn || (editor.session.getLine(m.endLine || m.startLine) || '').length);
const id = editor.session.addMarker(range, 'ace_selected_block', 'text');
editor._blockMarkers.push(id);
editor.selection.setRange(range, false);
editor.scrollToLine(m.startLine, true, true, ()=>{});
lastBlockRange = range.clone();
}
/* ================== Scanners ================== */
function scanFunctions(filter) {
const lines = editor.getValue().split('\n');
const out = [], f = (filter||'').toLowerCase(), has = !!f;
const pats = [
/^\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/,
/^\s*(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>\s*{/,
/^\s*(?:public|private|protected|static|\s)*function\s+(\w+)\s*\(/,
/^\s*def\s+(\w+)\s*\(/,
/^\s*(\w[\w\d_]*)\s*\([^)]*\)\s*\{/
];
for (let i=0;i<lines.length;i++){
const line=lines[i]; let name=null, isPy=false, braces=false;
for (const re of pats){ const m=line.match(re); if(m){ name=m[1]||''; isPy=/^\s*def\s+/.test(line); braces=/{/.test(line)||!isPy; break; } }
if (name===null) continue;
if (has && !name.toLowerCase().includes(f)) continue;
let end=i;
if (braces){ let b=0, opened=false; for (let j=i;j<lines.length;j++){ for (const ch of lines[j]){ if(ch==='{' ){b++;opened=true;} if(ch==='}'){b--;} } if (opened && b===0){ end=j; break; } if (j===lines.length-1) end=j; } }
else { const base=(line.match(/^\s*/)||[''])[0].length; for (let j=i+1;j<lines.length;j++){ const L=lines[j], ind=(L.match(/^\s*/)||[''])[0].length; if (L.trim() && ind<=base){ end=j-1; break; } if (j===lines.length-1) end=j; } }
out.push({kind:'function', name, startLine:i, endLine:end, startColumn:0, endColumn:(lines[end]||'').length});
}
return out;
}
function scanTags(filter) {
const lines = editor.getValue().split('\n');
const out = [];
const tagFilterRaw = (filter||'').trim().toLowerCase();
const want = tagFilterRaw || 'div';
const voids = new Set(['area','base','br','col','embed','hr','img','input','link','meta','param','source','track','wbr']);
const openRe = /<([a-zA-Z][\w:-]*)\b([^>]*?)>/g;
for (let i=0;i<lines.length;i++){
const line=lines[i]; openRe.lastIndex=0; let m;
while ((m=openRe.exec(line))!==null){
const tag=m[1].toLowerCase(); if (tagFilterRaw ? (tag!==want) : (tag!=='div')) continue;
const selfClosing=/\/>\s*$/.test(m[0]) || voids.has(tag);
if (selfClosing){ out.push({kind:'tag', name:tag, startLine:i, endLine:i, startColumn:m.index, endColumn:m.index+m[0].length}); continue; }
let depth=1, end=i;
for (let j=i;j<lines.length;j++){
const L=lines[j];
if (j===i){ const rest=L.slice(m.index+m[0].length); depth+=(rest.match(new RegExp(`<${tag}\\b`,'gi'))||[]).length; depth-=(rest.match(new RegExp(`</${tag}\\s*>`,'gi'))||[]).length; }
else { depth+=(L.match(new RegExp(`<${tag}\\b`,'gi'))||[]).length; depth-=(L.match(new RegExp(`</${tag}\\s*>`,'gi'))||[]).length; }
if (depth===0){ end=j; break; } if (j===lines.length-1) end=j;
}
out.push({kind:'tag', name:tag, startLine:i, endLine:end, startColumn:0, endColumn:(lines[end]||'').length});
}
}
return out;
}
function scanVariables(filter) {
const lines = editor.getValue().split('\n');
const out = [], f=(filter||'').toLowerCase(), has=!!f;
for (let i=0;i<lines.length;i++){
const line=lines[i];
if (/^\s*(def|class)\b/.test(line)) continue;
let m=line.match(/^\s*(?:var|let|const)\s+([^;]+)/);
if (m){ const names=m[1].split(',').map(s=>s.trim().replace(/^(\{|\[)/,'').replace(/=.*$/,'').trim()); for (const raw of names){ const name=(raw.match(/[$A-Za-z_][\w$]*/)||[])[0]; if(!name) continue; if (has && !name.toLowerCase().includes(f)) continue; out.push({kind:'variable', name, startLine:i, endLine:i, startColumn:0, endColumn:line.length}); } continue; }
m=line.match(/^\s*\$([A-Za-z_]\w*)\s*=/); if (m){ const name=m[1]; if(!has||name.toLowerCase().includes(f)) out.push({kind:'variable', name, startLine:i, endLine:i, startColumn:0, endColumn:line.length}); continue; }
m=line.match(/^\s*([A-Za-z_]\w*)\s*=\s*[^=]/); if (m){ const name=m[1]; if(!has||name.toLowerCase().includes(f)) out.push({kind:'variable', name, startLine:i, endLine:i, startColumn:0, endColumn:line.length}); continue; }
m=line.match(/^\s*(?:unsigned\s+|signed\s+)?[A-Za-z_]\w*(?:\s*\*+)?\s+([A-Za-z_]\w*)\s*(?:[=;,\)])/); if (m){ const name=m[1]; if(!has||name.toLowerCase().includes(f)) out.push({kind:'variable', name, startLine:i, endLine:i, startColumn:0, endColumn:line.length}); continue; }
}
return out;
}
function parseAttrs(attrStr){
const attrs={}; const re=/([:@A-Za-z_][\w:.-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g; let m;
while ((m=re.exec(attrStr))!==null){ const key=m[1].toLowerCase(); const val=m[2]??m[3]??m[4]??''; attrs[key]=val; if (key==='class' && val) attrs.classList=val.split(/\s+/).filter(Boolean); }
return attrs;
}
function matchesAttrFilter(attrs, filterRaw){
if (!filterRaw) return !!attrs.id;
const s=filterRaw.trim();
if (s.startsWith('#')) return (attrs.id||'').toLowerCase()===s.slice(1).toLowerCase();
if (s.startsWith('.')) return (attrs.classList||[]).map(c=>c.toLowerCase()).includes(s.slice(1).toLowerCase());
if (s.startsWith('[') && s.endsWith(']')){
const inner=s.slice(1,-1).trim();
const starEq=inner.split('*='); if (starEq.length===2){ const key=starEq[0].trim().toLowerCase(); const val=starEq[1].replace(/^['"]|['"]$/g,'').toLowerCase(); return (attrs[key]||'').toLowerCase().includes(val); }
const eq=inner.split('='); if (eq.length===2){ const key=eq[0].trim().toLowerCase(); const val=eq[1].replace(/^['"]|['"]$/g,'').toLowerCase(); return (attrs[key]||'').toLowerCase()===val; }
return inner in attrs;
}
const key=s.toLowerCase(); if (key in attrs) return true;
return Object.keys(attrs).some(k=>String(attrs[k]).toLowerCase().includes(key));
}
function scanAttributes(filter){
const lines = editor.getValue().split('\n');
const out=[]; const voids=new Set(['area','base','br','col','embed','hr','img','input','link','meta','param','source','track','wbr']); const openRe=/<([a-zA-Z][\w:-]*)\b([^>]*?)>/g;
for (let i=0;i<lines.length;i++){
const line=lines[i]; openRe.lastIndex=0; let m;
while ((m=openRe.exec(line))!==null){
const tag=m[1].toLowerCase(); const attrs=parseAttrs(m[2]||''); if (!matchesAttrFilter(attrs, filter)) continue;
const selfClosing=/\/>\s*$/.test(m[0]) || voids.has(tag);
if (selfClosing){ out.push({kind:'attribute', name:(attrs.id||attrs.class||tag||''), startLine:i, endLine:i, startColumn:m.index, endColumn:m.index+m[0].length}); continue; }
let depth=1, end=i;
for (let j=i;j<lines.length;j++){
const L=lines[j];
if (j===i){ const rest=L.slice(m.index+m[0].length); depth+=(rest.match(new RegExp(`<${tag}\\b`,'gi'))||[]).length; depth-=(rest.match(new RegExp(`</${tag}\\s*>`,'gi'))||[]).length; }
else { depth+=(L.match(new RegExp(`<${tag}\\b`,'gi'))||[]).length; depth-=(L.match(new RegExp(`</${tag}\\s*>`,'gi'))||[]).length; }
if (depth===0){ end=j; break; } if (j===lines.length-1) end=j;
}
out.push({kind:'attribute', name:(attrs.id||attrs.class||tag||''), startLine:i, endLine:end, startColumn:0, endColumn:(lines[end]||'').length});
}
}
return out;
}
/* ================== Search driver ================== */
function rebuildCache(kind){
if (kind==='function') cache.function = scanFunctions('');
else if (kind==='tag') cache.tag = scanTags('');
else if (kind==='variable') cache.variable = scanVariables('');
else if (kind==='attribute') cache.attribute = scanAttributes('');
}
function updateButtons(){
const mode=findMode.value, hasTerm=!!term, usingAll=(mode!=='normal' && !hasTerm);
const list = hasTerm ? matches : (cache[mode] || []);
const at = (idx>=0 && list.length) ? (idx+1) : '';
prevBtn.disabled = (mode==='normal') ? !hasTerm : (list.length===0);
nextBtn.disabled = prevBtn.disabled;
clearBtn.disabled = !(hasTerm || (usingAll && list.length));
let label = mode==='normal'?'text': (mode==='function'?'function': mode==='tag'?'tag': mode==='variable'?'variable':'element');
countHint.textContent = (mode==='normal')
? (hasTerm ? 'Text search (use editor next/prev)' : '')
: (list.length ? `${list.length} ${label}${list.length!==1?'s':''}${at?` • at ${at}`:''}` : `No ${label}s found`);
}
function doSearch(){
term = findInput.value.trim();
const mode=findMode.value;
if (mode==='normal'){
clearHighlights();
if (term) editor.find(term, {backwards:false, wrap:true, caseSensitive:false, wholeWord:false, regExp:false});
else { editor.find(''); editor.selection.clearSelection(); }
matches=[]; idx=-1; updateButtons(); return;
}
if (!term){
if (!cache[mode]) rebuildCache(mode);
const list = cache[mode] || [];
if (list.length && idx<0) idx = 0;
selectBlock(list[idx]);
matches=[]; updateButtons(); return;
}
if (mode==='function') matches = scanFunctions(term);
else if (mode==='tag') matches = scanTags(term);
else if (mode==='variable') matches = scanVariables(term);
else if (mode==='attribute') matches = scanAttributes(term);
if (matches.length){ idx=0; selectBlock(matches[0]); } else { clearHighlights(); idx=-1; }
updateButtons();
}
function step(delta){
const mode=findMode.value;
if (mode==='normal'){ if (term) (delta>0?editor.findNext():editor.findPrevious()); return; }
const list = term ? matches : (cache[mode] || []);
if (!list.length) return;
idx = ((idx<0?0:idx) + delta + list.length) % list.length;
selectBlock(list[idx]);
updateButtons();
}
let debounce;
findInput.addEventListener('input', () => { clearTimeout(debounce); debounce=setTimeout(()=>{ idx=-1; doSearch(); }, 180); });
findMode.addEventListener('change', () => { idx=-1; doSearch(); });
nextBtn.addEventListener('click', () => step(+1));
prevBtn.addEventListener('click', () => step(-1));
clearBtn.addEventListener('click', () => { findInput.value=''; term=''; matches=[]; idx=-1; doSearch(); editor.focus(); });
function scheduleCacheReset(){
cache={function:null, tag:null, variable:null, attribute:null};
if (findMode.value!=='normal' && !term){ idx=-1; updateButtons(); }
}
let rebuildTimer;
editor.session.on('change', () => { clearTimeout(rebuildTimer); rebuildTimer = setTimeout(scheduleCacheReset, 250); });
editor.commands.addCommand({ name:'focusFind', bindKey:{win:'Ctrl-F',mac:'Command-F'}, exec:()=>{ findInput.focus(); findInput.select(); } });
// initial
doSearch();
// Prevent pull-to-refresh on touch
(function(){ let prevent=false, startY=0;
window.addEventListener('touchstart', e=>{ startY=e.touches[0].clientY; prevent=(window.scrollY===0); }, {passive:true});
window.addEventListener('touchmove', e=>{ if(prevent){ const dy=e.touches[0].clientY-startY; if(dy>10) e.preventDefault(); } }, {passive:false});
})();
/* ================== Detection (Bring In) ================== */
function detectLanguage(text){
const t = text.trim();
if (/^<!DOCTYPE html>|^<html[\s>]|^<\w+[\s>]/i.test(t)) return 'html';
if (/^<\?php/i.test(t)) return 'php';
if (/^\s*#!/.test(t) && /python/.test(t)) return 'python';
if (/^\s*(def\s+\w+\s*\(|class\s+\w+)/.test(t)) return 'python';
if (/^\s*(import\s+\w+|from\s+\w+\s+import)/.test(t)) return 'python';
if (/^\s*(public|private|protected|class)\b/.test(t)) return 'java';
if (/^\s*(#include\b|int\s+main\s*\()/.test(t)) return 'c_cpp';
if (/^\s*(let|const|var)\s+/.test(t) || /\bfunction\s+\w+\s*\(/.test(t) || /=>\s*\{/.test(t)) return 'javascript';
if (/^\s*\{[\s\S]*\}\s*$/.test(t) && /:/.test(t)) return 'json';
return null;
}
function detectEntity(text){
const firstLine = text.split('\n').find(l => l.trim().length) || '';
const trimmed = firstLine.trim();
if (/^<\w+[\s>]/.test(trimmed)) {
const m = trimmed.match(/^<([a-zA-Z][\w:-]*)\b([^>]*?)>?/);
if (m) {
const tag = (m[1]||'').toLowerCase();
const attrStr = m[2] || '';
const id = (attrStr.match(/\bid=["']?([^"'\s>]+)["']?/i) || [])[1];
const cls = (attrStr.match(/\bclass=["']([^"']+)["']/i) || [])[1];
if (id) return {mode:'attribute', query:'#'+id, label:`id: ${id}`};
if (cls) {
const firstClass = cls.split(/\s+/).filter(Boolean)[0];
if (firstClass) return {mode:'attribute', query:'.'+firstClass, label:`class: ${firstClass}`};
}
if (tag) return {mode:'tag', query:tag, label:`tag: ${tag}`};
}
}
const fn =
trimmed.match(/^\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/) ||
trimmed.match(/^\s*(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>\s*\{/) ||
trimmed.match(/^\s*(?:public|private|protected|static|\s)*function\s+(\w+)\s*\(/) ||
trimmed.match(/^\s*def\s+(\w+)\s*\(/) ||
trimmed.match(/^\s*(\w[\w\d_]*)\s*\([^)]*\)\s*\{/);
if (fn) return {mode:'function', query:fn[1], label:`function: ${fn[1]}`};
const v =
trimmed.match(/^\s*(?:var|let|const)\s+([$A-Za-z_][\w$]*)/) ||
trimmed.match(/^\s*\$([A-Za-z_]\w*)\s*=/) ||
trimmed.match(/^\s*([A-Za-z_]\w*)\s*=\s*[^=]/) ||
trimmed.match(/^\s*(?:unsigned\s+|signed\s+)?[A-Za-z_]\w*(?:\s*\*+)?\s+([A-Za-z_]\w*)\s*(?:[=;,\)])/);
if (v) return {mode:'variable', query:v[1], label:`variable: ${v[1]}`};
if (/^#[-\w:]+$/.test(trimmed) || /^\.[-\w:]+$/.test(trimmed) || /^\[[^\]]+\]$/.test(trimmed)) {
return {mode:'attribute', query:trimmed, label:`selector: ${trimmed}`};
}
return null;
}
function applyDetectionAndSearch(fromText, shouldClose=true){
const lang = detectLanguage(fromText);
if (lang) { const aceMode = (lang==='c_cpp' ? 'c_cpp' : lang); modeSel.value = aceMode; editor.session.setMode('ace/mode/' + aceMode); }
const ent = detectEntity(fromText);
if (ent) {
findMode.value = ent.mode;
findInput.value = ent.query || '';
term = ent.query || '';
idx = -1;
doSearch();
detectedBadge.textContent = `Detected ${ent.label}`;
} else {
detectedBadge.textContent = 'No specific entity detected';
}
if (shouldClose) closeOverlay();
}
locateBtn.addEventListener('click', () => {
applyDetectionAndSearch(bringText.value, true);
});
pasteBtn.addEventListener('click', () => {
const txt = bringText.value;
if (!txt) { closeOverlay(); return; }
const pos = editor.getCursorPosition();
editor.session.insert(pos, txt);
scheduleCacheReset();
closeOverlay();
});
replaceBtn.addEventListener('click', () => {
const txt = bringText.value;
if (!txt) { closeOverlay(); return; }
const sel = editor.getSelectionRange();
if (!editor.session.getTextRange(sel)) {
// no selection: try replacing the currently highlighted block
if (lastBlockRange) {
editor.session.replace(lastBlockRange, txt);
} else {
// fallback: replace current line
const row = editor.getCursorPosition().row;
const line = editor.session.getLine(row);
const Range = ace.require('ace/range').Range;
editor.session.replace(new Range(row, 0, row, line.length), txt);
}
} else {
editor.session.replace(sel, txt);
}
scheduleCacheReset();
closeOverlay();
});
</script>
</body>
</html>