diff --git a/.gitignore b/.gitignore
index b5c87ff..e11d940 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,4 +27,5 @@ node_modules/
# Local-only notes
+temp/
POST_OVERVIEW.md
diff --git a/doc/agent-smoke-cases.md b/doc/agent-smoke-cases.md
index 7d90fb5..e213c3b 100644
--- a/doc/agent-smoke-cases.md
+++ b/doc/agent-smoke-cases.md
@@ -66,16 +66,20 @@ bash scripts/agent-smoke.sh --cdp-port 9222 --cases AGENT-CHROME-001,AGENT-CHROM
范围:
抽屉基础控件渲染
+状态:
+用例内会显式切到 `整页模式`
+
步骤:
1. 先执行 `AGENT-CHROME-001`
+2. 在 `选项` 中确认当前为 `整页模式`
断言:
1. `上一帖` 按钮存在
2. `下一帖` 按钮存在
3. `选项` 按钮存在
-4. `回复主题` 按钮存在
+4. `回复主题` 按钮存在但默认隐藏
5. `新标签打开` 链接存在
### AGENT-CHROME-003:设置面板打开和关闭
@@ -273,7 +277,7 @@ bash scripts/agent-smoke.sh --cdp-port 9222 --cases AGENT-CHROME-001,AGENT-CHROM
头部 `回复主题` 主入口 + 可选悬浮快捷入口
状态:
-当前不在默认批量脚本中
+当前不在默认批量脚本中;用例内会显式切到 `智能预览`
步骤:
@@ -310,6 +314,48 @@ bash scripts/agent-smoke.sh --cdp-port 9222 --cases AGENT-CHROME-001,AGENT-CHROM
2. 文本按钮和链接都保持 `white-space: nowrap`
3. 可见文本按钮的宽度应明显大于高度,避免被挤成竖排窄条
+### AGENT-CHROME-016:正文字号设置应即时生效
+
+范围:
+抽屉设置中的正文字号滑杆
+
+状态:
+当前不在默认批量脚本中;用例内会显式切到 `智能预览`
+
+步骤:
+
+1. 打开任一主题抽屉
+2. 打开 `选项`
+3. 读取第一条帖子正文 `.ld-post-body` 的计算字号
+4. 拖动 `正文字号` 滑杆到另一个档位
+
+断言:
+
+1. 滑杆右侧读数会同步更新成新的 `xxpx`
+2. `.ld-post-body` 的计算 `font-size` 会立即更新成对应像素值
+3. 若当前正文里存在 `pre` / `code`,其字号也会同步变化,并保持比正文小 `1px`
+
+### AGENT-CHROME-017:整页模式下正文字号设置应禁用
+
+范围:
+默认整页模式下的正文字号控件可用性
+
+状态:
+当前不在默认批量脚本中;用例内会显式切到 `整页模式`
+
+步骤:
+
+1. 打开任一主题抽屉
+2. 打开 `选项`
+3. 切到 `整页模式`
+4. 读取 `正文字号` 设置项状态
+
+断言:
+
+1. `正文字号` 设置项显示为禁用态
+2. 对应滑杆 `disabled=true`
+3. 提示文案明确说明该设置仅在 `智能预览` 下生效
+
## 3. 记录格式
执行完用例后,建议按下面格式留证据:
diff --git a/scripts/agent-smoke.sh b/scripts/agent-smoke.sh
index 441aae2..1c3e34b 100755
--- a/scripts/agent-smoke.sh
+++ b/scripts/agent-smoke.sh
@@ -21,6 +21,8 @@ ALL_CASES=(
"AGENT-CHROME-013"
"AGENT-CHROME-014"
"AGENT-CHROME-015"
+ "AGENT-CHROME-016"
+ "AGENT-CHROME-017"
)
CASES=("${DEFAULT_CASES[@]}")
@@ -469,22 +471,37 @@ set_setting_value() {
(() => {
const key = $key_json;
const value = $value_json;
- const select = document.querySelector(\`#ld-drawer-settings [data-setting="\${key}"]\`);
- if (!(select instanceof HTMLSelectElement)) {
+ const control = document.querySelector(\`#ld-drawer-settings [data-setting="\${key}"]\`);
+ if (!(control instanceof HTMLSelectElement) && !(control instanceof HTMLInputElement)) {
return { ok: false, reason: "setting-not-found", key };
}
- const hasOption = Array.from(select.options).some((option) => option.value === value);
- if (!hasOption) {
- return { ok: false, reason: "option-not-found", key, value };
+ if (control instanceof HTMLSelectElement) {
+ const hasOption = Array.from(control.options).some((option) => option.value === value);
+ if (!hasOption) {
+ return { ok: false, reason: "option-not-found", key, value };
+ }
+
+ if (control.value === value) {
+ return { ok: true, changed: false, key, value };
+ }
+
+ control.value = value;
+ control.dispatchEvent(new Event("change", { bubbles: true }));
+ return { ok: true, changed: true, key, value };
+ }
+
+ if (control.type !== "range") {
+ return { ok: false, reason: "unsupported-input-type", key, inputType: control.type || "text" };
}
- if (select.value === value) {
+ if (control.value === value) {
return { ok: true, changed: false, key, value };
}
- select.value = value;
- select.dispatchEvent(new Event("change", { bubbles: true }));
+ control.value = value;
+ control.dispatchEvent(new Event("input", { bubbles: true }));
+ control.dispatchEvent(new Event("change", { bubbles: true }));
return { ok: true, changed: true, key, value };
})()
EOF
@@ -622,6 +639,35 @@ EOF
ab_eval "$script"
}
+post_body_font_state() {
+ local script
+ script=$(cat <<'EOF'
+(() => {
+ const body = document.querySelector("#ld-drawer-root .ld-post-body");
+ const field = document.querySelector('#ld-drawer-settings [data-setting-group="postBodyFontSize"]');
+ const control = document.querySelector('#ld-drawer-settings [data-setting="postBodyFontSize"]');
+ const value = document.querySelector('#ld-drawer-settings [data-setting-value="postBodyFontSize"]');
+ const hint = document.querySelector('#ld-drawer-settings [data-setting-hint="postBodyFontSize"]');
+ const code = document.querySelector("#ld-drawer-root .ld-post-body pre, #ld-drawer-root .ld-post-body code");
+ return {
+ pageOpen: document.body.classList.contains("ld-drawer-page-open"),
+ bodyExists: body instanceof HTMLElement,
+ bodyFontSize: body ? Math.round(parseFloat(getComputedStyle(body).fontSize) || 0) : null,
+ codeExists: code instanceof HTMLElement,
+ codeFontSize: code ? Math.round(parseFloat(getComputedStyle(code).fontSize) || 0) : null,
+ fieldDisabled: field?.classList.contains("is-disabled") || false,
+ fieldAriaDisabled: field?.getAttribute("aria-disabled") || null,
+ controlDisabled: control?.disabled || false,
+ controlValue: control?.value || null,
+ valueText: value?.textContent?.trim() || null,
+ hintText: hint?.textContent?.trim() || null
+ };
+})()
+EOF
+)
+ ab_eval "$script"
+}
+
click_reply_trigger() {
local target=$1
local selector_json script
@@ -734,22 +780,39 @@ run_001() {
run_002() {
local case_id="AGENT-CHROME-002"
- local state
+ local original_settings original_preview preview_result state
open_topic_by_index 0 >/dev/null || {
record_fail "$case_id" "无法打开抽屉"
return 0
}
+
+ original_settings=$(settings_values)
+ original_preview=$(echo "$original_settings" | jq -r '.previewMode // empty')
+ if [ -z "$original_preview" ]; then
+ record_fail "$case_id" "无法读取当前预览模式"
+ return 0
+ fi
+
+ preview_result=$(set_setting_value previewMode iframe)
+ if [ "$(echo "$preview_result" | jq -r '.ok')" != "true" ]; then
+ record_fail "$case_id" "无法切到整页模式: $preview_result"
+ return 0
+ fi
+ ab wait 1200 >/dev/null
+ ensure_settings_closed
state=$(drawer_state)
+ set_setting_value previewMode "$original_preview" >/dev/null || true
+
if [ "$(echo "$state" | jq -r '.prevText // empty')" = "上一帖" ] \
&& [ "$(echo "$state" | jq -r '.nextText // empty')" = "下一帖" ] \
&& [ "$(echo "$state" | jq -r '.settingsText // empty')" = "选项" ] \
&& [ "$(echo "$state" | jq -r '.replyText // empty')" = "回复主题" ] \
- && [ "$(echo "$state" | jq -r '.replyHidden')" = "false" ] \
+ && [ "$(echo "$state" | jq -r '.replyHidden')" = "true" ] \
&& [ "$(echo "$state" | jq -r '.newTabText // empty')" = "新标签打开" ]; then
- record_pass "$case_id" "toolbar ready"
+ record_pass "$case_id" "toolbar ready in iframe mode"
else
- record_fail "$case_id" "toolbar incomplete"
+ record_fail "$case_id" "toolbar incomplete: $(echo "$state" | jq -c '{prevText, nextText, settingsText, replyText, replyHidden, newTabText}')"
fi
}
@@ -1130,7 +1193,7 @@ run_013() {
run_014() {
local case_id="AGENT-CHROME-014"
- local original_settings original_floating off_result off_state on_result on_state settings_open_state click_result panel_state
+ local original_settings original_floating original_preview preview_result off_result off_state on_result on_state settings_open_state click_result panel_state
local passed=0
local detail=""
@@ -1141,13 +1204,22 @@ run_014() {
original_settings=$(settings_values)
original_floating=$(echo "$original_settings" | jq -r '.floatingReplyButton // empty')
- if [ -z "$original_floating" ]; then
- record_fail "$case_id" "无法读取悬浮回复入口设置"
+ original_preview=$(echo "$original_settings" | jq -r '.previewMode // empty')
+ if [ -z "$original_floating" ] || [ -z "$original_preview" ]; then
+ record_fail "$case_id" "无法读取当前设置"
return 0
fi
+ preview_result=$(set_setting_value previewMode smart)
+ if [ "$(echo "$preview_result" | jq -r '.ok')" != "true" ]; then
+ record_fail "$case_id" "无法切到智能预览: $preview_result"
+ return 0
+ fi
+ ab wait 1200 >/dev/null
+
off_result=$(set_setting_value floatingReplyButton off)
if [ "$(echo "$off_result" | jq -r '.ok')" != "true" ]; then
+ set_setting_value previewMode "$original_preview" >/dev/null || true
record_fail "$case_id" "无法关闭悬浮回复入口: $off_result"
return 0
fi
@@ -1175,6 +1247,7 @@ run_014() {
close_reply_panel_if_open >/dev/null || true
ab wait 400 >/dev/null
set_setting_value floatingReplyButton "$original_floating" >/dev/null || true
+ set_setting_value previewMode "$original_preview" >/dev/null || true
ab wait 800 >/dev/null
ensure_settings_closed
@@ -1230,6 +1303,107 @@ run_015() {
fi
}
+run_016() {
+ local case_id="AGENT-CHROME-016"
+ local original_settings original_value original_preview preview_result target_value
+ local before set_result after
+
+ open_topic_by_index 0 >/dev/null || {
+ record_fail "$case_id" "无法打开抽屉"
+ return 0
+ }
+
+ original_settings=$(settings_values)
+ original_value=$(echo "$original_settings" | jq -r '.postBodyFontSize // empty')
+ original_preview=$(echo "$original_settings" | jq -r '.previewMode // empty')
+ if [ -z "$original_value" ] || [ -z "$original_preview" ]; then
+ record_fail "$case_id" "无法读取当前设置"
+ return 0
+ fi
+
+ preview_result=$(set_setting_value previewMode smart)
+ if [ "$(echo "$preview_result" | jq -r '.ok')" != "true" ]; then
+ record_fail "$case_id" "无法切到智能预览: $preview_result"
+ return 0
+ fi
+ ab wait 1200 >/dev/null
+ ensure_settings_open
+ before=$(post_body_font_state)
+ target_value=18
+ if [ "$original_value" = "18" ]; then
+ target_value=13
+ fi
+
+ set_result=$(set_setting_value postBodyFontSize "$target_value")
+ if [ "$(echo "$set_result" | jq -r '.ok')" != "true" ]; then
+ ensure_settings_closed
+ record_fail "$case_id" "无法设置正文字号: $set_result"
+ return 0
+ fi
+
+ ab wait 200 >/dev/null
+ after=$(post_body_font_state)
+
+ set_setting_value postBodyFontSize "$original_value" >/dev/null || true
+ set_setting_value previewMode "$original_preview" >/dev/null || true
+ ab wait 200 >/dev/null
+ ensure_settings_closed
+
+ if [ "$(echo "$before" | jq -r '.pageOpen')" = "true" ] \
+ && [ "$(echo "$before" | jq -r '.bodyExists')" = "true" ] \
+ && [ "$(echo "$after" | jq -r '.bodyExists')" = "true" ] \
+ && [ "$(echo "$after" | jq -r '.controlValue // empty')" = "$target_value" ] \
+ && [ "$(echo "$after" | jq -r '.valueText // empty')" = "${target_value}px" ] \
+ && [ "$(echo "$after" | jq -r '.bodyFontSize')" = "$target_value" ] \
+ && { [ "$(echo "$after" | jq -r '.codeExists')" != "true" ] || [ "$(echo "$after" | jq -r '.codeFontSize')" = "$((target_value - 1))" ]; } \
+ && [ "$(echo "$before" | jq -r '.bodyFontSize')" != "$(echo "$after" | jq -r '.bodyFontSize')" ]; then
+ record_pass "$case_id" "before=$(echo "$before" | jq -c '{bodyFontSize, codeExists, codeFontSize, controlValue, valueText}') after=$(echo "$after" | jq -c '{bodyFontSize, codeExists, codeFontSize, controlValue, valueText}')"
+ else
+ record_fail "$case_id" "before=$(echo "$before" | jq -c '{pageOpen, bodyExists, bodyFontSize, codeExists, codeFontSize, controlValue, valueText}') after=$(echo "$after" | jq -c '{pageOpen, bodyExists, bodyFontSize, codeExists, codeFontSize, controlValue, valueText}') target=$target_value"
+ fi
+}
+
+run_017() {
+ local case_id="AGENT-CHROME-017"
+ local original_settings original_preview preview_result state
+
+ open_topic_by_index 0 >/dev/null || {
+ record_fail "$case_id" "无法打开抽屉"
+ return 0
+ }
+
+ original_settings=$(settings_values)
+ original_preview=$(echo "$original_settings" | jq -r '.previewMode // empty')
+ if [ -z "$original_preview" ]; then
+ record_fail "$case_id" "无法读取当前预览模式"
+ return 0
+ fi
+
+ preview_result=$(set_setting_value previewMode iframe)
+ if [ "$(echo "$preview_result" | jq -r '.ok')" != "true" ]; then
+ record_fail "$case_id" "无法切到整页模式: $preview_result"
+ return 0
+ fi
+
+ ab wait 1200 >/dev/null
+ ensure_settings_open
+ state=$(post_body_font_state)
+
+ set_setting_value previewMode "$original_preview" >/dev/null || true
+ ab wait 200 >/dev/null
+ ensure_settings_closed
+
+ if [ "$(echo "$state" | jq -r '.pageOpen')" = "true" ] \
+ && [ "$(echo "$state" | jq -r '.fieldDisabled')" = "true" ] \
+ && [ "$(echo "$state" | jq -r '.fieldAriaDisabled // empty')" = "true" ] \
+ && [ "$(echo "$state" | jq -r '.controlDisabled')" = "true" ] \
+ && echo "$state" | jq -e '.hintText | test("仅智能预览可用")' >/dev/null; then
+ record_pass "$case_id" "state=$(echo "$state" | jq -c '{fieldDisabled, fieldAriaDisabled, controlDisabled, valueText, hintText}')"
+ else
+ record_fail "$case_id" "state=$(echo "$state" | jq -c '{pageOpen, fieldDisabled, fieldAriaDisabled, controlDisabled, controlValue, valueText, hintText}')"
+ fi
+}
+
run_case() {
local case_id=$1
case "$case_id" in
@@ -1245,6 +1419,8 @@ run_case() {
AGENT-CHROME-013) run_013 ;;
AGENT-CHROME-014) run_014 ;;
AGENT-CHROME-015) run_015 ;;
+ AGENT-CHROME-016) run_016 ;;
+ AGENT-CHROME-017) run_017 ;;
*)
record_fail "$case_id" "未知用例 ID"
;;
diff --git a/src/content.css b/src/content.css
index 742ec58..30e05fa 100644
--- a/src/content.css
+++ b/src/content.css
@@ -1,5 +1,6 @@
:root {
--ld-drawer-width: clamp(360px, 42vw, 920px);
+ --ld-post-body-font-size: 15px;
--ld-image-preview-scale: 1;
--ld-topic-tracker-left: 50vw;
--ld-topic-tracker-top: 112px;
@@ -350,11 +351,28 @@ body.ld-drawer-page-open #ld-drawer-root {
gap: 6px;
}
+#ld-drawer-root .ld-setting-field.is-disabled {
+ opacity: 0.58;
+}
+
+#ld-drawer-root .ld-setting-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+}
+
#ld-drawer-root .ld-setting-label {
font-size: 12px;
color: var(--primary-medium, rgba(15, 23, 42, 0.64));
}
+#ld-drawer-root .ld-setting-value {
+ color: var(--primary-medium, rgba(15, 23, 42, 0.72));
+ font-size: 12px;
+ font-variant-numeric: tabular-nums;
+}
+
#ld-drawer-root .ld-setting-hint {
font-size: 11px;
color: var(--primary-medium, rgba(15, 23, 42, 0.56));
@@ -371,6 +389,23 @@ body.ld-drawer-page-open #ld-drawer-root {
padding: 10px 12px;
}
+#ld-drawer-root .ld-setting-range {
+ width: 100%;
+ margin: 0;
+ accent-color: var(--tertiary, #3b82f6);
+ cursor: pointer;
+}
+
+#ld-drawer-root .ld-setting-field.is-disabled .ld-setting-range {
+ cursor: not-allowed;
+}
+
+#ld-drawer-root .ld-setting-range:focus-visible {
+ outline: 2px solid color-mix(in srgb, var(--tertiary, #3b82f6) 24%, transparent);
+ outline-offset: 3px;
+ border-radius: 999px;
+}
+
#ld-drawer-root .ld-settings-reset {
align-self: end;
justify-self: end;
@@ -707,7 +742,7 @@ body.ld-drawer-page-open #ld-drawer-root {
#ld-drawer-root .ld-post-body {
padding: 16px;
line-height: 1.72;
- font-size: 14px;
+ font-size: var(--ld-post-body-font-size, 14px);
}
#ld-drawer-root .ld-post-actions {
@@ -790,7 +825,7 @@ body.ld-drawer-page-open #ld-drawer-root {
#ld-drawer-root .ld-post-body pre,
#ld-drawer-root .ld-post-body code {
- font-size: 13px;
+ font-size: max(12px, calc(var(--ld-post-body-font-size, 14px) - 1px));
}
#ld-drawer-root .ld-post-body pre {
diff --git a/src/content.js b/src/content.js
index f406477..2b9384e 100644
--- a/src/content.js
+++ b/src/content.js
@@ -10,19 +10,22 @@
const IMAGE_PREVIEW_SCALE_MIN = 1;
const IMAGE_PREVIEW_SCALE_MAX = 4;
const IMAGE_PREVIEW_SCALE_STEP = 0.2;
+ const POST_BODY_FONT_SIZE_MIN = 13;
+ const POST_BODY_FONT_SIZE_MAX = 18;
const REPLY_UPLOAD_MARKER = "\u2063";
const POST_ACTION_TYPE_IDS = {
like: 2
};
const DEFAULT_SETTINGS = {
- previewMode: "smart",
+ previewMode: "iframe",
postMode: "all",
+ postBodyFontSize: 15,
authorFilter: "all",
replyOrder: "default",
floatingReplyButton: "off",
- drawerWidth: "medium",
+ drawerWidth: "narrow",
drawerWidthCustom: 720,
- drawerMode: "push"
+ drawerMode: "overlay"
};
const DRAWER_WIDTHS = {
narrow: "clamp(320px, 34vw, 680px)",
@@ -106,6 +109,10 @@
openInTab: null,
settingsPanel: null,
settingsCard: null,
+ postBodyFontSizeField: null,
+ postBodyFontSizeControl: null,
+ postBodyFontSizeValue: null,
+ postBodyFontSizeHint: null,
settingsCloseButton: null,
settingsToggle: null,
latestRepliesRefreshButton: null,
@@ -206,6 +213,21 @@
+