'+t('这是一次历史失败,不代表仓库当前不可查询。未关联后续请求,无法判断是否已重试成功。','This is a historical failure, not the current repository health. Later requests are not linked, so retry success is unknown.')+'
'+t('上下文过长,已截断。','Context exceeded the limit and was truncated.')+'
':''); + $('#detail-body').innerHTML=''+t('这是一次历史失败,不代表仓库当前不可查询。未关联后续请求,无法判断是否已重试成功。','This is a historical failure, not the current repository health. Later requests are not linked, so retry success is unknown.')+'
'+(batchText(detail)?''+esc(batchText(detail))+'
':'')+''+t('上下文过长,已截断。','Context exceeded the limit and was truncated.')+'
':''); if(detail.query_fragment || detail.suggested_query || detail.suggestion_omitted){ const recovery=document.createElement('section'); recovery.className='mt-6 rounded-doc bg-sand p-4'; diff --git a/web/assets/read-page.js b/web/assets/read-page.js index f6e59c4..de50c3e 100644 --- a/web/assets/read-page.js +++ b/web/assets/read-page.js @@ -79,7 +79,7 @@ function renderExtent(){ $('#read-extent').textContent=state.directory||!lines.length?'':!canBelow?t('已读到文件末尾','end of file'):t('已读到第 '+max+' 行','read up to line '+max); } function error(error){ - state.error=error;const messages={BINARY_FILE:t('这是二进制文件,不能预览','This is a binary file and cannot be previewed'),UNSUPPORTED_TEXT_ENCODING:t('文件不是 UTF-8 文本,不能预览','The file is not UTF-8 text and cannot be previewed'),FILE_LINE_TOO_LARGE:t('有一行太长,请在 代码托管站 查看','A line is too long. View it on the code host'),READ_SCAN_LIMIT:t('这几行离文件开头太远,请在 代码托管站 查看','These lines are too far into the file. View them on the code host'),LINE_RANGE_NOT_SATISFIABLE:t('文件没有这么多行','The file does not have that many lines'),FILE_NOT_FOUND:t('这个提交里没有这个文件','This file does not exist at this commit'),REVISION_NOT_ELIGIBLE:t('这个版本不在保留范围内','This version is outside the retention window'),REVISION_NOT_FOUND:t('找不到这个提交','This commit was not found')}; + state.error=error;const messages={BINARY_FILE:t('这是二进制文件,不能预览','This is a binary file and cannot be previewed'),UNSUPPORTED_TEXT_ENCODING:t('文件不是 UTF-8 文本,不能预览','The file is not UTF-8 text and cannot be previewed'),FILE_LINE_TOO_LARGE:t('有一行太长,请在 代码托管站 查看','A line is too long. View it on the code host'),READ_SCAN_LIMIT:t('这几行离文件开头太远,请在 代码托管站 查看','These lines are too far into the file. View them on the code host'),LINE_RANGE_NOT_SATISFIABLE:t('文件没有这么多行','The file does not have that many lines'),FILE_NOT_FOUND:t('这个提交里没有这个文件','This file does not exist at this commit'),REVISION_NOT_ELIGIBLE:t('该仓库尚未观察到此版本,请核对仓库与完整 SHA,再检查同步和监听范围','This version is not observed for this repository; verify the repository and full SHA, then check synchronization and monitored branches'),REVISION_NOT_FOUND:t('找不到这个提交','This commit was not found')}; const stale=FULL_SHA.test(state.revision)&&(error.code==='REVISION_NOT_ELIGIBLE'||error.code==='REVISION_NOT_FOUND'); const known=messages[error.code], msg=known||error.message; $('#read-notice').className='mt-4 text-[13px] text-danger';$('#read-notice').innerHTML=esc(msg)+(known?'':''+esc(error.code||'REQUEST_FAILED')+(error.requestId?' · '+esc(error.requestId):'')+'')+''+(error.status===401?'':'')+(error.code==='LINE_RANGE_NOT_SATISFIABLE'?'':stale?''+t('在默认分支最新提交打开','Open at the latest default-branch commit')+'':error.code==='INVALID_LOCATION'?'':'')+''; diff --git a/web/assets/repo-maintenance.js b/web/assets/repo-maintenance.js index 82b5a88..f9ff4b3 100644 --- a/web/assets/repo-maintenance.js +++ b/web/assets/repo-maintenance.js @@ -11,7 +11,7 @@ const fullSHA=commit=>typeof commit==='string'&&/^[0-9a-f]{40}$/.test(commit); const branchNames=()=>[...new Set([...(repo().policy.branches||[]),...(repo().policy.default_branch&&repo().default_branch?[repo().default_branch]:[])])]; function error(e){ - const messages={REPOSITORY_BUSY:t('仓库正在执行任务,请先在任务页停止运行中的任务,再重试','Stop the running repository jobs from Jobs, then retry'),LATEST_INDEX_NOT_READY:t('部分监听分支的最新提交尚未建立索引,请先检查更新并完成索引,再清理','Some watched branch heads are not indexed. Check for updates and finish indexing before cleanup'),REVISION_NOT_ELIGIBLE:t('此提交不在当前保留范围内,请调整仓库设置后重试','This commit is outside the retention policy. Adjust repository settings before retrying'),REVISION_NOT_ON_BRANCH:t('此提交不属于所选分支,请选择对应分支或其他提交','This commit does not belong to the selected branch'),DEFAULT_BRANCH_NOT_OBSERVED:t('主干尚未同步,请先检查更新','The default branch has not synchronized. Check for updates first'),BRANCH_NOT_OBSERVED:t('此分支尚未同步,请先检查更新','This branch has not synchronized. Check for updates first'),REPOSITORY_STORAGE_MISSING:t('仓库本地材料尚未就绪,请先检查更新','Local repository data is not ready. Check for updates first')}; + const messages={REPOSITORY_BUSY:t('仓库正在执行任务,请先在任务页停止运行中的任务,再重试','Stop the running repository jobs from Jobs, then retry'),LATEST_INDEX_NOT_READY:t('部分监听分支的最新提交尚未建立索引,请先检查更新并完成索引,再清理','Some watched branch heads are not indexed. Check for updates and finish indexing before cleanup'),REVISION_NOT_ELIGIBLE:t('该仓库尚未观察到此提交;先核对仓库与完整 SHA,再检查监听、同步和保留范围','This commit is not observed for this repository. Verify its repository and full SHA, then check monitored branches, synchronization and retention'),REVISION_NOT_ON_BRANCH:t('此提交不属于所选分支,请选择对应分支或其他提交','This commit does not belong to the selected branch'),DEFAULT_BRANCH_NOT_OBSERVED:t('主干尚未同步,请先检查更新','The default branch has not synchronized. Check for updates first'),BRANCH_NOT_OBSERVED:t('此分支尚未同步,请先检查更新','This branch has not synchronized. Check for updates first'),REPOSITORY_STORAGE_MISSING:t('仓库本地材料尚未就绪,请先检查更新','Local repository data is not ready. Check for updates first')}; const target=$('#maintenance-error');if(target)target.textContent=messages[e.code]||M.error(e); } function lock(value){working=value;options.busy(value);d.querySelectorAll('button,input,select').forEach(el=>el.disabled=value);} @@ -33,7 +33,7 @@ $('#index-commit-summary').innerHTML=(current?''+commitRow(current)+'':''+t('选择最近的提交','Choose a recent commit')+'')+''+A.ICON.chevron+''; $('#index-commit-list').innerHTML=indexState.commits.length?indexState.commits.map(c=>'').join(''):''+t('当前没有可选提交,请先检查更新','No commits available. Check for updates first')+'
'; d.querySelectorAll('[data-select-commit]').forEach(button=>button.onclick=()=>{indexState.commit=button.dataset.selectCommit;$('#index-sha').value=indexState.commit;$('#index-commits').open=false;renderCommitChoice();}); - const note=$('#index-note');note.textContent=current?.eligible===false?t('此提交不在当前保留范围内,需要先调整仓库设置','This commit is outside retention; adjust repository settings first'):current?.indexed?t('这个提交已有索引,确认时会检查是否仍有范围需要准备','This commit is indexed. Confirmation checks for any remaining scopes'):t('列出此分支最近 20 个已同步提交,也可输入完整 SHA','Shows the latest 20 synchronized commits on this branch. You can also enter a full SHA'); + const note=$('#index-note');note.textContent=current?.eligible===false?t('该仓库当前未观察到此提交,请先核对仓库、完整 SHA 与同步范围','This commit is not currently observed; verify the repository, full SHA and synchronization scope first'):current?.indexed?t('这个提交已有索引,确认时会检查是否仍有范围需要准备','This commit is indexed. Confirmation checks for any remaining scopes'):t('列出此分支最近 20 个已同步提交,也可输入完整 SHA','Shows the latest 20 synchronized commits on this branch. You can also enter a full SHA'); } function renderIndex(){ const branches=indexState.branches; diff --git a/web/assets/repo-page.js b/web/assets/repo-page.js index e8dcf4b..16b31ba 100644 --- a/web/assets/repo-page.js +++ b/web/assets/repo-page.js @@ -31,7 +31,7 @@ function kindName(k){const x=KIND[k]||[k,k];return t(x[0],x[1]);} function jobRecoveredAt(j){return R.recoveredAt(repo,j);} function jobText(j){ - if(!j)return ''+t('暂无任务','No jobs yet')+''; + if(!j)return ''+t('暂无保留任务','No retained jobs')+''; const k=KIND[j.kind]||[j.kind,j.kind], at=mono(esc(A.clock(j.updated_at||j.created_at))); if(jobRecoveredAt(j))return ''+t('后续检查或任务已恢复 · ','Recovered by a later check or task · ')+esc(A.clock(jobRecoveredAt(j)))+''; if(j.state==='failed')return ''+t(k[0]+'失败',k[1]+' failed')+(j.error?' · '+esc(API.jobError(j)):'')+''; @@ -131,8 +131,10 @@ function jobs(){ const js=R.jobs(repo); const checks=Object.entries(repo.sync_checks||{}).map(([group,c])=>''+(group?esc(group)+' · ':'')+t('最近检查 ','Last checked ')+esc(A.clock(c.checked_at))+' · '+(c.error?esc(API.jobError({error:c.error})):c.no_changes?t('版本未变化,无需索引','No version changes; indexing skipped'):t('检查完成','Check completed'))+(c.unchanged_count>1?t(' · 连续 '+c.unchanged_count+' 次无变化',' · '+c.unchanged_count+' consecutive unchanged checks'):'')+'
').join(''); - const summary=checks?''+t('还没有任务','No jobs yet')+'
'; + const failures=(repo.job_failures||[]).filter(f=>Date.now()-Date.parse(f.time)<7*86400000); + const history=failures.length?''+t('保留最近 7 天,最多 128 条;独立于任务队列。历史失败不代表当前状态。','Last 7 days, up to 128 records; separate from the task queue. Historical failures do not describe current health.')+'
'+t('暂无保留的任务。终态任务会自动清理;失败摘要另保留最近 7 天最多 128 条。','No retained jobs. Terminal jobs are automatically cleaned up; failure summaries retain the last 7 days, up to 128 records.')+'
'; const live=repo.enabled&&!repo.deleted; const rest=js.length-jobLimit; return summary+'