Merge remote-tracking branch 'origin/master' into blog-content

This commit is contained in:
github-actions[bot] 2026-09-19 06:21:58 +00:00
commit 1fb3da96c1
4 changed files with 191 additions and 13 deletions

View file

@ -1,11 +1,16 @@
---
import Remark42Status from "./Remark42Status.astro";
const { slug } = Astro.props;
const pagePath = `/posts/${slug}/`;
const host = import.meta.env.PUBLIC_REMARK42_HOST;
const siteId = import.meta.env.PUBLIC_REMARK42_SITE_ID;
---
<div id="remark42"></div>
<div class="remark42-embed">
<Remark42Status />
<div id="remark42"></div>
</div>
<script define:vars={{ pagePath, host, siteId }} is:inline data-astro-rerun>
function getTheme() {
return document.documentElement.classList.contains("dark")
@ -55,28 +60,99 @@ const siteId = import.meta.env.PUBLIC_REMARK42_SITE_ID;
}
script.onload = () => resolve(true);
script.onerror = reject;
script.onerror = (error) => {
script.remove();
reject(error);
};
document.head.appendChild(script);
});
}
async function mountRemark42() {
const node = document.getElementById("remark42");
if (!node) return;
const status = document.getElementById("remark42-status");
const loadingStatus = status?.querySelector("[data-remark42-loading]");
const failedStatus = status?.querySelector("[data-remark42-failed]");
if (!node || !loadingStatus || !failedStatus) return;
window.__remark42LoadCleanup?.();
let active = true;
let iframe = null;
let timer;
const onIframeLoad = () => {
if (!active || !iframe || !node.contains(iframe)) return;
loadingStatus.hidden = true;
failedStatus.hidden = true;
status.hidden = true;
clearTimeout(timer);
observer.disconnect();
};
const watchIframe = () => {
const nextIframe = node.querySelector("iframe");
if (nextIframe === iframe) return;
iframe?.removeEventListener("load", onIframeLoad);
iframe = nextIframe;
iframe?.addEventListener("load", onIframeLoad, { once: true });
};
const observer = new MutationObserver(watchIframe);
observer.observe(node, { childList: true, subtree: true });
watchIframe();
const showFailure = () => {
if (!active || loadingStatus.hidden) return;
loadingStatus.hidden = true;
failedStatus.hidden = false;
};
window.__remark42LoadCleanup = () => {
active = false;
clearTimeout(timer);
observer.disconnect();
iframe?.removeEventListener("load", onIframeLoad);
};
loadingStatus.hidden = false;
failedStatus.hidden = true;
status.hidden = false;
timer = setTimeout(showFailure, 15000);
if (!host || !siteId) {
showFailure();
return;
}
try {
const alreadyLoaded = Boolean(window.REMARK42);
setRemarkConfig();
await ensureScript();
if (!window.REMARK42) return;
if (!active) return;
if (!window.REMARK42) {
showFailure();
return;
}
if (typeof window.REMARK42.destroy === "function") {
if (alreadyLoaded && typeof window.REMARK42.destroy === "function") {
window.REMARK42.destroy();
}
if (typeof window.REMARK42.createInstance === "function") {
// The script creates the first iframe itself. Only remount when
// reusing it on a new page/theme, or if it did not create one.
if (
(alreadyLoaded || !node.querySelector("iframe")) &&
typeof window.REMARK42.createInstance === "function"
) {
node.innerHTML = "";
window.REMARK42.createInstance(window.remark_config);
}
} catch {
showFailure();
}
}
function setupRemark42ThemeObserver() {
@ -106,3 +182,18 @@ const siteId = import.meta.env.PUBLIC_REMARK42_SITE_ID;
mountRemark42();
setupRemark42ThemeObserver();
</script>
<style>
.remark42-embed {
position: relative;
min-width: 0;
}
/* Let the iframe load in the same space occupied by the centered status. */
:global(#remark42-status:not([hidden])) + #remark42 {
position: absolute;
inset: 0;
visibility: hidden;
pointer-events: none;
}
</style>

View file

@ -0,0 +1,75 @@
---
import { getLangFromUrl } from "@/i18n";
const lang = getLangFromUrl(Astro.url);
const loadingText = lang === "zh" ? "评论区加载中……" : "Loading comments…";
const failedText = lang === "zh" ? "评论区加载失败" : "Failed to load comments";
---
<div id="remark42-status" role="status" aria-atomic="true">
<p class="remark42-loading" data-remark42-loading>
<span class="status-spinner" aria-hidden="true"></span>
<span>{loadingText}</span>
</p>
<p class="remark42-failed" data-remark42-failed hidden>
<span>{failedText}</span>
</p>
</div>
<style>
#remark42-status {
--status-color: #6b7680;
--status-accent: var(--deep-blue);
display: grid;
place-items: center;
min-block-size: 10rem;
padding: 2rem 1rem;
text-align: center;
}
.remark42-loading,
.remark42-failed {
display: flex;
align-items: center;
justify-content: center;
gap: 0.65rem;
margin: 0;
color: var(--status-color);
font-size: 0.85rem;
font-weight: 400;
line-height: 1.6;
}
.status-spinner {
flex: 0 0 auto;
width: 1rem;
height: 1rem;
box-sizing: border-box;
border: 1.5px solid color-mix(in srgb, var(--status-accent) 20%, transparent);
border-top-color: var(--status-accent);
border-radius: 50%;
animation: status-spin 1s linear infinite;
}
#remark42-status[hidden],
[hidden] {
display: none;
}
:global(html.dark) #remark42-status {
--status-color: #aab7c4;
--status-accent: #9ecbff;
}
@keyframes status-spin {
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: reduce) {
.status-spinner {
animation: none;
}
}
</style>

View file

@ -17,7 +17,7 @@ export default {
publishedOn: "Published on",
commentsDesc: [
"Feel free to leave your thoughts here. 💭💡",
"After signing in, you can click the “Subscribe by Email” button at the bottom right of the text box to receive notifications of new interactions via email."
"After signing in, you can click “Subscribe by Email” at the bottom right of the text box to receive notifications via E-mail."
]
},
theme: {

View file

@ -103,7 +103,9 @@ const publishedDate = new Intl.DateTimeFormat(
aria-labelledby="comments-heading"
>
<h2 id="comments-heading">{comments}</h2>
{t.post.commentsDesc.map((line: string) => <p set:html={line} />)}
{t.post.commentsDesc.map((line: string) => (
<p class="comments-description" set:html={line} />
))}
<Remark42Embed slug={slug} />
</section>
</article>
@ -653,6 +655,16 @@ const publishedDate = new Intl.DateTimeFormat(
scroll-margin-top: 5rem;
}
.comments-description {
color: #6b7680;
font-size: 0.85rem;
line-height: 1.6;
}
:global(html.dark) .comments-description {
color: #aab7c4;
}
:global(html.dark) .post-content {
--article-muted: #aab7c4;
--article-rule: rgba(170, 183, 196, 0.28);