export const jobToolsSource = " const JOB_DETAILS_STORAGE_KEY = 'pytorrent.jobs.showDetails.v1';\n\n function readJobDetailsPreference(){\n try{ return localStorage.getItem(JOB_DETAILS_STORAGE_KEY) === '1'; }\n catch(e){ return false; }\n }\n\n function saveJobDetailsPreference(showDetails){\n try{ localStorage.setItem(JOB_DETAILS_STORAGE_KEY, showDetails ? '1' : '0'); }\n catch(e){}\n }\n\n function syncJobDetailsControl(){\n if($('jobsShowDetails')) $('jobsShowDetails').checked = readJobDetailsPreference();\n }\n\n function jobActions(r){ const id=esc(r.id); const status=String(r.status||''); const actions=[]; if(status==='failed'||status==='cancelled') actions.push(``); if(status==='pending') actions.push(``); if(status==='pending'||status==='running') actions.push(``); return actions.join(' ') || '-'; }\n function jobStatusBadgeClass(status){\n // Note: Running means active work, so it uses primary instead of danger; danger stays reserved for failed.\n const classes={done:'success',failed:'danger',running:'primary',cancelled:'secondary',pending:'warning'};\n return classes[String(status||'')] || 'warning';\n }\n async function loadJobs(page=jobsPage){\n const box=$('jobsTable');\n // Note: Finished shows only a real finished_at value; running/pending do not receive a date from updated_at.\n if(!box) return;\n jobsPage=Math.max(0,page|0);\n syncJobDetailsControl();\n box.innerHTML=' Loading jobs...';\n const offset=jobsPage*jobsLimit;\n const j=await (await fetch(`/api/jobs?limit=${jobsLimit}&offset=${offset}`, {cache:'no-store'})).json();\n const rows=j.jobs||[];\n jobsTotal=Number(j.total||rows.length);\n const showDetails = readJobDetailsPreference();\n const details=r=>{\n const count=Number(r.hash_count||0);\n if(!showDetails) return 'Details hidden';\n const bits=[];\n if(r.is_bulk || count>1) bits.push('bulk');\n if(count) bits.push(`${esc(count)} torrent${count === 1 ? '' : 's'}`);\n if(r.summary) bits.push(esc(r.summary));\n return bits.join('
') || '-';\n };\n box.innerHTML=responsiveTable(\n ['Status','Source','Action','Profile','Count','Details','Attempts','Started','Finished','Error','Actions'],\n rows.map(r=>[\n `${esc(r.status)}`,\n r.source==='automation'?` automation`:(r.is_forced?' forced':'user'),\n esc(r.action),\n esc(r.profile_id),\n esc(r.hash_count||0),\n details(r),\n esc(r.attempts||0),\n humanDateCell(r.started_at||r.created_at),\n humanDateCell(r.finished_at),\n compactCell(r.error||'',140),\n jobActions(r),\n ]),\n 'jobs-table'\n );\n renderJobsPager();\n }\n function renderJobsPager(){ const p=$('jobsPager'); if(!p)return; const pages=Math.max(1,Math.ceil(jobsTotal/jobsLimit)); p.innerHTML=`