将 Markdown 编辑器替换为 TipTap WYSIWYG,新增帖子修订记录与 diff 展示;首页支持最新/回复排序与本地缓存;后台可配置编辑时限与锁定帖子;侧边栏整合板块导航并优化 Feed 布局。 Co-authored-by: Cursor <cursoragent@cursor.com>
15 lines
32 KiB
JavaScript
15 lines
32 KiB
JavaScript
import{j as t,r as m,e as je,w as qe}from"./react-vendor-CFzzFOsg.js";import{P as He}from"./PinnedIcon-CWAQCHB6.js";import{c as le,a as z,n as E,S as Ne,u as Ue,B as T}from"./index-L-Mx2Hle.js";import{B as re}from"./badge-e5OBoJ6g.js";import{h as Je,a as Y,i as Ve}from"./content-BrFHUpp8.js";import{i as Xe,X as be,s as Ze,v as we,w as Qe,x as Ke,y as Ce,G as et,r as tt,z as st,B as nt,D as it,A as at,E as ce,J as ot,j as lt,K as rt,N as ct}from"./ui-vendor-DGgNN2YO.js";import{r as dt,P as ut}from"./postContent-D-mW2yDF.js";import{p as mt}from"./markdown-vendor-DxR1h-Bq.js";import{c as ft}from"./feedCache-D2GnU_BI.js";function ht({content:n,onMentionClick:e}){return t.jsx("div",{className:"floor-body",dangerouslySetInnerHTML:{__html:Je(n)}})}function q(n){var e;return(e=n.user)!=null&&e.nickname?n.user.nickname:n.guest_nick?n.guest_nick:"游客"}function vt(n){return q(n)[0]||"?"}function xt(n){return!n.user_id||n.user_id===0}function gt(n){const e=new Map,s=[];for(const i of n)e.set(i.id,{comment:i,children:[]});for(const i of n){const o=e.get(i.id);i.reply_to&&e.has(i.reply_to)?e.get(i.reply_to).children.push(o):s.push(o)}return s}function pt(n){const e=new Date(n),s=new Date,i=o=>String(o).padStart(2,"0");return e.getFullYear()===s.getFullYear()?`${i(e.getMonth()+1)}月${i(e.getDate())}日`:`${e.getFullYear()}年${e.getMonth()+1}月${e.getDate()}日`}function ke({node:n,nested:e,highlightFloor:s,replyToId:i,onReply:o,onCancelReply:a,renderReplyBox:l}){var v,b;const r=n.comment,d=q(r),f=xt(r),h=s===r.floor,w=!!r.content_hidden,C=i===r.id;return t.jsxs("div",{id:`floor-${r.floor}`,className:`waline-comment ${e?"nested":""} ${h?"highlight":""}`,children:[t.jsx("div",{className:`waline-comment-avatar ${f&&!((v=r.user)!=null&&v.avatar)?"guest":""}`,children:(b=r.user)!=null&&b.avatar?t.jsx("img",{src:r.user.avatar,alt:""}):vt(r)}),t.jsxs("div",{className:"waline-comment-main",children:[t.jsx("div",{className:"waline-comment-head",children:r.guest_url?t.jsx("a",{href:r.guest_url,target:"_blank",rel:"noopener noreferrer",className:"waline-comment-author",children:d}):t.jsx("span",{className:"waline-comment-author",children:d})}),w?t.jsx("div",{className:"waline-comment-private-mask",children:"该评论为私密评论,仅文章作者与评论发起者可见!"}):t.jsxs("div",{className:"waline-comment-bubble",children:[r.reply_target&&t.jsxs("span",{className:"waline-reply-at",children:["@",q(r.reply_target)]}),t.jsx(ht,{content:r.content})]}),t.jsxs("div",{className:"waline-comment-meta",children:[t.jsxs("span",{className:"waline-comment-date",children:[t.jsx(Xe,{size:14}),pt(r.created_at)]}),C?t.jsxs("button",{type:"button",className:"waline-comment-reply-btn cancel",onClick:a,children:[t.jsx(be,{size:14}),"取消"]}):t.jsxs("button",{type:"button",className:"waline-comment-reply-btn",onClick:()=>o(r),children:[t.jsx(Ze,{size:14}),"回复"]})]}),C&&l&&t.jsx("div",{id:`reply-box-${r.id}`,className:"comment-box-wrap inline",children:l(r)}),n.children.length>0&&t.jsx("div",{className:"waline-replies",children:n.children.map(S=>t.jsx(ke,{node:S,nested:!0,highlightFloor:s,replyToId:i,onReply:o,onCancelReply:a,renderReplyBox:l},S.comment.id))})]})]})}function jt({comments:n,highlightFloor:e,replyToId:s,onReply:i,onCancelReply:o,renderReplyBox:a}){const l=gt(n);return t.jsx("div",{className:"comment-thread-list",children:l.map(r=>t.jsx(ke,{node:r,highlightFloor:e,replyToId:s,onReply:i,onCancelReply:o,renderReplyBox:a},r.comment.id))})}const ye=m.forwardRef(({className:n,...e},s)=>t.jsx(we,{className:le("peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-[var(--j13-green)] data-[state=unchecked]:bg-input",n),...e,ref:s,children:t.jsx(Qe,{className:le("pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0")})}));ye.displayName=we.displayName;const Nt=["😀","😁","😂","🤣","😃","😄","😅","😆","😉","😊","😋","😎","😍","😘","🥰","😗","😙","😚","🙂","🤗","🤩","🤔","🤨","😐","😑","😶","🙄","😏","😣","😥","😮","🤐","😯","😪","😫","🥱","😴","😌","😛","😜","😝","🤤","😒","😓","😔","😕","🙃","🤑","😲","🙁","😖","😞","😟","😤","😢","😭","😦","😧","😨","😩","🤯","😬","😰","😱","🥵","🥶","😳","🤪","😵","🥴","😠","😡","🤬","😷","🤒","🤕","🤢","🤮","🤧","😇","🥳","🥺","🤠","🤡","🤥","🤫","🤭","🧐","🤓","😈","👻","💀","☠️","👽","👾","🤖","🎃","😺","😸","😹","😻","😼","😽","🙀","😿","😾","👋","🤚","🖐","✋","🖖","👌","🤏","✌️","🤞","🤟","🤘","🤙","👈","👉","👆","👇","☝️","👍","👎","✊","👊","🤛","🤜","👏","🙌","👐","🤲","🤝","🙏","💪","🦾","🦿","🦵","🦶","❤️","🧡","💛","💚","💙","💜","🖤","🤍","🤎","💔","❣️","💕","💞","💓","💗","💖","💘","💝","💟","☮️","✨","⭐","🌟","💫","🔥","💥","💯","✅","❌","❓","❗","💢","💤","💦","🎉","🎊","🎁","🏆","🥇","🥈"];function bt({onSelect:n}){return t.jsx("div",{className:"emoji-picker",children:Nt.map(e=>t.jsx("button",{type:"button",className:"emoji-picker-item",onClick:()=>n(e),children:e},e))})}const Se="j13_guest_nick",Ee="j13_guest_email",_e="j13_guest_url",Pe="j13_my_comment_ids";function wt(){return{nick:localStorage.getItem(Se)||"",email:localStorage.getItem(Ee)||"",url:localStorage.getItem(_e)||""}}function Ct(n){localStorage.setItem(Se,n.nick),localStorage.setItem(Ee,n.email),localStorage.setItem(_e,n.url)}function Ie(){try{const n=localStorage.getItem(Pe);if(!n)return[];const e=JSON.parse(n);return Array.isArray(e)?e.filter(s=>typeof s=="number"):[]}catch{return[]}}function kt(n){const e=Ie();e.includes(n)||(e.push(n),localStorage.setItem(Pe,JSON.stringify(e.slice(-200))))}function de({user:n,replyTo:e,inline:s,submitting:i,submitCount:o=0,onSubmit:a,onCancelReply:l}){var A;const r=wt(),[d,f]=m.useState(""),[h,w]=m.useState(r.nick),[C,v]=m.useState(r.email),[b,S]=m.useState(r.url),[p,k]=m.useState(!1),[x,u]=m.useState(!1),j=m.useRef(null),y=m.useRef(null);m.useEffect(()=>{var g;s&&e&&((g=j.current)==null||g.focus({preventScroll:!0}))},[e==null?void 0:e.id,s]),m.useEffect(()=>{f(""),u(!1),k(!1)},[o]),m.useEffect(()=>{if(!x)return;const g=_=>{y.current&&!y.current.contains(_.target)&&u(!1)};return document.addEventListener("mousedown",g),()=>document.removeEventListener("mousedown",g)},[x]);const P=g=>{const _=j.current;if(_){const D=_.selectionStart??d.length,F=_.selectionEnd??d.length,O=d.slice(0,D)+g+d.slice(F);f(O),requestAnimationFrame(()=>{_.focus();const B=D+g.length;_.setSelectionRange(B,B)})}else f(D=>D+g)},I=()=>{const g=d.trim();g&&(!n&&!h.trim()||(n||Ct({nick:h.trim(),email:C.trim(),url:b.trim()}),a({content:g,guestNick:n?void 0:h.trim(),guestEmail:n?void 0:C.trim(),guestUrl:n?void 0:b.trim(),isPrivate:p})))},L=g=>{g.key==="Enter"&&(g.ctrlKey||g.metaKey)&&(g.preventDefault(),I())},R=((A=n==null?void 0:n.nickname)==null?void 0:A[0])||(h==null?void 0:h[0])||"?";return t.jsxs("div",{className:"comment-box",ref:y,children:[t.jsx("div",{className:"comment-box-avatar",children:n!=null&&n.avatar?t.jsx("img",{src:n.avatar,alt:"",className:"comment-box-avatar-img"}):t.jsx("div",{className:`comment-box-avatar-placeholder ${n?"":"guest"}`,children:n?R:t.jsx("svg",{viewBox:"0 0 24 24",width:"20",height:"20",fill:"currentColor",children:t.jsx("path",{d:"M12 12c2.7 0 4.8-2.1 4.8-4.8S14.7 2.4 12 2.4 7.2 4.5 7.2 7.2 9.3 12 12 12zm0 2.4c-3.2 0-9.6 1.6-9.6 4.8v2.4h19.2v-2.4c0-3.2-6.4-4.8-9.6-4.8z"})})})}),t.jsxs("div",{className:"comment-box-main",children:[e&&!s&&t.jsxs("div",{className:"comment-box-reply-hint",children:[t.jsxs("span",{children:["回复 #",e.floor," ",q(e)]}),l&&t.jsx("button",{type:"button",className:"comment-box-reply-cancel",onClick:l,children:"取消"})]}),t.jsxs("div",{className:`comment-box-input-wrap ${p?"private-mode":""}`,children:[t.jsx("textarea",{ref:j,className:"comment-box-textarea",placeholder:p?"正在隐私评论中...":"说点什么吧",value:d,onChange:g=>f(g.target.value),onKeyDown:L,rows:3}),t.jsx("button",{type:"button",className:"comment-box-send",disabled:i||!d.trim()||!n&&!h.trim(),onClick:I,title:"发送",children:t.jsx(Ke,{size:16})})]}),!n&&t.jsxs("div",{className:"comment-box-guest-fields",children:[t.jsxs("label",{className:"comment-box-guest-field",children:[t.jsxs("span",{className:"comment-box-guest-label",children:["昵称",t.jsx("em",{className:"comment-box-guest-required",children:"必填"})]}),t.jsx("input",{className:"comment-box-guest-input",placeholder:"怎么称呼你",autoComplete:"nickname",value:h,onChange:g=>w(g.target.value)})]}),t.jsxs("label",{className:"comment-box-guest-field",children:[t.jsxs("span",{className:"comment-box-guest-label",children:["邮箱",t.jsx("em",{className:"comment-box-guest-optional",children:"选填"})]}),t.jsx("input",{className:"comment-box-guest-input",placeholder:"name@example.com",type:"email",autoComplete:"email",value:C,onChange:g=>v(g.target.value)})]}),t.jsxs("label",{className:"comment-box-guest-field",children:[t.jsxs("span",{className:"comment-box-guest-label",children:["网址",t.jsx("em",{className:"comment-box-guest-optional",children:"选填"})]}),t.jsx("input",{className:"comment-box-guest-input",placeholder:"https://example.com",type:"url",autoComplete:"url",value:b,onChange:g=>S(g.target.value)})]}),t.jsx("p",{className:"comment-box-guest-hint",children:"邮箱不会公开展示,仅用于站内记录。"})]}),t.jsxs("div",{className:"comment-box-toolbar",children:[t.jsx("button",{type:"button",className:`comment-box-owo ${x?"active":""}`,onClick:()=>u(g=>!g),children:"OwO"}),t.jsxs("label",{className:"comment-box-private",children:[t.jsx(ye,{checked:p,onCheckedChange:k}),t.jsx("span",{children:"隐私评论"})]})]}),x&&t.jsx(bt,{onSelect:P})]})]})}function J({html:n,isLoggedIn:e,className:s="post-detail-content"}){const i=je(),o=m.useMemo(()=>dt(n,e),[n,e]),a=m.useCallback(l=>{const r=l.target;if(r.closest("[data-members-login]")){l.preventDefault(),i("/login");return}r.closest("[data-members-register]")&&(l.preventDefault(),i("/register"))},[i]);return t.jsx("div",{className:s,onClick:a,dangerouslySetInnerHTML:{__html:o}})}class Le{diff(e,s,i={}){let o;typeof i=="function"?(o=i,i={}):"callback"in i&&(o=i.callback);const a=this.castInput(e,i),l=this.castInput(s,i),r=this.removeEmpty(this.tokenize(a,i)),d=this.removeEmpty(this.tokenize(l,i));return this.diffWithOptionsObj(r,d,i,o)}diffWithOptionsObj(e,s,i,o){var a;const l=x=>{if(x=this.postProcess(x,i),o){setTimeout(function(){o(x)},0);return}else return x},r=s.length,d=e.length;let f=1,h=r+d;i.maxEditLength!=null&&(h=Math.min(h,i.maxEditLength));const w=(a=i.timeout)!==null&&a!==void 0?a:1/0,C=Date.now()+w,v=[{oldPos:-1,lastComponent:void 0}];let b=this.extractCommon(v[0],s,e,0,i);if(v[0].oldPos+1>=d&&b+1>=r)return l(this.buildValues(v[0].lastComponent,s,e));let S=-1/0,p=1/0;const k=()=>{for(let x=Math.max(S,-f);x<=Math.min(p,f);x+=2){let u;const j=v[x-1],y=v[x+1];j&&(v[x-1]=void 0);let P=!1;if(y){const L=y.oldPos-x;P=y&&0<=L&&L<r}const I=j&&j.oldPos+1<d;if(!P&&!I){v[x]=void 0;continue}if(!I||P&&j.oldPos<y.oldPos?u=this.addToPath(y,!0,!1,0,i):u=this.addToPath(j,!1,!0,1,i),b=this.extractCommon(u,s,e,x,i),u.oldPos+1>=d&&b+1>=r)return l(this.buildValues(u.lastComponent,s,e))||!0;v[x]=u,u.oldPos+1>=d&&(p=Math.min(p,x-1)),b+1>=r&&(S=Math.max(S,x+1))}f++};if(o)(function x(){setTimeout(function(){if(f>h||Date.now()>C)return o(void 0);k()||x()},0)})();else for(;f<=h&&Date.now()<=C;){const x=k();if(x)return x}}addToPath(e,s,i,o,a){const l=e.lastComponent;return l&&!a.oneChangePerToken&&l.added===s&&l.removed===i?{oldPos:e.oldPos+o,lastComponent:{count:l.count+1,added:s,removed:i,previousComponent:l.previousComponent}}:{oldPos:e.oldPos+o,lastComponent:{count:1,added:s,removed:i,previousComponent:l}}}extractCommon(e,s,i,o,a){const l=s.length,r=i.length;let d=e.oldPos,f=d-o,h=0;for(;f+1<l&&d+1<r&&this.equals(i[d+1],s[f+1],a);)f++,d++,h++,a.oneChangePerToken&&(e.lastComponent={count:1,previousComponent:e.lastComponent,added:!1,removed:!1});return h&&!a.oneChangePerToken&&(e.lastComponent={count:h,previousComponent:e.lastComponent,added:!1,removed:!1}),e.oldPos=d,f}equals(e,s,i){return i.comparator?i.comparator(e,s):e===s||!!i.ignoreCase&&e.toLowerCase()===s.toLowerCase()}removeEmpty(e){const s=[];for(let i=0;i<e.length;i++)e[i]&&s.push(e[i]);return s}castInput(e,s){return e}tokenize(e,s){return Array.from(e)}join(e){return e.join("")}postProcess(e,s){return e}get useLongestToken(){return!1}buildValues(e,s,i){const o=[];let a;for(;e;)o.push(e),a=e.previousComponent,delete e.previousComponent,e=a;o.reverse();const l=o.length;let r=0,d=0,f=0;for(;r<l;r++){const h=o[r];if(h.removed)h.value=this.join(i.slice(f,f+h.count)),f+=h.count;else{if(!h.added&&this.useLongestToken){let w=s.slice(d,d+h.count);w=w.map(function(C,v){const b=i[f+v];return b.length>C.length?b:C}),h.value=this.join(w)}else h.value=this.join(s.slice(d,d+h.count));d+=h.count,h.added||(f+=h.count)}}return o}}function ue(n,e){let s;for(s=0;s<n.length&&s<e.length;s++)if(n[s]!=e[s])return n.slice(0,s);return n.slice(0,s)}function me(n,e){let s;if(!n||!e||n[n.length-1]!=e[e.length-1])return"";for(s=0;s<n.length&&s<e.length;s++)if(n[n.length-(s+1)]!=e[e.length-(s+1)])return n.slice(-s);return n.slice(-s)}function V(n,e,s){if(n.slice(0,e.length)!=e)throw Error(`string ${JSON.stringify(n)} doesn't start with prefix ${JSON.stringify(e)}; this is a bug`);return s+n.slice(e.length)}function X(n,e,s){if(!e)return n+s;if(n.slice(-e.length)!=e)throw Error(`string ${JSON.stringify(n)} doesn't end with suffix ${JSON.stringify(e)}; this is a bug`);return n.slice(0,-e.length)+s}function M(n,e){return V(n,e,"")}function G(n,e){return X(n,e,"")}function fe(n,e){return e.slice(0,yt(n,e))}function yt(n,e){let s=0;n.length>e.length&&(s=n.length-e.length);let i=e.length;n.length<e.length&&(i=n.length);const o=Array(i);let a=0;o[0]=0;for(let l=1;l<i;l++){for(e[l]==e[a]?o[l]=o[a]:o[l]=a;a>0&&e[l]!=e[a];)a=o[a];e[l]==e[a]&&a++}a=0;for(let l=s;l<n.length;l++){for(;a>0&&n[l]!=e[a];)a=o[a];n[l]==e[a]&&a++}return a}function Te(n,e){const s=[];for(const i of Array.from(e.segment(n))){const o=i.segment;s.length&&/\s/.test(s[s.length-1])&&/\s/.test(o)?s[s.length-1]+=o:s.push(o)}return s}function Z(n,e){if(e)return $(n,e)[1];let s;for(s=n.length-1;s>=0&&n[s].match(/\s/);s--);return n.substring(s+1)}function W(n,e){if(e)return $(n,e)[0];const s=n.match(/^\s*/);return s?s[0]:""}function $(n,e){if(!e)return[W(n),Z(n)];if(e.resolvedOptions().granularity!="word")throw new Error('The segmenter passed must have a granularity of "word"');const s=Te(n,e),i=s[0],o=s[s.length-1],a=/\s/.test(i)?i:"",l=/\s/.test(o)?o:"";return[a,l]}const he="a-zA-Z0-9_\\u{AD}\\u{C0}-\\u{D6}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}",St=new RegExp(`[${he}]+|\\s+|[^${he}]`,"ug");class Et extends Le{equals(e,s,i){return i.ignoreCase&&(e=e.toLowerCase(),s=s.toLowerCase()),e.trim()===s.trim()}tokenize(e,s={}){let i;if(s.intlSegmenter){const l=s.intlSegmenter;if(l.resolvedOptions().granularity!="word")throw new Error('The segmenter passed must have a granularity of "word"');i=Te(e,l)}else i=e.match(St)||[];const o=[];let a=null;return i.forEach(l=>{/\s/.test(l)?a==null?o.push(l):o.push(o.pop()+l):a!=null&&/\s/.test(a)?o[o.length-1]==a?o.push(o.pop()+l):o.push(a+l):o.push(l),a=l}),o}join(e){return e.map((s,i)=>i==0?s:s.replace(/^\s+/,"")).join("")}postProcess(e,s){if(!e||s.oneChangePerToken)return e;let i=null,o=null,a=null;return e.forEach(l=>{l.added?o=l:l.removed?a=l:((o||a)&&ve(i,a,o,l,s.intlSegmenter),i=l,o=null,a=null)}),(o||a)&&ve(i,a,o,null,s.intlSegmenter),e}}const _t=new Et;function Pt(n,e,s){return _t.diff(n,e,s)}function ve(n,e,s,i,o){if(e&&s){const[a,l]=$(e.value,o),[r,d]=$(s.value,o);if(n){const f=ue(a,r);n.value=X(n.value,r,f),e.value=M(e.value,f),s.value=M(s.value,f)}if(i){const f=me(l,d);i.value=V(i.value,d,f),e.value=G(e.value,f),s.value=G(s.value,f)}}else if(s){if(n){const a=W(s.value,o);s.value=s.value.substring(a.length)}if(i){const a=W(i.value,o);i.value=i.value.substring(a.length)}}else if(n&&i){const a=W(i.value,o),[l,r]=$(e.value,o),d=ue(a,l);e.value=M(e.value,d);const f=me(M(a,d),r);e.value=G(e.value,f),i.value=V(i.value,a,f),n.value=X(n.value,a,a.slice(0,a.length-f.length))}else if(i){const a=W(i.value,o),l=Z(e.value,o),r=fe(l,a);e.value=G(e.value,r)}else if(n){const a=Z(n.value,o),l=W(e.value,o),r=fe(a,l);e.value=M(e.value,r)}}class It extends Le{constructor(){super(...arguments),this.tokenize=zt}equals(e,s,i){return i.ignoreWhitespace?((!i.newlineIsToken||!e.includes(`
|
||
`))&&(e=e.trim()),(!i.newlineIsToken||!s.includes(`
|
||
`))&&(s=s.trim())):i.ignoreNewlineAtEof&&!i.newlineIsToken&&(e.endsWith(`
|
||
`)&&(e=e.slice(0,-1)),s.endsWith(`
|
||
`)&&(s=s.slice(0,-1))),super.equals(e,s,i)}}const Lt=new It;function Tt(n,e,s){return Lt.diff(n,e,s)}function zt(n,e){e.stripTrailingCr&&(n=n.replace(/\r\n/g,`
|
||
`));const s=[],i=n.split(/(\n|\r\n)/);i[i.length-1]||i.pop();for(let o=0;o<i.length;o++){const a=i[o];o%2&&!e.newlineIsToken?s[s.length-1]+=a:s.push(a)}return s}function H(n){if(!n.trim())return"";const e=new DOMParser().parseFromString(mt.sanitize(n,ut),"text/html");return e.querySelectorAll("br").forEach(i=>i.replaceWith(`
|
||
`)),e.querySelectorAll("p, div, li, h1, h2, h3, h4, h5, h6, blockquote, pre, members-only").forEach(i=>{i.prepend(e.createTextNode(`
|
||
`)),i.append(e.createTextNode(`
|
||
`))}),(e.body.textContent??"").replace(/\r\n/g,`
|
||
`).replace(/\n{3,}/g,`
|
||
|
||
`).trim()}function Dt(n,e){const s=n.title.trim()!==e.title.trim(),i=xe(n.tags)!==xe(e.tags),o=H(n.content)!==H(e.content);return{titleChanged:s,tagsChanged:i,contentChanged:o,hasChanges:s||i||o}}function xe(n){return n.split(/[,,]/).map(e=>e.trim()).filter(Boolean).join(",")}function Wt(n,e){return Tt(n||"",e||"")}function Mt(n,e){return Pt(n||"",e||"")}function $t(n){let e=0,s=0;for(const i of n){const o=i.value.split(`
|
||
`).filter(a=>a.length>0);i.added?e+=o.length:i.removed&&(s+=o.length)}return{added:e,removed:s}}function ge({before:n,after:e}){const s=Mt(n,e);return n===e?t.jsx("span",{className:"revision-diff-unchanged",children:n||"(空)"}):t.jsx("span",{className:"revision-diff-inline",children:s.map((i,o)=>i.added?t.jsx("ins",{children:i.value},o):i.removed?t.jsx("del",{children:i.value},o):t.jsx("span",{children:i.value},o))})}function Rt({before:n,after:e}){const s=Wt(n,e),{added:i,removed:o}=$t(s);return n===e?t.jsx("p",{className:"revision-diff-unchanged",children:"正文无变化"}):t.jsxs("div",{className:"revision-diff-lines",children:[t.jsxs("div",{className:"revision-diff-stats",children:[o>0&&t.jsxs("span",{className:"revision-diff-stat revision-diff-stat--del",children:["删除 ",o," 行"]}),i>0&&t.jsxs("span",{className:"revision-diff-stat revision-diff-stat--add",children:["新增 ",i," 行"]})]}),t.jsx("pre",{className:"revision-diff-pre",children:s.map((a,l)=>{const r=a.value.split(`
|
||
`);return r.map((d,f)=>{if(f===r.length-1&&d==="")return null;const h=a.added?"revision-diff-line revision-diff-line--add":a.removed?"revision-diff-line revision-diff-line--del":"revision-diff-line revision-diff-line--same",w=a.added?"+":a.removed?"−":" ";return t.jsxs("div",{className:h,children:[t.jsx("span",{className:"revision-diff-gutter","aria-hidden":"true",children:w}),t.jsx("span",{className:"revision-diff-text",children:d||" "})]},`${l}-${f}`)})})})]})}function pe({summary:n}){return n.hasChanges?t.jsxs(t.Fragment,{children:[n.titleChanged&&t.jsx("span",{className:"revision-badge",children:"标题"}),n.contentChanged&&t.jsx("span",{className:"revision-badge",children:"正文"}),n.tagsChanged&&t.jsx("span",{className:"revision-badge",children:"标签"})]}):t.jsx("span",{className:"revision-badge revision-badge--none",children:"无变更"})}function At({postId:n,currentPost:e,open:s,onClose:i,isLoggedIn:o}){var x;const[a,l]=m.useState([]),[r,d]=m.useState(null),[f,h]=m.useState(!1),[w,C]=m.useState("diff"),[v,b]=m.useState(!1);m.useEffect(()=>{if(!s){d(null),C("diff"),b(!1);return}h(!0),z.postRevisions(n).then(u=>{const j=u.revisions??[];l(j),j.length>0&&d(j[0].id)}).catch(u=>E.error(u instanceof Error?u.message:"加载历史失败")).finally(()=>h(!1))},[s,n]),m.useEffect(()=>{if(!s)return;const u=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=u}},[s]);const S=m.useMemo(()=>a.map((u,j)=>{const y=j===0?e:{title:a[j-1].title,content:a[j-1].content,tags:a[j-1].tags},P={title:u.title,content:u.content,tags:u.tags};return{rev:u,after:y,summary:Dt(P,y),index:a.length-j}}),[a,e]),p=S.find(u=>u.rev.id===r)??null;if(!s)return null;const k=p?{title:p.rev.title,content:p.rev.content,tags:p.rev.tags}:null;return t.jsx("div",{className:`post-revision-overlay${v?" post-revision-overlay--fullscreen":""}`,onClick:i,children:t.jsxs("div",{className:`post-revision-panel${v?" post-revision-panel--fullscreen":""}`,onClick:u=>u.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"编辑历史",children:[t.jsxs("header",{className:"post-revision-head",children:[t.jsxs("div",{className:"post-revision-head-left",children:[t.jsx(Ce,{size:18}),t.jsx("h3",{children:"编辑历史"}),p&&t.jsxs("span",{className:"post-revision-head-sub",children:["第 ",p.index," 次编辑"]})]}),t.jsxs("div",{className:"post-revision-head-actions",children:[t.jsx("div",{className:"post-revision-view-tabs",role:"tablist",children:[["diff",et,"变更对比"],["before",tt,"编辑前"],["after",st,"编辑后"]].map(([u,j,y])=>t.jsxs("button",{type:"button",role:"tab","aria-selected":w===u,className:`post-revision-tab${w===u?" active":""}`,onClick:()=>C(u),children:[t.jsx(j,{size:14}),t.jsx("span",{className:"post-revision-tab-label",children:y})]},u))}),t.jsx("button",{type:"button",className:"post-revision-icon-btn",onClick:()=>b(u=>!u),title:v?"退出全屏":"全屏显示","aria-label":v?"退出全屏":"全屏显示",children:v?t.jsx(nt,{size:18}):t.jsx(it,{size:18})}),t.jsx("button",{type:"button",className:"post-revision-icon-btn",onClick:i,"aria-label":"关闭",children:t.jsx(be,{size:18})})]})]}),f?t.jsx("div",{className:"post-revision-loading",children:t.jsx(Ne,{size:"lg"})}):S.length===0?t.jsx("p",{className:"post-revision-empty",children:"暂无编辑记录"}):t.jsxs("div",{className:"post-revision-body",children:[t.jsxs("aside",{className:"post-revision-sidebar",children:[t.jsx("div",{className:"post-revision-sidebar-label",children:"时间线"}),t.jsx("ul",{className:"post-revision-list",children:S.map(u=>{var j;return t.jsx("li",{children:t.jsxs("button",{type:"button",className:`post-revision-item${r===u.rev.id?" active":""}`,onClick:()=>d(u.rev.id),children:[t.jsxs("div",{className:"post-revision-item-head",children:[t.jsxs("span",{className:"post-revision-item-num",children:["#",u.index]}),t.jsx(pe,{summary:u.summary})]}),t.jsx("span",{className:"post-revision-item-title",children:u.rev.title}),t.jsxs("span",{className:"post-revision-item-meta",children:[((j=u.rev.editor)==null?void 0:j.nickname)??"未知"," · ",Y(u.rev.created_at)]})]})},u.rev.id)})}),t.jsxs("div",{className:"post-revision-current",children:[t.jsx("span",{className:"post-revision-current-label",children:"当前版本"}),t.jsx("span",{className:"post-revision-current-title",children:e.title})]})]}),t.jsx("main",{className:"post-revision-main",children:p&&k?t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"post-revision-main-head",children:[t.jsxs("div",{children:[t.jsx("span",{className:"post-revision-main-editor",children:((x=p.rev.editor)==null?void 0:x.nickname)??"未知"}),t.jsx("span",{className:"post-revision-main-time",children:Y(p.rev.created_at)})]}),t.jsx(pe,{summary:p.summary})]}),t.jsxs("div",{className:"post-revision-scroll",children:[w==="diff"&&t.jsxs("div",{className:"revision-diff-view",children:[t.jsxs("section",{className:"revision-diff-section",children:[t.jsx("h4",{children:"标题"}),t.jsx("div",{className:"revision-diff-block",children:t.jsx(ge,{before:k.title,after:p.after.title})})]}),(k.tags||p.after.tags)&&t.jsxs("section",{className:"revision-diff-section",children:[t.jsx("h4",{children:"标签"}),t.jsx("div",{className:"revision-diff-block",children:t.jsx(ge,{before:k.tags||"(无)",after:p.after.tags||"(无)"})})]}),t.jsxs("section",{className:"revision-diff-section revision-diff-section--content",children:[t.jsx("h4",{children:"正文"}),t.jsx(Rt,{before:H(k.content),after:H(p.after.content)})]})]}),w==="before"&&t.jsxs("div",{className:"revision-full-view",children:[t.jsx("h4",{children:k.title}),k.tags&&t.jsxs("p",{className:"revision-full-tags",children:["标签:",k.tags]}),t.jsx(J,{html:k.content,isLoggedIn:o})]}),w==="after"&&t.jsxs("div",{className:"revision-full-view",children:[t.jsx("h4",{children:p.after.title}),p.after.tags&&t.jsxs("p",{className:"revision-full-tags",children:["标签:",p.after.tags]}),t.jsx(J,{html:p.after.content,isLoggedIn:o})]})]})]}):t.jsx("div",{className:"post-revision-empty",children:"请从左侧选择一次编辑记录"})})]})]})})}function Ft(n,e){const{overflowY:s}=getComputedStyle(n);return s!=="auto"&&s!=="scroll"&&s!=="overlay"||n.scrollHeight<=n.clientHeight+1?!1:e<0?n.scrollTop>0:n.scrollTop+n.clientHeight<n.scrollHeight-1}function Ot(n,e,s){let i=n;for(;i&&i!==s;){if(Ft(i,e))return i;i=i.parentElement}return null}function Bt(n,e=!0){m.useEffect(()=>{if(!e)return;const s=n.current;if(!s)return;const i=s.closest(".app-shell");if(!i)return;const o=a=>{if(a.ctrlKey||a.metaKey)return;const l=a.target instanceof HTMLElement?a.target:null;if(!l)return;const r=Ot(l,a.deltaY,i);if(r&&r!==s&&s.contains(r)||r===s)return;const d=s.scrollTop;s.scrollTop+=a.deltaY,s.scrollTop!==d&&a.preventDefault()};return i.addEventListener("wheel",o,{passive:!1}),()=>i.removeEventListener("wheel",o)},[n,e])}function Kt(){var se,ne,ie,ae,oe;const{id:n}=qe(),e=Number(n),s=je(),{user:i,refresh:o}=Ue(),[a,l]=m.useState(null),[r,d]=m.useState([]),[f,h]=m.useState(!1),[w,C]=m.useState(!1),[v,b]=m.useState(null),[S,p]=m.useState(!1),[k,x]=m.useState(!0),[u,j]=m.useState(null),[y,P]=m.useState(0),[I,L]=m.useState(!1),[R,A]=m.useState(!1),[g,_]=m.useState(""),[D,F]=m.useState(!1),O=m.useRef(null),B=m.useRef(null),ze=m.useRef(null),U=m.useRef();Bt(O,!k&&!!a);const Q=m.useCallback(async()=>{const c=i?[]:Ie(),N=await z.comments(e,c);return Array.isArray(N.comments)?N.comments:[]},[e,i]),De=async()=>{if(e){x(!0);try{const[c,N]=await Promise.all([z.post(e),Q()]);l(c.post),h(c.liked),C(c.favorited),L(c.can_edit??!1),A(c.is_edited??Ve(c.post.created_at,c.post.updated_at??c.post.created_at)),_(c.edit_block_reason??""),d(N),await o()}catch(c){E.error(c instanceof Error?c.message:"加载失败")}finally{x(!1)}}};m.useEffect(()=>{b(null),De()},[e]);const We=m.useCallback(c=>{const N=document.getElementById(`floor-${c}`);N&&(N.scrollIntoView({behavior:"smooth",block:"center"}),j(c),clearTimeout(U.current),U.current=setTimeout(()=>j(null),2e3))},[]),Me=c=>{if((v==null?void 0:v.id)===c.id){b(null);return}b(c)};m.useLayoutEffect(()=>{if(!v)return;const c=document.getElementById(`reply-box-${v.id}`);c==null||c.scrollIntoView({behavior:"smooth",block:"nearest"})},[v==null?void 0:v.id]),m.useEffect(()=>()=>clearTimeout(U.current),[]);const $e=async()=>{if(!i){s("/login");return}try{const c=await z.like(e);h(c.liked),l(N=>N&&{...N,like_count:c.like_count})}catch(c){E.error(c instanceof Error?c.message:"操作失败")}},Re=async()=>{if(!i){s("/login");return}try{const c=await z.favorite(e);C(c.favorited),E.success(c.favorited?"已收藏":"已取消收藏")}catch(c){E.error(c instanceof Error?c.message:"操作失败")}},K={user:i,submitting:S,submitCount:y,onSubmit:async c=>{p(!0);try{const N=await z.addComment(e,{content:c.content,replyTo:v==null?void 0:v.id,guestNick:c.guestNick,guestEmail:c.guestEmail,guestUrl:c.guestUrl,isPrivate:c.isPrivate});i||kt(N.id),b(null),P(Ye=>Ye+1),E.success("评论成功"),d(await Q()),setTimeout(()=>We(N.floor),100)}catch(N){E.error(N instanceof Error?N.message:"评论失败")}finally{p(!1)}},onCancelReply:()=>b(null)};if(k)return t.jsx("div",{className:"post-detail-loading flex justify-center py-16",children:t.jsx(Ne,{size:"lg"})});if(!a)return t.jsxs("div",{className:"empty-state",children:[t.jsx("p",{children:"帖子不存在"}),t.jsx(T,{variant:"outline",onClick:()=>s("/"),children:"返回首页"})]});const Ae=((ne=(se=a.user)==null?void 0:se.nickname)==null?void 0:ne[0])||"?",ee=((ie=a.tags)==null?void 0:ie.split(/[,,]/).map(c=>c.trim()).filter(Boolean))??[],te=i&&(i.role==="admin"||i.id===a.user_id),Fe=(i==null?void 0:i.role)==="admin",Oe=R&&a.updated_at,Be=async()=>{if(a)try{const c=await z.adminPinPost(e,!a.pinned);l(N=>N&&{...N,pinned:c.pinned}),ft(),window.dispatchEvent(new Event("posts-refresh")),E.success(c.message)}catch(c){E.error(c instanceof Error?c.message:"操作失败")}},Ge=async()=>{if(a)try{const c=await z.adminLockPost(e,!a.edit_locked);l(N=>N&&{...N,edit_locked:c.edit_locked}),(i==null?void 0:i.role)==="admin"?L(!0):(i==null?void 0:i.id)===a.user_id&&c.edit_locked&&(L(!1),_("帖子已被管理员锁定,无法编辑")),E.success(c.message)}catch(c){E.error(c instanceof Error?c.message:"操作失败")}};return t.jsxs("div",{className:"page-wrap post-detail-page",ref:O,children:[t.jsxs("div",{className:"post-detail-header",children:[t.jsxs("div",{className:"post-detail-nav",children:[t.jsxs(T,{variant:"ghost",size:"sm",onClick:()=>s(-1),children:[t.jsx(at,{}),"返回"]}),a.board&&t.jsx(re,{variant:"green",className:"post-detail-board-tag",children:a.board.name})]}),t.jsxs("div",{className:"post-detail-head",children:[t.jsxs("h1",{className:"post-detail-title",children:[a.pinned&&t.jsx(He,{className:"mr-2",size:18}),a.title]}),t.jsxs("div",{className:"post-detail-author-row",children:[t.jsx("div",{className:"post-avatar post-avatar-lg",children:(ae=a.user)!=null&&ae.avatar?t.jsx("img",{src:a.user.avatar,alt:""}):Ae}),t.jsxs("div",{className:"post-detail-author-info",children:[t.jsx("span",{className:"post-detail-author-name",children:(oe=a.user)==null?void 0:oe.nickname}),t.jsxs("span",{className:"post-detail-meta-line",children:["发布于 ",Y(a.created_at),Oe&&t.jsxs(t.Fragment,{children:[" · 编辑于 ",Y(a.updated_at)]})," · ",a.view_count," 次浏览",a.edit_locked&&t.jsxs("span",{className:"post-detail-locked-tag",title:"管理员已锁定编辑",children:[t.jsx(ce,{size:12})," 已锁定"]})]})]})]})]}),ee.length>0&&t.jsx("div",{className:"post-detail-tags",children:ee.map(c=>t.jsx(re,{variant:"secondary",children:c},c))}),t.jsx(J,{html:a.content||"",isLoggedIn:!!i}),t.jsxs("div",{className:"post-detail-actions",children:[t.jsxs(T,{variant:f?"default":"outline",size:"sm",onClick:$e,children:[t.jsx(ot,{}),"点赞 ",a.like_count]}),t.jsxs(T,{variant:w?"default":"outline",size:"sm",onClick:Re,children:[t.jsx(lt,{}),w?"已收藏":"收藏"]}),I&&t.jsxs(T,{variant:"outline",size:"sm",onClick:()=>s(`/post/${e}/edit`),children:[t.jsx(rt,{}),"编辑"]}),te&&R&&t.jsxs(T,{variant:"outline",size:"sm",onClick:()=>F(!0),children:[t.jsx(Ce,{}),"编辑历史"]}),te&&!I&&g&&t.jsx("span",{className:"post-detail-edit-hint",title:g,children:g}),Fe&&t.jsxs(t.Fragment,{children:[t.jsxs(T,{variant:"outline",size:"sm",onClick:Be,children:[t.jsx(ct,{}),a.pinned?"取消置顶":"置顶"]}),t.jsxs(T,{variant:"outline",size:"sm",onClick:Ge,children:[t.jsx(ce,{}),a.edit_locked?"解锁编辑":"锁定编辑"]})]})]})]}),t.jsx(At,{postId:e,currentPost:{title:a.title,content:a.content??"",tags:a.tags??""},open:D,onClose:()=>F(!1),isLoggedIn:!!i}),t.jsxs("div",{className:"comment-section",ref:B,children:[t.jsxs("div",{className:"comment-section-bar",children:[t.jsx("span",{className:"comment-section-title",children:"评论区"}),t.jsxs("span",{className:"comment-section-count",children:[r.length," 条评论"]})]}),!v&&t.jsx("div",{className:"comment-box-wrap",ref:ze,children:t.jsx(de,{...K})}),t.jsx("div",{className:"comment-list-area",children:r.length===0&&!v?t.jsxs("div",{className:"comment-empty",children:[t.jsx("div",{className:"comment-empty-icon",children:"💬"}),t.jsx("p",{children:"暂无评论,来抢沙发吧"})]}):t.jsx(jt,{comments:r,highlightFloor:u,replyToId:(v==null?void 0:v.id)??null,onReply:Me,onCancelReply:()=>b(null),renderReplyBox:c=>t.jsx(de,{...K,replyTo:c,inline:!0},c.id)})})]})]})}export{Kt as default};
|