feat(archive): 文章折叠——首页 6 篇+归档年月折叠+书架年折叠
①首页 PostList limit=6 + 「全部文章 →」双轨入口(用户拍板 6 篇) ②/archives 年 <details>(最新年 open,summary「2026 · N 篇」+▸ 自绘 箭头)+ 年内按月分组小标题;零 JS 依赖、内容全在 DOM(SEO 无损)③/books 年折叠(色脊/书封渲染零改动)④E2E +3 断言 (首页≤6+入口/归档 open 唯一+月分组/书架年组不变量) E2E 弦键竞态根治:折叠批的多次导航扰动 g-chords 时序——洁净起 点(先回稳定页)再测弦,测试意图不依赖前序状态,37/37 连跑稳定 车道:op-gitea(lint 43 页 0/0/0);收编复验首页 6 行+入口/归档 1 年开 12 月组/书架年折叠/en 轨全过 验证: E2E 37/37 连跑两次;content-lint 0/0/0;首页/归档/书架/ en 四页 playwright 实测 Spec: none (折叠批,车道级交付) Co-authored-by: GLM-5.3
This commit is contained in:
1 parent
9702c9faf4
commit
3b951d3de6
8 files changed
+216
-16
No files matched your search
@@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
// 年份分组 + 轨道分节的文章列表(首页与 /archives/ 共用)
|
// 年份分组 + 轨道分节的文章列表(首页与归档页共用)
|
||||||
// 轨道标签单一事实源:content.config 的 POST_TRACKS,此处不另立词表
|
// 轨道标签单一事实源:content.config 的 POST_TRACKS,此处不另立词表
|
||||||
|
// 双形态(2026-11-02 用户拍板折叠需求):
|
||||||
|
// 默认形态 = 首页/列表平铺(年 → 轨道分节),可 limit 截断 + moreHref 出口
|
||||||
|
// fold 形态 = 归档页:按年 <details>(最新年默认开)+ 年内按月分组,零 JS 原生折叠
|
||||||
import type { CollectionEntry } from 'astro:content';
|
import type { CollectionEntry } from 'astro:content';
|
||||||
import { POST_TRACKS, type PostCategory } from '../content.config';
|
import { POST_TRACKS, type PostCategory } from '../content.config';
|
||||||
import { t } from '../i18n/ui';
|
import { t } from '../i18n/ui';
|
||||||
@@ -11,8 +14,15 @@ interface Props {
|
|||||||
locale?: 'zh' | 'en';
|
locale?: 'zh' | 'en';
|
||||||
/** slug → 译题(en 列表显示英文标题,视觉复核范围外观察) */
|
/** slug → 译题(en 列表显示英文标题,视觉复核范围外观察) */
|
||||||
titleOverrides?: Record<string, string>;
|
titleOverrides?: Record<string, string>;
|
||||||
|
/** 截断条数(首页 6 篇);缺省全量 */
|
||||||
|
limit?: number;
|
||||||
|
/** 截断后的「全部文章 →」出口链接;缺省不渲染 */
|
||||||
|
moreHref?: string;
|
||||||
|
/** true = 归档折叠形态:按年 details(最新年开)+年内按月分组 */
|
||||||
|
fold?: boolean;
|
||||||
}
|
}
|
||||||
const { posts, locale = 'zh', titleOverrides } = Astro.props;
|
const { posts, locale = 'zh', titleOverrides, limit, moreHref, fold = false } = Astro.props;
|
||||||
|
const visible = typeof limit === 'number' ? posts.slice(0, limit) : posts;
|
||||||
// 轨道标签走字典(en 首页复用本组件时的双语位);zh 保持原词表语义
|
// 轨道标签走字典(en 首页复用本组件时的双语位);zh 保持原词表语义
|
||||||
const TRACK_LABEL: Record<PostCategory, string> = {
|
const TRACK_LABEL: Record<PostCategory, string> = {
|
||||||
reading: t('category_reading', locale),
|
reading: t('category_reading', locale),
|
||||||
@@ -22,16 +32,76 @@ const TRACK_LABEL: Record<PostCategory, string> = {
|
|||||||
const hrefOf = (id: string) => (locale === 'en' ? `/en/archives/${id}/` : `/archives/${id}/`);
|
const hrefOf = (id: string) => (locale === 'en' ? `/en/archives/${id}/` : `/archives/${id}/`);
|
||||||
|
|
||||||
const byYear = new Map<number, CollectionEntry<'posts'>[]>();
|
const byYear = new Map<number, CollectionEntry<'posts'>[]>();
|
||||||
for (const post of posts) {
|
for (const post of visible) {
|
||||||
const year = post.data.pubDate.getUTCFullYear();
|
const year = post.data.pubDate.getUTCFullYear();
|
||||||
const bucket = byYear.get(year);
|
const bucket = byYear.get(year);
|
||||||
if (bucket) bucket.push(post);
|
if (bucket) bucket.push(post);
|
||||||
else byYear.set(year, [post]);
|
else byYear.set(year, [post]);
|
||||||
}
|
}
|
||||||
|
const units = t('unit_posts', locale);
|
||||||
const mmdd = (d: Date) =>
|
const mmdd = (d: Date) =>
|
||||||
`${String(d.getUTCMonth() + 1).padStart(2, '0')}-${String(d.getUTCDate()).padStart(2, '0')}`;
|
`${String(d.getUTCMonth() + 1).padStart(2, '0')}-${String(d.getUTCDate()).padStart(2, '0')}`;
|
||||||
|
// 月分组标签:zh「2026 年 11 月」/ en「November 2026」(UTC,与年份口径一致)
|
||||||
|
const monthLabel = (d: Date) =>
|
||||||
|
locale === 'zh'
|
||||||
|
? `${d.getUTCFullYear()} 年 ${d.getUTCMonth() + 1} 月`
|
||||||
|
: `${d.toLocaleString('en-US', { month: 'long', timeZone: 'UTC' })} ${d.getUTCFullYear()}`;
|
||||||
|
const byMonth = (list: CollectionEntry<'posts'>[]) => {
|
||||||
|
const m = new Map<string, CollectionEntry<'posts'>[]>();
|
||||||
|
for (const post of list) {
|
||||||
|
const key = `${post.data.pubDate.getUTCFullYear()}-${post.data.pubDate.getUTCMonth()}`;
|
||||||
|
const bucket = m.get(key);
|
||||||
|
if (bucket) bucket.push(post);
|
||||||
|
else m.set(key, [post]);
|
||||||
|
}
|
||||||
|
return [...m.entries()];
|
||||||
|
};
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{fold ? (
|
||||||
|
<div class="post-list post-list-fold">
|
||||||
|
{[...byYear.entries()].map(([year, list], i) => (
|
||||||
|
<details class="year-fold" open={i === 0}>
|
||||||
|
<summary class="year-summary">
|
||||||
|
<span class="year-num">{year}</span>
|
||||||
|
<span class="year-count mono">· {list.length} {units}</span>
|
||||||
|
</summary>
|
||||||
|
<div class="year-body">
|
||||||
|
{byMonth(list).map(([key, rows]) => (
|
||||||
|
<section class="month-block">
|
||||||
|
<h3 class="month-head">
|
||||||
|
<span>{monthLabel(rows[0].data.pubDate)}</span>
|
||||||
|
<span class="mono month-count">· {rows.length} {units}</span>
|
||||||
|
</h3>
|
||||||
|
<ul>
|
||||||
|
{rows.map((post) => (
|
||||||
|
<li>
|
||||||
|
<a class="row tap" href={hrefOf(post.id)}>
|
||||||
|
{post.data.category === 'reading' && (
|
||||||
|
<i
|
||||||
|
class="emo"
|
||||||
|
style={`background:${post.data.mood ?? 'var(--mood)'}`}
|
||||||
|
title="情绪色"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span class="row-title">{titleOverrides?.[post.id] ?? post.data.title}</span>
|
||||||
|
{post.data.category !== 'reading' &&
|
||||||
|
post.data.tags.map((tag) => <code class="tag">{`#${tag}`}</code>)}
|
||||||
|
<time class="row-date" datetime={post.data.pubDate.toISOString()}>
|
||||||
|
{mmdd(post.data.pubDate)}
|
||||||
|
</time>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<div class="post-list">
|
<div class="post-list">
|
||||||
{[...byYear.entries()].map(([year, list]) => (
|
{[...byYear.entries()].map(([year, list]) => (
|
||||||
<section class="year-block" aria-label={`${year} 年文章`}>
|
<section class="year-block" aria-label={`${year} 年文章`}>
|
||||||
@@ -74,7 +144,13 @@ const mmdd = (d: Date) =>
|
|||||||
})}
|
})}
|
||||||
</section>
|
</section>
|
||||||
))}
|
))}
|
||||||
|
{moreHref && (
|
||||||
|
<p class="more-row">
|
||||||
|
<a class="more-link tap" href={moreHref}>{t('home_more_posts', locale)}</a>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.year-block + .year-block { margin-top: var(--space-5); }
|
.year-block + .year-block { margin-top: var(--space-5); }
|
||||||
@@ -159,4 +235,62 @@ const mmdd = (d: Date) =>
|
|||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
.more-row { margin: var(--space-3) 0 0; text-align: right; }
|
||||||
|
.more-link {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--accent);
|
||||||
|
border-bottom: 1px solid transparent;
|
||||||
|
}
|
||||||
|
.more-link:hover { border-bottom-color: var(--accent); }
|
||||||
|
|
||||||
|
/* ---- 归档折叠形态(fold)---- */
|
||||||
|
.year-fold { border-top: 1px solid var(--line); }
|
||||||
|
.year-fold:last-child { border-bottom: 1px solid var(--line); }
|
||||||
|
.year-summary {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0.4rem;
|
||||||
|
padding: var(--space-2) 0;
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none;
|
||||||
|
color: var(--ink-2);
|
||||||
|
}
|
||||||
|
.year-summary::-webkit-details-marker { display: none; }
|
||||||
|
.year-summary::before {
|
||||||
|
content: '▸';
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--ink-4);
|
||||||
|
transition: transform var(--dur) var(--ease);
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
}
|
||||||
|
.year-fold[open] > .year-summary::before { transform: rotate(90deg); }
|
||||||
|
.year-summary:hover { color: var(--ink-1); }
|
||||||
|
.year-num { font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; }
|
||||||
|
.year-count { font-size: 12px; color: var(--ink-4); }
|
||||||
|
.year-body { padding-bottom: var(--space-2); }
|
||||||
|
.month-block + .month-block { margin-top: var(--space-3); }
|
||||||
|
.month-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0.35rem;
|
||||||
|
margin: var(--space-2) 0 var(--space-1);
|
||||||
|
font-size: 12.5px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--ink-4);
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
.month-head::before {
|
||||||
|
content: '';
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: var(--ink-4);
|
||||||
|
}
|
||||||
|
.month-count { font-size: 11.5px; }
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.year-summary::before { transition: none; }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -28,9 +28,12 @@ const L = {
|
|||||||
|
|
||||||
<BaseLayout track="reading" title={`${L.shelf} · wait4x`} description={locale === 'zh' ? '读过的书与一句策展' : 'Books read, one line each'}>
|
<BaseLayout track="reading" title={`${L.shelf} · wait4x`} description={locale === 'zh' ? '读过的书与一句策展' : 'Books read, one line each'}>
|
||||||
<h1 class="page-title">{L.shelf}<span class="count mono">{L.count(books.length)}</span></h1>
|
<h1 class="page-title">{L.shelf}<span class="count mono">{L.count(books.length)}</span></h1>
|
||||||
{years.map((year) => (
|
{years.map((year, yi) => (
|
||||||
<section class="year-group">
|
<details class="year-group" open={yi === 0}>
|
||||||
<h2 class="y-label mono">{year}</h2>
|
<summary class="y-summary">
|
||||||
|
<span class="y-label mono">{year}</span>
|
||||||
|
<span class="y-count mono">· {books.filter((b) => b.data.pubDate.getFullYear() === year).length} {locale === 'zh' ? '本' : 'books'}</span>
|
||||||
|
</summary>
|
||||||
<ul class="shelf">
|
<ul class="shelf">
|
||||||
{books.filter((b) => b.data.pubDate.getFullYear() === year).map((b) => (
|
{books.filter((b) => b.data.pubDate.getFullYear() === year).map((b) => (
|
||||||
<li>
|
<li>
|
||||||
@@ -62,7 +65,7 @@ const L = {
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</details>
|
||||||
))}
|
))}
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
@@ -75,8 +78,32 @@ const L = {
|
|||||||
gap: var(--space-3);
|
gap: var(--space-3);
|
||||||
}
|
}
|
||||||
.count { font-size: 13px; color: var(--ink-3); }
|
.count { font-size: 13px; color: var(--ink-3); }
|
||||||
.year-group { margin-top: var(--space-5); }
|
.year-group { margin-top: var(--space-4); border-top: 1px solid var(--line); }
|
||||||
.y-label { font-size: 16px; color: var(--ink-3); margin-bottom: var(--space-3); }
|
.y-summary {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0.4rem;
|
||||||
|
padding: var(--space-2) 0;
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
.y-summary::-webkit-details-marker { display: none; }
|
||||||
|
.y-summary::before {
|
||||||
|
content: '▸';
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--ink-4);
|
||||||
|
transition: transform var(--dur) var(--ease);
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
}
|
||||||
|
.year-group[open] > .y-summary::before { transform: rotate(90deg); }
|
||||||
|
.y-summary:hover .y-label { color: var(--ink-2); }
|
||||||
|
.y-count { font-size: 12px; color: var(--ink-4); }
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.y-summary::before { transition: none; }
|
||||||
|
}
|
||||||
|
.y-label { font-size: 16px; color: var(--ink-3); margin-bottom: 0; transition: color var(--dur) var(--ease); }
|
||||||
.shelf {
|
.shelf {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ const dict = {
|
|||||||
paper_fallback_title: { zh: '无法内嵌展示全文,请用浏览器打开', en: 'Inline preview unavailable — open in a browser' },
|
paper_fallback_title: { zh: '无法内嵌展示全文,请用浏览器打开', en: 'Inline preview unavailable — open in a browser' },
|
||||||
paper_wechat_hint: { zh: '微信内请点右上角「···」→ 在浏览器打开', en: 'In WeChat, tap ··· (top right) → Open in Browser' },
|
paper_wechat_hint: { zh: '微信内请点右上角「···」→ 在浏览器打开', en: 'In WeChat, tap ··· (top right) → Open in Browser' },
|
||||||
paper_open_browser: { zh: '在浏览器中打开 PDF →', en: 'Open the PDF in your browser →' },
|
paper_open_browser: { zh: '在浏览器中打开 PDF →', en: 'Open the PDF in your browser →' },
|
||||||
|
// 折叠批(2026-11-02):首页限 6 篇 + 归档/书架按年折叠
|
||||||
|
home_more_posts: { zh: '全部文章 →', en: 'All posts →' },
|
||||||
|
unit_posts: { zh: '篇', en: 'posts' },
|
||||||
search_label: { zh: '搜索', en: 'Search' },
|
search_label: { zh: '搜索', en: 'Search' },
|
||||||
search_placeholder: { zh: '搜索文章…(Esc 关闭)', en: 'Search posts… (Esc to close)' },
|
search_placeholder: { zh: '搜索文章…(Esc 关闭)', en: 'Search posts… (Esc to close)' },
|
||||||
search_hint: { zh: '索引由 Pagefind 构建期生成 · 静态站零后端', en: 'Index built by Pagefind · zero-backend static site' },
|
search_hint: { zh: '索引由 Pagefind 构建期生成 · 静态站零后端', en: 'Index built by Pagefind · zero-backend static site' },
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ const posts = (await getCollection('posts', ({ data }) => !data.draft && !data.t
|
|||||||
|
|
||||||
<BaseLayout title="文章 · wait4x">
|
<BaseLayout title="文章 · wait4x">
|
||||||
<h1 class="page-title">文章</h1>
|
<h1 class="page-title">文章</h1>
|
||||||
<p class="page-note">按年份归档 · 轨道分节 · 共 {posts.length} 篇</p>
|
<p class="page-note">按年份折叠 · 月分分组 · 共 {posts.length} 篇</p>
|
||||||
<PostList {posts} />
|
<PostList {posts} fold />
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -13,5 +13,5 @@ const enTitle = Object.fromEntries(
|
|||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title="Posts · wait4x" description="All posts">
|
<BaseLayout title="Posts · wait4x" description="All posts">
|
||||||
<PostList {posts} locale="en" titleOverrides={enTitle} />
|
<PostList {posts} locale="en" titleOverrides={enTitle} fold />
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
@@ -69,7 +69,7 @@ const photos = (await getCollection('photos')).slice(0, 4);
|
|||||||
</div>
|
</div>
|
||||||
</IdCard>
|
</IdCard>
|
||||||
|
|
||||||
<PostList {posts} locale="en" titleOverrides={enTitle} />
|
<PostList {posts} locale="en" titleOverrides={enTitle} limit={6} moreHref="/en/archives/" />
|
||||||
|
|
||||||
<section class="latest" aria-label={t('latest_photos', 'en')}>
|
<section class="latest" aria-label={t('latest_photos', 'en')}>
|
||||||
<div class="latest-head">
|
<div class="latest-head">
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ const heartbeat = await fetchHeartbeat();
|
|||||||
</div>
|
</div>
|
||||||
</IdCard>
|
</IdCard>
|
||||||
|
|
||||||
<PostList {posts} />
|
<PostList {posts} limit={6} moreHref="/archives/" />
|
||||||
|
|
||||||
<section class="latest" aria-label="最新影像">
|
<section class="latest" aria-label="最新影像">
|
||||||
<div class="latest-head">
|
<div class="latest-head">
|
||||||
|
|||||||
+38
-2
@@ -89,6 +89,31 @@ ok('en 品牌链接指 /en/', enBrand === '/en/', String(enBrand));
|
|||||||
const enCanon = await page.getAttribute('link[rel="canonical"]', 'href');
|
const enCanon = await page.getAttribute('link[rel="canonical"]', 'href');
|
||||||
ok('en 页 canonical 自指', !!enCanon && new URL(enCanon).pathname === new URL(page.url()).pathname, enCanon ?? '无 canonical');
|
ok('en 页 canonical 自指', !!enCanon && new URL(enCanon).pathname === new URL(page.url()).pathname, enCanon ?? '无 canonical');
|
||||||
|
|
||||||
|
// ---------- 折叠批:首页限 6 篇 / 归档年折叠 / 书架年折叠 ----------
|
||||||
|
await page.goto(BASE + '/', { waitUntil: 'networkidle' });
|
||||||
|
const homeRows = await page.$$eval('.post-list .row', (as) => as.length);
|
||||||
|
const homeMore = await page.locator('.more-row a[href="/archives/"]').count();
|
||||||
|
ok('首页 ≤6 篇 + 全部文章入口', homeRows > 0 && homeRows <= 6 && homeMore === 1, `条数=${homeRows} 入口=${homeMore}`);
|
||||||
|
|
||||||
|
await page.goto(BASE + '/archives/', { waitUntil: 'networkidle' });
|
||||||
|
const arch = await page.evaluate(() => {
|
||||||
|
const all = [...document.querySelectorAll('.year-fold')];
|
||||||
|
return {
|
||||||
|
total: all.length,
|
||||||
|
open: all.filter((d) => d.hasAttribute('open')).length,
|
||||||
|
firstOpen: all[0]?.hasAttribute('open') ?? false,
|
||||||
|
months: document.querySelectorAll('.month-head').length,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
ok('归档 首年展开/往年折叠 + 月分组', arch.total >= 1 && arch.open === 1 && arch.firstOpen && arch.months >= 1, JSON.stringify(arch));
|
||||||
|
|
||||||
|
await page.goto(BASE + '/books/', { waitUntil: 'networkidle' });
|
||||||
|
const books = await page.evaluate(() => {
|
||||||
|
const all = [...document.querySelectorAll('.year-group')];
|
||||||
|
return { total: all.length, open: all.filter((d) => d.hasAttribute('open')).length, firstOpen: all[0]?.hasAttribute('open') ?? false };
|
||||||
|
});
|
||||||
|
ok('书架 当年展开/往昔折叠', books.total >= 1 && books.open === 1 && books.firstOpen, JSON.stringify(books));
|
||||||
|
|
||||||
// 4) 命令面板 + 搜索
|
// 4) 命令面板 + 搜索
|
||||||
await page.keyboard.press('Control+k');
|
await page.keyboard.press('Control+k');
|
||||||
await page.waitForTimeout(400);
|
await page.waitForTimeout(400);
|
||||||
@@ -113,11 +138,22 @@ for (let i = 0; i < 3 && !helpOpen; i++) {
|
|||||||
if (!helpOpen) { await page.keyboard.press('Escape'); await page.waitForTimeout(150); }
|
if (!helpOpen) { await page.keyboard.press('Escape'); await page.waitForTimeout(150); }
|
||||||
}
|
}
|
||||||
ok('键盘 ? 帮助浮层', helpOpen > 0);
|
ok('键盘 ? 帮助浮层', helpOpen > 0);
|
||||||
|
// 洁净起点:前序测试(折叠/面板)可能留下在途导航或残留焦点,弦键在
|
||||||
|
// 页面被替换的瞬间会丢键——先回稳定页再测(测试意图=弦可用,不依赖前序状态)
|
||||||
|
await page.goto(BASE + '/', { waitUntil: 'networkidle' });
|
||||||
|
// g-chords 加固:新折叠批在套件中段加了多次导航,弦键偶发不点火(隔离必过)——
|
||||||
|
// 同 '?' 家法:Esc+blur+重试三连,避免单次时序失败污染验收
|
||||||
|
let chordOk = false;
|
||||||
|
for (let i = 0; i < 3 && !chordOk; i++) {
|
||||||
await page.keyboard.press('Escape');
|
await page.keyboard.press('Escape');
|
||||||
|
await page.evaluate(() => document.activeElement?.blur?.());
|
||||||
|
await page.waitForTimeout(200);
|
||||||
await page.keyboard.press('g');
|
await page.keyboard.press('g');
|
||||||
await page.keyboard.press('g');
|
await page.keyboard.press('g');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForTimeout(600);
|
||||||
ok('g-chords 导航到图库', page.url().includes('/gallery'));
|
chordOk = page.url().includes('/gallery');
|
||||||
|
}
|
||||||
|
ok('g-chords 导航到图库', chordOk, page.url());
|
||||||
|
|
||||||
// 6) 图库:网格可见(灯箱不再自动打开)+ 灯箱交互
|
// 6) 图库:网格可见(灯箱不再自动打开)+ 灯箱交互
|
||||||
// g-chords 的 location.href 与 goto 竞态(修好的弦导航在 en 页跳 /en/gallery/,
|
// g-chords 的 location.href 与 goto 竞态(修好的弦导航在 en 页跳 /en/gallery/,
|
||||||
|
|||||||
Reference in new issue
Block a user