@webnumseoagent/next 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blog.tsx +39 -28
- package/dist/blog-toc.js +18 -1
- package/dist/blog.js +15 -10
- package/package.json +1 -1
package/blog.tsx
CHANGED
|
@@ -124,26 +124,31 @@ function safeTocCss(css?: string): string {
|
|
|
124
124
|
return css.slice(0, 4000);
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
const TOC_CSS = `.sa-article-wrap{max-width:
|
|
128
|
-
.sa-article-
|
|
129
|
-
|
|
127
|
+
const TOC_CSS = `.sa-article-wrap{max-width:1400px;margin:0 auto;padding:var(--sa-blog-top,clamp(5rem,8vw,7rem)) 1.5rem 3rem;container-type:inline-size}
|
|
128
|
+
.sa-article-grid{display:block}
|
|
129
|
+
.sa-article-grid:not(.has-toc){max-width:760px;margin:0 auto}
|
|
130
130
|
.sa-article{min-width:0;line-height:1.7}
|
|
131
|
-
.sa-
|
|
132
|
-
.
|
|
133
|
-
@
|
|
131
|
+
.sa-toc{font-size:.9rem}
|
|
132
|
+
/* Средняя ширина контента: оглавление слева + статья рядом, блок по центру (формат для узкого контента). */
|
|
133
|
+
@container (min-width:700px){.sa-article-grid.has-toc{display:grid;column-gap:2.5rem;grid-template-columns:250px minmax(0,760px);justify-content:center;align-items:start}
|
|
134
|
+
.sa-toc{position:sticky;top:var(--sa-toc-top,88px)}}
|
|
135
|
+
/* Широкий контент (ZeroMax): статья ПО ЦЕНТРУ вьюпорта, оглавление в левом гуттере, прижато к статье (капнуто ~300px). */
|
|
136
|
+
@container (min-width:1180px){.sa-article-grid.has-toc{grid-template-columns:minmax(0,1fr) minmax(0,760px) minmax(0,1fr);column-gap:0}
|
|
137
|
+
.sa-article-grid.has-toc>.sa-toc{grid-column:1;justify-self:end;width:min(100%,300px);margin-right:2.5rem}
|
|
138
|
+
.sa-article-grid.has-toc>.sa-article{grid-column:2}}
|
|
134
139
|
.sa-toc>summary{list-style:none;font-weight:700;font-size:.95rem;margin:0 0 .75rem;display:flex;align-items:center;justify-content:space-between;gap:.5rem;color:inherit;cursor:pointer}
|
|
135
140
|
.sa-toc>summary::-webkit-details-marker{display:none}
|
|
136
141
|
.sa-toc>summary .sa-toc-chev{transition:transform .2s ease;color:var(--sa-muted,#94a3b8)}
|
|
137
142
|
.sa-toc[open]>summary .sa-toc-chev{transform:rotate(90deg)}
|
|
138
143
|
@media(min-width:1024px){.sa-toc>summary{pointer-events:none}.sa-toc>summary .sa-toc-chev{display:none}}
|
|
139
|
-
.sa-toc-
|
|
144
|
+
.sa-toc-body{position:relative}
|
|
145
|
+
.sa-toc-list{display:flex;flex-direction:column;gap:.1rem;max-height:var(--sa-toc-maxh,300px);overflow-y:auto;overscroll-behavior:contain;scrollbar-width:none;-ms-overflow-style:none}
|
|
140
146
|
.sa-toc-list::-webkit-scrollbar{width:0;height:0;display:none}
|
|
141
147
|
.sa-toc-list a{display:block;text-decoration:none;color:var(--sa-muted,#64748b);padding:.35rem .6rem;border-radius:var(--sa-radius,8px);line-height:1.4;transition:color .15s ease,background .15s ease,border-color .15s ease}
|
|
142
148
|
.sa-toc-list a[data-level="3"]{padding-left:1.4rem;font-size:.85em}
|
|
143
149
|
.sa-toc-list a:hover{color:inherit}
|
|
144
150
|
.sa-toc-list a.is-active{color:var(--sa-accent,#2563eb)}
|
|
145
|
-
.sa-toc[data-sa-toc="minimal"] .sa-toc-
|
|
146
|
-
.sa-toc[data-sa-toc="minimal"] .sa-toc-list::before{content:"";position:absolute;left:0;top:.2rem;bottom:.2rem;width:2px;background:var(--sa-border,#e5e7eb)}
|
|
151
|
+
.sa-toc[data-sa-toc="minimal"] .sa-toc-body::before{content:"";position:absolute;left:0;top:.2rem;bottom:.2rem;width:2px;background:var(--sa-border,#e5e7eb);z-index:0}
|
|
147
152
|
.sa-toc[data-sa-toc="minimal"] .sa-toc-list a{padding-left:.9rem;border-radius:0;box-shadow:inset 2px 0 0 transparent}
|
|
148
153
|
.sa-toc[data-sa-toc="minimal"] .sa-toc-list a[data-level="3"]{padding-left:1.7rem}
|
|
149
154
|
.sa-toc[data-sa-toc="minimal"] .sa-toc-list a.is-active{box-shadow:inset 2px 0 0 var(--sa-accent,#2563eb)}
|
|
@@ -236,26 +241,32 @@ export function SeoBlogArticle({ post, locale }: { post: SeoBlogPostFull; locale
|
|
|
236
241
|
const label = L(TOC_LABEL, locale ?? "", "Contents");
|
|
237
242
|
const rootStyle = { ...themeStyle(post.theme), ...(toc.top ? { ["--sa-toc-top"]: toc.top } : {}), ...(toc.maxh ? { ["--sa-toc-maxh"]: toc.maxh } : {}) } as CSSProperties;
|
|
238
243
|
return (
|
|
239
|
-
<div className=
|
|
244
|
+
<div className="sa-article-wrap" style={rootStyle}>
|
|
240
245
|
<style dangerouslySetInnerHTML={{ __html: BODY_CSS + TOC_CSS + (showToc ? safeTocCss(toc.css) : "") }} />
|
|
241
|
-
{
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
<
|
|
245
|
-
{
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
<
|
|
258
|
-
|
|
246
|
+
{/* Внутренний грид — CSS-контейнер .sa-article-wrap адаптирует его по ШИРИНЕ контента (@container). */}
|
|
247
|
+
<div className={showToc ? "sa-article-grid has-toc" : "sa-article-grid"}>
|
|
248
|
+
{showToc ? (
|
|
249
|
+
<details className="sa-toc" data-sa-toc={variant} data-motion={motion} open>
|
|
250
|
+
<summary>{label}<span className="sa-toc-chev" aria-hidden="true">›</span></summary>
|
|
251
|
+
{/* .sa-toc-body держит общий рельс (::before) вне скролла; скроллится только список. */}
|
|
252
|
+
<div className="sa-toc-body">
|
|
253
|
+
<nav className="sa-toc-list" aria-label={label}>
|
|
254
|
+
{items.map((it) => (
|
|
255
|
+
<a key={it.id} href={`#${it.id}`} data-toc-link={it.id} data-level={it.level}>{it.text}</a>
|
|
256
|
+
))}
|
|
257
|
+
</nav>
|
|
258
|
+
</div>
|
|
259
|
+
<SeoBlogTocSpy ids={items.map((it) => it.id)} />
|
|
260
|
+
</details>
|
|
261
|
+
) : null}
|
|
262
|
+
<article className="sa-article">
|
|
263
|
+
{post.cover ? <img src={post.cover} alt={post.title} style={{ width: "100%", aspectRatio: "3 / 2", objectFit: "cover", display: "block", borderRadius: "var(--sa-radius, 12px)", marginBottom: "1.5rem" }} /> : null}
|
|
264
|
+
<h1 style={{ fontSize: "2.2rem", lineHeight: 1.2, margin: "0 0 1rem" }}>{post.title}</h1>
|
|
265
|
+
{post.date ? <p style={{ color: "var(--sa-muted, #94a3b8)", margin: "0 0 1.5rem" }}>{fmtDate(post.date, locale)}</p> : null}
|
|
266
|
+
{/* Тело уже санитайзено при доставке; id-якоря добавлены buildToc при рендере. */}
|
|
267
|
+
<div className="seoagent-blog-body" dangerouslySetInnerHTML={{ __html: html }} />
|
|
268
|
+
</article>
|
|
269
|
+
</div>
|
|
259
270
|
</div>
|
|
260
271
|
);
|
|
261
272
|
}
|
package/dist/blog-toc.js
CHANGED
|
@@ -20,6 +20,21 @@ function SeoBlogTocSpy({ ids }) {
|
|
|
20
20
|
const heads = ids.map((id) => document.getElementById(id)).filter(Boolean);
|
|
21
21
|
if (!heads.length)
|
|
22
22
|
return;
|
|
23
|
+
const reduce = typeof matchMedia !== "undefined" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
24
|
+
// Держим активный пункт в видимой (обрезанной) зоне списка: подкручиваем сам скролл-контейнер
|
|
25
|
+
// .sa-toc-list, чтобы при скролле статьи выделенный заголовок не «уезжал» за пределы оглавления.
|
|
26
|
+
const keepInView = (a) => {
|
|
27
|
+
const box = a.closest(".sa-toc-list");
|
|
28
|
+
if (!box || box.scrollHeight <= box.clientHeight)
|
|
29
|
+
return;
|
|
30
|
+
const ar = a.getBoundingClientRect(), br = box.getBoundingClientRect();
|
|
31
|
+
const top = box.scrollTop + (ar.top - br.top);
|
|
32
|
+
const pad = 8;
|
|
33
|
+
if (top < box.scrollTop + pad)
|
|
34
|
+
box.scrollTo({ top: Math.max(0, top - pad), behavior: reduce ? "auto" : "smooth" });
|
|
35
|
+
else if (top + ar.height > box.scrollTop + box.clientHeight - pad)
|
|
36
|
+
box.scrollTo({ top: top + ar.height - box.clientHeight + pad, behavior: reduce ? "auto" : "smooth" });
|
|
37
|
+
};
|
|
23
38
|
let active = "";
|
|
24
39
|
const setActive = (id) => {
|
|
25
40
|
if (id === active)
|
|
@@ -33,8 +48,10 @@ function SeoBlogTocSpy({ ids }) {
|
|
|
33
48
|
else
|
|
34
49
|
a.removeAttribute("aria-current");
|
|
35
50
|
});
|
|
51
|
+
const cur = links.get(id);
|
|
52
|
+
if (cur)
|
|
53
|
+
keepInView(cur);
|
|
36
54
|
};
|
|
37
|
-
const reduce = typeof matchMedia !== "undefined" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
38
55
|
const onClick = (e) => {
|
|
39
56
|
const a = e.currentTarget;
|
|
40
57
|
const id = a.getAttribute("data-toc-link");
|
package/dist/blog.js
CHANGED
|
@@ -142,26 +142,31 @@ function safeTocCss(css) {
|
|
|
142
142
|
return "";
|
|
143
143
|
return css.slice(0, 4000);
|
|
144
144
|
}
|
|
145
|
-
const TOC_CSS = `.sa-article-wrap{max-width:
|
|
146
|
-
.sa-article-
|
|
147
|
-
|
|
145
|
+
const TOC_CSS = `.sa-article-wrap{max-width:1400px;margin:0 auto;padding:var(--sa-blog-top,clamp(5rem,8vw,7rem)) 1.5rem 3rem;container-type:inline-size}
|
|
146
|
+
.sa-article-grid{display:block}
|
|
147
|
+
.sa-article-grid:not(.has-toc){max-width:760px;margin:0 auto}
|
|
148
148
|
.sa-article{min-width:0;line-height:1.7}
|
|
149
|
-
.sa-
|
|
150
|
-
.
|
|
151
|
-
@
|
|
149
|
+
.sa-toc{font-size:.9rem}
|
|
150
|
+
/* Средняя ширина контента: оглавление слева + статья рядом, блок по центру (формат для узкого контента). */
|
|
151
|
+
@container (min-width:700px){.sa-article-grid.has-toc{display:grid;column-gap:2.5rem;grid-template-columns:250px minmax(0,760px);justify-content:center;align-items:start}
|
|
152
|
+
.sa-toc{position:sticky;top:var(--sa-toc-top,88px)}}
|
|
153
|
+
/* Широкий контент (ZeroMax): статья ПО ЦЕНТРУ вьюпорта, оглавление в левом гуттере, прижато к статье (капнуто ~300px). */
|
|
154
|
+
@container (min-width:1180px){.sa-article-grid.has-toc{grid-template-columns:minmax(0,1fr) minmax(0,760px) minmax(0,1fr);column-gap:0}
|
|
155
|
+
.sa-article-grid.has-toc>.sa-toc{grid-column:1;justify-self:end;width:min(100%,300px);margin-right:2.5rem}
|
|
156
|
+
.sa-article-grid.has-toc>.sa-article{grid-column:2}}
|
|
152
157
|
.sa-toc>summary{list-style:none;font-weight:700;font-size:.95rem;margin:0 0 .75rem;display:flex;align-items:center;justify-content:space-between;gap:.5rem;color:inherit;cursor:pointer}
|
|
153
158
|
.sa-toc>summary::-webkit-details-marker{display:none}
|
|
154
159
|
.sa-toc>summary .sa-toc-chev{transition:transform .2s ease;color:var(--sa-muted,#94a3b8)}
|
|
155
160
|
.sa-toc[open]>summary .sa-toc-chev{transform:rotate(90deg)}
|
|
156
161
|
@media(min-width:1024px){.sa-toc>summary{pointer-events:none}.sa-toc>summary .sa-toc-chev{display:none}}
|
|
157
|
-
.sa-toc-
|
|
162
|
+
.sa-toc-body{position:relative}
|
|
163
|
+
.sa-toc-list{display:flex;flex-direction:column;gap:.1rem;max-height:var(--sa-toc-maxh,300px);overflow-y:auto;overscroll-behavior:contain;scrollbar-width:none;-ms-overflow-style:none}
|
|
158
164
|
.sa-toc-list::-webkit-scrollbar{width:0;height:0;display:none}
|
|
159
165
|
.sa-toc-list a{display:block;text-decoration:none;color:var(--sa-muted,#64748b);padding:.35rem .6rem;border-radius:var(--sa-radius,8px);line-height:1.4;transition:color .15s ease,background .15s ease,border-color .15s ease}
|
|
160
166
|
.sa-toc-list a[data-level="3"]{padding-left:1.4rem;font-size:.85em}
|
|
161
167
|
.sa-toc-list a:hover{color:inherit}
|
|
162
168
|
.sa-toc-list a.is-active{color:var(--sa-accent,#2563eb)}
|
|
163
|
-
.sa-toc[data-sa-toc="minimal"] .sa-toc-
|
|
164
|
-
.sa-toc[data-sa-toc="minimal"] .sa-toc-list::before{content:"";position:absolute;left:0;top:.2rem;bottom:.2rem;width:2px;background:var(--sa-border,#e5e7eb)}
|
|
169
|
+
.sa-toc[data-sa-toc="minimal"] .sa-toc-body::before{content:"";position:absolute;left:0;top:.2rem;bottom:.2rem;width:2px;background:var(--sa-border,#e5e7eb);z-index:0}
|
|
165
170
|
.sa-toc[data-sa-toc="minimal"] .sa-toc-list a{padding-left:.9rem;border-radius:0;box-shadow:inset 2px 0 0 transparent}
|
|
166
171
|
.sa-toc[data-sa-toc="minimal"] .sa-toc-list a[data-level="3"]{padding-left:1.7rem}
|
|
167
172
|
.sa-toc[data-sa-toc="minimal"] .sa-toc-list a.is-active{box-shadow:inset 2px 0 0 var(--sa-accent,#2563eb)}
|
|
@@ -240,7 +245,7 @@ function SeoBlogArticle({ post, locale }) {
|
|
|
240
245
|
const motion = toc.motion === false ? "off" : "on";
|
|
241
246
|
const label = L(TOC_LABEL, locale ?? "", "Contents");
|
|
242
247
|
const rootStyle = { ...themeStyle(post.theme), ...(toc.top ? { ["--sa-toc-top"]: toc.top } : {}), ...(toc.maxh ? { ["--sa-toc-maxh"]: toc.maxh } : {}) };
|
|
243
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className:
|
|
248
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "sa-article-wrap", style: rootStyle, children: [(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: BODY_CSS + TOC_CSS + (showToc ? safeTocCss(toc.css) : "") } }), (0, jsx_runtime_1.jsxs)("div", { className: showToc ? "sa-article-grid has-toc" : "sa-article-grid", children: [showToc ? ((0, jsx_runtime_1.jsxs)("details", { className: "sa-toc", "data-sa-toc": variant, "data-motion": motion, open: true, children: [(0, jsx_runtime_1.jsxs)("summary", { children: [label, (0, jsx_runtime_1.jsx)("span", { className: "sa-toc-chev", "aria-hidden": "true", children: "\u203A" })] }), (0, jsx_runtime_1.jsx)("div", { className: "sa-toc-body", children: (0, jsx_runtime_1.jsx)("nav", { className: "sa-toc-list", "aria-label": label, children: items.map((it) => ((0, jsx_runtime_1.jsx)("a", { href: `#${it.id}`, "data-toc-link": it.id, "data-level": it.level, children: it.text }, it.id))) }) }), (0, jsx_runtime_1.jsx)(blog_toc_1.SeoBlogTocSpy, { ids: items.map((it) => it.id) })] })) : null, (0, jsx_runtime_1.jsxs)("article", { className: "sa-article", children: [post.cover ? (0, jsx_runtime_1.jsx)("img", { src: post.cover, alt: post.title, style: { width: "100%", aspectRatio: "3 / 2", objectFit: "cover", display: "block", borderRadius: "var(--sa-radius, 12px)", marginBottom: "1.5rem" } }) : null, (0, jsx_runtime_1.jsx)("h1", { style: { fontSize: "2.2rem", lineHeight: 1.2, margin: "0 0 1rem" }, children: post.title }), post.date ? (0, jsx_runtime_1.jsx)("p", { style: { color: "var(--sa-muted, #94a3b8)", margin: "0 0 1.5rem" }, children: fmtDate(post.date, locale) }) : null, (0, jsx_runtime_1.jsx)("div", { className: "seoagent-blog-body", dangerouslySetInnerHTML: { __html: html } })] })] })] }));
|
|
244
249
|
}
|
|
245
250
|
function fmtDate(d, locale) {
|
|
246
251
|
try {
|
package/package.json
CHANGED