diff --git a/assets/template-swiss.html b/assets/template-swiss.html
index 70ff265..76ec15a 100644
--- a/assets/template-swiss.html
+++ b/assets/template-swiss.html
@@ -1756,7 +1756,7 @@ window.__pptPresenter=(()=>{
观众屏 · 未连接
- 已进行00:00总时长
本页00:00计划 —
计划—未设置
+ 已进行00:00总时长
本页00:00计划 —
计划—未设置
`);
document.getElementById('ppt-reopen').onclick=reopenAudience;
@@ -1828,7 +1828,7 @@ window.__pptPresenter=(()=>{
if(plan)plan.textContent=totalPlan?`计划 ${Number(totalPlan/60000).toFixed(1)} 分钟 · 预计 ${clockTime(Date.now()+Math.max(0,totalPlan-totalUsed))}`:'计划 —';
const section=sectionInfo(idx),sectionEl=document.getElementById('ppt-section');
if(sectionEl){sectionEl.hidden=!section;sectionEl.textContent=section?`${section.name} · ${section.position}/${section.count}${section.planned?` · 剩余 ${durationText(Math.max(0,section.planned-section.used))}`:''}`:'';}
- const timerButton=document.getElementById('ppt-timer-toggle');if(timerButton)timerButton.textContent=timerRunning?'暂停':(totalUsed?'继续':'开始');
+ const timerButton=document.getElementById('ppt-timer-toggle');if(timerButton)timerButton.textContent=timerRunning?'暂停':(totalUsed?'继续计时':'开始计时');
const rehearsalButton=document.getElementById('ppt-rehearsal');if(rehearsalButton){rehearsalButton.textContent=rehearsalActive?'结束排练':'排练';rehearsalButton.classList.toggle('ppt-rehearsal-on',rehearsalActive);}
renderAutoState();
}
diff --git a/assets/template.html b/assets/template.html
index 1322a62..230e076 100644
--- a/assets/template.html
+++ b/assets/template.html
@@ -933,7 +933,7 @@ window.__pptPresenter=(()=>{
观众屏 · 未连接
- 已进行00:00总时长
本页00:00计划 —
计划—未设置
+ 已进行00:00总时长
本页00:00计划 —
计划—未设置
`);
document.getElementById('ppt-reopen').onclick=reopenAudience;
@@ -1005,7 +1005,7 @@ window.__pptPresenter=(()=>{
if(plan)plan.textContent=totalPlan?`计划 ${Number(totalPlan/60000).toFixed(1)} 分钟 · 预计 ${clockTime(Date.now()+Math.max(0,totalPlan-totalUsed))}`:'计划 —';
const section=sectionInfo(idx),sectionEl=document.getElementById('ppt-section');
if(sectionEl){sectionEl.hidden=!section;sectionEl.textContent=section?`${section.name} · ${section.position}/${section.count}${section.planned?` · 剩余 ${durationText(Math.max(0,section.planned-section.used))}`:''}`:'';}
- const timerButton=document.getElementById('ppt-timer-toggle');if(timerButton)timerButton.textContent=timerRunning?'暂停':(totalUsed?'继续':'开始');
+ const timerButton=document.getElementById('ppt-timer-toggle');if(timerButton)timerButton.textContent=timerRunning?'暂停':(totalUsed?'继续计时':'开始计时');
const rehearsalButton=document.getElementById('ppt-rehearsal');if(rehearsalButton){rehearsalButton.textContent=rehearsalActive?'结束排练':'排练';rehearsalButton.classList.toggle('ppt-rehearsal-on',rehearsalActive);}
renderAutoState();
}
diff --git a/references/checklist.md b/references/checklist.md
index d24fa16..8eaa029 100644
--- a/references/checklist.md
+++ b/references/checklist.md
@@ -16,7 +16,7 @@
- 演讲入口只放在右下角现有快捷控制区,显示 `P 演讲模式`。
- 演讲者主体只做“左侧预览 + 右侧备注”两栏;当前页在上、下一页在下,避免形成挤压当前页的三栏布局。
- 两个预览 iframe 必须严格保持 `16:9`,容器不足时等比缩小并留边,不得裁切或拉伸;小屏优先压缩下一页预览高度。
-- 底栏固定为“左时间、中控制、右页码进度”:左侧分组显示已进行/本页/剩余或超时,中间两行放翻页和会话控制,右侧只放页码与百分比;不要重复当前页标题。
+- 底栏固定为“左时间、中控制、右页码进度”:左侧分组显示已进行/本页/剩余或超时,中间两行放翻页和会话控制,右侧只放页码与百分比;不要重复当前页标题。会话控制使用“开始计时 / 继续计时 / 重置计时 / 排练”等明确动作名,不使用孤立的“开始”或“重置”。
- 翻页控制顺序固定为 `首页 / 上一页 / 下一页 / 尾页`;尾页时最后一个按钮变为“重新开始”。自动翻页放在右上角状态栏。
- `宫格` 放在当前页标题旁,打开时替换当前/下一页预览区;选中页面后回到预览,不要弹出突兀的全屏总览。
- 右侧卡片依次显示标题、本页目的和草稿备注;设置里的开关用胶囊 switch,时间间隔用美化后的加减 stepper。
diff --git a/references/presenter-mode.md b/references/presenter-mode.md
index 3efd956..91305a3 100644
--- a/references/presenter-mode.md
+++ b/references/presenter-mode.md
@@ -93,7 +93,7 @@ window.__SPEAKER_NOTES__ = SPEAKER_NOTES;
- 主体保持两栏:左侧预览、右侧备注;左侧当前页在上、下一页在下。
- 当前页与下一页 iframe 始终严格保持 `16:9`。空间不足时留边并整体等比缩小,不得裁切、压扁或让页面内部文字重新排版。
- 小屏优先缩小下一页预览,把空间留给当前页。
-- 底栏分成三段:左侧是 `已进行 / 本页 / 剩余或超时` 三组时间,中间是两行控制(第一行 `首页 / 上一页 / 下一页 / 尾页`,第二行 `开始或暂停 / 重置 / 排练`),右侧只显示 `第几页 / 总页数` 和完成百分比。不要在底栏重复当前页标题。
+- 底栏分成三段:左侧是 `已进行 / 本页 / 剩余或超时` 三组时间,中间是两行控制(第一行 `首页 / 上一页 / 下一页 / 尾页`,第二行 `开始计时或暂停 / 重置计时 / 排练`),右侧只显示 `第几页 / 总页数` 和完成百分比。不要在底栏重复当前页标题。计时按钮必须明确写成“开始计时 / 继续计时 / 重置计时”,避免让用户误以为是重置整场演示。
- `自动翻页` 放在右上角状态栏,不占用底部翻页区。
- `宫格` 放在“当前页”标题旁。打开后直接用宫格替换当前页/下一页预览区,不要弹出突兀的全屏层;卡片显示页码、标题、章节和进度,点击页面后立即回到当前页/下一页预览。`ESC` 切换宫格。
- 右侧卡片依次显示 `标题 / 本页目的 / 草稿(备注)`,避免把标题或页面目的混进草稿正文。
diff --git a/scripts/validate-presenter-mode.mjs b/scripts/validate-presenter-mode.mjs
index 75012e7..29db95b 100644
--- a/scripts/validate-presenter-mode.mjs
+++ b/scripts/validate-presenter-mode.mjs
@@ -113,6 +113,9 @@ const runtimeChecks=[
['shortcut help','openShortcuts'],
];
for(const [label,needle]of runtimeChecks)if(!html.includes(needle))errors.push(`Presenter runtime missing ${label}.`);
+if(!/id="ppt-timer-toggle"[^>]*>开始计时<\/button>/.test(html))errors.push('Presenter timer must use the explicit label "开始计时".');
+if(!/id="ppt-timer-reset"[^>]*>重置计时<\/button>/.test(html))errors.push('Presenter timer reset must use the explicit label "重置计时".');
+if(!html.includes("totalUsed?'继续计时':'开始计时'"))errors.push('Presenter timer resume state must use the explicit label "继续计时".');
if(/\.ppt-preview-stack\{[^}]*grid-template-columns/.test(html))errors.push('Presenter previews must stack vertically; remove grid-template-columns from .ppt-preview-stack.');
const timedNotes=speakerNotes.filter(n=>Number.isFinite(Number(n?.minutes))&&Number(n.minutes)>0);