if(typeof window.currentTasks==='undefined'){window.currentTasks=[]}
if(typeof window.activeTaskId==='undefined'){window.activeTaskId=null}
if(typeof window.taskCountdowns==='undefined'){window.taskCountdowns={}}
if(typeof window.tasksPollingTimer==='undefined'){window.tasksPollingTimer=null}
if(typeof window.taskTextareaContents==='undefined'){window.taskTextareaContents={}}
if(typeof window.taskOptionsStates==='undefined'){window.taskOptionsStates={}}
if(typeof window.taskImages==='undefined'){window.taskImages={}}
if(typeof window.pendingNewTaskCount==='undefined'){window.pendingNewTaskCount=0}
if(typeof window.newTaskHintTimer==='undefined'){window.newTaskHintTimer=null}
if(typeof window.serverTimeOffset==='undefined'){window.serverTimeOffset=0}
if(typeof window.taskDeadlines==='undefined'){window.taskDeadlines={}}
if(typeof window.feedbackPrompts==='undefined'){window.feedbackPrompts={resubmit_prompt:'请立即调用 interactive_feedback 工具',prompt_suffix:'\n请积极调用 interactive_feedback 工具'}}
var currentTasks=window.currentTasks
var activeTaskId=window.activeTaskId
var taskCountdowns=window.taskCountdowns
var tasksPollingTimer=window.tasksPollingTimer
var taskTextareaContents=window.taskTextareaContents
var taskOptionsStates=window.taskOptionsStates
var taskImages=window.taskImages
var feedbackPrompts=window.feedbackPrompts
async function fetchFeedbackPromptsFresh(){try{const resp=await fetch('/api/get-feedback-prompts',{cache:'no-store'})
if(!resp.ok)throw new Error(`HTTP ${resp.status}`)
const data=await resp.json()
if(data&&data.status==='success'&&data.config){window.feedbackPrompts=data.config
feedbackPrompts=window.feedbackPrompts
if(data.meta&&data.meta.config_file){const el=document.getElementById('config-file-path')
if(el){el.value=data.meta.config_file}}
return window.feedbackPrompts}}catch(e){console.warn('获取反馈提示语配置失败,使用本地默认值:',e)}
return window.feedbackPrompts}
if(typeof window.remainingSeconds==='undefined'){window.remainingSeconds=0}
if(typeof window.countdownTimer==='undefined'){window.countdownTimer=null}
var remainingSeconds=window.remainingSeconds
var countdownTimer=window.countdownTimer
if(typeof window.updateCountdownDisplay!=='function'){window.updateCountdownDisplay=function(seconds){const countdownContainer=document.getElementById('countdown-container')
const countdownText=document.getElementById('countdown-text')
if(!countdownContainer||!countdownText)return
const displaySeconds=typeof seconds==='number'?seconds:window.remainingSeconds
if(displaySeconds>0){countdownText.textContent=`${displaySeconds}秒后自动重新询问`
countdownContainer.classList.remove('hidden')}else{countdownContainer.classList.add('hidden')}}}
var updateCountdownDisplay=window.updateCountdownDisplay
var TASKS_POLL_BASE_MS=2000
var TASKS_POLL_MAX_MS=30000
var tasksPollBackoffMs=TASKS_POLL_BASE_MS
var tasksPollAbortController=null
var tasksPollVisibilityHandlerInstalled=false
function getNextBackoffMs(currentMs){const next=Math.min(TASKS_POLL_MAX_MS,Math.round(currentMs*1.7))
const jitter=Math.round(next*0.1*Math.random())
return next+jitter}
async function fetchAndApplyTasks(reason){if(typeof document!=='undefined'&&document.hidden){return false}
if(isManualSwitching){return false}
try{if(tasksPollAbortController&&typeof tasksPollAbortController.abort==='function'){tasksPollAbortController.abort()}}catch(e){}
if(typeof AbortController!=='undefined'){tasksPollAbortController=new AbortController()}else{tasksPollAbortController=null}
const fetchOptions={cache:'no-store'}
if(tasksPollAbortController){fetchOptions.signal=tasksPollAbortController.signal}
try{const response=await fetch('/api/tasks',fetchOptions)
if(!response.ok){throw new Error(`HTTP ${response.status}`)}
const data=await response.json()
if(data.success){if(data.server_time){const localTime=Date.now()/1000
window.serverTimeOffset=data.server_time-localTime
if(Math.abs(window.serverTimeOffset)>1){console.log(`服务器时间偏移: ${window.serverTimeOffset.toFixed(2)}s`)}}
if(data.tasks){data.tasks.forEach(task=>{if(task.deadline){window.taskDeadlines[task.task_id]=task.deadline}
if(taskCountdowns&&taskCountdowns[task.task_id]&&task.status!=='completed'){if(typeof task.auto_resubmit_timeout==='number'){if(task.auto_resubmit_timeout<=0){try{if(taskCountdowns[task.task_id].timer){clearInterval(taskCountdowns[task.task_id].timer)}}catch(e){}
delete taskCountdowns[task.task_id]
delete window.taskDeadlines[task.task_id]}else{taskCountdowns[task.task_id].timeout=task.auto_resubmit_timeout}}
if(typeof task.remaining_time==='number'&&taskCountdowns[task.task_id]){taskCountdowns[task.task_id].remaining=task.remaining_time}}})}
updateTasksList(data.tasks)
updateTasksStats(data.stats)
if(reason){console.debug(`任务列表已更新: ${reason}`)}
return true}
return false}catch(error){if(error&&(error.name==='AbortError'||error.code===20)){return false}
console.error('获取任务列表失败:',error)
return false}finally{tasksPollAbortController=null}}
function scheduleNextTasksPoll(delayMs){if(tasksPollingTimer){clearTimeout(tasksPollingTimer)
tasksPollingTimer=null}
tasksPollingTimer=setTimeout(async()=>{const ok=await fetchAndApplyTasks('poll')
if(ok){tasksPollBackoffMs=TASKS_POLL_BASE_MS}else{tasksPollBackoffMs=getNextBackoffMs(tasksPollBackoffMs)}
scheduleNextTasksPoll(tasksPollBackoffMs)},Math.max(0,delayMs))}
function startTasksPolling(){if(typeof document!=='undefined'&&document.hidden){console.log('页面不可见,跳过启动任务轮询')
return}
stopTasksPolling()
tasksPollBackoffMs=TASKS_POLL_BASE_MS
scheduleNextTasksPoll(0)
if(!tasksPollVisibilityHandlerInstalled&&typeof document!=='undefined'){tasksPollVisibilityHandlerInstalled=true
document.addEventListener('visibilitychange',()=>{if(document.hidden){stopTasksPolling()}else{startTasksPolling()}})
window.addEventListener('beforeunload',()=>{stopTasksPolling()})}
console.log('任务列表轮询已启动(治理:不可见暂停/指数退避/AbortController)')}
function stopTasksPolling(){if(tasksPollingTimer){clearTimeout(tasksPollingTimer)
tasksPollingTimer=null
console.log('任务列表轮询已停止')}
try{if(tasksPollAbortController&&typeof tasksPollAbortController.abort==='function'){tasksPollAbortController.abort()}}catch(e){}finally{tasksPollAbortController=null}}
let isManualSwitching=false
let manualSwitchingTimer=null
Object.defineProperty(window,'isManualSwitching',{get:()=>isManualSwitching,set:val=>{isManualSwitching=val},configurable:true})
function updateTasksList(tasks){const oldTaskIds=currentTasks.map(t=>t.task_id)
const newTaskIds=tasks.map(t=>t.task_id)
const addedTasks=newTaskIds.filter(id=>!oldTaskIds.includes(id))
if(addedTasks.length>0){console.log(`✨ 检测到 ${addedTasks.length} 个新任务`)
if(activeTaskId){pendingNewTaskCount+=addedTasks.length
if(newTaskHintTimer){clearTimeout(newTaskHintTimer)}
newTaskHintTimer=setTimeout(()=>{if(pendingNewTaskCount>0){showNewTaskVisualHint(pendingNewTaskCount)
pendingNewTaskCount=0}
newTaskHintTimer=null},500)}
tasks.filter(t=>addedTasks.includes(t.task_id)).forEach(task=>{if(task.status!=='completed'&&!taskCountdowns[task.task_id]){const timeout=task.remaining_time??task.auto_resubmit_timeout??250
startTaskCountdown(task.task_id,timeout,task.auto_resubmit_timeout||250)
console.log(`已为新任务启动倒计时: ${task.task_id}, 剩余 ${timeout}s`)}})}
const removedTasks=oldTaskIds.filter(id=>!newTaskIds.includes(id))
if(removedTasks.length>0){console.log(`🗑️ 检测到 ${removedTasks.length} 个已删除任务`)
removedTasks.forEach(taskId=>{if(taskCountdowns[taskId]){clearInterval(taskCountdowns[taskId].timer)
delete taskCountdowns[taskId]
console.log(`✅ 已清理任务 ${taskId} 的倒计时`)}
if(window.taskDeadlines[taskId]!==undefined){delete window.taskDeadlines[taskId]}
if(taskTextareaContents[taskId]!==undefined){delete taskTextareaContents[taskId]}
if(taskOptionsStates[taskId]!==undefined){delete taskOptionsStates[taskId]}
if(taskImages[taskId]!==undefined){delete taskImages[taskId]}})}
const hasActiveTasks=tasks.length>0&&tasks.some(t=>t.status!=='completed')
currentTasks=tasks
tasks.forEach(task=>{if(task.status==='completed')return
const total=typeof task.auto_resubmit_timeout==='number'?task.auto_resubmit_timeout:250
if(total<=0){if(taskCountdowns[task.task_id]){try{if(taskCountdowns[task.task_id].timer){clearInterval(taskCountdowns[task.task_id].timer)}}catch(e){}
delete taskCountdowns[task.task_id]}
return}
if(!taskCountdowns[task.task_id]){const remaining=task.remaining_time??total
startTaskCountdown(task.task_id,remaining,total)}})
const activeTask=tasks.find(t=>t.status==='active')
if(activeTask&&activeTask.task_id!==activeTaskId){const oldActiveTaskId=activeTaskId
activeTaskId=activeTask.task_id
console.log(`同步activeTaskId: ${oldActiveTaskId} -> ${activeTaskId}`)
updateCountdownRingColors(oldActiveTaskId,activeTaskId)}else if(!activeTaskId&&tasks.length>0){const firstIncompleteTask=tasks.find(t=>t.status!=='completed')
if(firstIncompleteTask){activeTaskId=firstIncompleteTask.task_id
console.log(`自动设置第一个未完成任务为active: ${activeTaskId}`)}else{console.log('所有任务已完成,不设置activeTaskId')}}else if(tasks.length===0&&activeTaskId){console.log(`✅ 任务列表已清空,重置 activeTaskId: ${activeTaskId} -> null`)
activeTaskId=null}
const contentContainer=document.getElementById('content-container')
const noContentContainer=document.getElementById('no-content-container')
const isShowingNoContent=noContentContainer&&noContentContainer.style.display==='flex'
if(hasActiveTasks&&isShowingNoContent){console.log('🚀 有任务但显示无内容页面,切换到内容页面')
if(typeof showContentPage==='function'){showContentPage()}}else if(!hasActiveTasks&&contentContainer&&contentContainer.style.display==='block'){console.log('📭 无任务但显示内容页面,切换到无内容页面')
if(typeof showNoContentPage==='function'){showNoContentPage()}}
renderTaskTabs()
if(isManualSwitching){return}
if(activeTask&&activeTask.task_id===activeTaskId){loadTaskDetails(activeTaskId)}}
function updateTasksStats(stats){return}
function renderTaskTabs(){const tabsContainer=document.getElementById('task-tabs')
const container=document.getElementById('task-tabs-container')
if(!container||!tabsContainer){console.warn('标签栏容器未找到,可能DOM还未加载完成,将在100ms后重试')
setTimeout(()=>{const retryContainer=document.getElementById('task-tabs-container')
const retryTabsContainer=document.getElementById('task-tabs')
if(retryContainer&&retryTabsContainer){console.log('✅ 重试成功,开始渲染标签栏')
renderTaskTabs()}else{console.error('❌ 重试失败,标签栏容器仍然未找到')}},100)
return}
const incompleteTasks=currentTasks.filter(task=>task.status!=='completed')
if(incompleteTasks.length===0){container.classList.add('hidden')
return}
container.classList.remove('hidden')
const existingTabs=tabsContainer.querySelectorAll('.task-tab')
const existingTaskIds=Array.from(existingTabs).map(tab=>tab.dataset.taskId)
const currentTaskIds=currentTasks.map(t=>t.task_id)
const incompleteTaskIds=incompleteTasks.map(t=>t.task_id)
const needsRebuild=existingTaskIds.length!==incompleteTaskIds.length||existingTaskIds.some((id,i)=>id!==incompleteTaskIds[i])
if(needsRebuild){tabsContainer.innerHTML=''
incompleteTasks.forEach(task=>{const tab=createTaskTab(task)
tabsContainer.appendChild(tab)})}else{existingTabs.forEach(tab=>{const taskId=tab.dataset.taskId
const isActive=taskId===activeTaskId
tab.classList.toggle('active',isActive)})}}
function createTaskTab(task){const tab=document.createElement('div')
tab.className='task-tab'
if(task.status==='active'){tab.classList.add('active')}
tab.dataset.taskId=task.task_id
const textSpan=document.createElement('span')
textSpan.className='task-tab-text'
const taskParts=task.task_id.split('-')
const lastPart=taskParts[taskParts.length-1]
const prefixParts=taskParts.slice(0,-1).join('-')
let displayName
if(prefixParts.length>12){displayName=`${prefixParts.substring(0, 11)}... ${lastPart}`}else{displayName=`${prefixParts} ${lastPart}`}
textSpan.textContent=displayName
textSpan.title=task.task_id
tab.appendChild(textSpan)
if(task.status!=='completed'){const countdownRing=document.createElement('div')
countdownRing.className='countdown-ring'
countdownRing.id=`countdown-${task.task_id}`
let remaining,total
if(taskCountdowns[task.task_id]){remaining=taskCountdowns[task.task_id].remaining
total=taskCountdowns[task.task_id].timeout||250}else{remaining=task.remaining_time??task.auto_resubmit_timeout??250
total=task.auto_resubmit_timeout||250}
const radius=9
const circumference=2*Math.PI*radius
const progress=remaining/total
const offset=circumference*(1-progress)
const isActive=task.task_id===activeTaskId
const strokeColor=isActive?'rgba(255, 255, 255, 0.9)':'rgba(139, 92, 246, 0.9)'
countdownRing.innerHTML=`
<svg width="22" height="22" viewBox="0 0 22 22">
<circle
cx="11" cy="11" r="${radius}"
stroke="${strokeColor}"
stroke-width="3"
fill="none"
stroke-dasharray="${circumference}"
stroke-dashoffset="${offset}"
stroke-linecap="round"
/>
</svg>
<span class="countdown-number">${remaining}</span>
`
countdownRing.title=`剩余${remaining}秒`
tab.appendChild(countdownRing)}
tab.onclick=()=>switchTask(task.task_id)
return tab}
async function switchTask(taskId){if(activeTaskId){const textarea=document.getElementById('feedback-text')
if(textarea){taskTextareaContents[activeTaskId]=textarea.value
console.log(`✅ 已保存任务 ${activeTaskId} 的 textarea 内容`)}
const optionsContainer=document.getElementById('options-container')
if(optionsContainer){const checkboxes=optionsContainer.querySelectorAll('input[type="checkbox"]')
const optionsStates=[]
checkboxes.forEach((checkbox,index)=>{optionsStates[index]=checkbox.checked})
taskOptionsStates[activeTaskId]=optionsStates
console.log(`✅ 已保存任务 ${activeTaskId} 的选项勾选状态`)}
taskImages[activeTaskId]=selectedImages.map(img=>({...img}))
console.log(`✅ 已保存任务 ${activeTaskId} 的图片列表 (${selectedImages.length} 张)`)}
isManualSwitching=true
window.dispatchEvent(new CustomEvent('taskSwitchStart',{detail:{taskId}}))
const oldActiveTaskId=activeTaskId
activeTaskId=taskId
renderTaskTabs()
updateCountdownRingColors(oldActiveTaskId,taskId)
const cachedTask=currentTasks.find(t=>t.task_id===taskId)
if(cachedTask&&cachedTask.prompt){console.log(`🚀 使用缓存任务信息立即更新内容: ${taskId}`)
const taskIdContainer=document.getElementById('task-id-container')
const taskIdText=document.getElementById('task-id-text')
if(taskIdContainer&&taskIdText){if(cachedTask.task_id&&cachedTask.task_id.trim()){taskIdText.textContent=cachedTask.task_id
taskIdContainer.classList.remove('hidden')}else{taskIdContainer.classList.add('hidden')}}
updateDescriptionDisplay(cachedTask.prompt)
if(cachedTask.predefined_options){updateOptionsDisplay(cachedTask.predefined_options)}}
try{fetch(`/api/tasks/${taskId}/activate`,{method:'POST'}).then(res=>res.json()).then(data=>{if(!data.success){console.error('激活任务失败:',data.error)}else{console.log(`✅ 任务已激活: ${taskId}`)}}).catch(err=>console.error('激活任务失败:',err))
loadTaskDetails(taskId).catch(err=>{console.warn('加载任务详情失败,但UI已从缓存更新:',err)})}catch(error){console.error('切换任务失败:',error)}finally{if(manualSwitchingTimer){clearTimeout(manualSwitchingTimer)}
manualSwitchingTimer=setTimeout(()=>{isManualSwitching=false
manualSwitchingTimer=null
window.dispatchEvent(new CustomEvent('taskSwitchComplete',{detail:{taskId}}))
console.log('✅ 任务切换锁定已解除,允许轮询恢复')},200)}}
function updateCountdownRingColors(oldActiveTaskId,newActiveTaskId){if(oldActiveTaskId){const oldRing=document.getElementById(`countdown-${oldActiveTaskId}`)
if(oldRing){const oldCircle=oldRing.querySelector('circle')
if(oldCircle){oldCircle.setAttribute('stroke','rgba(139, 92, 246, 0.9)')}}}
if(newActiveTaskId){const newRing=document.getElementById(`countdown-${newActiveTaskId}`)
if(newRing){const newCircle=newRing.querySelector('circle')
if(newCircle){newCircle.setAttribute('stroke','rgba(255, 255, 255, 0.9)')}}}}
async function loadTaskDetails(taskId){try{const response=await fetch(`/api/tasks/${taskId}`)
const data=await response.json()
if(taskId!==activeTaskId){console.log(`⏭️ 跳过过期的任务详情: ${taskId}(当前活动: ${activeTaskId})`)
return}
if(data.success){const task=data.task
const taskIdContainer=document.getElementById('task-id-container')
const taskIdText=document.getElementById('task-id-text')
if(taskIdContainer&&taskIdText){if(task.task_id&&task.task_id.trim()){taskIdText.textContent=task.task_id
taskIdContainer.classList.remove('hidden')}else{taskIdContainer.classList.add('hidden')}}
updateDescriptionDisplay(task.prompt)
updateOptionsDisplay(task.predefined_options)
const textarea=document.getElementById('feedback-text')
if(textarea&&taskTextareaContents[taskId]!==undefined){textarea.value=taskTextareaContents[taskId]
console.log(`✅ 已恢复任务 ${taskId} 的 textarea 内容`)}
if(taskImages[taskId]&&taskImages[taskId].length>0){selectedImages=taskImages[taskId].map(img=>({...img}))
const previewContainer=document.getElementById('image-previews')
if(previewContainer){previewContainer.innerHTML=''
selectedImages.forEach(imageItem=>{renderImagePreview(imageItem,false)})
updateImageCounter()
updateImagePreviewVisibility()}
console.log(`✅ 已恢复任务 ${taskId} 的图片列表 (${selectedImages.length} 张)`)}
if(!taskCountdowns[task.task_id]){const remaining=task.remaining_time??task.auto_resubmit_timeout
const total=task.auto_resubmit_timeout
startTaskCountdown(task.task_id,remaining,total)
console.log(`首次启动倒计时: ${taskId}, 剩余 ${remaining}s / 总 ${total}s`)}else{console.log(`倒计时已存在,不重置: ${taskId}`)}
console.log(`已加载任务详情: ${taskId}`)}else{console.error('加载任务详情失败:',data.error)}}catch(error){console.error('加载任务详情失败:',error)}}
async function updateDescriptionDisplay(prompt){const descriptionElement=document.getElementById('description')
if(!descriptionElement)return
try{let htmlContent=prompt
if(typeof marked!=='undefined'){try{htmlContent=marked.parse(prompt)}catch(e){console.warn('marked.js 解析失败:',e)}}
descriptionElement.innerHTML=htmlContent
if(typeof Prism!=='undefined'){Prism.highlightAllUnder(descriptionElement)}
if(typeof processCodeBlocks==='function'){processCodeBlocks(descriptionElement)}
if(typeof processStrikethrough==='function'){processStrikethrough(descriptionElement)}
console.log('✅ 同步渲染 Markdown 完成')
const textContent=descriptionElement.textContent||''
if(window.loadMathJaxIfNeeded){window.loadMathJaxIfNeeded(descriptionElement,textContent)}else if(window.MathJax&&window.MathJax.typesetPromise){window.MathJax.typesetPromise([descriptionElement]).catch(err=>{console.warn('MathJax 渲染失败:',err)})}}catch(error){console.error('更新描述失败:',error)
descriptionElement.textContent=prompt}}
function updateOptionsDisplay(options){const optionsContainer=document.getElementById('options-container')
if(!optionsContainer)return
let selectedStates={}
if(activeTaskId&&taskOptionsStates[activeTaskId]){selectedStates=taskOptionsStates[activeTaskId]
console.log(`✅ 已恢复任务 ${activeTaskId} 的选项勾选状态`)}else{const existingCheckboxes=optionsContainer.querySelectorAll('input[type="checkbox"]')
existingCheckboxes.forEach(checkbox=>{selectedStates[checkbox.id]=checkbox.checked})}
optionsContainer.innerHTML=''
if(options&&options.length>0){options.forEach((option,index)=>{const optionDiv=document.createElement('div')
optionDiv.className='option-item'
const checkbox=document.createElement('input')
checkbox.type='checkbox'
checkbox.id=`option-${index}`
checkbox.value=option
const checkboxId=`option-${index}`
if(selectedStates[checkboxId]||selectedStates[index]){checkbox.checked=true}
const label=document.createElement('label')
label.htmlFor=`option-${index}`
label.textContent=option
optionDiv.appendChild(checkbox)
optionDiv.appendChild(label)
optionsContainer.appendChild(optionDiv)})
optionsContainer.classList.remove('hidden')
optionsContainer.classList.add('visible')
const separator=document.getElementById('separator')
if(separator){separator.classList.remove('hidden')
separator.classList.add('visible')}}else{optionsContainer.classList.add('hidden')
optionsContainer.classList.remove('visible')}}
async function closeTask(taskId){if(!confirm(`确定要关闭任务 ${taskId} 吗?`)){return}
try{if(taskCountdowns[taskId]){clearInterval(taskCountdowns[taskId].timer)
delete taskCountdowns[taskId]}
if(taskTextareaContents[taskId]!==undefined){delete taskTextareaContents[taskId]
console.log(`✅ [关闭任务] 已清除任务 ${taskId} 保存的 textarea 内容`)}
if(taskOptionsStates[taskId]!==undefined){delete taskOptionsStates[taskId]
console.log(`✅ [关闭任务] 已清除任务 ${taskId} 保存的选项勾选状态`)}
if(taskImages[taskId]!==undefined){delete taskImages[taskId]
console.log(`✅ [关闭任务] 已清除任务 ${taskId} 保存的图片列表`)}
currentTasks=currentTasks.filter(t=>t.task_id!==taskId)
renderTaskTabs()
if(activeTaskId===taskId&¤tTasks.length>0){switchTask(currentTasks[0].task_id)}
console.log(`已关闭任务: ${taskId}`)}catch(error){console.error('关闭任务失败:',error)}}
function startTaskCountdown(taskId,remaining,total=null){const timeout=total||remaining
if(taskCountdowns[taskId]&&taskCountdowns[taskId].timer){clearInterval(taskCountdowns[taskId].timer)}
taskCountdowns[taskId]={remaining:remaining,timeout:timeout,timer:null}
if(taskId===activeTaskId){updateCountdownDisplay(remaining)}
function calculateRemainingFromDeadline(){const deadline=window.taskDeadlines[taskId]
if(deadline){const adjustedNow=Date.now()/1000+(window.serverTimeOffset||0)
return Math.max(0,Math.floor(deadline-adjustedNow))}
return taskCountdowns[taskId].remaining-1}
taskCountdowns[taskId].timer=setInterval(()=>{const newRemaining=calculateRemainingFromDeadline()
taskCountdowns[taskId].remaining=newRemaining
const countdownRing=document.getElementById(`countdown-${taskId}`)
if(countdownRing){const remaining=taskCountdowns[taskId].remaining
const total=taskCountdowns[taskId].timeout||250
const progress=remaining/total
const radius=9
const circumference=2*Math.PI*radius
const offset=circumference*(1-progress)
const circle=countdownRing.querySelector('circle')
const numberSpan=countdownRing.querySelector('.countdown-number')
if(circle){circle.setAttribute('stroke-dashoffset',offset)}
if(numberSpan){numberSpan.textContent=remaining}
countdownRing.title=`剩余${remaining}秒`}
if(taskId===activeTaskId){updateCountdownDisplay(taskCountdowns[taskId].remaining)}
if(taskCountdowns[taskId].remaining<=0){clearInterval(taskCountdowns[taskId].timer)
if(taskId===activeTaskId){autoSubmitTask(taskId)}else{if(!activeTaskId){console.log(`非激活任务 ${taskId} 超时,且无活动任务,自动提交`)
autoSubmitTask(taskId)}else{console.log(`任务 ${taskId} 超时,但用户正在处理其他任务 ${activeTaskId},暂不自动提交`)}}}},1000)
console.log(`已启动任务倒计时: ${taskId}, 剩余 ${remaining}s / 总 ${timeout}s`)}
function formatCountdown(seconds){if(seconds>60){return`${Math.floor(seconds / 60)}m`}
return`${seconds}s`}
async function autoSubmitTask(taskId){console.log(`任务 ${taskId} 倒计时结束,自动提交`)
const prompts=await fetchFeedbackPromptsFresh()
const defaultMessage=prompts&&prompts.resubmit_prompt?prompts.resubmit_prompt:'请立即调用 interactive_feedback 工具'
await submitTaskFeedback(taskId,defaultMessage,[])}
async function submitTaskFeedback(taskId,feedbackText,selectedOptions){try{const formData=new FormData()
formData.append('feedback_text',feedbackText)
formData.append('selected_options',JSON.stringify(selectedOptions))
selectedImages.forEach((img,index)=>{if(img.file){formData.append(`image_${index}`,img.file)}})
const response=await fetch(`/api/tasks/${taskId}/submit`,{method:'POST',body:formData})
const data=await response.json()
if(data.success){console.log(`任务 ${taskId} 提交成功`)
if(taskCountdowns[taskId]){clearInterval(taskCountdowns[taskId].timer)
delete taskCountdowns[taskId]}
if(taskTextareaContents[taskId]!==undefined){delete taskTextareaContents[taskId]
console.log(`✅ 已清除任务 ${taskId} 保存的 textarea 内容`)}
if(taskOptionsStates[taskId]!==undefined){delete taskOptionsStates[taskId]
console.log(`✅ 已清除任务 ${taskId} 保存的选项勾选状态`)}
if(taskImages[taskId]!==undefined){delete taskImages[taskId]
console.log(`✅ 已清除任务 ${taskId} 保存的图片列表`)}
setTimeout(async()=>{await refreshTasksList()
const nextTask=currentTasks.find(t=>t.task_id!==taskId&&t.status!=='completed')
if(nextTask){console.log(`🔄 自动切换到下一个任务: ${nextTask.task_id}`)
switchTask(nextTask.task_id)}else{console.log(`✅ 所有任务已完成`)}},300)}else{console.error('提交任务失败:',data.error)}}catch(error){console.error('提交任务反馈失败:',error)}}
function showNewTaskVisualHint(count){const container=document.getElementById('task-tabs-container')
if(!container)return
const html=document.documentElement
const currentTheme=html.getAttribute('data-theme')
const isLightTheme=currentTheme==='light'
const createSvg=`<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 20 20" fill="none" style="flex-shrink: 0; margin-right: 10px;"><path d="M15.5117 1.99707C15.9213 2.0091 16.3438 2.13396 16.6768 2.46679C17.0278 2.81814 17.1209 3.26428 17.0801 3.68261C17.0404 4.08745 16.8765 4.49344 16.6787 4.85058C16.3934 5.36546 15.9941 5.85569 15.6348 6.20898C15.7682 6.41421 15.8912 6.66414 15.9551 6.9453C16.0804 7.4977 15.9714 8.13389 15.4043 8.70116C14.8566 9.24884 13.974 9.54823 13.1943 9.71679C12.7628 9.81003 12.3303 9.86698 11.9473 9.90233C12.0596 10.2558 12.0902 10.7051 11.8779 11.2012L11.8223 11.3203C11.5396 11.8854 11.0275 12.2035 10.4785 12.3965C9.93492 12.5875 9.29028 12.6792 8.65332 12.75C7.99579 12.8231 7.34376 12.8744 6.70117 12.9775C6.14371 13.067 5.63021 13.1903 5.18652 13.3818L5.00585 13.4658C4.53515 14.2245 4.13745 14.9658 3.80957 15.6465C4.43885 15.2764 5.1935 15 5.99999 15C6.27614 15 6.49999 15.2238 6.49999 15.5C6.49999 15.7761 6.27613 16 5.99999 16C5.35538 16 4.71132 16.2477 4.15039 16.6103C3.58861 16.9736 3.14957 17.427 2.91601 17.7773C2.91191 17.7835 2.90568 17.788 2.90136 17.7939C2.88821 17.8119 2.8746 17.8289 2.85937 17.8447C2.85117 17.8533 2.84268 17.8612 2.83398 17.8691C2.81803 17.8835 2.80174 17.897 2.78417 17.9092C2.774 17.9162 2.76353 17.9225 2.75292 17.9287C2.73854 17.9372 2.72412 17.9451 2.70898 17.9521C2.69079 17.9605 2.6723 17.9675 2.65332 17.9736C2.6417 17.9774 2.63005 17.9805 2.61816 17.9834C2.60263 17.9872 2.5871 17.9899 2.57128 17.9922C2.55312 17.9948 2.53511 17.9974 2.5166 17.998C2.50387 17.9985 2.49127 17.9976 2.47851 17.9971C2.45899 17.9962 2.43952 17.9954 2.41992 17.9922C2.40511 17.9898 2.39062 17.9862 2.37597 17.9824C2.36477 17.9795 2.35294 17.9783 2.34179 17.9746C2.33697 17.973 2.33286 17.9695 2.32812 17.9678C2.31042 17.9612 2.29351 17.953 2.27636 17.9443C2.26332 17.9378 2.25053 17.9314 2.23828 17.9238C2.23339 17.9208 2.22747 17.9192 2.22265 17.916C2.21414 17.9103 2.20726 17.9026 2.19921 17.8965C2.18396 17.8849 2.16896 17.8735 2.15527 17.8603C2.14518 17.8507 2.13609 17.8404 2.12695 17.8301C2.11463 17.8161 2.10244 17.8023 2.09179 17.7871C2.08368 17.7756 2.07736 17.7631 2.07031 17.751C2.06168 17.7362 2.05297 17.7216 2.04589 17.706C2.03868 17.6901 2.03283 17.6738 2.02734 17.6572C2.0228 17.6436 2.01801 17.6302 2.01464 17.6162C2.01117 17.6017 2.009 17.587 2.00683 17.5722C2.00411 17.5538 2.00161 17.5354 2.00097 17.5166C2.00054 17.5039 2.00141 17.4912 2.00195 17.4785C2.00279 17.459 2.00364 17.4395 2.00683 17.4199C2.00902 17.4064 2.01327 17.3933 2.0166 17.3799C2.01973 17.3673 2.02123 17.3543 2.02539 17.3418C2.41772 16.1648 3.18163 14.466 4.30468 12.7012C4.31908 12.5557 4.34007 12.3582 4.36914 12.1201C4.43379 11.5907 4.53836 10.8564 4.69921 10.0381C5.0174 8.41955 5.56814 6.39783 6.50585 4.9912L6.73242 4.66894C7.27701 3.93277 7.93079 3.30953 8.61035 2.85156C9.3797 2.33311 10.2221 2 11.001 2C11.7951 2.00025 12.3531 2.35795 12.7012 2.70605C12.7723 2.77723 12.8348 2.84998 12.8896 2.91796C13.2829 2.66884 13.7917 2.39502 14.3174 2.21191C14.6946 2.08056 15.1094 1.98537 15.5117 1.99707ZM17.04 15.5537C17.1486 15.3 17.4425 15.1818 17.6963 15.29C17.95 15.3986 18.0683 15.6925 17.96 15.9463C17.4827 17.0612 16.692 18 15.5 18C14.6309 17.9999 13.9764 17.5003 13.5 16.7978C13.0236 17.5003 12.3691 18 11.5 18C10.6309 17.9999 9.97639 17.5003 9.49999 16.7978C9.02359 17.5003 8.36911 18 7.49999 18C7.22391 17.9999 7 17.7761 6.99999 17.5C6.99999 17.2239 7.22391 17 7.49999 17C8.07039 17 8.6095 16.5593 9.04003 15.5537L9.07421 15.4873C9.16428 15.3412 9.32494 15.25 9.49999 15.25C9.70008 15.25 9.88121 15.3698 9.95996 15.5537L10.042 15.7353C10.4581 16.6125 10.9652 16.9999 11.5 17C12.0704 17 12.6095 16.5593 13.04 15.5537L13.0742 15.4873C13.1643 15.3412 13.3249 15.25 13.5 15.25C13.7001 15.25 13.8812 15.3698 13.96 15.5537L14.042 15.7353C14.4581 16.6125 14.9652 16.9999 15.5 17C16.0704 17 16.6095 16.5593 17.04 15.5537ZM15.4824 2.99707C15.247 2.99022 14.9608 3.04682 14.6465 3.15624C14.0173 3.37541 13.389 3.76516 13.0498 4.01953C12.9277 4.11112 12.7697 4.14131 12.6221 4.10253C12.4745 4.06357 12.3522 3.9591 12.291 3.81933V3.81835C12.2892 3.81468 12.2861 3.80833 12.2822 3.80078C12.272 3.78092 12.2541 3.7485 12.2295 3.70898C12.1794 3.62874 12.1011 3.52019 11.9941 3.41308C11.7831 3.2021 11.4662 3.00024 11.001 2.99999C10.4904 2.99999 9.84173 3.22729 9.16894 3.68066C8.58685 4.07297 8.01568 4.61599 7.5371 5.26269L7.33789 5.54589C6.51634 6.77827 5.99475 8.63369 5.68066 10.2314C5.63363 10.4707 5.5913 10.7025 5.55371 10.9238C7.03031 9.01824 8.94157 7.19047 11.2812 6.05077C11.5295 5.92989 11.8283 6.03301 11.9492 6.28124C12.0701 6.52949 11.967 6.82829 11.7187 6.94921C9.33153 8.11208 7.38648 10.0746 5.91406 12.1103C6.12313 12.0632 6.33385 12.0238 6.54296 11.9902C7.21709 11.8821 7.92723 11.8243 8.54296 11.7558C9.17886 11.6852 9.72123 11.6025 10.1465 11.4531C10.5662 11.3056 10.8063 11.1158 10.9277 10.873L10.9795 10.7549C11.0776 10.487 11.0316 10.2723 10.9609 10.1123C10.918 10.0155 10.8636 9.93595 10.8203 9.88183C10.7996 9.85598 10.7822 9.83638 10.7715 9.82518L10.7607 9.81542L10.7627 9.8164L10.7646 9.81835C10.6114 9.67972 10.5597 9.46044 10.6338 9.26757C10.7082 9.07475 10.8939 8.94726 11.1006 8.94726C11.5282 8.94719 12.26 8.8956 12.9834 8.73925C13.7297 8.5779 14.3654 8.32602 14.6973 7.99413C15.0087 7.68254 15.0327 7.40213 14.9795 7.16698C14.9332 6.96327 14.8204 6.77099 14.707 6.62792L14.5957 6.50195C14.4933 6.39957 14.4401 6.25769 14.4502 6.11327C14.4605 5.96888 14.5327 5.83599 14.6484 5.74902C14.9558 5.51849 15.4742 4.96086 15.8037 4.3662C15.9675 4.07048 16.0637 3.80137 16.085 3.58593C16.1047 3.38427 16.0578 3.26213 15.9697 3.17382C15.8631 3.06726 15.7102 3.00377 15.4824 2.99707Z" fill="#d97757"/></svg>`
const themeStyles=isLightTheme?{background:'linear-gradient(135deg, #faf9f5 0%, #f2f1ec 100%)',color:'#131314',border:'1px solid rgba(217, 119, 87, 0.4)',boxShadow:'0 8px 24px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(217, 119, 87, 0.15)'}:{background:'rgba(45, 45, 60, 0.95)',color:'rgba(245, 245, 247, 0.95)',border:'1px solid rgba(255, 255, 255, 0.08)',boxShadow:'0 8px 24px rgba(0, 0, 0, 0.35)'}
const hint=document.createElement('div')
hint.id='new-task-hint'
hint.style.cssText=`
position: fixed;
top: 20px;
right: 20px;
display: flex;
align-items: center;
background: ${themeStyles.background};
color: ${themeStyles.color};
padding: 14px 20px;
border-radius: 12px;
border: ${themeStyles.border};
box-shadow: ${themeStyles.boxShadow};
font-size: 14px;
font-weight: 500;
letter-spacing: 0.02em;
z-index: 10000;
animation: slideInRight 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), fadeOutUp 0.3s ease-in 2.7s forwards;
pointer-events: none;
`
hint.innerHTML=`${createSvg}<span>${count} 个新任务已到达</span>`
document.body.appendChild(hint)
setTimeout(()=>{if(hint.parentNode){hint.parentNode.removeChild(hint)}},3000)
console.log(`显示新任务视觉提示: ${count} 个新任务`)}
function showNewTaskNotification(count){showNewTaskVisualHint(count)
if(typeof notificationManager!=='undefined'){notificationManager.sendNotification('AI Intervention Agent',`收到 ${count} 个新任务`,{tag:'new-tasks',requireInteraction:false}).catch(error=>{console.warn('发送新任务通知失败:',error)})}}
async function initMultiTaskSupport(){console.log('初始化多任务支持...')
await fetchFeedbackPromptsFresh()
await refreshTasksList()
startTasksPolling()
setInterval(()=>{if(typeof document!=='undefined'&&document.hidden){return}
if(!tasksPollingTimer){console.warn('⚠️ 任务轮询已停止,自动重新启动')
startTasksPolling()}},30000)
const textarea=document.getElementById('feedback-text')
if(textarea){textarea.addEventListener('input',()=>{if(activeTaskId){taskTextareaContents[activeTaskId]=textarea.value}})
console.log('✅ 已启用 textarea 实时保存')}
const optionsContainer=document.getElementById('options-container')
if(optionsContainer){optionsContainer.addEventListener('change',event=>{if(event.target.type==='checkbox'&&activeTaskId){const checkboxes=optionsContainer.querySelectorAll('input[type="checkbox"]')
const states={}
checkboxes.forEach(cb=>{states[cb.id]=cb.checked})
taskOptionsStates[activeTaskId]=states}})
console.log('✅ 已启用选项状态实时保存')}
console.log('多任务支持初始化完成 (包含轮询健康检查和实时保存)')}
async function refreshTasksList(){const ok=await fetchAndApplyTasks('manual')
if(ok){tasksPollBackoffMs=TASKS_POLL_BASE_MS
console.log('任务列表已手动刷新')}
if(!tasksPollingTimer&&!(typeof document!=='undefined'&&document.hidden)){startTasksPolling()}}
if(typeof window!=='undefined'){window.multiTaskModule={startTasksPolling,stopTasksPolling,switchTask,closeTask,initMultiTaskSupport,refreshTasksList}
window.refreshTasksList=refreshTasksList}
if(typeof document!=='undefined'&&typeof document.addEventListener==='function'){document.addEventListener('DOMContentLoaded',()=>{fetchFeedbackPromptsFresh()})}