Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions doc/agent-smoke-cases.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,31 @@ bash scripts/agent-smoke.sh --cdp-port 9222 --cases AGENT-CHROME-001,AGENT-CHROM
2. 对应滑杆 `disabled=true`
3. 提示文案明确说明该设置仅在 `智能预览` 下生效

### AGENT-CHROME-018:图片预览应全屏覆盖并支持滚轮缩放

范围:
正文图片点击预览、全屏覆盖、再次点击关闭、滚轮缩放

状态:
当前不在默认批量脚本中;用例内会显式切到 `智能预览`

建议目标:
优先选列表前几条中正文自带图片的公开主题

步骤:

1. 打开任一主题抽屉
2. 在 `选项` 中切到 `智能预览`
3. 点击正文中的第一张图片
4. 在预览层内滚轮向上缩放一次
5. 再点击预览图片本身

断言:

1. 图片预览层应覆盖整个视口,而不是只局限在右侧抽屉宽度内
2. 预览打开后再次点击预览图片本身,应立即关闭预览层
3. 在预览层内滚轮缩放后,预览图片的缩放值应大于默认的 `1`

## 3. 记录格式

执行完用例后,建议按下面格式留证据:
Expand Down
45 changes: 27 additions & 18 deletions src/content.css
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ body.ld-drawer-resizing * {
pointer-events: none;
}

#ld-image-preview-root {
position: fixed;
inset: 0;
z-index: 2147483647;
pointer-events: none;
}

body.ld-drawer-resizing #ld-drawer-root,
body.ld-drawer-resizing {
transition: none !important;
Expand Down Expand Up @@ -82,10 +89,10 @@ body.ld-drawer-page-open #ld-drawer-root {
pointer-events: auto;
}

#ld-drawer-root .ld-image-preview {
position: absolute;
#ld-image-preview-root .ld-image-preview {
position: fixed;
inset: 0;
z-index: 8;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
Expand All @@ -94,13 +101,14 @@ body.ld-drawer-page-open #ld-drawer-root {
backdrop-filter: blur(6px);
opacity: 1;
animation: ld-image-preview-fade-in 180ms ease-out;
pointer-events: auto;
}

#ld-drawer-root .ld-image-preview[hidden] {
#ld-image-preview-root .ld-image-preview[hidden] {
display: none !important;
}

#ld-drawer-root .ld-image-preview-close {
#ld-image-preview-root .ld-image-preview-close {
position: absolute;
top: 14px;
right: 18px;
Expand All @@ -115,13 +123,13 @@ body.ld-drawer-page-open #ld-drawer-root {
transition: background-color 0.18s ease, border-color 0.18s ease, transform 0.18s ease;
}

#ld-drawer-root .ld-image-preview-close:hover {
#ld-image-preview-root .ld-image-preview-close:hover {
background: rgba(15, 23, 42, 0.58);
border-color: rgba(255, 255, 255, 0.44);
transform: translateY(-1px);
}

#ld-drawer-root .ld-image-preview-stage {
#ld-image-preview-root .ld-image-preview-stage {
display: flex;
align-items: center;
justify-content: center;
Expand All @@ -130,7 +138,7 @@ body.ld-drawer-page-open #ld-drawer-root {
overflow: hidden;
}

#ld-drawer-root .ld-image-preview-image {
#ld-image-preview-root .ld-image-preview-image {
display: block;
max-width: 100%;
max-height: 100%;
Expand All @@ -143,18 +151,19 @@ body.ld-drawer-page-open #ld-drawer-root {
transition: opacity 0.2s ease, transform 0.24s ease, box-shadow 0.24s ease;
transform-origin: center center;
will-change: transform;
cursor: zoom-out;
}

#ld-drawer-root .ld-image-preview-image.is-ready {
#ld-image-preview-root .ld-image-preview-image.is-ready {
opacity: 1;
transform: translateY(0) scale(var(--ld-image-preview-scale, 1));
}

#ld-drawer-root .ld-image-preview.is-zoomed {
#ld-image-preview-root .ld-image-preview.is-zoomed {
cursor: zoom-out;
}

#ld-drawer-root .ld-image-preview.is-zoomed .ld-image-preview-image {
#ld-image-preview-root .ld-image-preview.is-zoomed .ld-image-preview-image {
box-shadow: 0 24px 54px rgba(0, 0, 0, 0.44);
}

Expand Down Expand Up @@ -991,28 +1000,28 @@ body.ld-drawer-page-open #ld-drawer-root {
width: 100vw;
}

