From 6b4ee89d19ab05b9e850307a33bfab539f5f6fd0 Mon Sep 17 00:00:00 2001
From: laoyang
Date: Wed, 28 Oct 2026 13:57:29 +0800
Subject: [PATCH] =?UTF-8?q?refactor:=20=E8=AE=BE=E7=BD=AE=E5=B7=A6?=
=?UTF-8?q?=E7=A7=BB=E3=80=81=E4=B8=BB=E6=93=8D=E4=BD=9C=E6=8C=89=E9=92=AE?=
=?UTF-8?q?=E7=AD=89=E5=A4=A7=E5=BC=82=E8=89=B2=E3=80=81=E7=A7=BB=E9=99=A4?=
=?UTF-8?q?=E6=9C=80=E8=BF=91=E7=94=9F=E6=88=90=20(#1)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
三处按需求调整:
1. 设置区移到左边
左 = 设置,右 = 输出。不调设置的人视线直接落到右侧密码与「复制密码」。
DOM 顺序也改成设置在前,窄屏单列时天然是设置在上、输出在下。
栅格比例同步反过来(设置 .92fr / 输出 1.08fr)。
2. 「复制密码」与「换一个」等大、不同色
.out-actions 从 flex 改成 grid repeat(2, 1fr),两个按钮严格等宽;
主按钮保留渐变,次按钮改为实心填充(新增 --surface-3,比 --surface-2 更实,
避免深色下与卡片同色而看着像没填充)。
3. 移除「最近生成」模块
删面板、清空按钮,以及 readHistory / pushHistory / renderHistory / ago
与 sp.history.v1 的全部读写。候选面板改为整宽单栏。
顺带清掉了共用工具 listItem 上的历史调用点。
验证(六套,全绿):
功能 jsdom 78 / 0
视觉几何对比度 114 / 0
真实剪贴板端到端 19 / 0
file:// 直开 15 / 0
功能反向注入 22 / 22 命中
视觉反向注入 6 / 6 命中
反向注入拆成两套:_sp_falsify.js 只喂 jsdom(不做布局),
把「按钮等大」「两栏左右」这类几何 bug 注进去影响不到被测判据,
会表现成"注入后仍全绿"、被误判成判据没牙齿(靶子选错)。
几何/视觉类归 _sp_visual_falsify.js。
删历史后专补两条"确实没了"的判据:DOM 节点不存在 +
localStorage 不再写入 sp.history.v1 —— 后者能抓到"面板删了但 JS 还在偷写"。
---
README.md | 17 ++--
docs/dark.png | Bin 449348 -> 391966 bytes
docs/light.png | Bin 415439 -> 420212 bytes
docs/mobile.png | Bin 353246 -> 326193 bytes
index.html | 215 ++++++++++++++++++------------------------------
5 files changed, 90 insertions(+), 142 deletions(-)
diff --git a/README.md b/README.md
index d847ab6..9242d5a 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,7 @@
## 特点
- **纯前端,零依赖** — 一个 `index.html`,没有构建步骤、没有 CDN、没有任何外部请求。断网可用。
+- **左设置、右操作** — 设置栏在左,不调任何设置时视线直接落到右侧的密码与「复制密码」。窄屏自动变上下排列,设置在上。
- **密码学安全随机** — 走 `crypto.getRandomValues` + 拒绝采样消除取模偏差,不用 `Math.random()`。
- **生成即复制** — 默认开启自动复制;剪贴板 API 不可用(如 `file://` 下的旧浏览器)时自动降级到 `execCommand`。
- **深浅色** — 跟随系统偏好,可手动切换并记住选择。首帧前定主题,不会白屏闪一下。
@@ -16,7 +17,6 @@
- **自定义排除** — 想避开 `@`、空格或某个符号,直接填进去。
- **强度与熵** — 按 `长度 × log₂(字符池)` 算真实熵,分五档给出评价,同时显示字符池规模。
- **批量候选** — 一次出 5 个,点任意一条即复制。
-- **最近生成** — 只存在本机 `localStorage`,最多 6 条,可一键清空。
## 使用
@@ -44,7 +44,7 @@ python -m http.server 8000
- 所有运算在浏览器里完成,**不联网、不上传、不写日志**。
- 用 `crypto.getRandomValues` 取随机数,并按「拒绝采样」丢弃落在偏置区间的值,避免取模带来的分布倾斜。
- 勾「每类至少一位」时,先从每类各取一位,剩余位从全池随机,最后整体洗牌 —— 保证覆盖的同时不引入位置可预测性。
-- 历史记录存在 `localStorage` 里,任何能访问这台机器的人都能看到。**在共用电脑上建议用完点「清空」**,或干脆关掉自动复制、不复制到历史。
+- **不保留任何生成记录**。密码只存在于剪贴板与页面显示,刷新即失去痕迹;`localStorage` 里只存偏好设置与主题,不存密码。
## 浏览器支持
@@ -63,11 +63,18 @@ index.html 全部内容:结构、样式、脚本
改完 `index.html` 后建议跑一遍(脚本在仓库外的 `_tooling/`,配合 `jsdom` 与 `playwright`):
```bash
-node _sp_test.js # 功能判据(jsdom,80 条,含反向注入 24 条)
-node _sp_visual.js # 真实浏览器几何 / 对比度 / 多视口(约 101 条)
-node _sp_clip.js # 真实剪贴板端到端(18 条)
+node _sp_test.js # 功能判据(jsdom,78 条)
+node _sp_visual.js # 真实浏览器几何 / 对比度 / 多视口(114 条)
+node _sp_clip.js # 真实剪贴板端到端(19 条)
+node _sp_file_check.js # file:// 双击直开(15 条)
+node _sp_falsify.js # 反向注入,证明功能判据有牙齿(22 条)
+node _sp_visual_falsify.js # 反向注入,证明几何判据有牙齿(6 条)
```
+反向注入必须单独跑视觉那套:`_sp_falsify.js` 只喂 `_sp_test.js`(jsdom 不做布局),
+把「按钮等大」「两栏左右」这类几何 bug 注进去根本影响不到被测判据,
+会表现成"注入后仍全绿",看着像判据没牙齿,其实是靶子选错。
+
## License
MIT
\ No newline at end of file
diff --git a/docs/dark.png b/docs/dark.png
index 467f66f61f562495e1289b3ac463a109611b445d..d547e0f23fba6d0b5fd81827f11b3233a573da59 100644
GIT binary patch
literal 391966
zcmYJ4V|ZQR*0p2XcCv%U+HvDXJ5Cxlwr$(yj%~YXY}>Yz1`XS<=k&bS_jg_E*Lv2R
zW6XPwCsGL{jfzBs1OWkoD)U7`1p)%m00IKe0s-pplT0qlZU_hr2pI`cH4pG*H}b08
zm^$9q$2>XryoGdvd3
$l?Ybsr9x_fR!!fR7O
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