feat(ssr): 真 SSR 收尾——干净 hydration 与浏览器原生滚动恢复

- 修复 onRenderClient 引入 serverClient 导致浏览器 process is not defined:
  抽出双端安全的 ssr/feedSeed.ts 作为唯一数据播种入口,serverClient 加 SSR 守卫
- 修复 React 19 Fizz Suspense Outlining:Suspense 边界从布局 Outlet 下移到
  各懒加载路由,SSR 输出不再出现 <!--$?--> / $RB / template 探针标记
- 修复窄屏 React #418 hydration 致命错误:useMediaQuery 首帧恒 false,
  useLayoutEffect 绘制前同步真实值;主题改 cookie 存储供 SSR 直出,
  head 内联防闪烁/缩放脚本移除(缩放锁改为客户端 zoomGuard 模块)
- 清理假 SSR 残留:删除 StaticFeedList 镜像组件、SSR 图标生成脚本、
  探针与死代码;列表接口裁剪正文,仅下发 excerpt/first_image
- Feed 与帖子详情改为窗口滚动(app-shell--doc-scroll):删除虚拟列表、
  自研滚动快照与全局滚轮 hack;F5/前进后退滚动位置全部交给浏览器原生
  历史恢复,HTML 响应头 no-cache 配合;锚点/楼层用 scrollIntoView +
  scroll-margin-top 适配 sticky 顶栏
This commit is contained in:
2026-09-09 20:53:28 +08:00
parent 3702e3d1f2
commit 2392768868
39 changed files with 468 additions and 1366 deletions

View File

@@ -1,6 +1,7 @@
package service
import (
"html"
"net/url"
"regexp"
"strings"
@@ -68,9 +69,10 @@ func TruncateRunes(s string, max int) string {
return string(runes[:max-1]) + "…"
}
// ExcerptFromHTML 从 HTML 生成摘要(剥离标签)
// ExcerptFromHTML 从 HTML 生成摘要(剥离标签、解码实体)
func ExcerptFromHTML(htmlContent string, maxRunes int) string {
plain := StripHTMLForSearch(htmlContent)
plain = html.UnescapeString(plain)
return TruncateRunes(plain, maxRunes)
}