#ld-drawer-root .ld-image-preview {
#ld-image-preview-root .ld-image-preview {
padding: max(18px, env(safe-area-inset-top, 0px) + 8px)
12px
max(12px, env(safe-area-inset-bottom, 0px) + 8px);
align-items: stretch;
}

#ld-drawer-root .ld-image-preview-close {
#ld-image-preview-root .ld-image-preview-close {
top: max(10px, env(safe-area-inset-top, 0px) + 2px);
right: 12px;
padding: 8px 12px;
background: rgba(15, 23, 42, 0.56);
backdrop-filter: blur(10px);
}

#ld-drawer-root .ld-image-preview-stage {
#ld-image-preview-root .ld-image-preview-stage {
align-items: center;
padding-top: 40px;
padding-bottom: 6px;
}

#ld-drawer-root .ld-image-preview-image {
#ld-image-preview-root .ld-image-preview-image {
max-width: 100%;
max-height: calc(100dvh - 88px);
border-radius: 12px;
Expand Down Expand Up @@ -1068,9 +1077,9 @@ body.ld-drawer-page-open #ld-drawer-root {
}

@media (prefers-reduced-motion: reduce) {
#ld-drawer-root .ld-image-preview,
#ld-drawer-root .ld-image-preview-close,
#ld-drawer-root .ld-image-preview-image {
#ld-image-preview-root .ld-image-preview,
#ld-image-preview-root .ld-image-preview-close,
#ld-image-preview-root .ld-image-preview-image {
animation: none;
transition: none;
}
Expand Down
98 changes: 67 additions & 31 deletions src/content.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
(function () {
const ROOT_ID = "ld-drawer-root";
const IMAGE_PREVIEW_ROOT_ID = "ld-image-preview-root";
const PAGE_OPEN_CLASS = "ld-drawer-page-open";
const PAGE_IFRAME_OPEN_CLASS = "ld-drawer-page-iframe-open";
const ACTIVE_LINK_CLASS = "ld-drawer-topic-link-active";
Expand Down Expand Up @@ -102,6 +103,7 @@
replySubmitButton: null,
replyCancelButton: null,
replyStatus: null,
imagePreviewRoot: null,
imagePreview: null,
imagePreviewImage: null,
imagePreviewCloseButton: null,
Expand Down Expand Up @@ -296,16 +298,22 @@
<button class="ld-reply-action ld-reply-action-primary" type="button" data-action="submit">发送回复</button>
</div>
</div>
<div class="ld-image-preview" hidden aria-hidden="true">
<button class="ld-image-preview-close" type="button" aria-label="关闭图片预览">关闭</button>
<div class="ld-image-preview-stage">
<img class="ld-image-preview-image" alt="图片预览" />
</div>
</div>
`;

const imagePreviewRoot = document.createElement("div");
imagePreviewRoot.id = IMAGE_PREVIEW_ROOT_ID;
imagePreviewRoot.setAttribute("aria-hidden", "true");
imagePreviewRoot.innerHTML = `
<div class="ld-image-preview" hidden aria-hidden="true">
<button class="ld-image-preview-close" type="button" aria-label="关闭图片预览">关闭</button>
<div class="ld-image-preview-stage">
<img class="ld-image-preview-image" alt="图片预览" />
</div>
</div>
`;

document.body.appendChild(root);
document.body.append(root, imagePreviewRoot);

state.root = root;
state.header = root.querySelector(".ld-drawer-header");
Expand All @@ -321,9 +329,10 @@
state.replySubmitButton = root.querySelector('[data-action="submit"]');
state.replyCancelButton = root.querySelector('[data-action="cancel"]');
state.replyStatus = root.querySelector(".ld-reply-status");
state.imagePreview = root.querySelector(".ld-image-preview");
state.imagePreviewImage = root.querySelector(".ld-image-preview-image");
state.imagePreviewCloseButton = root.querySelector(".ld-image-preview-close");
state.imagePreviewRoot = imagePreviewRoot;
state.imagePreview = imagePreviewRoot.querySelector(".ld-image-preview");
state.imagePreviewImage = imagePreviewRoot.querySelector(".ld-image-preview-image");
state.imagePreviewCloseButton = imagePreviewRoot.querySelector(".ld-image-preview-close");
state.openInTab = root.querySelector(".ld-drawer-link");
state.settingsPanel = root.querySelector(".ld-drawer-settings");
state.settingsCard = root.querySelector(".ld-drawer-settings-card");
Expand Down Expand Up @@ -352,6 +361,8 @@
state.replyTextarea.addEventListener("paste", handleReplyTextareaPaste);
root.addEventListener("click", handleDrawerRootClick);
root.addEventListener("wheel", handleDrawerRootWheel, { passive: false });
imagePreviewRoot.addEventListener("click", handleImagePreviewClick);
imagePreviewRoot.addEventListener("wheel", handleImagePreviewWheel, { passive: false });
state.drawerBody.addEventListener("scroll", handleDrawerBodyScroll, { passive: true });
state.settingsPanel.addEventListener("click", handleSettingsPanelClick);
state.settingsPanel.addEventListener("input", handleSettingsInput);
Expand Down Expand Up @@ -393,6 +404,10 @@
return;
}

if (!state.imagePreview?.hidden && target.closest(`#${IMAGE_PREVIEW_ROOT_ID}`)) {
return;
}

if (!state.settingsPanel?.hidden && !target.closest(".ld-drawer-settings-card") && !target.closest(".ld-drawer-settings-toggle")) {
setSettingsPanelOpen(false);
}
Expand Down Expand Up @@ -2082,14 +2097,6 @@
return;
}

