Files
MailUI4Agents/server/internal/static/static.go
JianFeeeee 0a4b98144c feat(webui): 通信二级页签与列表头一体 + 沉浸式(PWA 全屏)+ 日历滑动验收脚本
用户三条(同一线索):「通信页面的二级页面与其他位置极其割裂」、
「不支持沉浸式网页」、「日历页面还不支持左右滑动手势」。

## ① 二级页签不再割裂

页签原先是**带 shadow 的白色胶囊**浮在面板上,看起来像硬贴上去的另一套控件。
改成**下划线页签**:与列表头同一内边距、同一条下边框,选中态用蓝色下划线 +
`-mb-px` 压住分隔线(否则会出现"两条线"的接缝)。

## ② 沉浸式

根因不是 viewport(`viewport-fit=cover` 早就有了,安全区也接了
`env(safe-area-inset-*)`),而是**没有 Web App Manifest**:手机上"添加到主屏幕"后
打开仍然是带地址栏的网页。现在加了 `manifest.webmanifest`(`display: standalone`)
+ iOS 的 `apple-mobile-web-app-capable` / `black-translucent`(状态栏内容叠在页面上)。

manifest 放在**根路径**而不是 /assets/ 下:它里面的 `start_url`/`scope` 是相对
manifest 自己的 URL 解析的,挂在 /assets/ 下就得写 "../"。静态只挂了 `/` 与 `/assets/*`,
所以显式加了一条路由(并从 embed 读,而不是读磁盘 —— 前端产物必须与应用同源同版本)。
图标由项目唯一图标源生成 192/512(尺寸与声明一致,我用 struct 读文件头核对过)。

**实测**:`/manifest.webmanifest` → HTTP 200 `application/manifest+json`。

## ③ 日历滑动:补上真正的验收脚本

`test/manual/calendar-swipe-verify.mjs` 四条,含**反向对照**(纵向拖动不得翻页)
与前置断言。写它时又踩了一次自己的坑:标题真实格式是「2026 年 9 月」(数字与"年月"
之间有空格),我第一版正则按无空格写 ⇒ 匹配不到 ⇒ 三个值全是 null,
**看起来像"滑动没生效",其实是探针瞎了**。所以脚本里第一条就是"标题读得到"。

实测:9 月 →左滑→ 10 月 →右滑→ 9 月,纵向拖动不动。

## 顺带

把我为验收造的测试数据**归档**(不是删除):10 个 `/tmp/scrollprobe-*` 独立会话 +
12 封"滚动验收/窄屏验收样例"邮件。
2026-09-14 11:07:03 +08:00

46 lines
1.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package static
import (
"embed"
"io/fs"
"net/http"
"sync"
)
//go:embed static
var staticFS embed.FS
var (
indexHTML []byte
once sync.Once
)
// GetIndex 返回入口页。
//
// index.html 缺失时回退到 placeholder.html前端产物不进版本库
// 新克隆里只有占位页。回退而不是报错是故意的 ——
// 只改后端的人应当能直接 go run 起来调 API而不必先装 node。
func GetIndex() []byte {
once.Do(func() {
if data, err := fs.ReadFile(staticFS, "static/index.html"); err == nil {
indexHTML = data
return
}
indexHTML, _ = fs.ReadFile(staticFS, "static/placeholder.html")
})
return indexHTML
}
func Handler() http.Handler {
sub, _ := fs.Sub(staticFS, "static")
return http.FileServer(http.FS(sub))
}
// ReadFile 读打包进来的静态文件(目前只给 manifest 用)。
//
// 用 embed 而不是读磁盘:静态根可由 --static 指定成别的目录,
// 那时 manifest 会跟着一起"消失";它属于前端产物的一部分,必须和应用同源同版本。
func ReadFile(name string) ([]byte, error) {
return fs.ReadFile(staticFS, "static/"+name)
}