本文基于 2026 年 1 月对 CipherHUB web_app(Streamlit)的多次迭代,整理界面、性能与可维护性方面的优化,供后续回顾与扩展参考。


image-20260128213807287

一、概述

在保持 Python + Streamlit 技术栈的前提下,对 cipherhub.cloud 前端做了多轮「vibecoding」式优化:不追求大而全的重构,而是围绕观感统一、加载轻量、信息准确、易扩展逐项落地。主要涉及:

  • 品牌头部与页脚、全局样式的统一与克制化;
  • Logo、头像等静态资源的加载与缓存策略;
  • 多语言(i18n)从双语到多语的扩展及语言选择控件的弱化;
  • ECC 相关能力(Ed25519、X25519)与曲线说明的补全;
  • 若干功能页的提示信息、帮助文案与示例数据的微调。

以下按模块分述。


二、品牌头部、页脚与 PaperMode 样式

原先的 setup_page_config 承担了标题、副标题、博客与公众号入口、居中容器等多重职责,样式分散且与后续 i18n 难以协同。调整为:

  • brand_header():每一页顶部统一调用,负责注入全局 CSS、渲染 logo + 品牌名、tagline、博客链接,以及语言选择折叠框;
  • brand_footer():页脚横线、Powered by 文案、备案号链接,去除了厚重边框,改为简洁横线 + 小字居中。

二者在 CipherHUBApp.run() 中固定一前一后调用,保证全站一致性。

2.2 PaperMode 全局样式

确立一套名为 PaperMode 的视觉规范,目标为:理性、克制、沉静、略带温度。

  • 主色与背景:正文 #c9c9c9,背景 #1d1e20,全站 Georgia 衬线 + 斜体;
  • 字体规模:通过 CSS 变量集中控制:
    • --cipherhub-func-font-size:正文、标签等(默认 1.0rem);
    • --cipherhub-io-font-size:input、textarea、code、st.code 等(默认 1.2rem); 仅改这两处即可统一调节功能区与 IO 区域字号;
  • 链接.stMarkdown a 默认 #a0a0a0,hover #c9c9c9;博客链接使用 brand-header-blog,灰绿色 #9aaa88,hover #b0c0a0;主站 logo+ 品牌名 brand-header-logo,hover 略提亮;
  • 侧边栏[data-testid="stSidebar"][data-testid="collapsedControl"] 通过 display: none 彻底隐藏,功能区已迁移至主页面顶部分组 Tabs,不再依赖侧边栏。

2.3 语言选择控件的弱化

语言切换器放入 brand_header 中、博客链接之下、About 等 Tabs 之上,左侧列,st.expander 默认折叠。为不抢占视觉:

  • 折叠框:main [data-testid="stExpander"]:first-of-type 设置 font-size: 0.82remopacity: 0.88summary 颜色 #8a8a8a0.9rem
  • 展开后选项与 t("common.label_languages") 等多语言键绑定,随 i18n 扩展一并更新。

2.4 SEO 与 Meta

inject_custom_meta() 中补充了 og:imageog:typeog:localeog:keywordsog:url 等,便于社交与搜索抓取。因 st.html 会渲染进 iframe,meta 对主页面 SEO 无效,故继续使用 st.markdown(..., unsafe_allow_html=True) 将 meta 注入主文档;对主流爬虫,body 内 meta 亦可被利用。


三、Logo 与头像的加载与缓存

3.1 问题与原则

Logo(favicon)和关于我页的头像原先在每次渲染时读盘并做 base64,无缩放,体积与重复 I/O 都偏大。目标:首加载一次、缩放后缓存、尽量少占内存与传输

  • 使用 favicon.ico,在 utils 中预先用 PIL 打开、转为 RGBA、缩放到 _FAVICON_MAX_PX(48px),再写入 BytesIO 做 base64;
  • 失败时回退为直接读文件做 base64;
  • streamlit_web_app 中通过 @st.cache_data_load_favicon_logo_html() 生成 logo 的 <img src="data:image/...;base64,..."> 片段;
  • 服务启动时调用一次 _load_favicon_logo_html() 预热缓存,brand_header 内直接使用,后续请求命中缓存,不再读盘。

3.3 头像

  • 头像显示 180×180,缩放到 360px(_AVATAR_MAX_PX)已足够 2x 屏清晰度;
  • utils 中对该图做 thumbnail + PNG optimize=True,再 base64,存入 avator_b64
  • introduction_tab 的关于我区块直接引用该变量,不再在 tab 内做 I/O 或缩放。

3.4 加载方式合并

在「优化加载方式」中,将 favicon 与头像的打开、转换、缩放、输出统一到 utils,出错时各有回退逻辑,避免 streamlit_web_appintroduction_tab 中重复实现,便于后续替换资源或调整尺寸时只改一处。


四、多语言(i18n)扩展与收敛

