diff --git a/README.md b/README.md index 64e0e4e..f188004 100644 --- a/README.md +++ b/README.md @@ -215,17 +215,22 @@ epigraph: 题记 # 可选 export const commentConfig = { twikooEnvId: "https://twikoo.tianxuetao.top", lang: "zh-CN", - version: "1.7.4", stripTrailingSlash: true, } ``` `twikooEnvId` 留空则整个评论区不渲染;也可以用环境变量 `PUBLIC_TWIKOO_ENV_ID` 覆盖,优先级更高。 -前端实现在 `src/components/Comments.astro`,从 jsDelivr 加载 twikoo 脚本,另外处理了两件事: +**前端脚本不走 CDN**,而是自托管的 `public/assets/js/twikoo.min.js`。国内访问 jsDelivr 很不稳定(实测 3 秒以上,且常被墙),走 CDN 会让评论区长时间空白甚至加载不出来。该文件的版本要与后端保持一致(当前都是 **1.7.20**,可用 `GET_FUNC_VERSION` 查后端版本),升级后端时记得同步替换它。 -- **切页重新挂载**:本站启用了 Astro 的 `ClientRouter`,组件监听 `astro:page-load` 事件重新初始化。 -- **评论归属路径**:`trailingSlash` 是 `"always"`,但评论 key 统一去掉结尾斜杠、存成 `/posts/xxx`,与该路径在旧站点上的历史数据保持一致。改掉这条规则,已有评论会对不上。 +主题适配样式在 `public/assets/css/twikoo-custom.css`。twikoo 的样式是运行时注入 `
` 的、会排在后面,所以那里的选择器统一加了 `#twikoo` 前缀来提高优先级,颜色引用主题变量因而自动跟随亮/暗色。 + +前端逻辑在 `src/components/Comments.astro`,处理了两件事: + +- **切页重新挂载**:本站启用了 Astro 的 `ClientRouter`。注意 `astro:page-load` 在**首屏也会触发**,所以用 `dataset.mounted` 标记防止重复初始化;切页后是新容器、没有该标记,会正常重新挂载。 +- **评论归属路径**:`trailingSlash` 是 `"always"`,但评论 key 统一去掉结尾斜杠。浏览器提交的是**百分号编码**过的 `location.pathname`(如 `/posts/%E7%94%9F%E6%B4%BB/...`),后端按原样存储和匹配 —— 所以去斜杠后的编码路径正好与历史数据一致。改掉这条规则,老评论就会对不上。 + +评论出现在:文章页、关于页、友链页。 主题原作者的自托管评论后端 `cwd-api/` 已随迁移移除。 diff --git a/docs/操作指南.md b/docs/操作指南.md index d3d512f..bb3caae 100644 --- a/docs/操作指南.md +++ b/docs/操作指南.md @@ -213,6 +213,29 @@ sudo sysctl fs.inotify.max_user_watches=524288 检查路径是否以 `/` 开头(`/assets/images/x.webp`),以及文件是否真在 `public/` 下对应位置。 注意 `public/` 里的文件路径就是 URL 路径,**没有 `public` 这一段**。 +**评论区空白 / 不显示** + +先确认页面类型——评论**只出现在文章页、关于页、友链页**,首页、偶得、相册、书架、专题都没有,这是正常的。 + +如果文章页也是空的,按顺序查: + +1. `src/data/comments.ts` 里的 `twikooEnvId` 是否填了(留空则整个评论区不渲染) +2. 打开浏览器控制台,看有没有 `[twikoo] 初始化失败` 或 404 +3. 确认 `public/assets/js/twikoo.min.js` 存在,且版本与后端一致 +4. 直接测后端: + + ```bash + curl -s -X POST https://twikoo.tianxuetao.top \ + -H 'Content-Type: application/json' \ + -d '{"event":"GET_FUNC_VERSION"}' + ``` + + 正常会返回 `{"code":0,"version":{"VERSION":"1.7.20"}}`。若版本和前端脚本不一致,去 +'+(n?e:(0,o.escape)(e,!0))+"\n":""+(n?e:(0,o.escape)(e,!0))+"\n"}blockquote(e){return"\n"+e+"\n"}html(e){return e}heading(e,t,n,i){return this.options.headerIds?"
"+e+"
\n"}table(e,t){return t&&(t=""+t+""),""+e+""}br(){return this.options.xhtml?"An error occurred:
"+(0,c.escape)(e.message+"",!0)+"";throw e}}d.options=d.setOptions=function(e){return(0,c.merge)(d.defaults,e),(0,u.changeDefaults)(d.defaults),d},d.getDefaults=u.getDefaults,d.defaults=u.defaults,d.use=function(...e){const t=(0,c.merge)({},...e),n=d.defaults.extensions||{renderers:{},childTokens:{}};let i;e.forEach(e=>{if(e.extensions&&(i=!0,e.extensions.forEach(e=>{if(!e.name)throw new Error("extension name required");if(e.renderer){const t=n.renderers?n.renderers[e.name]:null;n.renderers[e.name]=t?function(...n){let i=e.renderer.apply(this,n);return!1===i&&(i=t.apply(this,n)),i}:e.renderer}if(e.tokenizer){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");n[e.level]?n[e.level].unshift(e.tokenizer):n[e.level]=[e.tokenizer],e.start&&("block"===e.level?n.startBlock?n.startBlock.push(e.start):n.startBlock=[e.start]:"inline"===e.level&&(n.startInline?n.startInline.push(e.start):n.startInline=[e.start]))}e.childTokens&&(n.childTokens[e.name]=e.childTokens)})),e.renderer){const n=d.defaults.renderer||new a.Renderer;for(const t in e.renderer){const i=n[t];n[t]=(...o)=>{let r=e.renderer[t].apply(n,o);return!1===r&&(r=i.apply(n,o)),r}}t.renderer=n}if(e.tokenizer){const n=d.defaults.tokenizer||new r.Tokenizer;for(const t in e.tokenizer){const i=n[t];n[t]=(...o)=>{let r=e.tokenizer[t].apply(n,o);return!1===r&&(r=i.apply(n,o)),r}}t.tokenizer=n}if(e.walkTokens){const n=d.defaults.walkTokens;t.walkTokens=function(t){e.walkTokens.call(this,t),n&&n.call(this,t)}}i&&(t.extensions=n),d.setOptions(t)})},d.walkTokens=function(e,t){for(const n of e)switch(t.call(d,n),n.type){case"table":for(const e of n.header)d.walkTokens(e.tokens,t);for(const e of n.rows)for(const n of e)d.walkTokens(n.tokens,t);break;case"list":d.walkTokens(n.items,t);break;default:d.defaults.extensions&&d.defaults.extensions.childTokens&&d.defaults.extensions.childTokens[n.type]?d.defaults.extensions.childTokens[n.type].forEach(function(e){d.walkTokens(n[e],t)}):n.tokens&&d.walkTokens(n.tokens,t)}},d.parseInline=function(e,t){if(null==e)throw new Error("marked.parseInline(): input parameter is undefined or null");if("string"!=typeof e)throw new Error("marked.parseInline(): input parameter is of type "+Object.prototype.toString.call(e)+", string expected");t=(0,c.merge)({},d.defaults,t||{}),(0,c.checkSanitizeDeprecation)(t);try{const n=i.Lexer.lexInline(e,t);return t.walkTokens&&d.walkTokens(n,t.walkTokens),o.Parser.parseInline(n,t)}catch(e){if(e.message+="\nPlease report this to https://github.com/markedjs/marked.",t.silent)return"
An error occurred:
"+(0,c.escape)(e.message+"",!0)+"";throw e}},d.Parser=o.Parser,d.parser=o.Parser.parse,d.Renderer=a.Renderer,d.TextRenderer=s.TextRenderer,d.Lexer=i.Lexer,d.lexer=i.Lexer.lex,d.Tokenizer=r.Tokenizer,d.Slugger=l.Slugger,d.parse=d,t.options=d.options,t.setOptions=d.setOptions,t.use=d.use,t.walkTokens=d.walkTokens,t.parseInline=d.parseInline,t.parse=d,t.parser=o.Parser.parse,t.lexer=i.Lexer.lex},5779:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.inline=t.block=void 0;var i=n(9105);const o=t.block={newline:/^(?: *(?:\n|$))+/,code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,fences:/^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?=\n|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3}bull)( [^\n]+?)?(?:\n|$)/,html:"^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n *)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)|(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$))",def:/^ {0,3}\[(label)\]: *(?:\n *)?([^\s>]+)>?(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/,table:i.noopTest,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,text:/^[^\n]+/};o._label=/(?!\s*\])(?:\\.|[^\[\]\\])+/,o._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/,o.def=(0,i.edit)(o.def).replace("label",o._label).replace("title",o._title).getRegex(),o.bullet=/(?:[*+-]|\d{1,9}[.)])/,o.listItemStart=(0,i.edit)(/^( *)(bull) */).replace("bull",o.bullet).getRegex(),o.list=(0,i.edit)(o.list).replace(/bull/g,o.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+o.def.source+")").getRegex(),o._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",o._comment=/|$)/,o.html=(0,i.edit)(o.html,"i").replace("comment",o._comment).replace("tag",o._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),o.paragraph=(0,i.edit)(o._paragraph).replace("hr",o.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",o._tag).getRegex(),o.blockquote=(0,i.edit)(o.blockquote).replace("paragraph",o.paragraph).getRegex(),o.normal=(0,i.merge)({},o),o.gfm=(0,i.merge)({},o.normal,{table:"^ *([^\\n ].*\\|.*)\\n {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)(?:\\| *)?(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)"}),o.gfm.table=(0,i.edit)(o.gfm.table).replace("hr",o.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",o._tag).getRegex(),o.gfm.paragraph=(0,i.edit)(o._paragraph).replace("hr",o.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("table",o.gfm.table).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",o._tag).getRegex(),o.pedantic=(0,i.merge)({},o.normal,{html:(0,i.edit)("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?\\1> *(?:\\n{2,}|\\s*$)|