diff --git a/doc/agent-smoke-cases.md b/doc/agent-smoke-cases.md index 3363f63..7d90fb5 100644 --- a/doc/agent-smoke-cases.md +++ b/doc/agent-smoke-cases.md @@ -75,7 +75,8 @@ bash scripts/agent-smoke.sh --cdp-port 9222 --cases AGENT-CHROME-001,AGENT-CHROM 1. `上一帖` 按钮存在 2. `下一帖` 按钮存在 3. `选项` 按钮存在 -4. `新标签打开` 链接存在 +4. `回复主题` 按钮存在 +5. `新标签打开` 链接存在 ### AGENT-CHROME-003:设置面板打开和关闭 @@ -266,6 +267,49 @@ bash scripts/agent-smoke.sh --cdp-port 9222 --cases AGENT-CHROME-001,AGENT-CHROM 2. 抽屉保持打开 3. `location.href` 仍然停留在列表页 +### AGENT-CHROME-014:主题回复入口与悬浮开关 + +范围: +头部 `回复主题` 主入口 + 可选悬浮快捷入口 + +状态: +当前不在默认批量脚本中 + +步骤: + +1. 打开任一主题抽屉 +2. 确认头部工具栏存在 `回复主题` +3. 读取右侧 `.ld-drawer-reply-fab` 的可见性 +4. 在 `选项` 中把 `悬浮回复入口` 切到 `开启` +5. 点击头部 `回复主题` 或右侧悬浮按钮 + +断言: + +1. 默认仅显示头部 `回复主题`,右侧悬浮按钮默认隐藏 +2. 切到 `开启` 后,右侧悬浮按钮变为可见 +3. 设置层打开时,右侧悬浮按钮不应继续透出或可点击 +4. 点击任一主题级回复入口后,`#ld-drawer-reply-panel` 打开 +5. 回复面板标题默认是 `回复主题` + +### AGENT-CHROME-015:头部工具栏按钮不应挤成竖排 + +范围: +抽屉头部工具栏布局回归 + +状态: +当前不在默认批量脚本中 + +步骤: + +1. 打开任一主题抽屉 +2. 读取 `.ld-drawer-actions` 中可见按钮和链接的布局信息 + +断言: + +1. 头部工具栏可见操作项不少于 5 个 +2. 文本按钮和链接都保持 `white-space: nowrap` +3. 可见文本按钮的宽度应明显大于高度,避免被挤成竖排窄条 + ## 3. 记录格式 执行完用例后,建议按下面格式留证据: diff --git a/scripts/agent-smoke.sh b/scripts/agent-smoke.sh index 6d48287..441aae2 100755 --- a/scripts/agent-smoke.sh +++ b/scripts/agent-smoke.sh @@ -16,6 +16,13 @@ DEFAULT_CASES=( "AGENT-CHROME-010" ) +ALL_CASES=( + "${DEFAULT_CASES[@]}" + "AGENT-CHROME-013" + "AGENT-CHROME-014" + "AGENT-CHROME-015" +) + CASES=("${DEFAULT_CASES[@]}") PASS_COUNT=0 FAIL_COUNT=0 @@ -34,7 +41,7 @@ EOF } list_cases() { - printf '%s\n' "${DEFAULT_CASES[@]}" + printf '%s\n' "${ALL_CASES[@]}" } while [ "$#" -gt 0 ]; do @@ -137,6 +144,7 @@ drawer_state() { const root = document.getElementById("ld-drawer-root"); const settingsButton = root?.querySelector(".ld-drawer-settings-toggle"); const settingsPanel = root?.querySelector("#ld-drawer-settings"); + const replyButton = root?.querySelector(".ld-drawer-reply-toggle"); const link = root?.querySelector(".ld-drawer-link"); return { pageUrl: location.href, @@ -148,6 +156,8 @@ drawer_state() { settingsText: settingsButton?.textContent?.trim() || null, settingsExpanded: settingsButton?.getAttribute("aria-expanded") || null, settingsHidden: settingsPanel?.hasAttribute("hidden") ?? null, + replyText: replyButton?.textContent?.trim() || null, + replyHidden: replyButton?.hidden ?? null, newTabText: link?.textContent?.trim() || null, newTabHref: link?.getAttribute("href") || null, newTabTarget: link?.getAttribute("target") || null, @@ -543,6 +553,139 @@ EOF ab_eval "$script" } +reply_entry_state() { + local script + script=$(cat <<'EOF' +(() => { + const root = document.getElementById("ld-drawer-root"); + const headerButton = root?.querySelector(".ld-drawer-reply-toggle"); + const fabButton = root?.querySelector(".ld-drawer-reply-fab"); + const panel = root?.querySelector("#ld-drawer-reply-panel"); + const fabRect = fabButton?.getBoundingClientRect?.() || null; + const fabStyle = fabButton ? getComputedStyle(fabButton) : null; + return { + headerExists: headerButton instanceof HTMLButtonElement, + headerText: headerButton?.textContent?.trim() || null, + headerHidden: headerButton?.hidden ?? null, + headerExpanded: headerButton?.getAttribute("aria-expanded") || null, + fabExists: fabButton instanceof HTMLButtonElement, + fabText: fabButton?.textContent?.trim() || null, + fabHidden: fabButton?.hidden ?? null, + fabDisplay: fabStyle?.display || null, + fabRendered: Boolean(fabRect && fabRect.width > 0 && fabRect.height > 0), + fabExpanded: fabButton?.getAttribute("aria-expanded") || null, + panelHidden: panel?.hasAttribute("hidden") ?? null, + panelTitle: panel?.querySelector(".ld-reply-panel-title")?.textContent?.trim() || null + }; +})() +EOF +) + ab_eval "$script" +} + +toolbar_layout_state() { + local script + script=$(cat <<'EOF' +(() => { + const toolbar = document.querySelector("#ld-drawer-root .ld-drawer-toolbar"); + const actions = document.querySelector("#ld-drawer-root .ld-drawer-actions"); + const items = Array.from(actions?.querySelectorAll("button, a") || []) + .filter((node) => !node.hidden) + .map((node) => { + const rect = node.getBoundingClientRect(); + const style = getComputedStyle(node); + const text = (node.textContent || "").replace(/\s+/g, " ").trim(); + return { + text, + width: Math.round(rect.width), + height: Math.round(rect.height), + whiteSpace: style.whiteSpace, + display: style.display + }; + }); + + const toolbarStyle = toolbar ? getComputedStyle(toolbar) : null; + const visibleTextItems = items.filter((item) => item.text); + + return { + toolbarExists: Boolean(toolbar), + actionsExists: Boolean(actions), + gridTemplateColumns: toolbarStyle?.gridTemplateColumns || null, + visibleCount: items.length, + items, + nowrapOk: visibleTextItems.every((item) => item.whiteSpace === "nowrap"), + widthOk: visibleTextItems.every((item) => item.width >= 44 && item.width > item.height) + }; +})() +EOF +) + ab_eval "$script" +} + +click_reply_trigger() { + local target=$1 + local selector_json script + case "$target" in + header) + selector_json='"#ld-drawer-root .ld-drawer-reply-toggle"' + ;; + fab) + selector_json='"#ld-drawer-root .ld-drawer-reply-fab"' + ;; + *) + echo "{\"ok\":false,\"reason\":\"unknown-target\",\"target\":\"$target\"}" + return 0 + ;; + esac + + script=$(cat < { + const target = $(printf '%s' "$target" | jq -Rs .); + const button = document.querySelector($selector_json); + if (!(button instanceof HTMLButtonElement)) { + return { ok: false, reason: "reply-trigger-not-found", target }; + } + + button.click(); + return { + ok: true, + target, + hidden: button.hidden, + expanded: button.getAttribute("aria-expanded") || null, + text: button.textContent?.trim() || null + }; +})() +EOF +) + ab_eval "$script" +} + +close_reply_panel_if_open() { + local script + script=$(cat <<'EOF' +(() => { + const panel = document.querySelector("#ld-drawer-root #ld-drawer-reply-panel"); + if (!(panel instanceof HTMLElement)) { + return { ok: false, reason: "reply-panel-not-found" }; + } + + if (panel.hasAttribute("hidden")) { + return { ok: true, closed: false }; + } + + const button = panel.querySelector(".ld-reply-panel-close"); + if (!(button instanceof HTMLButtonElement)) { + return { ok: false, reason: "reply-panel-close-not-found" }; + } + + button.click(); + return { ok: true, closed: true }; +})() +EOF +) + ab_eval "$script" +} + click_refresh_button() { local script script=$(cat <<'EOF' @@ -601,6 +744,8 @@ run_002() { 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 '.newTabText // empty')" = "新标签打开" ]; then record_pass "$case_id" "toolbar ready" else @@ -802,7 +947,12 @@ run_010() { return 0 } - before=$(refresh_button_state) + if ! before=$(wait_for_refresh_idle); then + restore_settings_subset "$original_settings" + record_fail "$case_id" "刷新按钮在切换场景后未进入可点击状态: $(echo "$before" | jq -c '{exists, hidden, disabled, text}')" + return 0 + fi + click=$(click_refresh_button) if ! after=$(wait_for_refresh_idle); then : @@ -978,6 +1128,108 @@ run_013() { fi } +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 passed=0 + local detail="" + + open_topic_by_index 0 >/dev/null || { + record_fail "$case_id" "无法打开抽屉" + return 0 + } + + original_settings=$(settings_values) + original_floating=$(echo "$original_settings" | jq -r '.floatingReplyButton // empty') + if [ -z "$original_floating" ]; then + record_fail "$case_id" "无法读取悬浮回复入口设置" + return 0 + fi + + off_result=$(set_setting_value floatingReplyButton off) + if [ "$(echo "$off_result" | jq -r '.ok')" != "true" ]; then + record_fail "$case_id" "无法关闭悬浮回复入口: $off_result" + return 0 + fi + ab wait 1200 >/dev/null + off_state=$(reply_entry_state) + + on_result=$(set_setting_value floatingReplyButton on) + if [ "$(echo "$on_result" | jq -r '.ok')" != "true" ]; then + set_setting_value floatingReplyButton "$original_floating" >/dev/null || true + record_fail "$case_id" "无法开启悬浮回复入口: $on_result" + return 0 + fi + ab wait 1200 >/dev/null + on_state=$(reply_entry_state) + + ensure_settings_open + settings_open_state=$(reply_entry_state) + ensure_settings_closed + ab wait 400 >/dev/null + + click_result=$(click_reply_trigger fab) + ab wait 800 >/dev/null + panel_state=$(reply_entry_state) + + close_reply_panel_if_open >/dev/null || true + ab wait 400 >/dev/null + set_setting_value floatingReplyButton "$original_floating" >/dev/null || true + ab wait 800 >/dev/null + ensure_settings_closed + + if [ "$(echo "$off_state" | jq -r '.headerExists')" = "true" ] \ + && [ "$(echo "$off_state" | jq -r '.headerText // empty')" = "回复主题" ] \ + && [ "$(echo "$off_state" | jq -r '.headerHidden')" = "false" ] \ + && [ "$(echo "$off_state" | jq -r '.fabExists')" = "true" ] \ + && [ "$(echo "$off_state" | jq -r '.fabHidden')" = "true" ] \ + && [ "$(echo "$off_state" | jq -r '.fabDisplay // empty')" = "none" ] \ + && [ "$(echo "$off_state" | jq -r '.fabRendered')" = "false" ] \ + && [ "$(echo "$on_state" | jq -r '.fabHidden')" = "false" ] \ + && [ "$(echo "$on_state" | jq -r '.fabDisplay // empty')" != "none" ] \ + && [ "$(echo "$on_state" | jq -r '.fabRendered')" = "true" ] \ + && [ "$(echo "$settings_open_state" | jq -r '.fabHidden')" = "true" ] \ + && [ "$(echo "$settings_open_state" | jq -r '.fabDisplay // empty')" = "none" ] \ + && [ "$(echo "$settings_open_state" | jq -r '.fabRendered')" = "false" ] \ + && [ "$(echo "$click_result" | jq -r '.ok')" = "true" ] \ + && [ "$(echo "$click_result" | jq -r '.target // empty')" = "fab" ] \ + && [ "$(echo "$panel_state" | jq -r '.panelHidden')" = "false" ] \ + && [ "$(echo "$panel_state" | jq -r '.panelTitle // empty')" = "回复主题" ]; then + passed=1 + detail="off=$(echo "$off_state" | jq -c '{headerHidden, fabHidden, fabDisplay, fabRendered}') on=$(echo "$on_state" | jq -c '{fabHidden, fabDisplay, fabRendered}') settings=$(echo "$settings_open_state" | jq -c '{fabHidden, fabDisplay, fabRendered}') panel=$(echo "$panel_state" | jq -c '{panelHidden, panelTitle}')" + else + detail="off=$(echo "$off_state" | jq -c '{headerExists, headerText, headerHidden, fabExists, fabHidden, fabDisplay, fabRendered}') on=$(echo "$on_state" | jq -c '{fabHidden, fabDisplay, fabRendered}') settings=$(echo "$settings_open_state" | jq -c '{fabHidden, fabDisplay, fabRendered, panelHidden}') click=$(echo "$click_result" | jq -c '.') panel=$(echo "$panel_state" | jq -c '{panelHidden, panelTitle, headerExpanded, fabExpanded}')" + fi + + if [ "$passed" -eq 1 ]; then + record_pass "$case_id" "$detail" + else + record_fail "$case_id" "$detail" + fi +} + +run_015() { + local case_id="AGENT-CHROME-015" + local state + + open_topic_by_index 0 >/dev/null || { + record_fail "$case_id" "无法打开抽屉" + return 0 + } + + state=$(toolbar_layout_state) + + if [ "$(echo "$state" | jq -r '.toolbarExists')" = "true" ] \ + && [ "$(echo "$state" | jq -r '.actionsExists')" = "true" ] \ + && [ "$(echo "$state" | jq -r '.nowrapOk')" = "true" ] \ + && [ "$(echo "$state" | jq -r '.widthOk')" = "true" ] \ + && [ "$(echo "$state" | jq -r '.visibleCount')" -ge 5 ]; then + record_pass "$case_id" "items=$(echo "$state" | jq -c '.items | map({text, width, height, whiteSpace})')" + else + record_fail "$case_id" "state=$(echo "$state" | jq -c '{gridTemplateColumns, visibleCount, nowrapOk, widthOk, items}')" + fi +} + run_case() { local case_id=$1 case "$case_id" in @@ -991,6 +1243,8 @@ run_case() { AGENT-CHROME-009) run_009 ;; AGENT-CHROME-010) run_010 ;; AGENT-CHROME-013) run_013 ;; + AGENT-CHROME-014) run_014 ;; + AGENT-CHROME-015) run_015 ;; *) record_fail "$case_id" "未知用例 ID" ;; diff --git a/src/content.css b/src/content.css index 1f062b2..742ec58 100644 --- a/src/content.css +++ b/src/content.css @@ -199,9 +199,9 @@ body.ld-drawer-page-open #ld-drawer-root { #ld-drawer-root .ld-drawer-toolbar { display: grid; width: 100%; - grid-template-columns: auto minmax(0, 1fr); + grid-template-columns: minmax(0, 1fr); align-items: start; - gap: 8px 12px; + gap: 8px; min-width: 0; } @@ -222,6 +222,7 @@ body.ld-drawer-page-open #ld-drawer-root { #ld-drawer-root .ld-drawer-actions { display: flex; + width: 100%; min-width: 0; align-items: center; flex-wrap: wrap; @@ -231,6 +232,7 @@ body.ld-drawer-page-open #ld-drawer-root { #ld-drawer-root .ld-drawer-nav, #ld-drawer-root .ld-drawer-refresh, +#ld-drawer-root .ld-drawer-reply-toggle, #ld-drawer-root .ld-drawer-link, #ld-drawer-root .ld-drawer-close, #ld-drawer-root .ld-drawer-settings-toggle, @@ -245,6 +247,8 @@ body.ld-drawer-page-open #ld-drawer-root { line-height: 1; cursor: pointer; text-decoration: none; + white-space: nowrap; + flex: 0 0 auto; } #ld-drawer-root .ld-drawer-nav:disabled, @@ -262,6 +266,7 @@ body.ld-drawer-page-open #ld-drawer-root { #ld-drawer-root .ld-drawer-close:hover, #ld-drawer-root .ld-drawer-nav:hover, #ld-drawer-root .ld-drawer-refresh:hover, +#ld-drawer-root .ld-drawer-reply-toggle:hover, #ld-drawer-root .ld-drawer-link:hover, #ld-drawer-root .ld-drawer-settings-toggle:hover, #ld-drawer-root .ld-settings-close:hover, @@ -276,6 +281,24 @@ body.ld-drawer-page-open #ld-drawer-root { background: color-mix(in srgb, var(--tertiary, #3b82f6) 10%, var(--secondary, #fff)); } +#ld-drawer-root .ld-drawer-reply-toggle { + border-color: color-mix(in srgb, var(--tertiary, #3b82f6) 30%, var(--primary-low, rgba(15, 23, 42, 0.12))); + background: color-mix(in srgb, var(--tertiary, #3b82f6) 10%, var(--secondary, #fff)); + color: var(--tertiary, #3b82f6); + font-weight: 700; +} + +#ld-drawer-root .ld-drawer-reply-toggle[aria-expanded="true"] { + border-color: var(--tertiary, #3b82f6); + background: color-mix(in srgb, var(--tertiary, #3b82f6) 16%, var(--secondary, #fff)); +} + +#ld-drawer-root .ld-drawer-reply-toggle:disabled, +#ld-drawer-root .ld-drawer-reply-toggle.is-disabled { + opacity: 0.64; + cursor: not-allowed; +} + #ld-drawer-root .ld-drawer-settings { position: absolute; inset: 0; @@ -399,6 +422,46 @@ body.ld-drawer-page-open #ld-drawer-root { background: color-mix(in srgb, var(--tertiary, #3b82f6) 64%, transparent); } +#ld-drawer-root .ld-drawer-body, +#ld-drawer-root .ld-drawer-settings-card, +#ld-drawer-root .ld-drawer-reply-panel, +#ld-drawer-root .ld-post-body pre { + overscroll-behavior: contain; +} + +#ld-drawer-root .ld-drawer-body, +#ld-drawer-root .ld-drawer-settings-card, +#ld-drawer-root .ld-drawer-reply-panel { + scrollbar-width: thin; + scrollbar-color: color-mix(in srgb, var(--tertiary, #3b82f6) 40%, transparent) transparent; +} + +#ld-drawer-root .ld-drawer-body::-webkit-scrollbar, +#ld-drawer-root .ld-drawer-settings-card::-webkit-scrollbar, +#ld-drawer-root .ld-drawer-reply-panel::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +#ld-drawer-root .ld-drawer-body::-webkit-scrollbar-track, +#ld-drawer-root .ld-drawer-settings-card::-webkit-scrollbar-track, +#ld-drawer-root .ld-drawer-reply-panel::-webkit-scrollbar-track { + background: transparent; +} + +#ld-drawer-root .ld-drawer-body::-webkit-scrollbar-thumb, +#ld-drawer-root .ld-drawer-settings-card::-webkit-scrollbar-thumb, +#ld-drawer-root .ld-drawer-reply-panel::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--tertiary, #3b82f6) 40%, transparent); + border-radius: 999px; +} + +#ld-drawer-root .ld-drawer-body::-webkit-scrollbar-thumb:hover, +#ld-drawer-root .ld-drawer-settings-card::-webkit-scrollbar-thumb:hover, +#ld-drawer-root .ld-drawer-reply-panel::-webkit-scrollbar-thumb:hover { + background: color-mix(in srgb, var(--tertiary, #3b82f6) 64%, transparent); +} + #ld-drawer-root .ld-drawer-content { padding: 16px 18px 28px; } @@ -439,6 +502,10 @@ body.ld-drawer-page-open #ld-drawer-root { transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease; } +#ld-drawer-root .ld-drawer-reply-fab[hidden] { + display: none !important; +} + #ld-drawer-root .ld-drawer-reply-fab:hover { transform: translateY(calc(-50% - 1px)); box-shadow: 0 18px 40px rgba(15, 23, 42, 0.22); @@ -475,13 +542,14 @@ body.ld-drawer-page-open #ld-drawer-root { #ld-drawer-root .ld-drawer-reply-panel { position: absolute; - top: 50%; + top: calc(var(--ld-reply-panel-top, 84px) + 8px); right: 14px; z-index: 5; width: min(360px, calc(100% - 28px)); display: grid; gap: 12px; - transform: translateY(-50%); + max-height: calc(100% - var(--ld-reply-panel-top, 84px) - 22px); + overflow: auto; padding: 14px; border: 1px solid var(--primary-low, rgba(15, 23, 42, 0.12)); border-radius: 18px; @@ -644,11 +712,14 @@ body.ld-drawer-page-open #ld-drawer-root { #ld-drawer-root .ld-post-actions { display: flex; + flex-wrap: wrap; + align-items: center; justify-content: flex-end; + gap: 8px; padding: 0 16px 14px; } -#ld-drawer-root .ld-post-reply-button { +#ld-drawer-root .ld-post-action-button { display: inline-flex; align-items: center; gap: 6px; @@ -660,28 +731,55 @@ body.ld-drawer-page-open #ld-drawer-root { font-size: 12px; line-height: 1; cursor: pointer; + transition: border-color 0.18s ease, color 0.18s ease, background-color 0.18s ease, opacity 0.18s ease; } -#ld-drawer-root .ld-post-reply-button:hover { +#ld-drawer-root .ld-post-action-button:hover { border-color: var(--tertiary, #3b82f6); color: var(--tertiary, #3b82f6); } -#ld-drawer-root .ld-post-reply-button-icon { +#ld-drawer-root .ld-post-action-button.is-active { + border-color: color-mix(in srgb, var(--tertiary, #3b82f6) 32%, var(--primary-low, rgba(15, 23, 42, 0.12))); + background: color-mix(in srgb, var(--tertiary, #3b82f6) 10%, var(--secondary, #fff)); + color: var(--tertiary, #3b82f6); +} + +#ld-drawer-root .ld-post-action-button:disabled, +#ld-drawer-root .ld-post-action-button.is-pending { + opacity: 0.68; + cursor: wait; +} + +#ld-drawer-root .ld-post-action-button.is-feedback-error { + color: #b91c1c; + border-color: rgba(185, 28, 28, 0.2); + background: rgba(254, 226, 226, 0.72); +} + +#ld-drawer-root .ld-post-action-button.is-icon-only { + padding-right: 8px; +} + +#ld-drawer-root .ld-post-action-button-icon { width: 14px; height: 14px; display: inline-flex; } -#ld-drawer-root .ld-post-reply-button-icon svg { +#ld-drawer-root .ld-post-action-button-icon svg { width: 14px; height: 14px; } -#ld-drawer-root .ld-post-reply-button-label { +#ld-drawer-root .ld-post-action-button-label { white-space: nowrap; } +#ld-drawer-root .ld-post-action-button-label:empty { + display: none; +} + #ld-drawer-root .ld-post-body > :first-child { margin-top: 0; } @@ -929,6 +1027,7 @@ body.ld-drawer-page-open #ld-drawer-root { left: 12px; bottom: max(72px, env(safe-area-inset-bottom, 0px) + 56px); width: auto; + max-height: calc(100dvh - max(72px, env(safe-area-inset-bottom, 0px) + 56px) - 16px); transform: none; } } diff --git a/src/content.js b/src/content.js index 5f083ca..f406477 100644 --- a/src/content.js +++ b/src/content.js @@ -11,10 +11,15 @@ const IMAGE_PREVIEW_SCALE_MAX = 4; const IMAGE_PREVIEW_SCALE_STEP = 0.2; const REPLY_UPLOAD_MARKER = "\u2063"; + const POST_ACTION_TYPE_IDS = { + like: 2 + }; const DEFAULT_SETTINGS = { previewMode: "smart", postMode: "all", + authorFilter: "all", replyOrder: "default", + floatingReplyButton: "off", drawerWidth: "medium", drawerWidthCustom: 720, drawerMode: "push" @@ -86,7 +91,8 @@ meta: null, drawerBody: null, content: null, - replyButton: null, + replyToggleButton: null, + replyFabButton: null, replyPanel: null, replyPanelTitle: null, replyTextarea: null, @@ -129,6 +135,7 @@ replyUploadPendingCount: 0, replyUploadSerial: 0, replyComposerSessionId: 0, + deferOwnerFilterAutoLoad: false, lastLocation: location.href, settings: loadSettings(), isResizing: false, @@ -173,6 +180,7 @@ + 新标签打开 @@ -198,6 +206,14 @@ + +