4.1 从双语到多语

初始为 zh / en,后扩展到 ja、ko、de、fr、th、ar、es 等。架构保持不变:

  • loaderget_localeset_localet()render_language_switcherst.session_state["cipherhub_lang"] + st.query_params["lang"] 做持久化,整页刷新可恢复;
  • fallbackFALLBACK_CHAIN = ["en","zh"],键缺失时依次回退;
  • 文案:各语言一个 T dict(如 zh.pyen.py),点分键如 common.taglineintro.about_1convert.label_dt_helpt(key, **kwargs) 支持 str.format 占位。

新增语言时只需在 loader 的 _LOCALESSWITCHER_LANGUAGESLANGUAGE_LABELS 中登记,并新建对应 T 模块,业务调用处无需改动。

4.2 语言选择样式与 copy

  • 语言切换器由写死的 "Languages" 改为 t("common.label_languages"),各语种中补全 label_languages(如「语言」「Languages」「Sprachen」等);
  • 博客链接由 [ ↳ CipherHUB's Blog ] 改为 t("common.blog_link"),各语言分别写成「访问 CipherHUB 博客」「↳ Visit CipherHUB’s Blog」等,语义更清晰,且与 PaperMode 的克制风格一致。

4.3 标签页与结构

tab_groups 的 key 与各 BaseTab.tab_name 使用 i18n 键(如 nav.group_aboutnav.tab_home),set_up_title_header 中通过 t(self.title)t(self.header) 解析,切换语言后 rerun 即可看到更新。brand_footer 的「Powered by」、备案号也统一为 t("common.footer_powered")t("common.footer_beian")


五、ECC:Ed25519 / X25519 与曲线说明

5.1 能力边界在界面上的外化

  • 密钥生成asymmetric_key_gen_tab 支持 Ed25519、X25519 的生成;
  • 签名 / 验签:仅 Ed25519 等曲线支持,X25519 不用于签名;在 ecc_sign_tabecc_verify_tab 增加 st.info(t("ecc_sign.info_curves", supported=..., unsupported=...))t("ecc_verify.info_curves", ...),明确「可生成但不支持签名/验签」的曲线;
  • 密钥协商:仅 X25519、各类 NIST 等曲线支持 ECDH,Ed25519 不用于 KEX;ecc_key_exchange_tab 通过 t("ecc_kex.info", curves=..., unsupported=...) 说明。

各语言的 ecc_signecc_verifyecc_kex 下补全了 info_curvesinfo{unsupported} 占位,避免用户误用。

5.2 Ed25519 对原始数据的限制

Ed25519(PureEdDSA)在算法内部完成哈希,不能对预先计算的摘要做签名或验签。在 ecc_sign_tabecc_verify_tab 增加:

  • st.info(t("ecc_sign.info_ed25519_raw_only"))
  • st.info(t("ecc_verify.info_ed25519_raw_only"))

文案中提示:使用 Ed25519 时须勾选「对原始明文做签名(ECC)」或「签名时直接对原始明文做签名(ECC)?」。该提示在 9 种语言的 ecc_signecc_verify 中均已补齐。

5.3 密钥协商的 X25519 与同对检测

ecc_key_exchange_tab 与后端配合,支持 X25519 的 ECDH。对 X25519,因无 curve 属性,改为用 x25519.X25519PrivateKey / x25519.X25519PublicKey 判断,并通过 _is_same_x25519(priv, pub) 比较双方公钥是否相同,避免「同一密钥对」误用于协商;对传统 EC,仍用 curve.name 比较及签名试算做校验。逻辑收敛到 _do_kex(),减少重复。


六、功能页的提示、帮助与示例

6.1 GCM 流式加密

GCM 模式本身不需要 PKCS#7 填充;当前实现为在加密前对明文做可选填充,以在密文中隐藏真实明文长度。在 stream_encryption_tab 开头增加:

  • st.info(t("stream_cipher.info_padding"))

stream_cipher.info_padding 在 9 种语言中均已添加,说明「为何有填充」「填充的作用」,减少与块加密模式的混淆。

6.2 日期时间字符串

在「格式转换」的「字符串 → 时间戳」中,日期时间输入框增加 help=t("convert.label_dt_help"),例如中文「输入格式须为:YYYY-MM-DD HH:MM:SS」,各语言统一补全 label_dt_help,降低格式错误导致的转换失败。

6.3 块加密示例密文

block_encryption_tab 解密区预填示例密文由一串固定 base64 换为一组新的有效示例,避免旧数据失效或与当前后端不兼容带来的困惑;仅作展示用,无逻辑变更。

6.4 关于我(About Me)

introduction_tab 的关于我区块:布局为 1:2 的 st.columns,一侧圆形头像,一侧两段自述。自述与博客链接样式放入 .intro-about,字体、颜色、字距、下划线与 hover 与 PaperMode 一致;intro.about_1intro.about_2 在 i18n 中可独立润色,不影响结构。


七、主应用结构与小配置

7.1 CipherHUBApp 与 Tabs

  • tab_groups 按功能分组:关于、随机与密钥生成、Hash/HMAC、对称(块/流/ZUC)、RSA、ECC、SM2、格式转换等;每组对应顶部分组 Tab,组内多页时再嵌套子 Tabs;
  • 各 tab 继承 BaseTab,实现 set_up_title_headerrendertitle/header/tab_name 为 i18n 键时,在 set_up_title_headerst.tabs([t(tab.tab_name) for ...]) 中统一用 t() 解析。

7.2 .streamlit/config.toml

  • toolbarMode = "minimal",减少顶部干扰;
  • base = "dark",与 PaperMode 的深色背景一致;codeFontSize 等若有需要可与 --cipherhub-io-font-size 协调,避免冲突。

八、近期更新(基于 2026-01-27 的 git 提交)

8.1 流式加密:SM4GCM 支持与密钥长度

  • 前端(stream_encryption_tab):在「对称流式加密」中完整支持 SM4GCM。根据所选算法动态设置密钥长度:SM4GCM 为 16 字节,AES256GCM、ChaCha20Poly1305 为 32 字节;密钥输入框的 help、默认长度与 st.session_state.gcm_key_len 随之更新。为避免切换算法时沿用旧密钥长度,密钥输入框的 key 改为包含算法名(如 stream_cipher_key_{algorithm}),保证切换算法时控件刷新。
  • 后端(handlers/stream_cipher):修复 SM4GCM 加解密逻辑的 BUG,与 api/stream_cipher 中 SM4GCM 的 16 字节密钥约定一致,加解密结果正确。

8.2 移动端输出框与布局

在 PaperMode 的 @media (max-width: 768px)@media (max-width: 480px) 中新增/强化了以下规则:

  • 768px 及以下
    • 功能区内的 stTextAreastTextInputstCodeBlock、textarea、input、pre 统一 width/max-width: 100%min-width: 0box-sizing: border-box,占满可用宽度,取消固定最小宽度。
    • 公钥/私钥类 textarea(aria-label 含 KEY、密钥、公钥、私钥)在移动端 min-width: 0min-height: 150px,避免撑破布局。
    • 水平多列布局(stHorizontalBlock)改为 flex-direction: columnstColumn 占满宽度,实现单列堆叠,便于小屏阅读与操作。
  • 480px 及以下(手机/微信等)
    • 输入框、输出框、pre、code 的 font-size: 1rempadding: 0.5rem,textarea 的 min-height: 120px
    • 标签与说明类文案(label.stMarkdown p)设为 0.9rem,整体更紧凑。

上述规则均限制在 [data-testid="stTabs"] 下,仅影响主内容区,不改变品牌头、语言选择等已有样式。

8.3 首页「关于我」布局

introduction_tab 的关于我区块在 2026-01-27 的「优化首页布局展示」中做了布局微调:保持圆形头像与自述文案的左右/上下结构,对 .intro-container.intro-avatar.intro-about 的间距、字号与媒体查询进行细调,使 768px、480px 下与新版移动端输出框策略一致,观感统一且易读。


九、小结

本次优化没有引入新框架,而是在既有 Streamlit 架构上做「vibecoding」式的局部改进:

  1. 观感:PaperMode 统一样式与 brand_header/footer,侧边栏隐藏、语言选择弱化,链接与字重克制,利于长时间使用时的舒适度;
  2. 性能与资源:Logo、头像缩放后缓存,启动预热,减少重复 I/O 与传输;
  3. 信息准确:ECC 曲线支持范围、Ed25519 仅支持原始数据、GCM 填充含义、日期时间格式等,通过 i18n 在相关页以 st.infohelp 明确传达;
  4. 可维护与可扩展:i18n 键与 t() 覆盖头部、页脚、各 tab;新增语言或新提示时,只需在对应 T 与 loader 中增键,业务逻辑保持稳定;
  5. 算法与后端:流式加密完整支持 SM4GCM(16 字节密钥),并与后端 handlers 修复后的 SM4GCM 加解密逻辑一致;
  6. 移动端:在 768px、480px 断点下对输入/输出框、公钥私钥框、多列布局及关于我区块做专门优化,小屏下单列展示、宽度与字号更适配,与 PaperMode 统一。

后续若继续扩展语言、算法或页面,可延用同一套:先落 i18n 键与 t(),再在 PaperMode 和 brand_header/brand_footer 的约束下做样式与提示的微调,即可保持整体风格与可读性。


文档版本:基于 2026-01-26 前 web_app 与 i18n 的 git 历史整理。

第八章「近期更新」据 2026-01-27 的 git 提交(SM4GCM、移动端输出框与布局、首页关于我)补充。