texlite 0.8.4 → 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/DESIGN.md +75 -8
- package/OPERATIONS.md +19 -2
- package/dist/client/assets/{AdminUsers-CqOmV9rq.js → AdminUsers-Dazar9cj.js} +1 -1
- package/dist/client/assets/{CitationLibraryDialog-BmWFXTW2.js → CitationLibraryDialog-VSUiSPgG.js} +1 -1
- package/dist/client/assets/Dashboard-CoE5ESfb.js +2 -0
- package/dist/client/assets/GitDialog-C9Loz4Al.js +3 -0
- package/dist/client/assets/HistoryDialog-3UNN_jhU.js +7 -0
- package/dist/client/assets/{LatexEditor-DSopAHbC.js → LatexEditor-Bq76v8sO.js} +2 -2
- package/dist/client/assets/{PdfPreview-BDkU20jP.js → PdfPreview-BKhcS-2c.js} +1 -1
- package/dist/client/assets/{ProjectIconPickerDialog-GAd8HGOx.js → ProjectIconPickerDialog-BoWMjQVm.js} +1 -1
- package/dist/client/assets/{ProjectNavigationDialogs-BTLcRY7i.js → ProjectNavigationDialogs-CZKTnxrl.js} +1 -1
- package/dist/client/assets/{ProjectSettings-BJWTkoZq.js → ProjectSettings-DiOQVJ40.js} +1 -1
- package/dist/client/assets/ProjectWorkspace-DqLwkGpT.js +27 -0
- package/dist/client/assets/SelectionHistoryDialog-D3dO83lg.js +3 -0
- package/dist/client/assets/{SystemMetricsDialog-C9P4TSQl.js → SystemMetricsDialog-0zCwLtH_.js} +1 -1
- package/dist/client/assets/{TagManagementDialog-LYbib36_.js → TagManagementDialog-C6KDwtvd.js} +1 -1
- package/dist/client/assets/{WordCountDialog-BFg0GeJy.js → WordCountDialog-TwpyAP3m.js} +1 -1
- package/dist/client/assets/check-sZxOOKtS.js +1 -0
- package/dist/client/assets/chevron-left-32SrOwa5.js +1 -0
- package/dist/client/assets/hard-drive-XHSiS_A7.js +1 -0
- package/dist/client/assets/index-BRr3Kuxh.css +1 -0
- package/dist/client/assets/index-BjzELSQ-.js +12 -0
- package/dist/client/assets/index-Cd6-F29b.js +5 -0
- package/dist/client/assets/{key-round-BaIj7Jch.js → key-round-C-CXCWnz.js} +1 -1
- package/dist/client/assets/minimize-2-BMDPVaF-.js +1 -0
- package/dist/client/assets/{minus-Beso_n-6.js → minus-BIa7-KuM.js} +1 -1
- package/dist/client/assets/{plus-BfEC7mzf.js → plus-BKDWy4Ui.js} +1 -1
- package/dist/client/assets/{refresh-cw-FZs9MtoA.js → refresh-cw-Bo7-a7-I.js} +1 -1
- package/dist/client/assets/{search-DL2UGvdj.js → search-Bz9Ob5mT.js} +1 -1
- package/dist/client/assets/{sigma-CSRwPPkk.js → sigma-DxlHf9gv.js} +1 -1
- package/dist/client/assets/spellCheck-BfZEnpwY.js +1 -0
- package/dist/client/assets/{tags-CjkWzXqX.js → tags-IMZehjzE.js} +1 -1
- package/dist/client/assets/{type-D1mmDdLM.js → type-DTWpFQNQ.js} +1 -1
- package/dist/client/assets/{user-plus-YJ3ftuL5.js → user-plus--aVpd8K6.js} +1 -1
- package/dist/client/assets/x-Tbo3aA0J.js +1 -0
- package/dist/client/index.html +2 -2
- package/dist/server/app.js +45 -4
- package/dist/server/cli.js +4 -0
- package/dist/server/collaboration.js +219 -7
- package/dist/server/commentMentions.js +139 -0
- package/dist/server/config.js +9 -2
- package/dist/server/db.js +59 -0
- package/dist/server/editHistory.js +411 -0
- package/dist/server/editHistoryRetry.js +90 -0
- package/dist/server/harper.js +205 -8
- package/dist/server/history.js +189 -23
- package/dist/server/historyRetention.js +37 -0
- package/dist/server/i18n.js +12 -0
- package/dist/server/index.js +6 -0
- package/dist/server/latexSpellMask.js +197 -43
- package/dist/server/logRotation.js +82 -0
- package/dist/server/pm2.js +7 -0
- package/dist/server/routes/comments.js +120 -17
- package/dist/server/routes/projectHistory.js +106 -8
- package/dist/server/routes/projectShared.js +2 -1
- package/dist/server/routes/projects.js +39 -5
- package/package.json +1 -1
- package/texlite.config.example.json +4 -1
- package/dist/client/assets/Dashboard-BfN2bCgZ.js +0 -2
- package/dist/client/assets/GitDialog-BCfSHuMB.js +0 -3
- package/dist/client/assets/HistoryDialog-BtQhyTr9.js +0 -5
- package/dist/client/assets/ProjectWorkspace-WLYA7BSv.js +0 -27
- package/dist/client/assets/check-bnimQPun.js +0 -1
- package/dist/client/assets/chevron-left-DNM7mhNi.js +0 -1
- package/dist/client/assets/hard-drive-CF3WDfkG.js +0 -1
- package/dist/client/assets/index-CzVDvC5h.js +0 -14
- package/dist/client/assets/index-RjUuWk8F.css +0 -1
- package/dist/client/assets/spellCheck-L9gD2d1M.js +0 -1
- package/dist/client/assets/x-BDSEosRE.js +0 -1
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{c as F}from"./index-BjzELSQ-.js";import{g as B}from"./react-BVUjbTTn.js";const z=F("Clock3",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["polyline",{points:"12 6 12 12 16.5 12",key:"1aq6pp"}]]);var R={exports:{}},C;function L(){return C||(C=1,(function(T){var f=function(){this.Diff_Timeout=1,this.Diff_EditCost=4,this.Match_Threshold=.5,this.Match_Distance=1e3,this.Patch_DeleteThreshold=.5,this.Patch_Margin=4,this.Match_MaxBits=32},p=-1,M=1,_=0;f.Diff=function(r,n){return[r,n]},f.prototype.diff_main=function(r,n,e,t){typeof t>"u"&&(this.Diff_Timeout<=0?t=Number.MAX_VALUE:t=new Date().getTime()+this.Diff_Timeout*1e3);var a=t;if(r==null||n==null)throw new Error("Null input. (diff_main)");if(r==n)return r?[new f.Diff(_,r)]:[];typeof e>"u"&&(e=!0);var i=e,s=this.diff_commonPrefix(r,n),h=r.substring(0,s);r=r.substring(s),n=n.substring(s),s=this.diff_commonSuffix(r,n);var l=r.substring(r.length-s);r=r.substring(0,r.length-s),n=n.substring(0,n.length-s);var g=this.diff_compute_(r,n,i,a);return h&&g.unshift(new f.Diff(_,h)),l&&g.push(new f.Diff(_,l)),this.diff_cleanupMerge(g),g},f.prototype.diff_compute_=function(r,n,e,t){var a;if(!r)return[new f.Diff(M,n)];if(!n)return[new f.Diff(p,r)];var i=r.length>n.length?r:n,s=r.length>n.length?n:r,h=i.indexOf(s);if(h!=-1)return a=[new f.Diff(M,i.substring(0,h)),new f.Diff(_,s),new f.Diff(M,i.substring(h+s.length))],r.length>n.length&&(a[0][0]=a[2][0]=p),a;if(s.length==1)return[new f.Diff(p,r),new f.Diff(M,n)];var l=this.diff_halfMatch_(r,n);if(l){var g=l[0],v=l[1],u=l[2],o=l[3],c=l[4],b=this.diff_main(g,u,e,t),w=this.diff_main(v,o,e,t);return b.concat([new f.Diff(_,c)],w)}return e&&r.length>100&&n.length>100?this.diff_lineMode_(r,n,t):this.diff_bisect_(r,n,t)},f.prototype.diff_lineMode_=function(r,n,e){var t=this.diff_linesToChars_(r,n);r=t.chars1,n=t.chars2;var a=t.lineArray,i=this.diff_main(r,n,!1,e);this.diff_charsToLines_(i,a),this.diff_cleanupSemantic(i),i.push(new f.Diff(_,""));for(var s=0,h=0,l=0,g="",v="";s<i.length;){switch(i[s][0]){case M:l++,v+=i[s][1];break;case p:h++,g+=i[s][1];break;case _:if(h>=1&&l>=1){i.splice(s-h-l,h+l),s=s-h-l;for(var u=this.diff_main(g,v,!1,e),o=u.length-1;o>=0;o--)i.splice(s,0,u[o]);s=s+u.length}l=0,h=0,g="",v="";break}s++}return i.pop(),i},f.prototype.diff_bisect_=function(r,n,e){for(var t=r.length,a=n.length,i=Math.ceil((t+a)/2),s=i,h=2*i,l=new Array(h),g=new Array(h),v=0;v<h;v++)l[v]=-1,g[v]=-1;l[s+1]=0,g[s+1]=0;for(var u=t-a,o=u%2!=0,c=0,b=0,w=0,I=0,m=0;m<i&&!(new Date().getTime()>e);m++){for(var D=-m+c;D<=m-b;D+=2){var A=s+D,k;D==-m||D!=m&&l[A-1]<l[A+1]?k=l[A+1]:k=l[A-1]+1;for(var y=k-D;k<t&&y<a&&r.charAt(k)==n.charAt(y);)k++,y++;if(l[A]=k,k>t)b+=2;else if(y>a)c+=2;else if(o){var E=s+u-D;if(E>=0&&E<h&&g[E]!=-1){var P=t-g[E];if(k>=P)return this.diff_bisectSplit_(r,n,k,y,e)}}}for(var d=-m+w;d<=m-I;d+=2){var E=s+d,P;d==-m||d!=m&&g[E-1]<g[E+1]?P=g[E+1]:P=g[E-1]+1;for(var S=P-d;P<t&&S<a&&r.charAt(t-P-1)==n.charAt(a-S-1);)P++,S++;if(g[E]=P,P>t)I+=2;else if(S>a)w+=2;else if(!o){var A=s+u-d;if(A>=0&&A<h&&l[A]!=-1){var k=l[A],y=s+k-A;if(P=t-P,k>=P)return this.diff_bisectSplit_(r,n,k,y,e)}}}}return[new f.Diff(p,r),new f.Diff(M,n)]},f.prototype.diff_bisectSplit_=function(r,n,e,t,a){var i=r.substring(0,e),s=n.substring(0,t),h=r.substring(e),l=n.substring(t),g=this.diff_main(i,s,!1,a),v=this.diff_main(h,l,!1,a);return g.concat(v)},f.prototype.diff_linesToChars_=function(r,n){var e=[],t={};e[0]="";function a(l){for(var g="",v=0,u=-1,o=e.length;u<l.length-1;){u=l.indexOf(`
|
|
2
|
+
`,v),u==-1&&(u=l.length-1);var c=l.substring(v,u+1);(t.hasOwnProperty?t.hasOwnProperty(c):t[c]!==void 0)?g+=String.fromCharCode(t[c]):(o==i&&(c=l.substring(v),u=l.length),g+=String.fromCharCode(o),t[c]=o,e[o++]=c),v=u+1}return g}var i=4e4,s=a(r);i=65535;var h=a(n);return{chars1:s,chars2:h,lineArray:e}},f.prototype.diff_charsToLines_=function(r,n){for(var e=0;e<r.length;e++){for(var t=r[e][1],a=[],i=0;i<t.length;i++)a[i]=n[t.charCodeAt(i)];r[e][1]=a.join("")}},f.prototype.diff_commonPrefix=function(r,n){if(!r||!n||r.charAt(0)!=n.charAt(0))return 0;for(var e=0,t=Math.min(r.length,n.length),a=t,i=0;e<a;)r.substring(i,a)==n.substring(i,a)?(e=a,i=e):t=a,a=Math.floor((t-e)/2+e);return a},f.prototype.diff_commonSuffix=function(r,n){if(!r||!n||r.charAt(r.length-1)!=n.charAt(n.length-1))return 0;for(var e=0,t=Math.min(r.length,n.length),a=t,i=0;e<a;)r.substring(r.length-a,r.length-i)==n.substring(n.length-a,n.length-i)?(e=a,i=e):t=a,a=Math.floor((t-e)/2+e);return a},f.prototype.diff_commonOverlap_=function(r,n){var e=r.length,t=n.length;if(e==0||t==0)return 0;e>t?r=r.substring(e-t):e<t&&(n=n.substring(0,e));var a=Math.min(e,t);if(r==n)return a;for(var i=0,s=1;;){var h=r.substring(a-s),l=n.indexOf(h);if(l==-1)return i;s+=l,(l==0||r.substring(a-s)==n.substring(0,s))&&(i=s,s++)}},f.prototype.diff_halfMatch_=function(r,n){if(this.Diff_Timeout<=0)return null;var e=r.length>n.length?r:n,t=r.length>n.length?n:r;if(e.length<4||t.length*2<e.length)return null;var a=this;function i(b,w,I){for(var m=b.substring(I,I+Math.floor(b.length/4)),D=-1,A="",k,y,E,P;(D=w.indexOf(m,D+1))!=-1;){var d=a.diff_commonPrefix(b.substring(I),w.substring(D)),S=a.diff_commonSuffix(b.substring(0,I),w.substring(0,D));A.length<S+d&&(A=w.substring(D-S,D)+w.substring(D,D+d),k=b.substring(0,I-S),y=b.substring(I+d),E=w.substring(0,D-S),P=w.substring(D+d))}return A.length*2>=b.length?[k,y,E,P,A]:null}var s=i(e,t,Math.ceil(e.length/4)),h=i(e,t,Math.ceil(e.length/2)),l;if(!s&&!h)return null;h?s?l=s[4].length>h[4].length?s:h:l=h:l=s;var g,v,u,o;r.length>n.length?(g=l[0],v=l[1],u=l[2],o=l[3]):(u=l[0],o=l[1],g=l[2],v=l[3]);var c=l[4];return[g,v,u,o,c]},f.prototype.diff_cleanupSemantic=function(r){for(var n=!1,e=[],t=0,a=null,i=0,s=0,h=0,l=0,g=0;i<r.length;)r[i][0]==_?(e[t++]=i,s=l,h=g,l=0,g=0,a=r[i][1]):(r[i][0]==M?l+=r[i][1].length:g+=r[i][1].length,a&&a.length<=Math.max(s,h)&&a.length<=Math.max(l,g)&&(r.splice(e[t-1],0,new f.Diff(p,a)),r[e[t-1]+1][0]=M,t--,t--,i=t>0?e[t-1]:-1,s=0,h=0,l=0,g=0,a=null,n=!0)),i++;for(n&&this.diff_cleanupMerge(r),this.diff_cleanupSemanticLossless(r),i=1;i<r.length;){if(r[i-1][0]==p&&r[i][0]==M){var v=r[i-1][1],u=r[i][1],o=this.diff_commonOverlap_(v,u),c=this.diff_commonOverlap_(u,v);o>=c?(o>=v.length/2||o>=u.length/2)&&(r.splice(i,0,new f.Diff(_,u.substring(0,o))),r[i-1][1]=v.substring(0,v.length-o),r[i+1][1]=u.substring(o),i++):(c>=v.length/2||c>=u.length/2)&&(r.splice(i,0,new f.Diff(_,v.substring(0,c))),r[i-1][0]=M,r[i-1][1]=u.substring(0,u.length-c),r[i+1][0]=p,r[i+1][1]=v.substring(c),i++),i++}i++}},f.prototype.diff_cleanupSemanticLossless=function(r){function n(c,b){if(!c||!b)return 6;var w=c.charAt(c.length-1),I=b.charAt(0),m=w.match(f.nonAlphaNumericRegex_),D=I.match(f.nonAlphaNumericRegex_),A=m&&w.match(f.whitespaceRegex_),k=D&&I.match(f.whitespaceRegex_),y=A&&w.match(f.linebreakRegex_),E=k&&I.match(f.linebreakRegex_),P=y&&c.match(f.blanklineEndRegex_),d=E&&b.match(f.blanklineStartRegex_);return P||d?5:y||E?4:m&&!A&&k?3:A||k?2:m||D?1:0}for(var e=1;e<r.length-1;){if(r[e-1][0]==_&&r[e+1][0]==_){var t=r[e-1][1],a=r[e][1],i=r[e+1][1],s=this.diff_commonSuffix(t,a);if(s){var h=a.substring(a.length-s);t=t.substring(0,t.length-s),a=h+a.substring(0,a.length-s),i=h+i}for(var l=t,g=a,v=i,u=n(t,a)+n(a,i);a.charAt(0)===i.charAt(0);){t+=a.charAt(0),a=a.substring(1)+i.charAt(0),i=i.substring(1);var o=n(t,a)+n(a,i);o>=u&&(u=o,l=t,g=a,v=i)}r[e-1][1]!=l&&(l?r[e-1][1]=l:(r.splice(e-1,1),e--),r[e][1]=g,v?r[e+1][1]=v:(r.splice(e+1,1),e--))}e++}},f.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/,f.whitespaceRegex_=/\s/,f.linebreakRegex_=/[\r\n]/,f.blanklineEndRegex_=/\n\r?\n$/,f.blanklineStartRegex_=/^\r?\n\r?\n/,f.prototype.diff_cleanupEfficiency=function(r){for(var n=!1,e=[],t=0,a=null,i=0,s=!1,h=!1,l=!1,g=!1;i<r.length;)r[i][0]==_?(r[i][1].length<this.Diff_EditCost&&(l||g)?(e[t++]=i,s=l,h=g,a=r[i][1]):(t=0,a=null),l=g=!1):(r[i][0]==p?g=!0:l=!0,a&&(s&&h&&l&&g||a.length<this.Diff_EditCost/2&&s+h+l+g==3)&&(r.splice(e[t-1],0,new f.Diff(p,a)),r[e[t-1]+1][0]=M,t--,a=null,s&&h?(l=g=!0,t=0):(t--,i=t>0?e[t-1]:-1,l=g=!1),n=!0)),i++;n&&this.diff_cleanupMerge(r)},f.prototype.diff_cleanupMerge=function(r){r.push(new f.Diff(_,""));for(var n=0,e=0,t=0,a="",i="",s;n<r.length;)switch(r[n][0]){case M:t++,i+=r[n][1],n++;break;case p:e++,a+=r[n][1],n++;break;case _:e+t>1?(e!==0&&t!==0&&(s=this.diff_commonPrefix(i,a),s!==0&&(n-e-t>0&&r[n-e-t-1][0]==_?r[n-e-t-1][1]+=i.substring(0,s):(r.splice(0,0,new f.Diff(_,i.substring(0,s))),n++),i=i.substring(s),a=a.substring(s)),s=this.diff_commonSuffix(i,a),s!==0&&(r[n][1]=i.substring(i.length-s)+r[n][1],i=i.substring(0,i.length-s),a=a.substring(0,a.length-s))),n-=e+t,r.splice(n,e+t),a.length&&(r.splice(n,0,new f.Diff(p,a)),n++),i.length&&(r.splice(n,0,new f.Diff(M,i)),n++),n++):n!==0&&r[n-1][0]==_?(r[n-1][1]+=r[n][1],r.splice(n,1)):n++,t=0,e=0,a="",i="";break}r[r.length-1][1]===""&&r.pop();var h=!1;for(n=1;n<r.length-1;)r[n-1][0]==_&&r[n+1][0]==_&&(r[n][1].substring(r[n][1].length-r[n-1][1].length)==r[n-1][1]?(r[n][1]=r[n-1][1]+r[n][1].substring(0,r[n][1].length-r[n-1][1].length),r[n+1][1]=r[n-1][1]+r[n+1][1],r.splice(n-1,1),h=!0):r[n][1].substring(0,r[n+1][1].length)==r[n+1][1]&&(r[n-1][1]+=r[n+1][1],r[n][1]=r[n][1].substring(r[n+1][1].length)+r[n+1][1],r.splice(n+1,1),h=!0)),n++;h&&this.diff_cleanupMerge(r)},f.prototype.diff_xIndex=function(r,n){var e=0,t=0,a=0,i=0,s;for(s=0;s<r.length&&(r[s][0]!==M&&(e+=r[s][1].length),r[s][0]!==p&&(t+=r[s][1].length),!(e>n));s++)a=e,i=t;return r.length!=s&&r[s][0]===p?i:i+(n-a)},f.prototype.diff_prettyHtml=function(r){for(var n=[],e=/&/g,t=/</g,a=/>/g,i=/\n/g,s=0;s<r.length;s++){var h=r[s][0],l=r[s][1],g=l.replace(e,"&").replace(t,"<").replace(a,">").replace(i,"¶<br>");switch(h){case M:n[s]='<ins style="background:#e6ffe6;">'+g+"</ins>";break;case p:n[s]='<del style="background:#ffe6e6;">'+g+"</del>";break;case _:n[s]="<span>"+g+"</span>";break}}return n.join("")},f.prototype.diff_text1=function(r){for(var n=[],e=0;e<r.length;e++)r[e][0]!==M&&(n[e]=r[e][1]);return n.join("")},f.prototype.diff_text2=function(r){for(var n=[],e=0;e<r.length;e++)r[e][0]!==p&&(n[e]=r[e][1]);return n.join("")},f.prototype.diff_levenshtein=function(r){for(var n=0,e=0,t=0,a=0;a<r.length;a++){var i=r[a][0],s=r[a][1];switch(i){case M:e+=s.length;break;case p:t+=s.length;break;case _:n+=Math.max(e,t),e=0,t=0;break}}return n+=Math.max(e,t),n},f.prototype.diff_toDelta=function(r){for(var n=[],e=0;e<r.length;e++)switch(r[e][0]){case M:n[e]="+"+encodeURI(r[e][1]);break;case p:n[e]="-"+r[e][1].length;break;case _:n[e]="="+r[e][1].length;break}return n.join(" ").replace(/%20/g," ")},f.prototype.diff_fromDelta=function(r,n){for(var e=[],t=0,a=0,i=n.split(/\t/g),s=0;s<i.length;s++){var h=i[s].substring(1);switch(i[s].charAt(0)){case"+":try{e[t++]=new f.Diff(M,decodeURI(h))}catch{throw new Error("Illegal escape in diff_fromDelta: "+h)}break;case"-":case"=":var l=parseInt(h,10);if(isNaN(l)||l<0)throw new Error("Invalid number in diff_fromDelta: "+h);var g=r.substring(a,a+=l);i[s].charAt(0)=="="?e[t++]=new f.Diff(_,g):e[t++]=new f.Diff(p,g);break;default:if(i[s])throw new Error("Invalid diff operation in diff_fromDelta: "+i[s])}}if(a!=r.length)throw new Error("Delta length ("+a+") does not equal source text length ("+r.length+").");return e},f.prototype.match_main=function(r,n,e){if(r==null||n==null||e==null)throw new Error("Null input. (match_main)");return e=Math.max(0,Math.min(e,r.length)),r==n?0:r.length?r.substring(e,e+n.length)==n?e:this.match_bitap_(r,n,e):-1},f.prototype.match_bitap_=function(r,n,e){if(n.length>this.Match_MaxBits)throw new Error("Pattern too long for this browser.");var t=this.match_alphabet_(n),a=this;function i(k,y){var E=k/n.length,P=Math.abs(e-y);return a.Match_Distance?E+P/a.Match_Distance:P?1:E}var s=this.Match_Threshold,h=r.indexOf(n,e);h!=-1&&(s=Math.min(i(0,h),s),h=r.lastIndexOf(n,e+n.length),h!=-1&&(s=Math.min(i(0,h),s)));var l=1<<n.length-1;h=-1;for(var g,v,u=n.length+r.length,o,c=0;c<n.length;c++){for(g=0,v=u;g<v;)i(c,e+v)<=s?g=v:u=v,v=Math.floor((u-g)/2+g);u=v;var b=Math.max(1,e-v+1),w=Math.min(e+v,r.length)+n.length,I=Array(w+2);I[w+1]=(1<<c)-1;for(var m=w;m>=b;m--){var D=t[r.charAt(m-1)];if(c===0?I[m]=(I[m+1]<<1|1)&D:I[m]=(I[m+1]<<1|1)&D|((o[m+1]|o[m])<<1|1)|o[m+1],I[m]&l){var A=i(c,m-1);if(A<=s)if(s=A,h=m-1,h>e)b=Math.max(1,2*e-h);else break}}if(i(c+1,e)>s)break;o=I}return h},f.prototype.match_alphabet_=function(r){for(var n={},e=0;e<r.length;e++)n[r.charAt(e)]=0;for(var e=0;e<r.length;e++)n[r.charAt(e)]|=1<<r.length-e-1;return n},f.prototype.patch_addContext_=function(r,n){if(n.length!=0){if(r.start2===null)throw Error("patch not initialized");for(var e=n.substring(r.start2,r.start2+r.length1),t=0;n.indexOf(e)!=n.lastIndexOf(e)&&e.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)t+=this.Patch_Margin,e=n.substring(r.start2-t,r.start2+r.length1+t);t+=this.Patch_Margin;var a=n.substring(r.start2-t,r.start2);a&&r.diffs.unshift(new f.Diff(_,a));var i=n.substring(r.start2+r.length1,r.start2+r.length1+t);i&&r.diffs.push(new f.Diff(_,i)),r.start1-=a.length,r.start2-=a.length,r.length1+=a.length+i.length,r.length2+=a.length+i.length}},f.prototype.patch_make=function(r,n,e){var t,a;if(typeof r=="string"&&typeof n=="string"&&typeof e>"u")t=r,a=this.diff_main(t,n,!0),a.length>2&&(this.diff_cleanupSemantic(a),this.diff_cleanupEfficiency(a));else if(r&&typeof r=="object"&&typeof n>"u"&&typeof e>"u")a=r,t=this.diff_text1(a);else if(typeof r=="string"&&n&&typeof n=="object"&&typeof e>"u")t=r,a=n;else if(typeof r=="string"&&typeof n=="string"&&e&&typeof e=="object")t=r,a=e;else throw new Error("Unknown call format to patch_make.");if(a.length===0)return[];for(var i=[],s=new f.patch_obj,h=0,l=0,g=0,v=t,u=t,o=0;o<a.length;o++){var c=a[o][0],b=a[o][1];switch(!h&&c!==_&&(s.start1=l,s.start2=g),c){case M:s.diffs[h++]=a[o],s.length2+=b.length,u=u.substring(0,g)+b+u.substring(g);break;case p:s.length1+=b.length,s.diffs[h++]=a[o],u=u.substring(0,g)+u.substring(g+b.length);break;case _:b.length<=2*this.Patch_Margin&&h&&a.length!=o+1?(s.diffs[h++]=a[o],s.length1+=b.length,s.length2+=b.length):b.length>=2*this.Patch_Margin&&h&&(this.patch_addContext_(s,v),i.push(s),s=new f.patch_obj,h=0,v=u,l=g);break}c!==M&&(l+=b.length),c!==p&&(g+=b.length)}return h&&(this.patch_addContext_(s,v),i.push(s)),i},f.prototype.patch_deepCopy=function(r){for(var n=[],e=0;e<r.length;e++){var t=r[e],a=new f.patch_obj;a.diffs=[];for(var i=0;i<t.diffs.length;i++)a.diffs[i]=new f.Diff(t.diffs[i][0],t.diffs[i][1]);a.start1=t.start1,a.start2=t.start2,a.length1=t.length1,a.length2=t.length2,n[e]=a}return n},f.prototype.patch_apply=function(r,n){if(r.length==0)return[n,[]];r=this.patch_deepCopy(r);var e=this.patch_addPadding(r);n=e+n+e,this.patch_splitMax(r);for(var t=0,a=[],i=0;i<r.length;i++){var s=r[i].start2+t,h=this.diff_text1(r[i].diffs),l,g=-1;if(h.length>this.Match_MaxBits?(l=this.match_main(n,h.substring(0,this.Match_MaxBits),s),l!=-1&&(g=this.match_main(n,h.substring(h.length-this.Match_MaxBits),s+h.length-this.Match_MaxBits),(g==-1||l>=g)&&(l=-1))):l=this.match_main(n,h,s),l==-1)a[i]=!1,t-=r[i].length2-r[i].length1;else{a[i]=!0,t=l-s;var v;if(g==-1?v=n.substring(l,l+h.length):v=n.substring(l,g+this.Match_MaxBits),h==v)n=n.substring(0,l)+this.diff_text2(r[i].diffs)+n.substring(l+h.length);else{var u=this.diff_main(h,v,!1);if(h.length>this.Match_MaxBits&&this.diff_levenshtein(u)/h.length>this.Patch_DeleteThreshold)a[i]=!1;else{this.diff_cleanupSemanticLossless(u);for(var o=0,c,b=0;b<r[i].diffs.length;b++){var w=r[i].diffs[b];w[0]!==_&&(c=this.diff_xIndex(u,o)),w[0]===M?n=n.substring(0,l+c)+w[1]+n.substring(l+c):w[0]===p&&(n=n.substring(0,l+c)+n.substring(l+this.diff_xIndex(u,o+w[1].length))),w[0]!==p&&(o+=w[1].length)}}}}}return n=n.substring(e.length,n.length-e.length),[n,a]},f.prototype.patch_addPadding=function(r){for(var n=this.Patch_Margin,e="",t=1;t<=n;t++)e+=String.fromCharCode(t);for(var t=0;t<r.length;t++)r[t].start1+=n,r[t].start2+=n;var a=r[0],i=a.diffs;if(i.length==0||i[0][0]!=_)i.unshift(new f.Diff(_,e)),a.start1-=n,a.start2-=n,a.length1+=n,a.length2+=n;else if(n>i[0][1].length){var s=n-i[0][1].length;i[0][1]=e.substring(i[0][1].length)+i[0][1],a.start1-=s,a.start2-=s,a.length1+=s,a.length2+=s}if(a=r[r.length-1],i=a.diffs,i.length==0||i[i.length-1][0]!=_)i.push(new f.Diff(_,e)),a.length1+=n,a.length2+=n;else if(n>i[i.length-1][1].length){var s=n-i[i.length-1][1].length;i[i.length-1][1]+=e.substring(0,s),a.length1+=s,a.length2+=s}return e},f.prototype.patch_splitMax=function(r){for(var n=this.Match_MaxBits,e=0;e<r.length;e++)if(!(r[e].length1<=n)){var t=r[e];r.splice(e--,1);for(var a=t.start1,i=t.start2,s="";t.diffs.length!==0;){var h=new f.patch_obj,l=!0;for(h.start1=a-s.length,h.start2=i-s.length,s!==""&&(h.length1=h.length2=s.length,h.diffs.push(new f.Diff(_,s)));t.diffs.length!==0&&h.length1<n-this.Patch_Margin;){var g=t.diffs[0][0],v=t.diffs[0][1];g===M?(h.length2+=v.length,i+=v.length,h.diffs.push(t.diffs.shift()),l=!1):g===p&&h.diffs.length==1&&h.diffs[0][0]==_&&v.length>2*n?(h.length1+=v.length,a+=v.length,l=!1,h.diffs.push(new f.Diff(g,v)),t.diffs.shift()):(v=v.substring(0,n-h.length1-this.Patch_Margin),h.length1+=v.length,a+=v.length,g===_?(h.length2+=v.length,i+=v.length):l=!1,h.diffs.push(new f.Diff(g,v)),v==t.diffs[0][1]?t.diffs.shift():t.diffs[0][1]=t.diffs[0][1].substring(v.length))}s=this.diff_text2(h.diffs),s=s.substring(s.length-this.Patch_Margin);var u=this.diff_text1(t.diffs).substring(0,this.Patch_Margin);u!==""&&(h.length1+=u.length,h.length2+=u.length,h.diffs.length!==0&&h.diffs[h.diffs.length-1][0]===_?h.diffs[h.diffs.length-1][1]+=u:h.diffs.push(new f.Diff(_,u))),l||r.splice(++e,0,h)}}},f.prototype.patch_toText=function(r){for(var n=[],e=0;e<r.length;e++)n[e]=r[e];return n.join("")},f.prototype.patch_fromText=function(r){var n=[];if(!r)return n;for(var e=r.split(`
|
|
3
|
+
`),t=0,a=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;t<e.length;){var i=e[t].match(a);if(!i)throw new Error("Invalid patch string: "+e[t]);var s=new f.patch_obj;for(n.push(s),s.start1=parseInt(i[1],10),i[2]===""?(s.start1--,s.length1=1):i[2]=="0"?s.length1=0:(s.start1--,s.length1=parseInt(i[2],10)),s.start2=parseInt(i[3],10),i[4]===""?(s.start2--,s.length2=1):i[4]=="0"?s.length2=0:(s.start2--,s.length2=parseInt(i[4],10)),t++;t<e.length;){var h=e[t].charAt(0);try{var l=decodeURI(e[t].substring(1))}catch{throw new Error("Illegal escape in patch_fromText: "+l)}if(h=="-")s.diffs.push(new f.Diff(p,l));else if(h=="+")s.diffs.push(new f.Diff(M,l));else if(h==" ")s.diffs.push(new f.Diff(_,l));else{if(h=="@")break;if(h!=="")throw new Error('Invalid patch mode "'+h+'" in: '+l)}t++}}return n},f.patch_obj=function(){this.diffs=[],this.start1=null,this.start2=null,this.length1=0,this.length2=0},f.patch_obj.prototype.toString=function(){var r,n;this.length1===0?r=this.start1+",0":this.length1==1?r=this.start1+1:r=this.start1+1+","+this.length1,this.length2===0?n=this.start2+",0":this.length2==1?n=this.start2+1:n=this.start2+1+","+this.length2;for(var e=["@@ -"+r+" +"+n+` @@
|
|
4
|
+
`],t,a=0;a<this.diffs.length;a++){switch(this.diffs[a][0]){case M:t="+";break;case p:t="-";break;case _:t=" ";break}e[a+1]=t+encodeURI(this.diffs[a][1])+`
|
|
5
|
+
`}return e.join("").replace(/%20/g," ")},T.exports=f,T.exports.diff_match_patch=f,T.exports.DIFF_DELETE=p,T.exports.DIFF_INSERT=M,T.exports.DIFF_EQUAL=_})(R)),R.exports}var N=L();const H=B(N);export{z as C,H as d};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as a}from"./index-
|
|
1
|
+
import{c as a}from"./index-BjzELSQ-.js";const c=a("KeyRound",[["path",{d:"M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z",key:"1s6t7t"}],["circle",{cx:"16.5",cy:"7.5",r:".5",fill:"currentColor",key:"w0ekpg"}]]);export{c as K};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as e}from"./index-BjzELSQ-.js";const n=e("Minimize2",[["polyline",{points:"4 14 10 14 10 20",key:"11kfnr"}],["polyline",{points:"20 10 14 10 14 4",key:"rlmsce"}],["line",{x1:"14",x2:"21",y1:"10",y2:"3",key:"o5lafz"}],["line",{x1:"3",x2:"10",y1:"21",y2:"14",key:"1atl0r"}]]);export{n as M};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as e}from"./index-
|
|
1
|
+
import{c as e}from"./index-BjzELSQ-.js";const n=e("Maximize2",[["polyline",{points:"15 3 21 3 21 9",key:"mznyad"}],["polyline",{points:"9 21 3 21 3 15",key:"1avn1i"}],["line",{x1:"21",x2:"14",y1:"3",y2:"10",key:"ota7mn"}],["line",{x1:"3",x2:"10",y1:"21",y2:"14",key:"1atl0r"}]]);const a=e("Minus",[["path",{d:"M5 12h14",key:"1ays0h"}]]);export{a as M,n as a};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as e}from"./index-
|
|
1
|
+
import{c as e}from"./index-BjzELSQ-.js";const a=e("Plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);export{a as P};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as e}from"./index-
|
|
1
|
+
import{c as e}from"./index-BjzELSQ-.js";const t=e("RefreshCw",[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]]);export{t as R};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c}from"./index-
|
|
1
|
+
import{c}from"./index-BjzELSQ-.js";const r=c("Search",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]]);export{r as S};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as a}from"./index-
|
|
1
|
+
import{c as a}from"./index-BjzELSQ-.js";const e=a("Sigma",[["path",{d:"M18 7V5a1 1 0 0 0-1-1H6.5a.5.5 0 0 0-.4.8l4.5 6a2 2 0 0 1 0 2.4l-4.5 6a.5.5 0 0 0 .4.8H17a1 1 0 0 0 1-1v-2",key:"wuwx1p"}]]);export{e as S};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{f as w,A as L}from"./index-BjzELSQ-.js";import{c as y}from"./ProjectWorkspace-DqLwkGpT.js";import"./react-BVUjbTTn.js";import"./check-sZxOOKtS.js";import"./x-Tbo3aA0J.js";import"./search-Bz9Ob5mT.js";import"./user-plus--aVpd8K6.js";import"./refresh-cw-Bo7-a7-I.js";const S=5,f=new Map,k=12,C=y();let x=0;class p extends Error{constructor(){super("A newer writing check replaced this request."),this.name="HarperLintSupersededError"}}function D(e){return e instanceof p}function E(e){const t=[0];let n=0;for(const r of e)n+=r.length,t.push(n);return t}function d(e){return[...new Set(e.map(t=>t.trim()).filter(Boolean))].sort((t,n)=>t.localeCompare(n)).join("\0")}async function $(e,t,n,r){try{return(await w(`/api/projects/${e}/spellcheck`,{method:"POST",body:JSON.stringify({path:t,source:n,clientId:C,sequence:r})})).lints}catch(o){throw o instanceof L&&o.code==="SPELLCHECK_SUPERSEDED"?new p:o}}function v(e,t,n){if(!t.problem)return null;const r={from:t.start,to:t.end},o={from:n[t.start]??-1,to:n[t.end]??-1},i=c=>c.from>=0&&c.to>c.from&&c.to<=e.length,a=c=>i(c)&&e.slice(c.from,c.to)===t.problem;return a(o)?o:a(r)?r:null}function A(e,t,n){const r=v(e,t,n);if(!r)return null;const{from:o,to:i}=r,a=e.slice(o,i);if(!a.trim()||!/[A-Za-z]/.test(a)||t.kind==="Formatting")return null;const c=t.kind==="Spelling"||t.kind==="Typo"?"spelling":"grammar",u=t.suggestions.filter((m,s,l)=>s<S&&l.indexOf(m)===s);return{from:o,to:i,word:a,kind:c,message:t.message,suggestions:u}}function h(e,t){let n=2166136261;for(let r=0;r<t.length;r+=1)n^=t.charCodeAt(r),n=Math.imul(n,16777619);return`${e}\0${t.length}:${n>>>0}`}function g(e,t,n){const r=h(e,t),o=f.get(r);return!o||o.path!==e||o.source!==t||o.dictionary!==n?null:(f.delete(r),f.set(r,o),o.issues)}function H(e,t,n,r){const o=h(e,t);for(f.set(o,{path:e,source:t,dictionary:n,issues:r});f.size>k;){const i=f.keys().next().value;if(i===void 0)break;f.delete(i)}}async function R(e,t,n,r="main.tex"){const o=d(t),i=g(r,e,o);if(i)return i;const a=E(e),c=new Set(t.map(s=>s.trim().toLocaleLowerCase("en-US")).filter(Boolean)),u=new Set,m=n.map(s=>A(e,s,a)).filter(s=>!!s).filter(s=>s.kind!=="spelling"||!c.has(s.word.trim().toLocaleLowerCase("en-US"))).filter(s=>{const l=`${s.from}:${s.to}:${s.kind}`;return u.has(l)?!1:(u.add(l),!0)}).sort((s,l)=>s.from-l.from||s.to-l.to);return H(r,e,o,m),m}async function b(e,t,n,r){const o=d(r),i=g(t,n,o);if(i)return i;const a=await $(e,t,n,++x);return R(n,r,a,t)}function I(e,t,n,r=[]){return b(e,t,n,r)}export{p as HarperLintSupersededError,D as isHarperLintSupersededError,I as lintLatex,R as mapLatexLints};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as a}from"./index-
|
|
1
|
+
import{c as a}from"./index-BjzELSQ-.js";const e=a("Tags",[["path",{d:"m15 5 6.3 6.3a2.4 2.4 0 0 1 0 3.4L17 19",key:"1cbfv1"}],["path",{d:"M9.586 5.586A2 2 0 0 0 8.172 5H3a1 1 0 0 0-1 1v5.172a2 2 0 0 0 .586 1.414L8.29 18.29a2.426 2.426 0 0 0 3.42 0l3.58-3.58a2.426 2.426 0 0 0 0-3.42z",key:"135mg7"}],["circle",{cx:"6.5",cy:"9.5",r:".5",fill:"currentColor",key:"5pm5xn"}]]);export{e as T};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as e}from"./index-
|
|
1
|
+
import{c as e}from"./index-BjzELSQ-.js";const n=e("Type",[["polyline",{points:"4 7 4 4 20 4 20 7",key:"1nosan"}],["line",{x1:"9",x2:"15",y1:"20",y2:"20",key:"swin9y"}],["line",{x1:"12",x2:"12",y1:"4",y2:"20",key:"1tx1rr"}]]);export{n as T};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as e}from"./index-
|
|
1
|
+
import{c as e}from"./index-BjzELSQ-.js";const c=e("UserPlus",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["line",{x1:"19",x2:"19",y1:"8",y2:"14",key:"1bvyxn"}],["line",{x1:"22",x2:"16",y1:"11",y2:"11",key:"1shjgl"}]]);export{c as U};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as a}from"./index-BjzELSQ-.js";const t=a("ArrowLeft",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]]);const h=a("AtSign",[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8",key:"7n84p3"}]]);const y=a("BookMarked",[["path",{d:"M10 2v8l3-3 3 3V2",key:"sqw3rj"}],["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20",key:"k3hazp"}]]);const k=a("BookOpen",[["path",{d:"M12 7v14",key:"1akyts"}],["path",{d:"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z",key:"ruj8y"}]]);const d=a("ChevronRight",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);const c=a("Download",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"7 10 12 15 17 10",key:"2ggqvy"}],["line",{x1:"12",x2:"12",y1:"15",y2:"3",key:"1vk2je"}]]);const p=a("FileArchive",[["path",{d:"M10 12v-1",key:"v7bkov"}],["path",{d:"M10 18v-2",key:"1cjy8d"}],["path",{d:"M10 7V6",key:"dljcrl"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M15.5 22H18a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v16a2 2 0 0 0 .274 1.01",key:"gkbcor"}],["circle",{cx:"10",cy:"20",r:"2",key:"1xzdoj"}]]);const s=a("FileSearch",[["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M4.268 21a2 2 0 0 0 1.727 1H18a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v3",key:"ms7g94"}],["path",{d:"m9 18-1.5-1.5",key:"1j6qii"}],["circle",{cx:"5",cy:"14",r:"3",key:"ufru5t"}]]);const r=a("FileText",[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]]);const l=a("FolderOpen",[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2",key:"usdka0"}]]);const o=a("FolderPlus",[["path",{d:"M12 10v6",key:"1bos4e"}],["path",{d:"M9 13h6",key:"1uhe8q"}],["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}]]);const n=a("Folder",[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}]]);const M=a("GitBranch",[["line",{x1:"6",x2:"6",y1:"3",y2:"15",key:"17qcm7"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M18 9a9 9 0 0 1-9 9",key:"n2h4wq"}]]);const i=a("ListTree",[["path",{d:"M21 12h-8",key:"1bmf0i"}],["path",{d:"M21 6H8",key:"1pqkrb"}],["path",{d:"M21 18h-8",key:"1tm79t"}],["path",{d:"M3 6v4c0 1.1.9 2 2 2h3",key:"1ywdgy"}],["path",{d:"M3 10v6c0 1.1.9 2 2 2h3",key:"2wc746"}]]);const v=a("MessageSquare",[["path",{d:"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z",key:"1lielz"}]]);const x=a("Pencil",[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}],["path",{d:"m15 5 4 4",key:"1mk7zo"}]]);const H=a("ScrollText",[["path",{d:"M15 12h-5",key:"r7krc0"}],["path",{d:"M15 8h-5",key:"1khuty"}],["path",{d:"M19 17V5a2 2 0 0 0-2-2H4",key:"zz82l3"}],["path",{d:"M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3",key:"1ph1d7"}]]);const m=a("Trash2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]]);const u=a("TriangleAlert",[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);const q=a("Upload",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"17 8 12 3 7 8",key:"t8dd8p"}],["line",{x1:"12",x2:"12",y1:"3",y2:"15",key:"widbto"}]]);const g=a("Users",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["path",{d:"M22 21v-2a4 4 0 0 0-3-3.87",key:"kshegd"}],["path",{d:"M16 3.13a4 4 0 0 1 0 7.75",key:"1da9ce"}]]);const A=a("X",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);export{h as A,y as B,d as C,c as D,s as F,M as G,i as L,v as M,x as P,H as S,m as T,g as U,A as X,l as a,n as b,k as c,r as d,t as e,q as f,o as g,p as h,u as i};
|
package/dist/client/index.html
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/iosevka@5.2.5/400.css" />
|
|
11
11
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
|
12
12
|
<title>TexLite</title>
|
|
13
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
+
<script type="module" crossorigin src="/assets/index-BjzELSQ-.js"></script>
|
|
14
14
|
<link rel="modulepreload" crossorigin href="/assets/react-BVUjbTTn.js">
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BRr3Kuxh.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
18
18
|
<div id="root"></div>
|
package/dist/server/app.js
CHANGED
|
@@ -16,6 +16,9 @@ import { ProjectMutationCoordinator } from "./projectMutations.js";
|
|
|
16
16
|
import { ProjectGitService } from "./git.js";
|
|
17
17
|
import { LatexCompletionService } from "./latexCompletion.js";
|
|
18
18
|
import { ProjectHistoryService } from "./history.js";
|
|
19
|
+
import { ProjectEditHistoryService } from "./editHistory.js";
|
|
20
|
+
import { HistoryRetentionScheduler } from "./historyRetention.js";
|
|
21
|
+
import { EditHistoryRetry } from "./editHistoryRetry.js";
|
|
19
22
|
import { ProjectOutlineService } from "./projectOutline.js";
|
|
20
23
|
import { MetricRegistry } from "./metrics.js";
|
|
21
24
|
import { apiError, HttpError } from "./http.js";
|
|
@@ -49,6 +52,10 @@ export async function buildApp(config, db, options = {}) {
|
|
|
49
52
|
const eventLoopDelay = monitorEventLoopDelay({ resolution: 20 });
|
|
50
53
|
eventLoopDelay.enable();
|
|
51
54
|
const history = new ProjectHistoryService(config, db);
|
|
55
|
+
const editHistory = new ProjectEditHistoryService(db, config.editHistoryMaxStorageBytes);
|
|
56
|
+
const historyRetention = new HistoryRetentionScheduler(history, {
|
|
57
|
+
onError: (error, projectId) => app.log.error({ err: error, projectId }, "Failed to enforce project history retention")
|
|
58
|
+
});
|
|
52
59
|
const latexCompletions = new LatexCompletionService(config);
|
|
53
60
|
const projectOutlines = new ProjectOutlineService(config);
|
|
54
61
|
const harper = new HarperService();
|
|
@@ -56,18 +63,47 @@ export async function buildApp(config, db, options = {}) {
|
|
|
56
63
|
// Probe the optional host Harper CLI without delaying startup. Its absence is
|
|
57
64
|
// supported: the browser spellchecker remains the writing-check fallback.
|
|
58
65
|
void harper.preload().catch((error) => app.log.info({ err: error }, "Optional Harper CLI is unavailable"));
|
|
66
|
+
const failedSnapshots = new Set();
|
|
67
|
+
const failedEdits = new Map();
|
|
68
|
+
const signalHistory = (id) => collaboration.setHistoryWarning(id, failedSnapshots.has(id) || failedEdits.has(id));
|
|
69
|
+
const editRetry = new EditHistoryRetry((id, edits) => editHistory.record(id, edits), (id) => Boolean(db.prepare("SELECT 1 FROM projects WHERE id = ?").get(id)), (id, state, error) => {
|
|
70
|
+
if (state === "discarded") {
|
|
71
|
+
failedEdits.delete(id);
|
|
72
|
+
failedSnapshots.delete(id);
|
|
73
|
+
signalHistory(id);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (failedEdits.get(id) !== "incomplete") {
|
|
77
|
+
if (state === "ok")
|
|
78
|
+
failedEdits.delete(id);
|
|
79
|
+
else
|
|
80
|
+
failedEdits.set(id, state);
|
|
81
|
+
}
|
|
82
|
+
if (error)
|
|
83
|
+
app.log.error({ err: error, projectId: id }, "Failed to record project edit history");
|
|
84
|
+
signalHistory(id);
|
|
85
|
+
});
|
|
59
86
|
const recordHistory = (projectId, userId, reason, paths) => {
|
|
60
87
|
try {
|
|
61
|
-
|
|
88
|
+
const version = history.record(projectId, userId, reason, failedSnapshots.has(projectId) ? undefined : paths, { deferRetention: reason === "autosave" });
|
|
89
|
+
if (failedSnapshots.delete(projectId))
|
|
90
|
+
signalHistory(projectId);
|
|
91
|
+
if (version && reason === "autosave")
|
|
92
|
+
historyRetention.schedule(projectId);
|
|
93
|
+
return version;
|
|
62
94
|
}
|
|
63
95
|
catch (error) {
|
|
96
|
+
failedSnapshots.add(projectId);
|
|
97
|
+
signalHistory(projectId);
|
|
64
98
|
app.log.error({ err: error, projectId }, "Failed to record project history");
|
|
65
99
|
return null;
|
|
66
100
|
}
|
|
67
101
|
};
|
|
68
|
-
const collaboration = new CollaborationService(config, db, ({ projectId, userId, paths, durationMs }) => {
|
|
69
|
-
|
|
102
|
+
const collaboration = new CollaborationService(config, db, ({ projectId, userId, paths, edits, durationMs }) => {
|
|
103
|
+
const started = performance.now();
|
|
104
|
+
editRetry.save(projectId, edits);
|
|
70
105
|
recordHistory(projectId, userId, "autosave", paths);
|
|
106
|
+
metrics.record("collaboration.persist", durationMs + performance.now() - started);
|
|
71
107
|
});
|
|
72
108
|
const projectMutations = new ProjectMutationCoordinator(collaboration);
|
|
73
109
|
const projectGit = new ProjectGitService(config, db, options.githubFetch);
|
|
@@ -103,11 +139,14 @@ export async function buildApp(config, db, options = {}) {
|
|
|
103
139
|
await pruneTrashDirectory(config);
|
|
104
140
|
for (const row of db.prepare("SELECT id FROM projects").all()) {
|
|
105
141
|
history.enforceRetention(row.id);
|
|
142
|
+
editHistory.enforceRetention(row.id);
|
|
106
143
|
pruneCompileRuns(row.id);
|
|
107
144
|
pruneOrphanedCompileRuns(config, row.id);
|
|
108
145
|
}
|
|
109
146
|
app.addHook("onClose", async () => {
|
|
110
147
|
eventLoopDelay.disable();
|
|
148
|
+
historyRetention.dispose();
|
|
149
|
+
editRetry.dispose();
|
|
111
150
|
await harper.dispose();
|
|
112
151
|
await texcount.dispose();
|
|
113
152
|
});
|
|
@@ -187,7 +226,9 @@ export async function buildApp(config, db, options = {}) {
|
|
|
187
226
|
metrics,
|
|
188
227
|
recordHistory
|
|
189
228
|
});
|
|
190
|
-
registerProjectHistoryRoutes(app, { config, db, history, projectMutations, recordHistory
|
|
229
|
+
registerProjectHistoryRoutes(app, { config, db, history, editHistory, projectMutations, recordHistory,
|
|
230
|
+
clearPendingEdits: (id) => { editRetry.clear(id); failedEdits.delete(id); signalHistory(id); },
|
|
231
|
+
scheduleHistoryRetention: (id) => historyRetention.schedule(id) });
|
|
191
232
|
registerProjectGitRoutes(app, { config, db, collaboration, projectMutations, projectGit, recordHistory });
|
|
192
233
|
registerProjectCatalogRoutes(app, {
|
|
193
234
|
config,
|
package/dist/server/cli.js
CHANGED
|
@@ -117,6 +117,9 @@ function writeInitialConfig(configPath, siteName, adminEmail) {
|
|
|
117
117
|
maxVersions: CONFIG_DEFAULTS.historyMaxVersions,
|
|
118
118
|
maxStorageMB: CONFIG_DEFAULTS.historyMaxStorageMB
|
|
119
119
|
},
|
|
120
|
+
editHistory: {
|
|
121
|
+
maxStorageMB: CONFIG_DEFAULTS.editHistoryMaxStorageMB
|
|
122
|
+
},
|
|
120
123
|
git: {
|
|
121
124
|
binary: CONFIG_DEFAULTS.git,
|
|
122
125
|
operationTimeoutSeconds: CONFIG_DEFAULTS.gitOperationTimeoutSeconds,
|
|
@@ -289,6 +292,7 @@ async function printConfig(options) {
|
|
|
289
292
|
pdfRangeThresholdMB: config.pdfRangeThresholdBytes / (1024 * 1024),
|
|
290
293
|
historyMaxVersions: config.historyMaxVersions,
|
|
291
294
|
historyMaxStorageBytes: config.historyMaxStorageBytes,
|
|
295
|
+
editHistoryMaxStorageBytes: config.editHistoryMaxStorageBytes,
|
|
292
296
|
git: config.git
|
|
293
297
|
}, null, 2));
|
|
294
298
|
}
|
|
@@ -11,6 +11,7 @@ import { Awareness, applyAwarenessUpdate, encodeAwarenessUpdate, modifyAwareness
|
|
|
11
11
|
import { listProjectFilesAsync, outputRoot, resolveSourcePath, safeRelativePath, sourceRoot } from "./files.js";
|
|
12
12
|
import { collaborationProjectAccessFromStatement, canEdit, prepareCollaborationProjectAccessStatement } from "./projects.js";
|
|
13
13
|
import { reanchorFileComments } from "./anchors.js";
|
|
14
|
+
import { hashText } from "./editHistory.js";
|
|
14
15
|
const MESSAGE_SYNC = 0;
|
|
15
16
|
const MESSAGE_AWARENESS = 1;
|
|
16
17
|
const MESSAGE_QUERY_AWARENESS = 3;
|
|
@@ -43,11 +44,17 @@ const STATE_SAVE_DELAY_MS = 750;
|
|
|
43
44
|
const ROOM_IDLE_MS = 30_000;
|
|
44
45
|
const FORMAT_LEASE_TTL_MS = 45_000;
|
|
45
46
|
const MAX_FORMAT_LEASE_WAITERS = MAX_PROJECT_SESSIONS * 2;
|
|
47
|
+
// A normal typing burst produces a few small Yjs updates. Keep adjacent
|
|
48
|
+
// updates from the same user together without ever merging across another
|
|
49
|
+
// collaborator's edit, so the resulting record remains attributable.
|
|
50
|
+
const EDIT_SEGMENT_IDLE_MS = 30_000;
|
|
51
|
+
const MAX_EDIT_SPANS_PER_STEP = 96;
|
|
52
|
+
const MAX_EDIT_PREVIEW_CHARS = 1_200;
|
|
46
53
|
// Completion notifications are useful only to browsers that were already
|
|
47
54
|
// present when the operation finished. The database and retained manifests
|
|
48
55
|
// are the authority for a later workspace open.
|
|
49
56
|
const COMPLETED_COMPILE_STATE_TTL_MS = 60_000;
|
|
50
|
-
const EPHEMERAL_META_KEYS = ["compileStates", "filesEvent", "commentsRevision", "dictionaryRevision"];
|
|
57
|
+
const EPHEMERAL_META_KEYS = ["compileStates", "filesEvent", "commentsRevision", "dictionaryRevision", "historyWarning"];
|
|
51
58
|
const COLORS = [
|
|
52
59
|
["#1677c8", "#1677c833"], ["#d65745", "#d6574533"], ["#16866a", "#16866a33"],
|
|
53
60
|
["#9a58b5", "#9a58b533"], ["#d27b18", "#d27b1833"], ["#3f7d20", "#3f7d2033"],
|
|
@@ -64,6 +71,8 @@ export class CollaborationService {
|
|
|
64
71
|
projectGenerations = new Map();
|
|
65
72
|
snapshotBarriers = new Map();
|
|
66
73
|
pendingConnections = new Map();
|
|
74
|
+
/** Transient server-side status, restored into each newly opened room. */
|
|
75
|
+
historyWarnings = new Set();
|
|
67
76
|
closed = false;
|
|
68
77
|
userByIdStatement;
|
|
69
78
|
collaborationProjectAccessStatement;
|
|
@@ -397,6 +406,16 @@ export class CollaborationService {
|
|
|
397
406
|
const filePath = filePathInput ? safeRelativePath(filePathInput) : "";
|
|
398
407
|
this.bumpFiles(room, { kind: "update", path: filePath });
|
|
399
408
|
}
|
|
409
|
+
setHistoryWarning(projectId, warning) {
|
|
410
|
+
if (warning)
|
|
411
|
+
this.historyWarnings.add(projectId);
|
|
412
|
+
else
|
|
413
|
+
this.historyWarnings.delete(projectId);
|
|
414
|
+
const room = this.rooms.get(projectId);
|
|
415
|
+
if (!room || room.meta.get("historyWarning") === warning)
|
|
416
|
+
return;
|
|
417
|
+
room.doc.transact(() => room.meta.set("historyWarning", warning), META_ORIGIN);
|
|
418
|
+
}
|
|
400
419
|
signalComments(projectId) {
|
|
401
420
|
const room = this.rooms.get(projectId);
|
|
402
421
|
if (!room)
|
|
@@ -565,6 +584,7 @@ export class CollaborationService {
|
|
|
565
584
|
this.roomInitializations.clear();
|
|
566
585
|
this.snapshotBarriers.clear();
|
|
567
586
|
this.pendingConnections.clear();
|
|
587
|
+
this.historyWarnings.clear();
|
|
568
588
|
}
|
|
569
589
|
initializeRoom(projectId, generation) {
|
|
570
590
|
const existing = this.rooms.get(projectId);
|
|
@@ -684,7 +704,9 @@ export class CollaborationService {
|
|
|
684
704
|
awarenessOwners: new Map(),
|
|
685
705
|
allowedPaths: new Set(),
|
|
686
706
|
persistedContent: new Map(),
|
|
707
|
+
observedContent: new Map(),
|
|
687
708
|
dirtyPaths: new Set(),
|
|
709
|
+
pendingEditSegments: [],
|
|
688
710
|
textObservers: new Map(),
|
|
689
711
|
saveTimer: null,
|
|
690
712
|
stateSaveTimer: null,
|
|
@@ -707,6 +729,12 @@ export class CollaborationService {
|
|
|
707
729
|
// longer exists. The database/source tree are authoritative after a
|
|
708
730
|
// restart; clear the markers before any browser can sync them back.
|
|
709
731
|
const recoveredMetadataChanged = bootstrap.recoveredState && this.clearRecoveredMetadata(room);
|
|
732
|
+
// History bookkeeping is not stored in a project Yjs document, but a
|
|
733
|
+
// temporary failure must remain visible if its room was idled out before
|
|
734
|
+
// the editor reconnects.
|
|
735
|
+
if (this.historyWarnings.has(projectId)) {
|
|
736
|
+
room.doc.transact(() => room.meta.set("historyWarning", true), META_ORIGIN);
|
|
737
|
+
}
|
|
710
738
|
this.sanitizeCompileStates(room);
|
|
711
739
|
let recoveredDirty = false;
|
|
712
740
|
const diskPaths = new Set();
|
|
@@ -727,13 +755,17 @@ export class CollaborationService {
|
|
|
727
755
|
else {
|
|
728
756
|
replaceText(text, file.content);
|
|
729
757
|
}
|
|
758
|
+
room.observedContent.set(file.path, text.toString());
|
|
730
759
|
}
|
|
731
760
|
for (const name of room.doc.share.keys()) {
|
|
732
761
|
if (!name.startsWith(SOURCE_PREFIX))
|
|
733
762
|
continue;
|
|
734
763
|
const filePath = name.slice(SOURCE_PREFIX.length);
|
|
735
|
-
if (!diskPaths.has(filePath))
|
|
736
|
-
|
|
764
|
+
if (!diskPaths.has(filePath)) {
|
|
765
|
+
const text = room.doc.getText(name);
|
|
766
|
+
replaceText(text, "");
|
|
767
|
+
room.observedContent.set(filePath, text.toString());
|
|
768
|
+
}
|
|
737
769
|
}
|
|
738
770
|
}, DISK_ORIGIN);
|
|
739
771
|
const rejectedRecoveredPaths = this.rejectOversizedTexts(room);
|
|
@@ -839,9 +871,17 @@ export class CollaborationService {
|
|
|
839
871
|
const text = room.doc.getText(typeName(filePath));
|
|
840
872
|
if (room.textObservers.has(filePath))
|
|
841
873
|
return text;
|
|
842
|
-
const observer = (
|
|
843
|
-
|
|
874
|
+
const observer = (event, transaction) => {
|
|
875
|
+
const before = room.observedContent.get(filePath) ?? text.toString();
|
|
876
|
+
const after = text.toString();
|
|
877
|
+
if (transaction.origin === DISK_ORIGIN || transaction.origin === HTTP_ORIGIN || transaction.origin === META_ORIGIN) {
|
|
878
|
+
// Non-collaborative mutations deliberately reset the source chain.
|
|
879
|
+
// They are represented by recovery snapshots, not by a guessed user
|
|
880
|
+
// edit record; keeping this mirror current prevents later edits from
|
|
881
|
+
// being mapped across such a replacement.
|
|
882
|
+
room.observedContent.set(filePath, after);
|
|
844
883
|
return;
|
|
884
|
+
}
|
|
845
885
|
if (!room.allowedPaths.has(filePath)) {
|
|
846
886
|
// A client can still have the old Y.Text bound briefly after another
|
|
847
887
|
// session deletes or moves a file. Correct any late update immediately;
|
|
@@ -850,12 +890,60 @@ export class CollaborationService {
|
|
|
850
890
|
room.doc.transact(() => replaceText(text, ""), DISK_ORIGIN);
|
|
851
891
|
return;
|
|
852
892
|
}
|
|
893
|
+
if (isConnectionOrigin(transaction.origin) && before !== after) {
|
|
894
|
+
const connection = transaction.origin;
|
|
895
|
+
this.recordEditStep(room, filePath, connection, event, before, after);
|
|
896
|
+
}
|
|
897
|
+
room.observedContent.set(filePath, after);
|
|
853
898
|
room.dirtyPaths.add(filePath);
|
|
854
899
|
};
|
|
855
900
|
text.observe(observer);
|
|
856
901
|
room.textObservers.set(filePath, observer);
|
|
857
902
|
return text;
|
|
858
903
|
}
|
|
904
|
+
/**
|
|
905
|
+
* Keep live collaborative changes in short, author-isolated segments. The
|
|
906
|
+
* segment is only handed to the history service after `flushRoom()` has
|
|
907
|
+
* made the matching source content durable.
|
|
908
|
+
*/
|
|
909
|
+
recordEditStep(room, filePath, connection, event, before, after) {
|
|
910
|
+
const spans = editSpansFromYDelta(event.delta, before, after);
|
|
911
|
+
if (!spans.length)
|
|
912
|
+
return;
|
|
913
|
+
const createdAt = new Date().toISOString();
|
|
914
|
+
const activeLease = this.activeFormatLease(room, filePath);
|
|
915
|
+
const kind = activeLease?.connection === connection ? "format" : "edit";
|
|
916
|
+
const step = {
|
|
917
|
+
beforeHash: hashText(before),
|
|
918
|
+
afterHash: hashText(after),
|
|
919
|
+
createdAt,
|
|
920
|
+
spans
|
|
921
|
+
};
|
|
922
|
+
const previous = room.pendingEditSegments.at(-1);
|
|
923
|
+
const previousTime = previous ? Date.parse(previous.updatedAt) : Number.NaN;
|
|
924
|
+
if (previous
|
|
925
|
+
&& previous.filePath === filePath
|
|
926
|
+
&& previous.authorId === connection.user.id
|
|
927
|
+
&& previous.kind === kind
|
|
928
|
+
&& previous.afterHash === step.beforeHash
|
|
929
|
+
&& Number.isFinite(previousTime)
|
|
930
|
+
&& Date.parse(createdAt) - previousTime <= EDIT_SEGMENT_IDLE_MS) {
|
|
931
|
+
previous.steps.push(step);
|
|
932
|
+
previous.afterHash = step.afterHash;
|
|
933
|
+
previous.updatedAt = createdAt;
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
room.pendingEditSegments.push({
|
|
937
|
+
filePath,
|
|
938
|
+
authorId: connection.user.id,
|
|
939
|
+
kind,
|
|
940
|
+
beforeHash: step.beforeHash,
|
|
941
|
+
afterHash: step.afterHash,
|
|
942
|
+
createdAt,
|
|
943
|
+
updatedAt: createdAt,
|
|
944
|
+
steps: [step]
|
|
945
|
+
});
|
|
946
|
+
}
|
|
859
947
|
handleMessage(room, connection, bytes) {
|
|
860
948
|
const refreshedUser = this.userByIdStatement.get(connection.user.id);
|
|
861
949
|
if (!refreshedUser || refreshedUser.disabled) {
|
|
@@ -1287,21 +1375,24 @@ export class CollaborationService {
|
|
|
1287
1375
|
if (room.saveTimer)
|
|
1288
1376
|
clearTimeout(room.saveTimer);
|
|
1289
1377
|
room.saveTimer = null;
|
|
1290
|
-
this.rejectOversizedTexts(room);
|
|
1378
|
+
const rejectedDuringFlush = this.rejectOversizedTexts(room);
|
|
1291
1379
|
this.persistRoomState(room);
|
|
1292
1380
|
let changed = false;
|
|
1293
1381
|
const changedPaths = [];
|
|
1294
1382
|
const failedPaths = [...room.rejectedPaths];
|
|
1383
|
+
const finalizedPaths = new Set(rejectedDuringFlush);
|
|
1295
1384
|
const dirtyPaths = [...room.dirtyPaths];
|
|
1296
1385
|
for (const filePath of dirtyPaths) {
|
|
1297
1386
|
if (!room.allowedPaths.has(filePath)) {
|
|
1298
1387
|
room.dirtyPaths.delete(filePath);
|
|
1388
|
+
finalizedPaths.add(filePath);
|
|
1299
1389
|
continue;
|
|
1300
1390
|
}
|
|
1301
1391
|
const next = this.trackedText(room, filePath).toString();
|
|
1302
1392
|
const previous = room.persistedContent.get(filePath) ?? "";
|
|
1303
1393
|
if (next === previous) {
|
|
1304
1394
|
room.dirtyPaths.delete(filePath);
|
|
1395
|
+
finalizedPaths.add(filePath);
|
|
1305
1396
|
continue;
|
|
1306
1397
|
}
|
|
1307
1398
|
if (Buffer.byteLength(next, "utf8") > maxCollaborativeFileBytes(this.config)) {
|
|
@@ -1328,6 +1419,7 @@ export class CollaborationService {
|
|
|
1328
1419
|
}
|
|
1329
1420
|
room.persistedContent.set(filePath, next);
|
|
1330
1421
|
room.dirtyPaths.delete(filePath);
|
|
1422
|
+
finalizedPaths.add(filePath);
|
|
1331
1423
|
try {
|
|
1332
1424
|
reanchorFileComments(this.db, room.projectId, filePath, previous, next);
|
|
1333
1425
|
}
|
|
@@ -1335,6 +1427,13 @@ export class CollaborationService {
|
|
|
1335
1427
|
changed = true;
|
|
1336
1428
|
changedPaths.push(filePath);
|
|
1337
1429
|
}
|
|
1430
|
+
const persistedPaths = new Set(changedPaths);
|
|
1431
|
+
const edits = room.pendingEditSegments.filter((segment) => persistedPaths.has(segment.filePath));
|
|
1432
|
+
// A reverted edit or rejected oversized file did not produce a durable
|
|
1433
|
+
// source revision, so it must not become a misleading edit-history row.
|
|
1434
|
+
if (finalizedPaths.size > 0) {
|
|
1435
|
+
room.pendingEditSegments = room.pendingEditSegments.filter((segment) => !finalizedPaths.has(segment.filePath));
|
|
1436
|
+
}
|
|
1338
1437
|
if (changed && room.lastModifiedUserId) {
|
|
1339
1438
|
this.db.prepare("UPDATE projects SET updated_at = ?, last_modified_by = ? WHERE id = ?")
|
|
1340
1439
|
.run(new Date().toISOString(), room.lastModifiedUserId, room.projectId);
|
|
@@ -1343,7 +1442,7 @@ export class CollaborationService {
|
|
|
1343
1442
|
this.signalComments(room.projectId);
|
|
1344
1443
|
if (changed && this.onPersist) {
|
|
1345
1444
|
try {
|
|
1346
|
-
this.onPersist({ projectId: room.projectId, userId: room.lastModifiedUserId, paths: changedPaths, durationMs: performance.now() - startedAt });
|
|
1445
|
+
this.onPersist({ projectId: room.projectId, userId: room.lastModifiedUserId, paths: changedPaths, edits, durationMs: performance.now() - startedAt });
|
|
1347
1446
|
}
|
|
1348
1447
|
catch { /* Source durability must not depend on optional history bookkeeping. */ }
|
|
1349
1448
|
}
|
|
@@ -1520,6 +1619,119 @@ function replaceText(text, content) {
|
|
|
1520
1619
|
if (content)
|
|
1521
1620
|
text.insert(0, content);
|
|
1522
1621
|
}
|
|
1622
|
+
/**
|
|
1623
|
+
* Turn Y.Text's transaction delta into compact positional spans. We retain
|
|
1624
|
+
* lengths for exact range mapping, while previews keep a pasted/formatter
|
|
1625
|
+
* change from turning the edit-history database into a second source store.
|
|
1626
|
+
*/
|
|
1627
|
+
function editSpansFromYDelta(delta, before, after) {
|
|
1628
|
+
if (before === after)
|
|
1629
|
+
return [];
|
|
1630
|
+
let beforeOffset = 0;
|
|
1631
|
+
let afterOffset = 0;
|
|
1632
|
+
let invalid = false;
|
|
1633
|
+
const spans = [];
|
|
1634
|
+
let pending = null;
|
|
1635
|
+
const flushPending = () => {
|
|
1636
|
+
if (!pending)
|
|
1637
|
+
return;
|
|
1638
|
+
const deletedLength = pending.deleted.length;
|
|
1639
|
+
const insertedLength = pending.inserted.length;
|
|
1640
|
+
if (deletedLength || insertedLength) {
|
|
1641
|
+
spans.push(editSpan(pending.beforeStart, pending.beforeStart + deletedLength, pending.afterStart, pending.afterStart + insertedLength, pending.deleted, pending.inserted));
|
|
1642
|
+
}
|
|
1643
|
+
pending = null;
|
|
1644
|
+
};
|
|
1645
|
+
const startPending = () => {
|
|
1646
|
+
if (!pending)
|
|
1647
|
+
pending = { beforeStart: beforeOffset, afterStart: afterOffset, deleted: "", inserted: "" };
|
|
1648
|
+
return pending;
|
|
1649
|
+
};
|
|
1650
|
+
for (const raw of delta) {
|
|
1651
|
+
if (!raw || typeof raw !== "object") {
|
|
1652
|
+
invalid = true;
|
|
1653
|
+
break;
|
|
1654
|
+
}
|
|
1655
|
+
const operation = raw;
|
|
1656
|
+
if (typeof operation.retain === "number" && Number.isInteger(operation.retain) && operation.retain >= 0) {
|
|
1657
|
+
flushPending();
|
|
1658
|
+
beforeOffset += operation.retain;
|
|
1659
|
+
afterOffset += operation.retain;
|
|
1660
|
+
continue;
|
|
1661
|
+
}
|
|
1662
|
+
if (typeof operation.delete === "number" && Number.isInteger(operation.delete) && operation.delete >= 0) {
|
|
1663
|
+
const length = operation.delete;
|
|
1664
|
+
if (beforeOffset + length > before.length) {
|
|
1665
|
+
invalid = true;
|
|
1666
|
+
break;
|
|
1667
|
+
}
|
|
1668
|
+
startPending().deleted += before.slice(beforeOffset, beforeOffset + length);
|
|
1669
|
+
beforeOffset += length;
|
|
1670
|
+
continue;
|
|
1671
|
+
}
|
|
1672
|
+
if (typeof operation.insert === "string") {
|
|
1673
|
+
startPending().inserted += operation.insert;
|
|
1674
|
+
afterOffset += operation.insert.length;
|
|
1675
|
+
continue;
|
|
1676
|
+
}
|
|
1677
|
+
invalid = true;
|
|
1678
|
+
break;
|
|
1679
|
+
}
|
|
1680
|
+
flushPending();
|
|
1681
|
+
// Yjs normally emits retains for all unchanged text. Be defensive about
|
|
1682
|
+
// omitted trailing retains, then reject anything whose coordinate model does
|
|
1683
|
+
// not reconstruct the actual strings.
|
|
1684
|
+
const beforeRemaining = before.length - beforeOffset;
|
|
1685
|
+
const afterRemaining = after.length - afterOffset;
|
|
1686
|
+
if (beforeRemaining === afterRemaining && beforeRemaining >= 0) {
|
|
1687
|
+
beforeOffset = before.length;
|
|
1688
|
+
afterOffset = after.length;
|
|
1689
|
+
}
|
|
1690
|
+
if (invalid || beforeOffset !== before.length || afterOffset !== after.length || spans.length > MAX_EDIT_SPANS_PER_STEP) {
|
|
1691
|
+
return [wholeDocumentEditSpan(before, after)];
|
|
1692
|
+
}
|
|
1693
|
+
return spans.length ? spans : [wholeDocumentEditSpan(before, after)];
|
|
1694
|
+
}
|
|
1695
|
+
function wholeDocumentEditSpan(before, after) {
|
|
1696
|
+
const prefix = commonPrefixLength(before, after);
|
|
1697
|
+
const suffix = commonSuffixLength(before, after, prefix);
|
|
1698
|
+
return editSpan(prefix, before.length - suffix, prefix, after.length - suffix, before.slice(prefix, before.length - suffix), after.slice(prefix, after.length - suffix));
|
|
1699
|
+
}
|
|
1700
|
+
function editSpan(beforeStart, beforeEnd, afterStart, afterEnd, deleted, inserted) {
|
|
1701
|
+
const deletedPreview = previewEditText(deleted);
|
|
1702
|
+
const insertedPreview = previewEditText(inserted);
|
|
1703
|
+
return {
|
|
1704
|
+
beforeStart,
|
|
1705
|
+
beforeEnd,
|
|
1706
|
+
afterStart,
|
|
1707
|
+
afterEnd,
|
|
1708
|
+
deletedLength: deleted.length,
|
|
1709
|
+
insertedLength: inserted.length,
|
|
1710
|
+
deletedPreview,
|
|
1711
|
+
insertedPreview,
|
|
1712
|
+
truncated: deletedPreview.length < deleted.length || insertedPreview.length < inserted.length
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
function previewEditText(value) {
|
|
1716
|
+
if (value.length <= MAX_EDIT_PREVIEW_CHARS)
|
|
1717
|
+
return value;
|
|
1718
|
+
const edge = Math.floor((MAX_EDIT_PREVIEW_CHARS - 1) / 2);
|
|
1719
|
+
return `${value.slice(0, edge)}…${value.slice(value.length - edge)}`;
|
|
1720
|
+
}
|
|
1721
|
+
function commonPrefixLength(left, right) {
|
|
1722
|
+
const max = Math.min(left.length, right.length);
|
|
1723
|
+
let index = 0;
|
|
1724
|
+
while (index < max && left.charCodeAt(index) === right.charCodeAt(index))
|
|
1725
|
+
index += 1;
|
|
1726
|
+
return index;
|
|
1727
|
+
}
|
|
1728
|
+
function commonSuffixLength(left, right, prefixLength) {
|
|
1729
|
+
const max = Math.min(left.length, right.length) - prefixLength;
|
|
1730
|
+
let index = 0;
|
|
1731
|
+
while (index < max && left.charCodeAt(left.length - 1 - index) === right.charCodeAt(right.length - 1 - index))
|
|
1732
|
+
index += 1;
|
|
1733
|
+
return index;
|
|
1734
|
+
}
|
|
1523
1735
|
function syncUpdateMessage(update) {
|
|
1524
1736
|
const encoder = encoding.createEncoder();
|
|
1525
1737
|
encoding.writeVarUint(encoder, MESSAGE_SYNC);
|