if (!state.imagePreview?.hidden) {
if (target.closest(".ld-image-preview-close") || !target.closest(".ld-image-preview-image")) {
event.preventDefault();
closeImagePreview();
}
return;
}

const image = target.closest(".ld-post-body img");
if (!(image instanceof HTMLImageElement)) {
return;
Expand All @@ -2100,8 +2107,23 @@
openImagePreview(image);
}

function handleImagePreviewClick(event) {
if (state.imagePreview?.hidden) {
return;
}

const target = event.target;
if (!(target instanceof Element) || !target.closest(".ld-image-preview")) {
return;
}

event.preventDefault();
event.stopPropagation();
closeImagePreview();
}

function openImagePreview(image) {
if (!state.imagePreview || !state.imagePreviewImage) {
if (!state.imagePreviewRoot || !state.imagePreview || !state.imagePreviewImage) {
return;
}

Expand All @@ -2114,6 +2136,7 @@
state.imagePreviewImage.src = previewSrc;
state.imagePreviewImage.alt = image.alt || "图片预览";
state.imagePreviewImage.classList.remove("is-ready");
state.imagePreviewRoot.setAttribute("aria-hidden", "false");
state.imagePreview.hidden = false;
state.imagePreview.setAttribute("aria-hidden", "false");
if (state.imagePreviewImage.complete) {
Expand All @@ -2126,10 +2149,11 @@
}

function closeImagePreview() {
if (!state.imagePreview || !state.imagePreviewImage) {
if (!state.imagePreviewRoot || !state.imagePreview || !state.imagePreviewImage) {
return;
}

state.imagePreviewRoot.setAttribute("aria-hidden", "true");
state.imagePreview.hidden = true;
state.imagePreview.setAttribute("aria-hidden", "true");
resetImagePreviewScale();
Expand All @@ -2142,26 +2166,38 @@
state.imagePreviewImage?.classList.add("is-ready");
}

function handleDrawerRootWheel(event) {
function handleImagePreviewWheel(event) {
if (state.imagePreview?.hidden) {
return;
}

const target = event.target;
if (!(target instanceof Element)) {
return;
}

if (!state.imagePreview?.hidden && target.closest(".ld-image-preview-stage")) {
event.preventDefault();
event.preventDefault();

const nextScale = clampImagePreviewScale(
state.imagePreviewScale + (event.deltaY < 0 ? IMAGE_PREVIEW_SCALE_STEP : -IMAGE_PREVIEW_SCALE_STEP)
);
if (!target.closest(".ld-image-preview-stage")) {
return;
}

if (nextScale === state.imagePreviewScale) {
return;
}
const nextScale = clampImagePreviewScale(
state.imagePreviewScale + (event.deltaY < 0 ? IMAGE_PREVIEW_SCALE_STEP : -IMAGE_PREVIEW_SCALE_STEP)
);

if (nextScale === state.imagePreviewScale) {
return;
}

updateImagePreviewTransformOrigin(event.clientX, event.clientY);
state.imagePreviewScale = nextScale;
applyImagePreviewScale();
updateImagePreviewTransformOrigin(event.clientX, event.clientY);
state.imagePreviewScale = nextScale;
applyImagePreviewScale();
}

function handleDrawerRootWheel(event) {
const target = event.target;
if (!(target instanceof Element)) {
return;
}

Expand Down
Loading