@webnumseoagent/next 0.8.2 → 0.9.1
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 +35 -28
- package/dist/blog-toc.js +18 -1
- package/dist/blog.js +11 -10
- package/package.json +1 -1
package/blog.tsx
CHANGED
|
@@ -124,26 +124,27 @@ 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:1080px;margin:0 auto;padding:var(--sa-blog-top,clamp(5rem,8vw,7rem)) 1.5rem 3rem}
|
|
128
|
-
.sa-article-
|
|
129
|
-
|
|
127
|
+
const TOC_CSS = `.sa-article-wrap{max-width:1080px;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;margin-bottom:2.5rem}
|
|
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);margin-bottom:0}}
|
|
134
135
|
.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
136
|
.sa-toc>summary::-webkit-details-marker{display:none}
|
|
136
137
|
.sa-toc>summary .sa-toc-chev{transition:transform .2s ease;color:var(--sa-muted,#94a3b8)}
|
|
137
138
|
.sa-toc[open]>summary .sa-toc-chev{transform:rotate(90deg)}
|
|
138
139
|
@media(min-width:1024px){.sa-toc>summary{pointer-events:none}.sa-toc>summary .sa-toc-chev{display:none}}
|
|
139
|
-
.sa-toc-
|
|
140
|
+
.sa-toc-body{position:relative}
|
|
141
|
+
.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
142
|
.sa-toc-list::-webkit-scrollbar{width:0;height:0;display:none}
|
|
141
143
|
.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
144
|
.sa-toc-list a[data-level="3"]{padding-left:1.4rem;font-size:.85em}
|
|
143
145
|
.sa-toc-list a:hover{color:inherit}
|
|
144
146
|
.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)}
|
|
147
|
+
.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
148
|
.sa-toc[data-sa-toc="minimal"] .sa-toc-list a{padding-left:.9rem;border-radius:0;box-shadow:inset 2px 0 0 transparent}
|
|
148
149
|
.sa-toc[data-sa-toc="minimal"] .sa-toc-list a[data-level="3"]{padding-left:1.7rem}
|
|
149
150
|
.sa-toc[data-sa-toc="minimal"] .sa-toc-list a.is-active{box-shadow:inset 2px 0 0 var(--sa-accent,#2563eb)}
|
|
@@ -236,26 +237,32 @@ export function SeoBlogArticle({ post, locale }: { post: SeoBlogPostFull; locale
|
|
|
236
237
|
const label = L(TOC_LABEL, locale ?? "", "Contents");
|
|
237
238
|
const rootStyle = { ...themeStyle(post.theme), ...(toc.top ? { ["--sa-toc-top"]: toc.top } : {}), ...(toc.maxh ? { ["--sa-toc-maxh"]: toc.maxh } : {}) } as CSSProperties;
|
|
238
239
|
return (
|
|
239
|
-
<div className=
|
|
240
|
+
<div className="sa-article-wrap" style={rootStyle}>
|
|
240
241
|
<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
|
-
|
|
242
|
+
{/* Внутренний грид — CSS-контейнер .sa-article-wrap адаптирует его по ШИРИНЕ контента (@container). */}
|
|
243
|
+
<div className={showToc ? "sa-article-grid has-toc" : "sa-article-grid"}>
|
|
244
|
+
{showToc ? (
|
|
245
|
+
<details className="sa-toc" data-sa-toc={variant} data-motion={motion} open>
|
|
246
|
+
<summary>{label}<span className="sa-toc-chev" aria-hidden="true">›</span></summary>
|
|
247
|
+
{/* .sa-toc-body держит общий рельс (::before) вне скролла; скроллится только список. */}
|
|
248
|
+
<div className="sa-toc-body">
|
|
249
|
+
<nav className="sa-toc-list" aria-label={label}>
|
|
250
|
+
{items.map((it) => (
|
|
251
|
+
<a key={it.id} href={`#${it.id}`} data-toc-link={it.id} data-level={it.level}>{it.text}</a>
|
|
252
|
+
))}
|
|
253
|
+
</nav>
|
|
254
|
+
</div>
|
|
255
|
+
<SeoBlogTocSpy ids={items.map((it) => it.id)} />
|
|
256
|
+
</details>
|
|
257
|
+
) : null}
|
|
258
|
+
<article className="sa-article">
|
|
259
|
+
{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}
|
|
260
|
+
<h1 style={{ fontSize: "2.2rem", lineHeight: 1.2, margin: "0 0 1rem" }}>{post.title}</h1>
|
|
261
|
+
{post.date ? <p style={{ color: "var(--sa-muted, #94a3b8)", margin: "0 0 1.5rem" }}>{fmtDate(post.date, locale)}</p> : null}
|
|
262
|
+
{/* Тело уже санитайзено при доставке; id-якоря добавлены buildToc при рендере. */}
|
|
263
|
+
<div className="seoagent-blog-body" dangerouslySetInnerHTML={{ __html: html }} />
|
|
264
|
+
</article>
|
|
265
|
+
</div>
|
|
259
266
|
</div>
|
|
260
267
|
);
|
|
261
268
|
}
|
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,27 @@ function safeTocCss(css) {
|
|
|
142
142
|
return "";
|
|
143
143
|
return css.slice(0, 4000);
|
|
144
144
|
}
|
|
145
|
-
const TOC_CSS = `.sa-article-wrap{max-width:1080px;margin:0 auto;padding:var(--sa-blog-top,clamp(5rem,8vw,7rem)) 1.5rem 3rem}
|
|
146
|
-
.sa-article-
|
|
147
|
-
|
|
145
|
+
const TOC_CSS = `.sa-article-wrap{max-width:1080px;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;margin-bottom:2.5rem}
|
|
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);margin-bottom:0}}
|
|
152
153
|
.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
154
|
.sa-toc>summary::-webkit-details-marker{display:none}
|
|
154
155
|
.sa-toc>summary .sa-toc-chev{transition:transform .2s ease;color:var(--sa-muted,#94a3b8)}
|
|
155
156
|
.sa-toc[open]>summary .sa-toc-chev{transform:rotate(90deg)}
|
|
156
157
|
@media(min-width:1024px){.sa-toc>summary{pointer-events:none}.sa-toc>summary .sa-toc-chev{display:none}}
|
|
157
|
-
.sa-toc-
|
|
158
|
+
.sa-toc-body{position:relative}
|
|
159
|
+
.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
160
|
.sa-toc-list::-webkit-scrollbar{width:0;height:0;display:none}
|
|
159
161
|
.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
162
|
.sa-toc-list a[data-level="3"]{padding-left:1.4rem;font-size:.85em}
|
|
161
163
|
.sa-toc-list a:hover{color:inherit}
|
|
162
164
|
.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)}
|
|
165
|
+
.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
166
|
.sa-toc[data-sa-toc="minimal"] .sa-toc-list a{padding-left:.9rem;border-radius:0;box-shadow:inset 2px 0 0 transparent}
|
|
166
167
|
.sa-toc[data-sa-toc="minimal"] .sa-toc-list a[data-level="3"]{padding-left:1.7rem}
|
|
167
168
|
.sa-toc[data-sa-toc="minimal"] .sa-toc-list a.is-active{box-shadow:inset 2px 0 0 var(--sa-accent,#2563eb)}
|
|
@@ -240,7 +241,7 @@ function SeoBlogArticle({ post, locale }) {
|
|
|
240
241
|
const motion = toc.motion === false ? "off" : "on";
|
|
241
242
|
const label = L(TOC_LABEL, locale ?? "", "Contents");
|
|
242
243
|
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:
|
|
244
|
+
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
245
|
}
|
|
245
246
|
function fmtDate(d, locale) {
|
|
246
247
|
try {
|
package/package.json
CHANGED