feat: 新增5部经典下拉选经、TTS重试、PWA可安装

- 新增阿弥陀经/普门品/道德经/清静经/太上感应篇(维基文库底本,OpenCC转简体)
- 经典切换改为下拉框(佛/道分组),移动端复用底部面板并显示分组标题
- TTS 请求失败自动重试3次;普门品偈颂拆分修复600字上限问题
- 服务端缓存原子写(.tmp+rename),上限放宽至800字
- PWA:manifest + Service Worker,安装后无地址栏独立窗口
- 偏好记忆/逐字光标/回到诵读处/夜间模式等此前改动一并入库

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
selfrelease
2026-10-04 15:47:18 +08:00
co-authored by Devin
parent ad56a147a6
commit bc0bfd9263
9 changed files with 928 additions and 74 deletions
+30 -10
View File
@@ -3,16 +3,23 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#f6f1e6">
<meta name="description" content="心经、金刚经、阿弥陀经、普门品、道德经、清静经、太上感应篇诵读——千问语音合成,晨钟暮鼓配乐,逐段跟读高亮。">
<title>心经</title>
<link rel="icon" type="image/svg+xml" href="icon.svg">
<link rel="apple-touch-icon" href="icon-180.png">
<link rel="stylesheet" href="style.css?v=4">
<link rel="manifest" href="manifest.json">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="经典诵读">
<link rel="stylesheet" href="style.css?v=9">
</head>
<body>
<div class="site-logo" aria-hidden="true">
<img src="icon.svg" alt="" class="logo-mark">
<span class="logo-text">佛经诵读</span>
<span class="logo-text">经典诵读</span>
</div>
<main class="page">
@@ -32,10 +39,10 @@
<svg viewBox="0 0 24 24" class="icon icon-stop"><rect x="6" y="6" width="12" height="12" rx="1.5"/></svg>
<span class="play-label">播 放</span>
</button>
<div class="tool-group" role="group" aria-label="经典">
<span class="tool-name">经典</span>
<button type="button" data-sutra="xinjing" class="sutra-btn active">心经</button>
<button type="button" data-sutra="jingang" class="sutra-btn">金刚经</button>
<span id="sleep-remaining" class="sleep-remaining" aria-label="定时剩余时间"></span>
<div class="field-pair">
<label class="tool-name" for="sutra-select">经典</label>
<select id="sutra-select" aria-label="选择经典"></select>
</div>
<div class="player-meta">
<label class="voice-label" for="voice-select">诵读音色</label>
@@ -48,20 +55,32 @@
<button type="button" data-size="md" class="size-btn active">中</button>
<button type="button" data-size="sm" class="size-btn">小</button>
</div>
<span class="flex-break flex-break-2" aria-hidden="true"></span>
<div class="tool-group" role="group" aria-label="排版">
<button type="button" id="btn-layout" class="layout-btn">切换竖排</button>
</div>
<div class="tool-group" role="group" aria-label="模式">
<button type="button" id="btn-loop" class="layout-btn" aria-pressed="false">循环</button>
<button type="button" id="btn-bgm" class="layout-btn" aria-pressed="false">配乐</button>
<label class="tool-name" for="timer-select">定时</label>
<select id="timer-select" aria-label="定时停止">
<button type="button" id="btn-night" class="layout-btn" aria-pressed="false">夜间</button>
<div class="field-pair">
<label class="tool-name" for="timer-select">定时</label>
<select id="timer-select" aria-label="定时停止">
<option value="0">关闭</option>
<option value="30">30 分钟</option>
<option value="60">60 分钟</option>
<option value="90">90 分钟</option>
<option value="120">120 分钟</option>
</select>
</select>
</div>
<div class="field-pair">
<label class="tool-name" for="rate-select">语速</label>
<select id="rate-select" aria-label="诵读语速">
<option value="0.8">慢 0.8×</option>
<option value="1" selected>常速</option>
<option value="1.25">快 1.25×</option>
</select>
</div>
</div>
<div id="status" class="status" role="status"></div>
<button type="button" id="btn-expand" class="expand-btn" aria-expanded="false" aria-label="展开设置" title="设置">
@@ -74,6 +93,7 @@
<article class="sutra-paper">
<div id="sutra" class="sutra size-md" role="document"></div>
<button type="button" id="btn-jump-now" class="jump-now" hidden>⤵ 回到诵读处</button>
</article>
<nav class="pager" id="pager" aria-label="分页" hidden>
@@ -89,6 +109,6 @@
</footer>
<audio id="audio" preload="auto"></audio>
<script src="app.js?v=4"></script>
<script src="app.js?v=9"></script>
</body>
</html>