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

一、概述
在保持 Python + Streamlit 技术栈的前提下,对 cipherhub.cloud 前端做了多轮「vibecoding」式优化:不追求大而全的重构,而是围绕观感统一、加载轻量、信息准确、易扩展逐项落地。主要涉及:
- 品牌头部与页脚、全局样式的统一与克制化;
- Logo、头像等静态资源的加载与缓存策略;
- 多语言(i18n)从双语到多语的扩展及语言选择控件的弱化;
- ECC 相关能力(Ed25519、X25519)与曲线说明的补全;
- 若干功能页的提示信息、帮助文案与示例数据的微调。
以下按模块分述。
二、品牌头部、页脚与 PaperMode 样式
2.1 从分散布局到统一 brand_header / brand_footer
原先的 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.82rem、opacity: 0.88,summary颜色#8a8a8a、0.9rem; - 展开后选项与
t("common.label_languages")等多语言键绑定,随 i18n 扩展一并更新。
2.4 SEO 与 Meta
inject_custom_meta() 中补充了 og:image、og:type、og:locale、og:keywords、og:url 等,便于社交与搜索抓取。因 st.html 会渲染进 iframe,meta 对主页面 SEO 无效,故继续使用 st.markdown(..., unsafe_allow_html=True) 将 meta 注入主文档;对主流爬虫,body 内 meta 亦可被利用。
三、Logo 与头像的加载与缓存
3.1 问题与原则
Logo(favicon)和关于我页的头像原先在每次渲染时读盘并做 base64,无缩放,体积与重复 I/O 都偏大。目标:首加载一次、缩放后缓存、尽量少占内存与传输。
3.2 Favicon / Logo
- 使用
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+ PNGoptimize=True,再 base64,存入avator_b64; introduction_tab的关于我区块直接引用该变量,不再在 tab 内做 I/O 或缩放。
3.4 加载方式合并
在「优化加载方式」中,将 favicon 与头像的打开、转换、缩放、输出统一到 utils,出错时各有回退逻辑,避免 streamlit_web_app、introduction_tab 中重复实现,便于后续替换资源或调整尺寸时只改一处。
四、多语言(i18n)扩展与收敛
4.1 从双语到多语
初始为 zh / en,后扩展到 ja、ko、de、fr、th、ar、es 等。架构保持不变:
- loader:
get_locale、set_locale、t()、render_language_switcher;st.session_state["cipherhub_lang"]+st.query_params["lang"]做持久化,整页刷新可恢复; - fallback:
FALLBACK_CHAIN = ["en","zh"],键缺失时依次回退; - 文案:各语言一个
Tdict(如zh.py、en.py),点分键如common.tagline、intro.about_1、convert.label_dt_help;t(key, **kwargs)支持str.format占位。
新增语言时只需在 loader 的 _LOCALES、SWITCHER_LANGUAGES、LANGUAGE_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_about、nav.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_tab、ecc_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_sign、ecc_verify、ecc_kex 下补全了 info_curves、info 的 {unsupported} 占位,避免用户误用。
5.2 Ed25519 对原始数据的限制
Ed25519(PureEdDSA)在算法内部完成哈希,不能对预先计算的摘要做签名或验签。在 ecc_sign_tab、ecc_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_sign、ecc_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_1、intro.about_2 在 i18n 中可独立润色,不影响结构。
七、主应用结构与小配置
7.1 CipherHUBApp 与 Tabs
tab_groups按功能分组:关于、随机与密钥生成、Hash/HMAC、对称(块/流/ZUC)、RSA、ECC、SM2、格式转换等;每组对应顶部分组 Tab,组内多页时再嵌套子 Tabs;- 各 tab 继承
BaseTab,实现set_up_title_header、render;title/header/tab_name为 i18n 键时,在set_up_title_header与st.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 及以下
- 功能区内的
stTextArea、stTextInput、stCodeBlock、textarea、input、pre 统一width/max-width: 100%、min-width: 0、box-sizing: border-box,占满可用宽度,取消固定最小宽度。 - 公钥/私钥类 textarea(aria-label 含 KEY、密钥、公钥、私钥)在移动端
min-width: 0、min-height: 150px,避免撑破布局。 - 水平多列布局(
stHorizontalBlock)改为flex-direction: column,stColumn占满宽度,实现单列堆叠,便于小屏阅读与操作。
- 功能区内的
- 480px 及以下(手机/微信等)
- 输入框、输出框、pre、code 的
font-size: 1rem、padding: 0.5rem,textarea 的min-height: 120px。 - 标签与说明类文案(
label、.stMarkdown p)设为0.9rem,整体更紧凑。
- 输入框、输出框、pre、code 的
上述规则均限制在 [data-testid="stTabs"] 下,仅影响主内容区,不改变品牌头、语言选择等已有样式。
8.3 首页「关于我」布局
introduction_tab 的关于我区块在 2026-01-27 的「优化首页布局展示」中做了布局微调:保持圆形头像与自述文案的左右/上下结构,对 .intro-container、.intro-avatar、.intro-about 的间距、字号与媒体查询进行细调,使 768px、480px 下与新版移动端输出框策略一致,观感统一且易读。
九、小结
本次优化没有引入新框架,而是在既有 Streamlit 架构上做「vibecoding」式的局部改进:
- 观感:PaperMode 统一样式与 brand_header/footer,侧边栏隐藏、语言选择弱化,链接与字重克制,利于长时间使用时的舒适度;
- 性能与资源:Logo、头像缩放后缓存,启动预热,减少重复 I/O 与传输;
- 信息准确:ECC 曲线支持范围、Ed25519 仅支持原始数据、GCM 填充含义、日期时间格式等,通过 i18n 在相关页以
st.info或help明确传达; - 可维护与可扩展:i18n 键与
t()覆盖头部、页脚、各 tab;新增语言或新提示时,只需在对应T与 loader 中增键,业务逻辑保持稳定; - 算法与后端:流式加密完整支持 SM4GCM(16 字节密钥),并与后端 handlers 修复后的 SM4GCM 加解密逻辑一致;
- 移动端:在 768px、480px 断点下对输入/输出框、公钥私钥框、多列布局及关于我区块做专门优化,小屏下单列展示、宽度与字号更适配,与 PaperMode 统一。
后续若继续扩展语言、算法或页面,可延用同一套:先落 i18n 键与 t(),再在 PaperMode 和 brand_header/brand_footer 的约束下做样式与提示的微调,即可保持整体风格与可读性。
文档版本:基于 2026-01-26 前 web_app 与 i18n 的 git 历史整理。
第八章「近期更新」据 2026-01-27 的 git 提交(SM4GCM、移动端输出框与布局、首页关于我)补充。