diff --git a/apps/ios/EdgeEver/Editor/TipTapWarmPool.swift b/apps/ios/EdgeEver/Editor/TipTapWarmPool.swift index 8c4740df..bb10b794 100644 --- a/apps/ios/EdgeEver/Editor/TipTapWarmPool.swift +++ b/apps/ios/EdgeEver/Editor/TipTapWarmPool.swift @@ -57,7 +57,7 @@ final class SharedTipTapRuntime: NSObject, WKScriptMessageHandler, WKNavigationD private var lastEditorEmittedFingerprint: String? private var lastAppliedConfiguration: String? private var hydrateGeneration: UInt64 = 0 - private var bodyReadyGeneration: UInt64 = 0 + private var bodyReadyGeneration: UInt64? private var contentGeneration: UInt64 = 0 /// Only auto-focus caret once per content generation (open edit), never while typing. private var focusedGeneration: UInt64 = 0 @@ -133,6 +133,7 @@ final class SharedTipTapRuntime: NSObject, WKScriptMessageHandler, WKNavigationD func detach(from container: UIView) { guard hostContainer === container else { return } + bodyReadyGeneration = nil // Keep the engine alive off-screen; only leave the hierarchy. webView.removeFromSuperview() hostContainer = nil @@ -171,7 +172,7 @@ final class SharedTipTapRuntime: NSObject, WKScriptMessageHandler, WKNavigationD if fp != previousFingerprint || needsForcePushOnBind { contentGeneration &+= 1 hydrateGeneration &+= 1 - // Do not zero bodyReadyGeneration here — pushContent / skip path will notify. + bodyReadyGeneration = nil } let modeChanged = previousMode != nil && previousMode != newSession.mode let forcePush = modeChanged || needsForcePushOnBind @@ -412,15 +413,15 @@ final class SharedTipTapRuntime: NSObject, WKScriptMessageHandler, WKNavigationD /// Read current editor markdown + JSON after a mutation (avoids racing the async bridge onChange). func snapshotContent() async -> (markdown: String, json: String)? { - guard ready else { return nil } + guard ready, bodyReadyGeneration == contentGeneration else { return nil } + let gen = contentGeneration let js = """ (function(){ try { if (!window.EdgeEverEditor) return null; - var md = ''; - var json = ''; - try { md = window.EdgeEverEditor.getMarkdown() || ''; } catch (e) {} - try { json = window.EdgeEverEditor.getDocument() || ''; } catch (e) {} + var md = window.EdgeEverEditor.getMarkdown(); + var json = window.EdgeEverEditor.getDocument(); + if (typeof md !== 'string' || typeof json !== 'string') return null; return JSON.stringify({ md: md, json: json }); } catch (e) { return null; } })(); @@ -430,7 +431,8 @@ final class SharedTipTapRuntime: NSObject, WKScriptMessageHandler, WKNavigationD cont.resume(returning: value) } } - guard let text = raw as? String, + guard contentGeneration == gen, bodyReadyGeneration == gen, + let text = raw as? String, let data = text.data(using: .utf8), let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return nil } @@ -505,7 +507,9 @@ final class SharedTipTapRuntime: NSObject, WKScriptMessageHandler, WKNavigationD // MARK: - WKNavigationDelegate func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { - ready = true + // Navigation completion does not acknowledge that the editor bridge exists. + // The bundle (including the fallback) posts `ready` after installing its API. + guard ready else { return } lastAppliedConfiguration = nil applyMode() pushContentIfNeeded(force: true) @@ -570,21 +574,22 @@ final class SharedTipTapRuntime: NSObject, WKScriptMessageHandler, WKNavigationD let fn = useJSON ? "setDocumentFromJSON" : "setMarkdown" let payload = decision.payload let js = TipTapResourceLoader.jsCall(fn: fn, arg: payload) - // Capture callback now — detach may nil session callbacks before the JS completion runs. - let bodyReadyCb = session.onBodyReady - webView.evaluateJavaScript(js) { [weak self] _, error in + bodyReadyGeneration = nil + lastPushedJSON = nil + lastEditorEmittedFingerprint = nil + webView.evaluateJavaScript(js) { [weak self] result, error in #if DEBUG if let error { NSLog("SharedTipTapRuntime pushContent error: \(error)") } #endif - guard let self else { return } + guard let self, error == nil, result as? Bool == true else { return } Task { @MainActor in // Only stamp fingerprints after a successful push for this generation. // Stamping before JS completed caused empty-create to skip a later force // while the WebView still held the previous note body. - guard self.contentGeneration == gen else { return } + guard self.contentGeneration == gen, self.hostContainer != nil else { return } self.lastPushedJSON = fingerprint self.lastEditorEmittedFingerprint = fingerprint - self.notifyBodyReady(generation: gen, callback: bodyReadyCb) + self.notifyBodyReady(generation: gen) if shouldFocusAfterPush { self.scheduleFocusEnd(for: gen) } @@ -593,12 +598,15 @@ final class SharedTipTapRuntime: NSObject, WKScriptMessageHandler, WKNavigationD } } - private func notifyBodyReady(generation: UInt64, callback: (() -> Void)? = nil) { - let cb = callback ?? session?.onBodyReady + private func notifyBodyReady(generation: UInt64) { // Always invoke the *current* host callback when re-binding the same content, // even if we already marked this generation ready (new SwiftUI view needs it). bodyReadyGeneration = generation - DispatchQueue.main.async { cb?() } + DispatchQueue.main.async { [weak self] in + guard let self, self.bodyReadyGeneration == generation, + self.contentGeneration == generation else { return } + self.session?.onBodyReady?() + } } // MARK: - Image hydrate @@ -687,7 +695,8 @@ final class SharedTipTapRuntime: NSObject, WKScriptMessageHandler, WKNavigationD case "change": // Host dismantled (create/edit dismissed) — drop late events so they cannot // resurrect the previous body in a later editor session. - guard let session, session.onChange != nil else { return } + guard bodyReadyGeneration == contentGeneration, + let session, session.onChange != nil else { return } let md = body["contentMarkdown"] as? String ?? "" let json = body["contentJson"] as? String ?? session.documentJSON let emptyStub = "{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\"}]}" diff --git a/apps/ios/EdgeEver/Editor/TipTapWebView.swift b/apps/ios/EdgeEver/Editor/TipTapWebView.swift index c388e3f0..3fbddd69 100644 --- a/apps/ios/EdgeEver/Editor/TipTapWebView.swift +++ b/apps/ios/EdgeEver/Editor/TipTapWebView.swift @@ -154,14 +154,16 @@ enum TipTapResourceLoader { return """ (function(){ try { - if (!window.EdgeEverEditor) return; + if (!window.EdgeEverEditor || typeof window.EdgeEverEditor.\(fn) !== 'function') return false; var bin = atob('\(b64)'); var bytes = new Uint8Array(bin.length); for (var i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i); var text = new TextDecoder('utf-8').decode(bytes); window.EdgeEverEditor.\(fn)(text); + return true; } catch (e) { try { window.webkit.messageHandlers.edgeever.postMessage({type:'error', message: String(e)}); } catch (_) {} + return false; } })(); """ diff --git a/apps/ios/EdgeEver/Features/Share/WebClipper.swift b/apps/ios/EdgeEver/Features/Share/WebClipper.swift index c6f9c1f0..586e9a59 100644 --- a/apps/ios/EdgeEver/Features/Share/WebClipper.swift +++ b/apps/ios/EdgeEver/Features/Share/WebClipper.swift @@ -63,12 +63,13 @@ enum WebClipper { static func build( _ sourceURL: URL, capturedAt: Date = Date(), - labels: WebClipLabels = .chinese + labels: WebClipLabels = .chinese, + session: URLSession = .shared ) async -> WebClipDraft { var request = URLRequest(url: sourceURL) request.setValue("text/html,application/xhtml+xml", forHTTPHeaderField: "Accept") do { - let (data, response) = try await URLSession.shared.data(for: request) + let (data, response) = try await session.data(for: request) guard let http = response as? HTTPURLResponse, (200 ..< 300).contains(http.statusCode), let html = String(data: data, encoding: .utf8) diff --git a/apps/ios/EdgeEver/Features/Workspace/MemoEditView.swift b/apps/ios/EdgeEver/Features/Workspace/MemoEditView.swift index e3b288f3..d9185a9c 100644 --- a/apps/ios/EdgeEver/Features/Workspace/MemoEditView.swift +++ b/apps/ios/EdgeEver/Features/Workspace/MemoEditView.swift @@ -301,10 +301,9 @@ struct MemoEditView: View { .task { await loadInitial() contentHydrated = true - // editorReady flips true from TipTap onBodyReady (or fallback below). + // Only TipTap's acknowledged content push may mark the body ready. try? await Task.sleep(nanoseconds: 800_000_000) if !Task.isCancelled, !editorReady { - editorReady = true if initialFocus == .title { focusTitleOnce() } else { @@ -853,7 +852,6 @@ struct MemoEditView: View { Task { @MainActor in try? await Task.sleep(nanoseconds: 50_000_000) SharedTipTapRuntime.editor.focusEnd() - editorReady = true } } diff --git a/apps/ios/EdgeEver/Resources/EditorBundle/index.html b/apps/ios/EdgeEver/Resources/EditorBundle/index.html index 3bc5f508..ffc218f1 100644 --- a/apps/ios/EdgeEver/Resources/EditorBundle/index.html +++ b/apps/ios/EdgeEver/Resources/EditorBundle/index.html @@ -3468,7 +3468,7 @@ l0,-`+(r+144)+`c-2,-159.3,-10,-310.7,-24,-454c-53.3,-528,-210,-949.7, `)},RF="edgeever-inline-sentinel",UWt=(t,e,r)=>{var i;const n=(i=t.storage.markdown)==null?void 0:i.manager;return n!=null&&n.serialize?n.serialize(Tte({type:"doc",content:[{type:"paragraph",content:e}]})).replaceAll(wte,"\\$"):r},yWt=t=>{const e=t.state.selection;if(e.empty||e.from>=e.to)return null;const r=[];if(t.state.doc.nodesBetween(e.from,e.to,(i,a)=>{if(!i.isTextblock)return!0;const s=a+1,o=s+i.content.size,l=Math.max(e.from,s),u=Math.min(e.to,o);return u>l&&r.push({node:i,contentFrom:s,contentTo:o,from:l,to:u}),!1}),r.length===1){const i=r[0],a=i.node.cut(i.from-i.contentFrom,i.to-i.contentFrom).toJSON(),s=t.state.doc.textBetween(i.from,i.to,` `),o=UWt(t,a.content??[],s).trim();return o?{from:i.from,to:i.to,isInline:!0,markdown:o,text:s}:null}const n=mWt(t,e.from,e.to).trim();return n?{from:e.from,to:e.to,isInline:!1,markdown:n,text:t.state.doc.textBetween(e.from,e.to,` -`)}:null},bWt=(t,e,r)=>{var f,p,g;const n=(f=t.storage.markdown)==null?void 0:f.manager,i=e.trim(),a=((p=n==null?void 0:n.parse)==null?void 0:p.call(n,i).content)??[{type:"text",text:i}];if(!r)return a;const s=i.replace(/\s*\n+\s*/g," "),o=(g=n==null?void 0:n.parse)==null?void 0:g.call(n,`${RF}${s}`).content,l=(o==null?void 0:o.length)===1?o[0]:null,u=(l==null?void 0:l.type)==="paragraph"?l.content??[]:[],h=u[0];if((h==null?void 0:h.type)!=="text"||typeof h.text!="string"||!h.text.startsWith(RF))return[{type:"text",text:s}];const d=h.text.slice(RF.length);return[...d?[{...h,text:d}]:[],...u.slice(1)]};function Ud(t){try{const e=JSON.stringify(kee(t.getJSON())),r=HRe(t);fs({type:"change",contentMarkdown:r,contentJson:e})}catch(e){fs({type:"error",message:e instanceof Error?e.message:String(e)})}}function vWt(t,e){const r=e.trim().toLocaleLowerCase();if(r.length===0)return[];const n=[];let i=null;t.state.doc.descendants((l,u)=>{if(!(!l.isText||!l.text)){i!==null&&u>i&&n.push({char:"\0",pos:-1});for(let h=0;hl.char).join("").toLocaleLowerCase(),s=[];let o=a.indexOf(r);for(;o!==-1;){const l=n[o],u=n[o+r.length-1];l&&u&&l.pos>=0&&u.pos>=0&&s.push({from:l.pos,to:u.pos+1}),o=a.indexOf(r,o+r.length)}return s}const bpe=()=>Ur.isActive("taskItem")?"taskItem":"listItem";function VRe(t){var r;if(lM.classList.toggle("editor-mode",t),lM.innerHTML="",!t)return;const e=[{id:"undo",label:"↩",run:()=>Ur.chain().focus().undo().run()},{id:"redo",label:"↪",run:()=>Ur.chain().focus().redo().run()},{id:"image",label:"▧+",run:()=>fs({type:"pickImage"})},{id:"bold",label:"B",run:()=>Ur.chain().focus().toggleBold().run()},{id:"bullet",label:"•",run:()=>Ur.chain().focus().toggleBulletList().run()},{id:"task",label:"☑",run:()=>Ur.chain().focus().toggleTaskList().run()},{id:"indent",label:"⇥",run:()=>Ur.chain().focus().sinkListItem(bpe()).run()},{id:"outdent",label:"⇤",run:()=>Ur.chain().focus().liftListItem(bpe()).run()},{id:"quote",label:"❝",run:()=>Ur.chain().focus().toggleBlockquote().run()},{id:"hr",label:"—",run:()=>Ur.chain().focus().setHorizontalRule().run()}];for(const n of e){const i=document.createElement("button");i.type="button",i.textContent=n.label,i.dataset.action=n.id;const a={undo:{"zh-CN":"撤销","en-US":"Undo",ja:"元に戻す",pl:"Cofnij"},redo:{"zh-CN":"重做","en-US":"Redo",ja:"やり直す",pl:"Ponów"},image:{"zh-CN":"插入图片","en-US":"Insert image",ja:"画像を挿入",pl:"Wstaw obraz"},bold:{"zh-CN":"粗体","en-US":"Bold",ja:"太字",pl:"Pogrubienie"},bullet:{"zh-CN":"项目符号列表","en-US":"Bullet list",ja:"箇条書き",pl:"Lista punktowana"},task:{"zh-CN":"任务清单","en-US":"Task list",ja:"タスクリスト",pl:"Lista zadań"},indent:{"zh-CN":"增加列表缩进","en-US":"Increase list indent",ja:"リストのインデントを増やす",pl:"Zwiększ wcięcie listy"},outdent:{"zh-CN":"减少列表缩进","en-US":"Decrease list indent",ja:"リストのインデントを減らす",pl:"Zmniejsz wcięcie listy"},quote:{"zh-CN":"引用","en-US":"Block quote",ja:"引用",pl:"Cytat"},hr:{"zh-CN":"分隔线","en-US":"Horizontal rule",ja:"区切り線",pl:"Linia pozioma"}};i.setAttribute("aria-label",((r=a[n.id])==null?void 0:r[LL])??n.id),i.addEventListener("click",()=>{n.run(),n.id!=="image"&&Ud(Ur),a4()}),lM.appendChild(i)}a4()}function a4(){const t={bold:Ur.isActive("bold"),bullet:Ur.isActive("bulletList"),task:Ur.isActive("taskList"),quote:Ur.isActive("blockquote")},e=n=>{try{return Ur.can().chain().focus()[n]().run()}catch{return!1}},r={undo:e("undo"),redo:e("redo")};lM.querySelectorAll("button[data-action]").forEach(n=>{const i=n.dataset.action??"";n.classList.toggle("is-active",t[i]??!1),i in r&&(n.disabled=!r[i])})}async function IF(t="light"){Rs.querySelectorAll("[data-placeholder]").forEach(e=>{e.dataset.placeholder=jq}),BRe(Rs),await cWt(Rs),Da==="viewer"&&(!DL||!hWt(Rs,DL,t))&&(xte(),await uWt(Rs,t))}async function xWt(t){var p;if(!t.requestId||t.format!=="png"&&t.format!=="jpeg")return;const e=LC(t.background,t.theme),r=t.fontStyle??"auto",n=t.fontSize??"lg",i=t.cardWidth??"wide",a=SV[i]||SV.wide,s=kV[e]||kV.aurora,o=Ur.view.dom.cloneNode(!0);o.removeAttribute("contenteditable"),o.querySelectorAll("button, [contenteditable='true']").forEach(g=>{g.removeAttribute("contenteditable"),g instanceof HTMLButtonElement&&g.remove()});const l=o.innerHTML,u=document.createElement("div");u.style.cssText=`position:fixed;left:-100000px;top:0;width:${a}px;pointer-events:none;`;const h=document.createElement("style");h.textContent=Fse({theme:e,fontStyle:r,fontSize:n,cardWidth:i});const d=MJe({title:t.title||t.fallbackTitle,notebook:t.notebook,tags:t.tags,updatedAt:t.updatedAt,bodyHtml:l,theme:e,showTitle:t.showTitle??!0,showNotebook:t.showNotebook??!1,showTags:t.showTags??!1,showUpdatedAt:t.showUpdatedAt??!0,showBranding:t.branding??!0});u.appendChild(h),u.insertAdjacentHTML("beforeend",d);const f=u.lastElementChild;f.style.width=`${a}px`,f.style.maxWidth="none",f.style.margin="0",document.body.appendChild(u);try{const g=await $Je({theme:e,fontStyle:r,text:[t.title,t.notebook,...t.tags||[],t.updatedAt,l].join(" "),urls:FJe});h.textContent=g.css+Fse({theme:e,fontStyle:r,fontSize:n,cardWidth:i});const m=Array.from(f.querySelectorAll("img"));await Promise.race([Promise.all([(p=document.fonts)==null?void 0:p.ready,...m.map(async O=>{if(!O.complete)try{await O.decode()}catch{}})]),new Promise(O=>window.setTimeout(O,8e3))]);const U=Array.from(f.querySelectorAll(".edgeever-card-body img")),y=U.filter(O=>!O.complete||O.naturalWidth===0).length,b=Math.max(1,Math.ceil(f.getBoundingClientRect().height)),v=wJe(a,b);let w=0,T=f,S=null;v.sourceScale<1&&(S=document.createElement("div"),S.style.cssText=`position:relative;width:${v.sourceWidth}px;height:${v.sourceHeight}px;overflow:hidden;`,f.replaceWith(S),S.appendChild(f),f.style.transform=`scale(${v.sourceScale})`,f.style.transformOrigin="top left",T=S);const C=TJe[e]||s.canvasBg,k="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=",E=(O,B,F)=>$Et(O,{backgroundColor:C,cacheBust:!1,height:F,imagePlaceholder:k,onImageErrorHandler:z=>{w+=1,z instanceof Event&&z.target instanceof HTMLImageElement&&(z.target.src=k)},pixelRatio:v.pixelRatio,fontEmbedCSS:g.fontCss,width:B});let A;try{A=await E(T,v.sourceWidth,v.sourceHeight)}catch{const O=v.sourceScale*.75;if(O<.5)throw new Error("NOTE_IMAGE_RENDER_FAILED");const B=Math.max(1,Math.floor(a*O)),F=Math.max(1,Math.floor(b*O));S||(S=document.createElement("div"),f.replaceWith(S),S.appendChild(f)),S.style.cssText=`position:relative;width:${B}px;height:${F}px;overflow:hidden;`,f.style.transform=`scale(${O})`,f.style.transformOrigin="top left";try{A=await E(S,B,F)}catch{throw new Error("NOTE_IMAGE_RENDER_FAILED")}}const R=await new Promise((O,B)=>{A.toBlob(F=>F?O(F):B(new Error("Image renderer returned an empty file")),t.format==="jpeg"?"image/jpeg":"image/png",t.format==="jpeg"?.92:1)}),P=t.format==="jpeg"?"jpg":"png",N=EJe(t.title,t.fallbackTitle),I=await oWt(R),L=`${N}.${P}`,_=t.format==="jpeg"?"image/jpeg":"image/png",D=lWt(I);for(let O=0;OsetTimeout(B,0));fs({type:"imageExportComplete",requestId:t.requestId,filename:L,mimeType:_,width:A.width,height:A.height,totalImages:U.length,failedImages:Math.min(U.length,y+w)})}catch(g){fs({type:"imageExportError",requestId:t.requestId,message:g instanceof Error?g.message:"Image export failed"})}finally{u.remove()}}const wWt={resetReaderScroll:gWt,exportImage(t){xWt(t)},configure(t){const e=t.mode==="editor"?"editor":"viewer",r=e!==Da;Da=e,LL=t.locale==="en-US"||t.locale==="ja"||t.locale==="pl"?t.locale:"zh-CN",Ur.setEditable(Da==="editor"),VRe(Da==="editor"),document.documentElement.dataset.theme=t.theme||"light",document.body.classList.toggle("viewer-mode",Da==="viewer"),document.body.classList.toggle("editor-mode",Da==="editor"),t.placeholder&&(jq=t.placeholder,Rs.querySelectorAll("[data-placeholder]").forEach(n=>{n.dataset.placeholder=jq})),Da==="editor"&&r&&requestAnimationFrame(()=>{try{Ur.commands.focus("end")}catch{}}),IF(t.theme||"light")},setMarkdown(t){var n;Z3=!0;const e=Da==="viewer"?xzt(t):null;DL=e;const r=Da==="viewer"?e?"":wzt(t):t;try{Ur.commands.setContent(r||"",{contentType:"markdown"})}catch{try{const i=(n=Ur.storage.markdown)==null?void 0:n.manager;if(i)Ur.commands.setContent(i.parse(r||""));else throw new Error("no markdown manager")}catch{Ur.commands.setContent({type:"doc",content:[{type:"paragraph",content:r?[{type:"text",text:r}]:[]}]})}}Ur.setEditable(Da==="editor"),C0e(Ur),a4(),Z3=!1,IF(document.documentElement.dataset.theme||"light")},setDocumentFromJSON(t){Z3=!0,DL=null,xte();try{const e=JSON.parse(t);Ur.commands.setContent(PNt(aFt(e),LL))}catch{Ur.commands.setContent({type:"doc",content:[{type:"paragraph"}]})}Ur.setEditable(Da==="editor"),C0e(Ur),a4(),Z3=!1,IF(document.documentElement.dataset.theme||"light")},resolveResource(t,e){const r=NL.get(t);r&&(NL.delete(t),r(e))},getMarkdown(){return HRe(Ur)},getDocument(){return JSON.stringify(kee(Ur.getJSON()))},captureSelection(){const t=yWt(Ur);return t?(n3={from:t.from,to:t.to,isInline:t.isInline,documentFingerprint:JSON.stringify(Ur.getJSON())},JSON.stringify({from:t.from,to:t.to,markdown:t.markdown,text:t.text})):(n3=null,null)},applySelectionDraft(t,e){var s,o;const r=n3;if(!r||!t.trim())return!1;if(JSON.stringify(Ur.getJSON())!==r.documentFingerprint)return n3=null,!1;const n=Ur.state.doc.content.size,i=Math.min(Math.max(r.from,0),n),a=Math.min(Math.max(r.to,i),n);try{const l=(s=Ur.storage.markdown)==null?void 0:s.manager,u=(o=l==null?void 0:l.parse)==null?void 0:o.call(l,t),h=e==="replace"?bWt(Ur,t,r.isInline):(u==null?void 0:u.content)??t,d=e==="append"?{from:a,to:a}:{from:i,to:a};return Ur.chain().focus().insertContentAt(d,h).run(),n3=null,Ud(Ur),!0}catch{return!1}},undo(){if(!Ur.can().undo())return!1;const t=Ur.commands.undo();return t&&Ud(Ur),t},focusEnd(){var t;try{Ur.commands.focus("end")}catch{}try{const e=(t=Ur.view)==null?void 0:t.dom;e&&typeof e.focus=="function"&&e.focus({preventScroll:!0})}catch{}},flush(){Ud(Ur)},exec(t){var r;const e={bold:()=>Ur.chain().focus().toggleBold().run(),bulletList:()=>Ur.chain().focus().toggleBulletList().run(),taskList:()=>Ur.chain().focus().toggleTaskList().run(),blockquote:()=>Ur.chain().focus().toggleBlockquote().run(),horizontalRule:()=>Ur.chain().focus().setHorizontalRule().run(),heading2:()=>Ur.chain().focus().toggleHeading({level:2}).run(),codeBlock:()=>Ur.chain().focus().toggleCodeBlock().run()};(r=e[t])==null||r.call(e),Ud(Ur)},beginImageUpload(t,e){Ur.chain().focus().setImage({src:e,alt:t,width:pfe}).run();const r=Rs.querySelectorAll("img"),n=r[r.length-1];n&&(n.dataset.uploadId=t),Ud(Ur)},completeImageUpload(t,e,r){Ur.isEditable&&(Ur.view.dispatch(ZGt(Ur.state,{src:e,alt:r||t||"",width:pfe})),Ud(Ur))},groupImages(t){const e=JGt(Ur,t);return e&&Ud(Ur),e},cancelImageUpload(t){const e=Rs.querySelector(`img[data-upload-id="${t}"]`);e==null||e.remove(),Ud(Ur)},search(t,e){const r=vWt(Ur,t),n=r.length>0?Math.min(Math.max(Number.isFinite(e)?e:0,0),r.length-1):0,i=r[n];if(i){Ur.commands.setTextSelection({from:i.from,to:i.to});try{const a=Ur.view.domAtPos(i.from).node,s=a instanceof Element?a:a.parentElement;s==null||s.scrollIntoView({block:"center",behavior:"smooth"})}catch{}}fs({type:"searchResult",count:r.length,index:n})}};window.EdgeEverEditor=wWt;VRe(!1);fs({type:"ready",startupMs:Math.round(performance.now()-aWt)});class Gt extends Error{constructor(e,r){var n="KaTeX parse error: "+e,i,a,s=r&&r.loc;if(s&&s.start<=s.end){var o=s.lexer.input;i=s.start,a=s.end,i===o.length?n+=" at end of input: ":n+=" at position "+(i+1)+": ";var l=o.slice(i,a).replace(/[^]/g,"$&̲"),u;i>15?u="…"+o.slice(i-15,i):u=o.slice(0,i);var h;a+15t.replace(TWt,"-$1").toLowerCase(),SWt={"&":"&",">":">","<":"<",'"':""","'":"'"},kWt=/[&><"']/g,Qo=t=>String(t).replace(kWt,e=>SWt[e]),uM=t=>t.type==="ordgroup"||t.type==="color"?t.body.length===1?uM(t.body[0]):t:t.type==="font"?uM(t.body):t,EWt=new Set(["mathord","textord","atom"]),hp=t=>EWt.has(uM(t).type),AWt=t=>{var e=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(t);return e?e[2]!==":"||!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(e[1])?null:e[1].toLowerCase():"_relative"},PL={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format "},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color ",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:t=>"#"+t},macros:{type:"object",cli:"-m, --macro ",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:(t,e)=>(e.push(t),e)},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:t=>Math.max(0,t),cli:"--min-rule-thickness ",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily large",processor:t=>Math.max(0,t),cli:"-s, --max-size ",cliProcessor:parseInt},maxExpand:{type:"number",default:1e3,description:"Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to Infinity, the macro expander will try to fully expand as in LaTeX.",processor:t=>Math.max(0,t),cli:"-e, --max-expand ",cliProcessor:t=>t==="Infinity"?1/0:parseInt(t)},globalGroup:{type:"boolean",cli:!1}};function MWt(t){if(typeof t!="string")return t.enum[0];switch(t){case"boolean":return!1;case"string":return"";case"number":return 0;case"object":return{};default:throw new Error("Unexpected schema type; settings must declare an explicit default.")}}function RWt(t){if(t.default!==void 0)return t.default;var e=Array.isArray(t.type)?t.type[0]:t.type;return MWt(e)}function IWt(t,e,r,n){var i=r[e];t[e]=i!==void 0?n.processor?n.processor(i):i:RWt(n)}class Cte{constructor(e){e===void 0&&(e={}),this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,this.globalGroup=void 0,e=e||{};for(var r of Object.keys(PL)){var n=PL[r];n&&IWt(this,r,e,n)}}reportNonstrict(e,r,n){var i=this.strict;if(typeof i=="function"&&(i=i(e,r,n)),!(!i||i==="ignore")){if(i===!0||i==="error")throw new Gt("LaTeX-incompatible input and strict mode is set to 'error': "+(r+" ["+e+"]"),n);i==="warn"?typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(r+" ["+e+"]")):typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+i+"': "+r+" ["+e+"]"))}}useStrictBehavior(e,r,n){var i=this.strict;if(typeof i=="function")try{i=i(e,r,n)}catch{i="error"}return!i||i==="ignore"?!1:i===!0||i==="error"?!0:i==="warn"?(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(r+" ["+e+"]")),!1):(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+i+"': "+r+" ["+e+"]")),!1)}isTrusted(e){if("url"in e&&e.url&&!e.protocol){var r=AWt(e.url);if(r==null)return!1;e.protocol=r}var n=typeof this.trust=="function"?this.trust(e):this.trust;return!!n}}class Pp{constructor(e,r,n){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=e,this.size=r,this.cramped=n}sup(){return Ed[LWt[this.id]]}sub(){return Ed[NWt[this.id]]}fracNum(){return Ed[OWt[this.id]]}fracDen(){return Ed[DWt[this.id]]}cramp(){return Ed[PWt[this.id]]}text(){return Ed[_Wt[this.id]]}isTight(){return this.size>=2}}var Ste=0,_L=1,Ub=2,S0=3,HT=4,Du=5,s4=6,Dl=7,Ed=[new Pp(Ste,0,!1),new Pp(_L,0,!0),new Pp(Ub,1,!1),new Pp(S0,1,!0),new Pp(HT,2,!1),new Pp(Du,2,!0),new Pp(s4,3,!1),new Pp(Dl,3,!0)],LWt=[HT,Du,HT,Du,s4,Dl,s4,Dl],NWt=[Du,Du,Du,Du,Dl,Dl,Dl,Dl],OWt=[Ub,S0,HT,Du,s4,Dl,s4,Dl],DWt=[S0,S0,Du,Du,Dl,Dl,Dl,Dl],PWt=[_L,_L,S0,S0,Du,Du,Dl,Dl],_Wt=[Ste,_L,Ub,S0,Ub,S0,Ub,S0],pn={DISPLAY:Ed[Ste],TEXT:Ed[Ub],SCRIPT:Ed[HT],SCRIPTSCRIPT:Ed[s4]},Yq=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"armenian",blocks:[[1328,1423]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];function BWt(t){for(var e=0;e=i[0]&&t<=i[1])return r.name}return null}var hM=[];Yq.forEach(t=>t.blocks.forEach(e=>hM.push(...e)));function GRe(t){for(var e=0;e=hM[e]&&t<=hM[e+1])return!0;return!1}var Cs=t=>t+" "+t,ky=80,$Wt=function(e,r){return"M95,"+(622+e+r)+` +`)}:null},bWt=(t,e,r)=>{var f,p,g;const n=(f=t.storage.markdown)==null?void 0:f.manager,i=e.trim(),a=((p=n==null?void 0:n.parse)==null?void 0:p.call(n,i).content)??[{type:"text",text:i}];if(!r)return a;const s=i.replace(/\s*\n+\s*/g," "),o=(g=n==null?void 0:n.parse)==null?void 0:g.call(n,`${RF}${s}`).content,l=(o==null?void 0:o.length)===1?o[0]:null,u=(l==null?void 0:l.type)==="paragraph"?l.content??[]:[],h=u[0];if((h==null?void 0:h.type)!=="text"||typeof h.text!="string"||!h.text.startsWith(RF))return[{type:"text",text:s}];const d=h.text.slice(RF.length);return[...d?[{...h,text:d}]:[],...u.slice(1)]};function Ud(t){try{const e=JSON.stringify(kee(t.getJSON())),r=HRe(t);fs({type:"change",contentMarkdown:r,contentJson:e})}catch(e){fs({type:"error",message:e instanceof Error?e.message:String(e)})}}function vWt(t,e){const r=e.trim().toLocaleLowerCase();if(r.length===0)return[];const n=[];let i=null;t.state.doc.descendants((l,u)=>{if(!(!l.isText||!l.text)){i!==null&&u>i&&n.push({char:"\0",pos:-1});for(let h=0;hl.char).join("").toLocaleLowerCase(),s=[];let o=a.indexOf(r);for(;o!==-1;){const l=n[o],u=n[o+r.length-1];l&&u&&l.pos>=0&&u.pos>=0&&s.push({from:l.pos,to:u.pos+1}),o=a.indexOf(r,o+r.length)}return s}const bpe=()=>Ur.isActive("taskItem")?"taskItem":"listItem";function VRe(t){var r;if(lM.classList.toggle("editor-mode",t),lM.innerHTML="",!t)return;const e=[{id:"undo",label:"↩",run:()=>Ur.chain().focus().undo().run()},{id:"redo",label:"↪",run:()=>Ur.chain().focus().redo().run()},{id:"image",label:"▧+",run:()=>fs({type:"pickImage"})},{id:"bold",label:"B",run:()=>Ur.chain().focus().toggleBold().run()},{id:"bullet",label:"•",run:()=>Ur.chain().focus().toggleBulletList().run()},{id:"task",label:"☑",run:()=>Ur.chain().focus().toggleTaskList().run()},{id:"indent",label:"⇥",run:()=>Ur.chain().focus().sinkListItem(bpe()).run()},{id:"outdent",label:"⇤",run:()=>Ur.chain().focus().liftListItem(bpe()).run()},{id:"quote",label:"❝",run:()=>Ur.chain().focus().toggleBlockquote().run()},{id:"hr",label:"—",run:()=>Ur.chain().focus().setHorizontalRule().run()}];for(const n of e){const i=document.createElement("button");i.type="button",i.textContent=n.label,i.dataset.action=n.id;const a={undo:{"zh-CN":"撤销","en-US":"Undo",ja:"元に戻す",pl:"Cofnij"},redo:{"zh-CN":"重做","en-US":"Redo",ja:"やり直す",pl:"Ponów"},image:{"zh-CN":"插入图片","en-US":"Insert image",ja:"画像を挿入",pl:"Wstaw obraz"},bold:{"zh-CN":"粗体","en-US":"Bold",ja:"太字",pl:"Pogrubienie"},bullet:{"zh-CN":"项目符号列表","en-US":"Bullet list",ja:"箇条書き",pl:"Lista punktowana"},task:{"zh-CN":"任务清单","en-US":"Task list",ja:"タスクリスト",pl:"Lista zadań"},indent:{"zh-CN":"增加列表缩进","en-US":"Increase list indent",ja:"リストのインデントを増やす",pl:"Zwiększ wcięcie listy"},outdent:{"zh-CN":"减少列表缩进","en-US":"Decrease list indent",ja:"リストのインデントを減らす",pl:"Zmniejsz wcięcie listy"},quote:{"zh-CN":"引用","en-US":"Block quote",ja:"引用",pl:"Cytat"},hr:{"zh-CN":"分隔线","en-US":"Horizontal rule",ja:"区切り線",pl:"Linia pozioma"}};i.setAttribute("aria-label",((r=a[n.id])==null?void 0:r[LL])??n.id),i.addEventListener("click",()=>{n.run(),n.id!=="image"&&Ud(Ur),a4()}),lM.appendChild(i)}a4()}function a4(){const t={bold:Ur.isActive("bold"),bullet:Ur.isActive("bulletList"),task:Ur.isActive("taskList"),quote:Ur.isActive("blockquote")},e=n=>{try{return Ur.can().chain().focus()[n]().run()}catch{return!1}},r={undo:e("undo"),redo:e("redo")};lM.querySelectorAll("button[data-action]").forEach(n=>{const i=n.dataset.action??"";n.classList.toggle("is-active",t[i]??!1),i in r&&(n.disabled=!r[i])})}async function IF(t="light"){Rs.querySelectorAll("[data-placeholder]").forEach(e=>{e.dataset.placeholder=jq}),BRe(Rs),await cWt(Rs),Da==="viewer"&&(!DL||!hWt(Rs,DL,t))&&(xte(),await uWt(Rs,t))}async function xWt(t){var p;if(!t.requestId||t.format!=="png"&&t.format!=="jpeg")return;const e=LC(t.background,t.theme),r=t.fontStyle??"auto",n=t.fontSize??"lg",i=t.cardWidth??"wide",a=SV[i]||SV.wide,s=kV[e]||kV.aurora,o=Ur.view.dom.cloneNode(!0);o.removeAttribute("contenteditable"),o.querySelectorAll("button, [contenteditable='true']").forEach(g=>{g.removeAttribute("contenteditable"),g instanceof HTMLButtonElement&&g.remove()});const l=o.innerHTML,u=document.createElement("div");u.style.cssText=`position:fixed;left:-100000px;top:0;width:${a}px;pointer-events:none;`;const h=document.createElement("style");h.textContent=Fse({theme:e,fontStyle:r,fontSize:n,cardWidth:i});const d=MJe({title:t.title||t.fallbackTitle,notebook:t.notebook,tags:t.tags,updatedAt:t.updatedAt,bodyHtml:l,theme:e,showTitle:t.showTitle??!0,showNotebook:t.showNotebook??!1,showTags:t.showTags??!1,showUpdatedAt:t.showUpdatedAt??!0,showBranding:t.branding??!0});u.appendChild(h),u.insertAdjacentHTML("beforeend",d);const f=u.lastElementChild;f.style.width=`${a}px`,f.style.maxWidth="none",f.style.margin="0",document.body.appendChild(u);try{const g=await $Je({theme:e,fontStyle:r,text:[t.title,t.notebook,...t.tags||[],t.updatedAt,l].join(" "),urls:FJe});h.textContent=g.css+Fse({theme:e,fontStyle:r,fontSize:n,cardWidth:i});const m=Array.from(f.querySelectorAll("img"));await Promise.race([Promise.all([(p=document.fonts)==null?void 0:p.ready,...m.map(async O=>{if(!O.complete)try{await O.decode()}catch{}})]),new Promise(O=>window.setTimeout(O,8e3))]);const U=Array.from(f.querySelectorAll(".edgeever-card-body img")),y=U.filter(O=>!O.complete||O.naturalWidth===0).length,b=Math.max(1,Math.ceil(f.getBoundingClientRect().height)),v=wJe(a,b);let w=0,T=f,S=null;v.sourceScale<1&&(S=document.createElement("div"),S.style.cssText=`position:relative;width:${v.sourceWidth}px;height:${v.sourceHeight}px;overflow:hidden;`,f.replaceWith(S),S.appendChild(f),f.style.transform=`scale(${v.sourceScale})`,f.style.transformOrigin="top left",T=S);const C=TJe[e]||s.canvasBg,k="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=",E=(O,B,F)=>$Et(O,{backgroundColor:C,cacheBust:!1,height:F,imagePlaceholder:k,onImageErrorHandler:z=>{w+=1,z instanceof Event&&z.target instanceof HTMLImageElement&&(z.target.src=k)},pixelRatio:v.pixelRatio,fontEmbedCSS:g.fontCss,width:B});let A;try{A=await E(T,v.sourceWidth,v.sourceHeight)}catch{const O=v.sourceScale*.75;if(O<.5)throw new Error("NOTE_IMAGE_RENDER_FAILED");const B=Math.max(1,Math.floor(a*O)),F=Math.max(1,Math.floor(b*O));S||(S=document.createElement("div"),f.replaceWith(S),S.appendChild(f)),S.style.cssText=`position:relative;width:${B}px;height:${F}px;overflow:hidden;`,f.style.transform=`scale(${O})`,f.style.transformOrigin="top left";try{A=await E(S,B,F)}catch{throw new Error("NOTE_IMAGE_RENDER_FAILED")}}const R=await new Promise((O,B)=>{A.toBlob(F=>F?O(F):B(new Error("Image renderer returned an empty file")),t.format==="jpeg"?"image/jpeg":"image/png",t.format==="jpeg"?.92:1)}),P=t.format==="jpeg"?"jpg":"png",N=EJe(t.title,t.fallbackTitle),I=await oWt(R),L=`${N}.${P}`,_=t.format==="jpeg"?"image/jpeg":"image/png",D=lWt(I);for(let O=0;OsetTimeout(B,0));fs({type:"imageExportComplete",requestId:t.requestId,filename:L,mimeType:_,width:A.width,height:A.height,totalImages:U.length,failedImages:Math.min(U.length,y+w)})}catch(g){fs({type:"imageExportError",requestId:t.requestId,message:g instanceof Error?g.message:"Image export failed"})}finally{u.remove()}}const wWt={resetReaderScroll:gWt,exportImage(t){xWt(t)},configure(t){const e=t.mode==="editor"?"editor":"viewer",r=e!==Da;Da=e,LL=t.locale==="en-US"||t.locale==="ja"||t.locale==="pl"?t.locale:"zh-CN",Ur.setEditable(Da==="editor",!1),VRe(Da==="editor"),document.documentElement.dataset.theme=t.theme||"light",document.body.classList.toggle("viewer-mode",Da==="viewer"),document.body.classList.toggle("editor-mode",Da==="editor"),t.placeholder&&(jq=t.placeholder,Rs.querySelectorAll("[data-placeholder]").forEach(n=>{n.dataset.placeholder=jq})),Da==="editor"&&r&&requestAnimationFrame(()=>{try{Ur.commands.focus("end")}catch{}}),IF(t.theme||"light")},setMarkdown(t){var n;Z3=!0;const e=Da==="viewer"?xzt(t):null;DL=e;const r=Da==="viewer"?e?"":wzt(t):t;try{Ur.commands.setContent(r||"",{contentType:"markdown"})}catch{try{const i=(n=Ur.storage.markdown)==null?void 0:n.manager;if(i)Ur.commands.setContent(i.parse(r||""));else throw new Error("no markdown manager")}catch{Ur.commands.setContent({type:"doc",content:[{type:"paragraph",content:r?[{type:"text",text:r}]:[]}]})}}Ur.setEditable(Da==="editor"),C0e(Ur),a4(),Z3=!1,IF(document.documentElement.dataset.theme||"light")},setDocumentFromJSON(t){Z3=!0,DL=null,xte();try{const e=JSON.parse(t);Ur.commands.setContent(PNt(aFt(e),LL))}catch{Ur.commands.setContent({type:"doc",content:[{type:"paragraph"}]})}Ur.setEditable(Da==="editor"),C0e(Ur),a4(),Z3=!1,IF(document.documentElement.dataset.theme||"light")},resolveResource(t,e){const r=NL.get(t);r&&(NL.delete(t),r(e))},getMarkdown(){return HRe(Ur)},getDocument(){return JSON.stringify(kee(Ur.getJSON()))},captureSelection(){const t=yWt(Ur);return t?(n3={from:t.from,to:t.to,isInline:t.isInline,documentFingerprint:JSON.stringify(Ur.getJSON())},JSON.stringify({from:t.from,to:t.to,markdown:t.markdown,text:t.text})):(n3=null,null)},applySelectionDraft(t,e){var s,o;const r=n3;if(!r||!t.trim())return!1;if(JSON.stringify(Ur.getJSON())!==r.documentFingerprint)return n3=null,!1;const n=Ur.state.doc.content.size,i=Math.min(Math.max(r.from,0),n),a=Math.min(Math.max(r.to,i),n);try{const l=(s=Ur.storage.markdown)==null?void 0:s.manager,u=(o=l==null?void 0:l.parse)==null?void 0:o.call(l,t),h=e==="replace"?bWt(Ur,t,r.isInline):(u==null?void 0:u.content)??t,d=e==="append"?{from:a,to:a}:{from:i,to:a};return Ur.chain().focus().insertContentAt(d,h).run(),n3=null,Ud(Ur),!0}catch{return!1}},undo(){if(!Ur.can().undo())return!1;const t=Ur.commands.undo();return t&&Ud(Ur),t},focusEnd(){var t;try{Ur.commands.focus("end")}catch{}try{const e=(t=Ur.view)==null?void 0:t.dom;e&&typeof e.focus=="function"&&e.focus({preventScroll:!0})}catch{}},flush(){Ud(Ur)},exec(t){var r;const e={bold:()=>Ur.chain().focus().toggleBold().run(),bulletList:()=>Ur.chain().focus().toggleBulletList().run(),taskList:()=>Ur.chain().focus().toggleTaskList().run(),blockquote:()=>Ur.chain().focus().toggleBlockquote().run(),horizontalRule:()=>Ur.chain().focus().setHorizontalRule().run(),heading2:()=>Ur.chain().focus().toggleHeading({level:2}).run(),codeBlock:()=>Ur.chain().focus().toggleCodeBlock().run()};(r=e[t])==null||r.call(e),Ud(Ur)},beginImageUpload(t,e){Ur.chain().focus().setImage({src:e,alt:t,width:pfe}).run();const r=Rs.querySelectorAll("img"),n=r[r.length-1];n&&(n.dataset.uploadId=t),Ud(Ur)},completeImageUpload(t,e,r){Ur.isEditable&&(Ur.view.dispatch(ZGt(Ur.state,{src:e,alt:r||t||"",width:pfe})),Ud(Ur))},groupImages(t){const e=JGt(Ur,t);return e&&Ud(Ur),e},cancelImageUpload(t){const e=Rs.querySelector(`img[data-upload-id="${t}"]`);e==null||e.remove(),Ud(Ur)},search(t,e){const r=vWt(Ur,t),n=r.length>0?Math.min(Math.max(Number.isFinite(e)?e:0,0),r.length-1):0,i=r[n];if(i){Ur.commands.setTextSelection({from:i.from,to:i.to});try{const a=Ur.view.domAtPos(i.from).node,s=a instanceof Element?a:a.parentElement;s==null||s.scrollIntoView({block:"center",behavior:"smooth"})}catch{}}fs({type:"searchResult",count:r.length,index:n})}};window.EdgeEverEditor=wWt;VRe(!1);fs({type:"ready",startupMs:Math.round(performance.now()-aWt)});class Gt extends Error{constructor(e,r){var n="KaTeX parse error: "+e,i,a,s=r&&r.loc;if(s&&s.start<=s.end){var o=s.lexer.input;i=s.start,a=s.end,i===o.length?n+=" at end of input: ":n+=" at position "+(i+1)+": ";var l=o.slice(i,a).replace(/[^]/g,"$&̲"),u;i>15?u="…"+o.slice(i-15,i):u=o.slice(0,i);var h;a+15t.replace(TWt,"-$1").toLowerCase(),SWt={"&":"&",">":">","<":"<",'"':""","'":"'"},kWt=/[&><"']/g,Qo=t=>String(t).replace(kWt,e=>SWt[e]),uM=t=>t.type==="ordgroup"||t.type==="color"?t.body.length===1?uM(t.body[0]):t:t.type==="font"?uM(t.body):t,EWt=new Set(["mathord","textord","atom"]),hp=t=>EWt.has(uM(t).type),AWt=t=>{var e=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(t);return e?e[2]!==":"||!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(e[1])?null:e[1].toLowerCase():"_relative"},PL={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format "},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color ",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:t=>"#"+t},macros:{type:"object",cli:"-m, --macro ",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:(t,e)=>(e.push(t),e)},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:t=>Math.max(0,t),cli:"--min-rule-thickness ",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily large",processor:t=>Math.max(0,t),cli:"-s, --max-size ",cliProcessor:parseInt},maxExpand:{type:"number",default:1e3,description:"Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to Infinity, the macro expander will try to fully expand as in LaTeX.",processor:t=>Math.max(0,t),cli:"-e, --max-expand ",cliProcessor:t=>t==="Infinity"?1/0:parseInt(t)},globalGroup:{type:"boolean",cli:!1}};function MWt(t){if(typeof t!="string")return t.enum[0];switch(t){case"boolean":return!1;case"string":return"";case"number":return 0;case"object":return{};default:throw new Error("Unexpected schema type; settings must declare an explicit default.")}}function RWt(t){if(t.default!==void 0)return t.default;var e=Array.isArray(t.type)?t.type[0]:t.type;return MWt(e)}function IWt(t,e,r,n){var i=r[e];t[e]=i!==void 0?n.processor?n.processor(i):i:RWt(n)}class Cte{constructor(e){e===void 0&&(e={}),this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,this.globalGroup=void 0,e=e||{};for(var r of Object.keys(PL)){var n=PL[r];n&&IWt(this,r,e,n)}}reportNonstrict(e,r,n){var i=this.strict;if(typeof i=="function"&&(i=i(e,r,n)),!(!i||i==="ignore")){if(i===!0||i==="error")throw new Gt("LaTeX-incompatible input and strict mode is set to 'error': "+(r+" ["+e+"]"),n);i==="warn"?typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(r+" ["+e+"]")):typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+i+"': "+r+" ["+e+"]"))}}useStrictBehavior(e,r,n){var i=this.strict;if(typeof i=="function")try{i=i(e,r,n)}catch{i="error"}return!i||i==="ignore"?!1:i===!0||i==="error"?!0:i==="warn"?(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(r+" ["+e+"]")),!1):(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+i+"': "+r+" ["+e+"]")),!1)}isTrusted(e){if("url"in e&&e.url&&!e.protocol){var r=AWt(e.url);if(r==null)return!1;e.protocol=r}var n=typeof this.trust=="function"?this.trust(e):this.trust;return!!n}}class Pp{constructor(e,r,n){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=e,this.size=r,this.cramped=n}sup(){return Ed[LWt[this.id]]}sub(){return Ed[NWt[this.id]]}fracNum(){return Ed[OWt[this.id]]}fracDen(){return Ed[DWt[this.id]]}cramp(){return Ed[PWt[this.id]]}text(){return Ed[_Wt[this.id]]}isTight(){return this.size>=2}}var Ste=0,_L=1,Ub=2,S0=3,HT=4,Du=5,s4=6,Dl=7,Ed=[new Pp(Ste,0,!1),new Pp(_L,0,!0),new Pp(Ub,1,!1),new Pp(S0,1,!0),new Pp(HT,2,!1),new Pp(Du,2,!0),new Pp(s4,3,!1),new Pp(Dl,3,!0)],LWt=[HT,Du,HT,Du,s4,Dl,s4,Dl],NWt=[Du,Du,Du,Du,Dl,Dl,Dl,Dl],OWt=[Ub,S0,HT,Du,s4,Dl,s4,Dl],DWt=[S0,S0,Du,Du,Dl,Dl,Dl,Dl],PWt=[_L,_L,S0,S0,Du,Du,Dl,Dl],_Wt=[Ste,_L,Ub,S0,Ub,S0,Ub,S0],pn={DISPLAY:Ed[Ste],TEXT:Ed[Ub],SCRIPT:Ed[HT],SCRIPTSCRIPT:Ed[s4]},Yq=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"armenian",blocks:[[1328,1423]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];function BWt(t){for(var e=0;e=i[0]&&t<=i[1])return r.name}return null}var hM=[];Yq.forEach(t=>t.blocks.forEach(e=>hM.push(...e)));function GRe(t){for(var e=0;e=hM[e]&&t<=hM[e+1])return!0;return!1}var Cs=t=>t+" "+t,ky=80,$Wt=function(e,r){return"M95,"+(622+e+r)+` c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14 c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54 c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10 diff --git a/apps/ios/EditorBundle/index.html b/apps/ios/EditorBundle/index.html index 3bc5f508..ffc218f1 100644 --- a/apps/ios/EditorBundle/index.html +++ b/apps/ios/EditorBundle/index.html @@ -3468,7 +3468,7 @@ l0,-`+(r+144)+`c-2,-159.3,-10,-310.7,-24,-454c-53.3,-528,-210,-949.7, `)},RF="edgeever-inline-sentinel",UWt=(t,e,r)=>{var i;const n=(i=t.storage.markdown)==null?void 0:i.manager;return n!=null&&n.serialize?n.serialize(Tte({type:"doc",content:[{type:"paragraph",content:e}]})).replaceAll(wte,"\\$"):r},yWt=t=>{const e=t.state.selection;if(e.empty||e.from>=e.to)return null;const r=[];if(t.state.doc.nodesBetween(e.from,e.to,(i,a)=>{if(!i.isTextblock)return!0;const s=a+1,o=s+i.content.size,l=Math.max(e.from,s),u=Math.min(e.to,o);return u>l&&r.push({node:i,contentFrom:s,contentTo:o,from:l,to:u}),!1}),r.length===1){const i=r[0],a=i.node.cut(i.from-i.contentFrom,i.to-i.contentFrom).toJSON(),s=t.state.doc.textBetween(i.from,i.to,` `),o=UWt(t,a.content??[],s).trim();return o?{from:i.from,to:i.to,isInline:!0,markdown:o,text:s}:null}const n=mWt(t,e.from,e.to).trim();return n?{from:e.from,to:e.to,isInline:!1,markdown:n,text:t.state.doc.textBetween(e.from,e.to,` -`)}:null},bWt=(t,e,r)=>{var f,p,g;const n=(f=t.storage.markdown)==null?void 0:f.manager,i=e.trim(),a=((p=n==null?void 0:n.parse)==null?void 0:p.call(n,i).content)??[{type:"text",text:i}];if(!r)return a;const s=i.replace(/\s*\n+\s*/g," "),o=(g=n==null?void 0:n.parse)==null?void 0:g.call(n,`${RF}${s}`).content,l=(o==null?void 0:o.length)===1?o[0]:null,u=(l==null?void 0:l.type)==="paragraph"?l.content??[]:[],h=u[0];if((h==null?void 0:h.type)!=="text"||typeof h.text!="string"||!h.text.startsWith(RF))return[{type:"text",text:s}];const d=h.text.slice(RF.length);return[...d?[{...h,text:d}]:[],...u.slice(1)]};function Ud(t){try{const e=JSON.stringify(kee(t.getJSON())),r=HRe(t);fs({type:"change",contentMarkdown:r,contentJson:e})}catch(e){fs({type:"error",message:e instanceof Error?e.message:String(e)})}}function vWt(t,e){const r=e.trim().toLocaleLowerCase();if(r.length===0)return[];const n=[];let i=null;t.state.doc.descendants((l,u)=>{if(!(!l.isText||!l.text)){i!==null&&u>i&&n.push({char:"\0",pos:-1});for(let h=0;hl.char).join("").toLocaleLowerCase(),s=[];let o=a.indexOf(r);for(;o!==-1;){const l=n[o],u=n[o+r.length-1];l&&u&&l.pos>=0&&u.pos>=0&&s.push({from:l.pos,to:u.pos+1}),o=a.indexOf(r,o+r.length)}return s}const bpe=()=>Ur.isActive("taskItem")?"taskItem":"listItem";function VRe(t){var r;if(lM.classList.toggle("editor-mode",t),lM.innerHTML="",!t)return;const e=[{id:"undo",label:"↩",run:()=>Ur.chain().focus().undo().run()},{id:"redo",label:"↪",run:()=>Ur.chain().focus().redo().run()},{id:"image",label:"▧+",run:()=>fs({type:"pickImage"})},{id:"bold",label:"B",run:()=>Ur.chain().focus().toggleBold().run()},{id:"bullet",label:"•",run:()=>Ur.chain().focus().toggleBulletList().run()},{id:"task",label:"☑",run:()=>Ur.chain().focus().toggleTaskList().run()},{id:"indent",label:"⇥",run:()=>Ur.chain().focus().sinkListItem(bpe()).run()},{id:"outdent",label:"⇤",run:()=>Ur.chain().focus().liftListItem(bpe()).run()},{id:"quote",label:"❝",run:()=>Ur.chain().focus().toggleBlockquote().run()},{id:"hr",label:"—",run:()=>Ur.chain().focus().setHorizontalRule().run()}];for(const n of e){const i=document.createElement("button");i.type="button",i.textContent=n.label,i.dataset.action=n.id;const a={undo:{"zh-CN":"撤销","en-US":"Undo",ja:"元に戻す",pl:"Cofnij"},redo:{"zh-CN":"重做","en-US":"Redo",ja:"やり直す",pl:"Ponów"},image:{"zh-CN":"插入图片","en-US":"Insert image",ja:"画像を挿入",pl:"Wstaw obraz"},bold:{"zh-CN":"粗体","en-US":"Bold",ja:"太字",pl:"Pogrubienie"},bullet:{"zh-CN":"项目符号列表","en-US":"Bullet list",ja:"箇条書き",pl:"Lista punktowana"},task:{"zh-CN":"任务清单","en-US":"Task list",ja:"タスクリスト",pl:"Lista zadań"},indent:{"zh-CN":"增加列表缩进","en-US":"Increase list indent",ja:"リストのインデントを増やす",pl:"Zwiększ wcięcie listy"},outdent:{"zh-CN":"减少列表缩进","en-US":"Decrease list indent",ja:"リストのインデントを減らす",pl:"Zmniejsz wcięcie listy"},quote:{"zh-CN":"引用","en-US":"Block quote",ja:"引用",pl:"Cytat"},hr:{"zh-CN":"分隔线","en-US":"Horizontal rule",ja:"区切り線",pl:"Linia pozioma"}};i.setAttribute("aria-label",((r=a[n.id])==null?void 0:r[LL])??n.id),i.addEventListener("click",()=>{n.run(),n.id!=="image"&&Ud(Ur),a4()}),lM.appendChild(i)}a4()}function a4(){const t={bold:Ur.isActive("bold"),bullet:Ur.isActive("bulletList"),task:Ur.isActive("taskList"),quote:Ur.isActive("blockquote")},e=n=>{try{return Ur.can().chain().focus()[n]().run()}catch{return!1}},r={undo:e("undo"),redo:e("redo")};lM.querySelectorAll("button[data-action]").forEach(n=>{const i=n.dataset.action??"";n.classList.toggle("is-active",t[i]??!1),i in r&&(n.disabled=!r[i])})}async function IF(t="light"){Rs.querySelectorAll("[data-placeholder]").forEach(e=>{e.dataset.placeholder=jq}),BRe(Rs),await cWt(Rs),Da==="viewer"&&(!DL||!hWt(Rs,DL,t))&&(xte(),await uWt(Rs,t))}async function xWt(t){var p;if(!t.requestId||t.format!=="png"&&t.format!=="jpeg")return;const e=LC(t.background,t.theme),r=t.fontStyle??"auto",n=t.fontSize??"lg",i=t.cardWidth??"wide",a=SV[i]||SV.wide,s=kV[e]||kV.aurora,o=Ur.view.dom.cloneNode(!0);o.removeAttribute("contenteditable"),o.querySelectorAll("button, [contenteditable='true']").forEach(g=>{g.removeAttribute("contenteditable"),g instanceof HTMLButtonElement&&g.remove()});const l=o.innerHTML,u=document.createElement("div");u.style.cssText=`position:fixed;left:-100000px;top:0;width:${a}px;pointer-events:none;`;const h=document.createElement("style");h.textContent=Fse({theme:e,fontStyle:r,fontSize:n,cardWidth:i});const d=MJe({title:t.title||t.fallbackTitle,notebook:t.notebook,tags:t.tags,updatedAt:t.updatedAt,bodyHtml:l,theme:e,showTitle:t.showTitle??!0,showNotebook:t.showNotebook??!1,showTags:t.showTags??!1,showUpdatedAt:t.showUpdatedAt??!0,showBranding:t.branding??!0});u.appendChild(h),u.insertAdjacentHTML("beforeend",d);const f=u.lastElementChild;f.style.width=`${a}px`,f.style.maxWidth="none",f.style.margin="0",document.body.appendChild(u);try{const g=await $Je({theme:e,fontStyle:r,text:[t.title,t.notebook,...t.tags||[],t.updatedAt,l].join(" "),urls:FJe});h.textContent=g.css+Fse({theme:e,fontStyle:r,fontSize:n,cardWidth:i});const m=Array.from(f.querySelectorAll("img"));await Promise.race([Promise.all([(p=document.fonts)==null?void 0:p.ready,...m.map(async O=>{if(!O.complete)try{await O.decode()}catch{}})]),new Promise(O=>window.setTimeout(O,8e3))]);const U=Array.from(f.querySelectorAll(".edgeever-card-body img")),y=U.filter(O=>!O.complete||O.naturalWidth===0).length,b=Math.max(1,Math.ceil(f.getBoundingClientRect().height)),v=wJe(a,b);let w=0,T=f,S=null;v.sourceScale<1&&(S=document.createElement("div"),S.style.cssText=`position:relative;width:${v.sourceWidth}px;height:${v.sourceHeight}px;overflow:hidden;`,f.replaceWith(S),S.appendChild(f),f.style.transform=`scale(${v.sourceScale})`,f.style.transformOrigin="top left",T=S);const C=TJe[e]||s.canvasBg,k="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=",E=(O,B,F)=>$Et(O,{backgroundColor:C,cacheBust:!1,height:F,imagePlaceholder:k,onImageErrorHandler:z=>{w+=1,z instanceof Event&&z.target instanceof HTMLImageElement&&(z.target.src=k)},pixelRatio:v.pixelRatio,fontEmbedCSS:g.fontCss,width:B});let A;try{A=await E(T,v.sourceWidth,v.sourceHeight)}catch{const O=v.sourceScale*.75;if(O<.5)throw new Error("NOTE_IMAGE_RENDER_FAILED");const B=Math.max(1,Math.floor(a*O)),F=Math.max(1,Math.floor(b*O));S||(S=document.createElement("div"),f.replaceWith(S),S.appendChild(f)),S.style.cssText=`position:relative;width:${B}px;height:${F}px;overflow:hidden;`,f.style.transform=`scale(${O})`,f.style.transformOrigin="top left";try{A=await E(S,B,F)}catch{throw new Error("NOTE_IMAGE_RENDER_FAILED")}}const R=await new Promise((O,B)=>{A.toBlob(F=>F?O(F):B(new Error("Image renderer returned an empty file")),t.format==="jpeg"?"image/jpeg":"image/png",t.format==="jpeg"?.92:1)}),P=t.format==="jpeg"?"jpg":"png",N=EJe(t.title,t.fallbackTitle),I=await oWt(R),L=`${N}.${P}`,_=t.format==="jpeg"?"image/jpeg":"image/png",D=lWt(I);for(let O=0;OsetTimeout(B,0));fs({type:"imageExportComplete",requestId:t.requestId,filename:L,mimeType:_,width:A.width,height:A.height,totalImages:U.length,failedImages:Math.min(U.length,y+w)})}catch(g){fs({type:"imageExportError",requestId:t.requestId,message:g instanceof Error?g.message:"Image export failed"})}finally{u.remove()}}const wWt={resetReaderScroll:gWt,exportImage(t){xWt(t)},configure(t){const e=t.mode==="editor"?"editor":"viewer",r=e!==Da;Da=e,LL=t.locale==="en-US"||t.locale==="ja"||t.locale==="pl"?t.locale:"zh-CN",Ur.setEditable(Da==="editor"),VRe(Da==="editor"),document.documentElement.dataset.theme=t.theme||"light",document.body.classList.toggle("viewer-mode",Da==="viewer"),document.body.classList.toggle("editor-mode",Da==="editor"),t.placeholder&&(jq=t.placeholder,Rs.querySelectorAll("[data-placeholder]").forEach(n=>{n.dataset.placeholder=jq})),Da==="editor"&&r&&requestAnimationFrame(()=>{try{Ur.commands.focus("end")}catch{}}),IF(t.theme||"light")},setMarkdown(t){var n;Z3=!0;const e=Da==="viewer"?xzt(t):null;DL=e;const r=Da==="viewer"?e?"":wzt(t):t;try{Ur.commands.setContent(r||"",{contentType:"markdown"})}catch{try{const i=(n=Ur.storage.markdown)==null?void 0:n.manager;if(i)Ur.commands.setContent(i.parse(r||""));else throw new Error("no markdown manager")}catch{Ur.commands.setContent({type:"doc",content:[{type:"paragraph",content:r?[{type:"text",text:r}]:[]}]})}}Ur.setEditable(Da==="editor"),C0e(Ur),a4(),Z3=!1,IF(document.documentElement.dataset.theme||"light")},setDocumentFromJSON(t){Z3=!0,DL=null,xte();try{const e=JSON.parse(t);Ur.commands.setContent(PNt(aFt(e),LL))}catch{Ur.commands.setContent({type:"doc",content:[{type:"paragraph"}]})}Ur.setEditable(Da==="editor"),C0e(Ur),a4(),Z3=!1,IF(document.documentElement.dataset.theme||"light")},resolveResource(t,e){const r=NL.get(t);r&&(NL.delete(t),r(e))},getMarkdown(){return HRe(Ur)},getDocument(){return JSON.stringify(kee(Ur.getJSON()))},captureSelection(){const t=yWt(Ur);return t?(n3={from:t.from,to:t.to,isInline:t.isInline,documentFingerprint:JSON.stringify(Ur.getJSON())},JSON.stringify({from:t.from,to:t.to,markdown:t.markdown,text:t.text})):(n3=null,null)},applySelectionDraft(t,e){var s,o;const r=n3;if(!r||!t.trim())return!1;if(JSON.stringify(Ur.getJSON())!==r.documentFingerprint)return n3=null,!1;const n=Ur.state.doc.content.size,i=Math.min(Math.max(r.from,0),n),a=Math.min(Math.max(r.to,i),n);try{const l=(s=Ur.storage.markdown)==null?void 0:s.manager,u=(o=l==null?void 0:l.parse)==null?void 0:o.call(l,t),h=e==="replace"?bWt(Ur,t,r.isInline):(u==null?void 0:u.content)??t,d=e==="append"?{from:a,to:a}:{from:i,to:a};return Ur.chain().focus().insertContentAt(d,h).run(),n3=null,Ud(Ur),!0}catch{return!1}},undo(){if(!Ur.can().undo())return!1;const t=Ur.commands.undo();return t&&Ud(Ur),t},focusEnd(){var t;try{Ur.commands.focus("end")}catch{}try{const e=(t=Ur.view)==null?void 0:t.dom;e&&typeof e.focus=="function"&&e.focus({preventScroll:!0})}catch{}},flush(){Ud(Ur)},exec(t){var r;const e={bold:()=>Ur.chain().focus().toggleBold().run(),bulletList:()=>Ur.chain().focus().toggleBulletList().run(),taskList:()=>Ur.chain().focus().toggleTaskList().run(),blockquote:()=>Ur.chain().focus().toggleBlockquote().run(),horizontalRule:()=>Ur.chain().focus().setHorizontalRule().run(),heading2:()=>Ur.chain().focus().toggleHeading({level:2}).run(),codeBlock:()=>Ur.chain().focus().toggleCodeBlock().run()};(r=e[t])==null||r.call(e),Ud(Ur)},beginImageUpload(t,e){Ur.chain().focus().setImage({src:e,alt:t,width:pfe}).run();const r=Rs.querySelectorAll("img"),n=r[r.length-1];n&&(n.dataset.uploadId=t),Ud(Ur)},completeImageUpload(t,e,r){Ur.isEditable&&(Ur.view.dispatch(ZGt(Ur.state,{src:e,alt:r||t||"",width:pfe})),Ud(Ur))},groupImages(t){const e=JGt(Ur,t);return e&&Ud(Ur),e},cancelImageUpload(t){const e=Rs.querySelector(`img[data-upload-id="${t}"]`);e==null||e.remove(),Ud(Ur)},search(t,e){const r=vWt(Ur,t),n=r.length>0?Math.min(Math.max(Number.isFinite(e)?e:0,0),r.length-1):0,i=r[n];if(i){Ur.commands.setTextSelection({from:i.from,to:i.to});try{const a=Ur.view.domAtPos(i.from).node,s=a instanceof Element?a:a.parentElement;s==null||s.scrollIntoView({block:"center",behavior:"smooth"})}catch{}}fs({type:"searchResult",count:r.length,index:n})}};window.EdgeEverEditor=wWt;VRe(!1);fs({type:"ready",startupMs:Math.round(performance.now()-aWt)});class Gt extends Error{constructor(e,r){var n="KaTeX parse error: "+e,i,a,s=r&&r.loc;if(s&&s.start<=s.end){var o=s.lexer.input;i=s.start,a=s.end,i===o.length?n+=" at end of input: ":n+=" at position "+(i+1)+": ";var l=o.slice(i,a).replace(/[^]/g,"$&̲"),u;i>15?u="…"+o.slice(i-15,i):u=o.slice(0,i);var h;a+15t.replace(TWt,"-$1").toLowerCase(),SWt={"&":"&",">":">","<":"<",'"':""","'":"'"},kWt=/[&><"']/g,Qo=t=>String(t).replace(kWt,e=>SWt[e]),uM=t=>t.type==="ordgroup"||t.type==="color"?t.body.length===1?uM(t.body[0]):t:t.type==="font"?uM(t.body):t,EWt=new Set(["mathord","textord","atom"]),hp=t=>EWt.has(uM(t).type),AWt=t=>{var e=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(t);return e?e[2]!==":"||!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(e[1])?null:e[1].toLowerCase():"_relative"},PL={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format "},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color ",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:t=>"#"+t},macros:{type:"object",cli:"-m, --macro ",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:(t,e)=>(e.push(t),e)},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:t=>Math.max(0,t),cli:"--min-rule-thickness ",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily large",processor:t=>Math.max(0,t),cli:"-s, --max-size ",cliProcessor:parseInt},maxExpand:{type:"number",default:1e3,description:"Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to Infinity, the macro expander will try to fully expand as in LaTeX.",processor:t=>Math.max(0,t),cli:"-e, --max-expand ",cliProcessor:t=>t==="Infinity"?1/0:parseInt(t)},globalGroup:{type:"boolean",cli:!1}};function MWt(t){if(typeof t!="string")return t.enum[0];switch(t){case"boolean":return!1;case"string":return"";case"number":return 0;case"object":return{};default:throw new Error("Unexpected schema type; settings must declare an explicit default.")}}function RWt(t){if(t.default!==void 0)return t.default;var e=Array.isArray(t.type)?t.type[0]:t.type;return MWt(e)}function IWt(t,e,r,n){var i=r[e];t[e]=i!==void 0?n.processor?n.processor(i):i:RWt(n)}class Cte{constructor(e){e===void 0&&(e={}),this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,this.globalGroup=void 0,e=e||{};for(var r of Object.keys(PL)){var n=PL[r];n&&IWt(this,r,e,n)}}reportNonstrict(e,r,n){var i=this.strict;if(typeof i=="function"&&(i=i(e,r,n)),!(!i||i==="ignore")){if(i===!0||i==="error")throw new Gt("LaTeX-incompatible input and strict mode is set to 'error': "+(r+" ["+e+"]"),n);i==="warn"?typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(r+" ["+e+"]")):typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+i+"': "+r+" ["+e+"]"))}}useStrictBehavior(e,r,n){var i=this.strict;if(typeof i=="function")try{i=i(e,r,n)}catch{i="error"}return!i||i==="ignore"?!1:i===!0||i==="error"?!0:i==="warn"?(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(r+" ["+e+"]")),!1):(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+i+"': "+r+" ["+e+"]")),!1)}isTrusted(e){if("url"in e&&e.url&&!e.protocol){var r=AWt(e.url);if(r==null)return!1;e.protocol=r}var n=typeof this.trust=="function"?this.trust(e):this.trust;return!!n}}class Pp{constructor(e,r,n){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=e,this.size=r,this.cramped=n}sup(){return Ed[LWt[this.id]]}sub(){return Ed[NWt[this.id]]}fracNum(){return Ed[OWt[this.id]]}fracDen(){return Ed[DWt[this.id]]}cramp(){return Ed[PWt[this.id]]}text(){return Ed[_Wt[this.id]]}isTight(){return this.size>=2}}var Ste=0,_L=1,Ub=2,S0=3,HT=4,Du=5,s4=6,Dl=7,Ed=[new Pp(Ste,0,!1),new Pp(_L,0,!0),new Pp(Ub,1,!1),new Pp(S0,1,!0),new Pp(HT,2,!1),new Pp(Du,2,!0),new Pp(s4,3,!1),new Pp(Dl,3,!0)],LWt=[HT,Du,HT,Du,s4,Dl,s4,Dl],NWt=[Du,Du,Du,Du,Dl,Dl,Dl,Dl],OWt=[Ub,S0,HT,Du,s4,Dl,s4,Dl],DWt=[S0,S0,Du,Du,Dl,Dl,Dl,Dl],PWt=[_L,_L,S0,S0,Du,Du,Dl,Dl],_Wt=[Ste,_L,Ub,S0,Ub,S0,Ub,S0],pn={DISPLAY:Ed[Ste],TEXT:Ed[Ub],SCRIPT:Ed[HT],SCRIPTSCRIPT:Ed[s4]},Yq=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"armenian",blocks:[[1328,1423]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];function BWt(t){for(var e=0;e=i[0]&&t<=i[1])return r.name}return null}var hM=[];Yq.forEach(t=>t.blocks.forEach(e=>hM.push(...e)));function GRe(t){for(var e=0;e=hM[e]&&t<=hM[e+1])return!0;return!1}var Cs=t=>t+" "+t,ky=80,$Wt=function(e,r){return"M95,"+(622+e+r)+` +`)}:null},bWt=(t,e,r)=>{var f,p,g;const n=(f=t.storage.markdown)==null?void 0:f.manager,i=e.trim(),a=((p=n==null?void 0:n.parse)==null?void 0:p.call(n,i).content)??[{type:"text",text:i}];if(!r)return a;const s=i.replace(/\s*\n+\s*/g," "),o=(g=n==null?void 0:n.parse)==null?void 0:g.call(n,`${RF}${s}`).content,l=(o==null?void 0:o.length)===1?o[0]:null,u=(l==null?void 0:l.type)==="paragraph"?l.content??[]:[],h=u[0];if((h==null?void 0:h.type)!=="text"||typeof h.text!="string"||!h.text.startsWith(RF))return[{type:"text",text:s}];const d=h.text.slice(RF.length);return[...d?[{...h,text:d}]:[],...u.slice(1)]};function Ud(t){try{const e=JSON.stringify(kee(t.getJSON())),r=HRe(t);fs({type:"change",contentMarkdown:r,contentJson:e})}catch(e){fs({type:"error",message:e instanceof Error?e.message:String(e)})}}function vWt(t,e){const r=e.trim().toLocaleLowerCase();if(r.length===0)return[];const n=[];let i=null;t.state.doc.descendants((l,u)=>{if(!(!l.isText||!l.text)){i!==null&&u>i&&n.push({char:"\0",pos:-1});for(let h=0;hl.char).join("").toLocaleLowerCase(),s=[];let o=a.indexOf(r);for(;o!==-1;){const l=n[o],u=n[o+r.length-1];l&&u&&l.pos>=0&&u.pos>=0&&s.push({from:l.pos,to:u.pos+1}),o=a.indexOf(r,o+r.length)}return s}const bpe=()=>Ur.isActive("taskItem")?"taskItem":"listItem";function VRe(t){var r;if(lM.classList.toggle("editor-mode",t),lM.innerHTML="",!t)return;const e=[{id:"undo",label:"↩",run:()=>Ur.chain().focus().undo().run()},{id:"redo",label:"↪",run:()=>Ur.chain().focus().redo().run()},{id:"image",label:"▧+",run:()=>fs({type:"pickImage"})},{id:"bold",label:"B",run:()=>Ur.chain().focus().toggleBold().run()},{id:"bullet",label:"•",run:()=>Ur.chain().focus().toggleBulletList().run()},{id:"task",label:"☑",run:()=>Ur.chain().focus().toggleTaskList().run()},{id:"indent",label:"⇥",run:()=>Ur.chain().focus().sinkListItem(bpe()).run()},{id:"outdent",label:"⇤",run:()=>Ur.chain().focus().liftListItem(bpe()).run()},{id:"quote",label:"❝",run:()=>Ur.chain().focus().toggleBlockquote().run()},{id:"hr",label:"—",run:()=>Ur.chain().focus().setHorizontalRule().run()}];for(const n of e){const i=document.createElement("button");i.type="button",i.textContent=n.label,i.dataset.action=n.id;const a={undo:{"zh-CN":"撤销","en-US":"Undo",ja:"元に戻す",pl:"Cofnij"},redo:{"zh-CN":"重做","en-US":"Redo",ja:"やり直す",pl:"Ponów"},image:{"zh-CN":"插入图片","en-US":"Insert image",ja:"画像を挿入",pl:"Wstaw obraz"},bold:{"zh-CN":"粗体","en-US":"Bold",ja:"太字",pl:"Pogrubienie"},bullet:{"zh-CN":"项目符号列表","en-US":"Bullet list",ja:"箇条書き",pl:"Lista punktowana"},task:{"zh-CN":"任务清单","en-US":"Task list",ja:"タスクリスト",pl:"Lista zadań"},indent:{"zh-CN":"增加列表缩进","en-US":"Increase list indent",ja:"リストのインデントを増やす",pl:"Zwiększ wcięcie listy"},outdent:{"zh-CN":"减少列表缩进","en-US":"Decrease list indent",ja:"リストのインデントを減らす",pl:"Zmniejsz wcięcie listy"},quote:{"zh-CN":"引用","en-US":"Block quote",ja:"引用",pl:"Cytat"},hr:{"zh-CN":"分隔线","en-US":"Horizontal rule",ja:"区切り線",pl:"Linia pozioma"}};i.setAttribute("aria-label",((r=a[n.id])==null?void 0:r[LL])??n.id),i.addEventListener("click",()=>{n.run(),n.id!=="image"&&Ud(Ur),a4()}),lM.appendChild(i)}a4()}function a4(){const t={bold:Ur.isActive("bold"),bullet:Ur.isActive("bulletList"),task:Ur.isActive("taskList"),quote:Ur.isActive("blockquote")},e=n=>{try{return Ur.can().chain().focus()[n]().run()}catch{return!1}},r={undo:e("undo"),redo:e("redo")};lM.querySelectorAll("button[data-action]").forEach(n=>{const i=n.dataset.action??"";n.classList.toggle("is-active",t[i]??!1),i in r&&(n.disabled=!r[i])})}async function IF(t="light"){Rs.querySelectorAll("[data-placeholder]").forEach(e=>{e.dataset.placeholder=jq}),BRe(Rs),await cWt(Rs),Da==="viewer"&&(!DL||!hWt(Rs,DL,t))&&(xte(),await uWt(Rs,t))}async function xWt(t){var p;if(!t.requestId||t.format!=="png"&&t.format!=="jpeg")return;const e=LC(t.background,t.theme),r=t.fontStyle??"auto",n=t.fontSize??"lg",i=t.cardWidth??"wide",a=SV[i]||SV.wide,s=kV[e]||kV.aurora,o=Ur.view.dom.cloneNode(!0);o.removeAttribute("contenteditable"),o.querySelectorAll("button, [contenteditable='true']").forEach(g=>{g.removeAttribute("contenteditable"),g instanceof HTMLButtonElement&&g.remove()});const l=o.innerHTML,u=document.createElement("div");u.style.cssText=`position:fixed;left:-100000px;top:0;width:${a}px;pointer-events:none;`;const h=document.createElement("style");h.textContent=Fse({theme:e,fontStyle:r,fontSize:n,cardWidth:i});const d=MJe({title:t.title||t.fallbackTitle,notebook:t.notebook,tags:t.tags,updatedAt:t.updatedAt,bodyHtml:l,theme:e,showTitle:t.showTitle??!0,showNotebook:t.showNotebook??!1,showTags:t.showTags??!1,showUpdatedAt:t.showUpdatedAt??!0,showBranding:t.branding??!0});u.appendChild(h),u.insertAdjacentHTML("beforeend",d);const f=u.lastElementChild;f.style.width=`${a}px`,f.style.maxWidth="none",f.style.margin="0",document.body.appendChild(u);try{const g=await $Je({theme:e,fontStyle:r,text:[t.title,t.notebook,...t.tags||[],t.updatedAt,l].join(" "),urls:FJe});h.textContent=g.css+Fse({theme:e,fontStyle:r,fontSize:n,cardWidth:i});const m=Array.from(f.querySelectorAll("img"));await Promise.race([Promise.all([(p=document.fonts)==null?void 0:p.ready,...m.map(async O=>{if(!O.complete)try{await O.decode()}catch{}})]),new Promise(O=>window.setTimeout(O,8e3))]);const U=Array.from(f.querySelectorAll(".edgeever-card-body img")),y=U.filter(O=>!O.complete||O.naturalWidth===0).length,b=Math.max(1,Math.ceil(f.getBoundingClientRect().height)),v=wJe(a,b);let w=0,T=f,S=null;v.sourceScale<1&&(S=document.createElement("div"),S.style.cssText=`position:relative;width:${v.sourceWidth}px;height:${v.sourceHeight}px;overflow:hidden;`,f.replaceWith(S),S.appendChild(f),f.style.transform=`scale(${v.sourceScale})`,f.style.transformOrigin="top left",T=S);const C=TJe[e]||s.canvasBg,k="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=",E=(O,B,F)=>$Et(O,{backgroundColor:C,cacheBust:!1,height:F,imagePlaceholder:k,onImageErrorHandler:z=>{w+=1,z instanceof Event&&z.target instanceof HTMLImageElement&&(z.target.src=k)},pixelRatio:v.pixelRatio,fontEmbedCSS:g.fontCss,width:B});let A;try{A=await E(T,v.sourceWidth,v.sourceHeight)}catch{const O=v.sourceScale*.75;if(O<.5)throw new Error("NOTE_IMAGE_RENDER_FAILED");const B=Math.max(1,Math.floor(a*O)),F=Math.max(1,Math.floor(b*O));S||(S=document.createElement("div"),f.replaceWith(S),S.appendChild(f)),S.style.cssText=`position:relative;width:${B}px;height:${F}px;overflow:hidden;`,f.style.transform=`scale(${O})`,f.style.transformOrigin="top left";try{A=await E(S,B,F)}catch{throw new Error("NOTE_IMAGE_RENDER_FAILED")}}const R=await new Promise((O,B)=>{A.toBlob(F=>F?O(F):B(new Error("Image renderer returned an empty file")),t.format==="jpeg"?"image/jpeg":"image/png",t.format==="jpeg"?.92:1)}),P=t.format==="jpeg"?"jpg":"png",N=EJe(t.title,t.fallbackTitle),I=await oWt(R),L=`${N}.${P}`,_=t.format==="jpeg"?"image/jpeg":"image/png",D=lWt(I);for(let O=0;OsetTimeout(B,0));fs({type:"imageExportComplete",requestId:t.requestId,filename:L,mimeType:_,width:A.width,height:A.height,totalImages:U.length,failedImages:Math.min(U.length,y+w)})}catch(g){fs({type:"imageExportError",requestId:t.requestId,message:g instanceof Error?g.message:"Image export failed"})}finally{u.remove()}}const wWt={resetReaderScroll:gWt,exportImage(t){xWt(t)},configure(t){const e=t.mode==="editor"?"editor":"viewer",r=e!==Da;Da=e,LL=t.locale==="en-US"||t.locale==="ja"||t.locale==="pl"?t.locale:"zh-CN",Ur.setEditable(Da==="editor",!1),VRe(Da==="editor"),document.documentElement.dataset.theme=t.theme||"light",document.body.classList.toggle("viewer-mode",Da==="viewer"),document.body.classList.toggle("editor-mode",Da==="editor"),t.placeholder&&(jq=t.placeholder,Rs.querySelectorAll("[data-placeholder]").forEach(n=>{n.dataset.placeholder=jq})),Da==="editor"&&r&&requestAnimationFrame(()=>{try{Ur.commands.focus("end")}catch{}}),IF(t.theme||"light")},setMarkdown(t){var n;Z3=!0;const e=Da==="viewer"?xzt(t):null;DL=e;const r=Da==="viewer"?e?"":wzt(t):t;try{Ur.commands.setContent(r||"",{contentType:"markdown"})}catch{try{const i=(n=Ur.storage.markdown)==null?void 0:n.manager;if(i)Ur.commands.setContent(i.parse(r||""));else throw new Error("no markdown manager")}catch{Ur.commands.setContent({type:"doc",content:[{type:"paragraph",content:r?[{type:"text",text:r}]:[]}]})}}Ur.setEditable(Da==="editor"),C0e(Ur),a4(),Z3=!1,IF(document.documentElement.dataset.theme||"light")},setDocumentFromJSON(t){Z3=!0,DL=null,xte();try{const e=JSON.parse(t);Ur.commands.setContent(PNt(aFt(e),LL))}catch{Ur.commands.setContent({type:"doc",content:[{type:"paragraph"}]})}Ur.setEditable(Da==="editor"),C0e(Ur),a4(),Z3=!1,IF(document.documentElement.dataset.theme||"light")},resolveResource(t,e){const r=NL.get(t);r&&(NL.delete(t),r(e))},getMarkdown(){return HRe(Ur)},getDocument(){return JSON.stringify(kee(Ur.getJSON()))},captureSelection(){const t=yWt(Ur);return t?(n3={from:t.from,to:t.to,isInline:t.isInline,documentFingerprint:JSON.stringify(Ur.getJSON())},JSON.stringify({from:t.from,to:t.to,markdown:t.markdown,text:t.text})):(n3=null,null)},applySelectionDraft(t,e){var s,o;const r=n3;if(!r||!t.trim())return!1;if(JSON.stringify(Ur.getJSON())!==r.documentFingerprint)return n3=null,!1;const n=Ur.state.doc.content.size,i=Math.min(Math.max(r.from,0),n),a=Math.min(Math.max(r.to,i),n);try{const l=(s=Ur.storage.markdown)==null?void 0:s.manager,u=(o=l==null?void 0:l.parse)==null?void 0:o.call(l,t),h=e==="replace"?bWt(Ur,t,r.isInline):(u==null?void 0:u.content)??t,d=e==="append"?{from:a,to:a}:{from:i,to:a};return Ur.chain().focus().insertContentAt(d,h).run(),n3=null,Ud(Ur),!0}catch{return!1}},undo(){if(!Ur.can().undo())return!1;const t=Ur.commands.undo();return t&&Ud(Ur),t},focusEnd(){var t;try{Ur.commands.focus("end")}catch{}try{const e=(t=Ur.view)==null?void 0:t.dom;e&&typeof e.focus=="function"&&e.focus({preventScroll:!0})}catch{}},flush(){Ud(Ur)},exec(t){var r;const e={bold:()=>Ur.chain().focus().toggleBold().run(),bulletList:()=>Ur.chain().focus().toggleBulletList().run(),taskList:()=>Ur.chain().focus().toggleTaskList().run(),blockquote:()=>Ur.chain().focus().toggleBlockquote().run(),horizontalRule:()=>Ur.chain().focus().setHorizontalRule().run(),heading2:()=>Ur.chain().focus().toggleHeading({level:2}).run(),codeBlock:()=>Ur.chain().focus().toggleCodeBlock().run()};(r=e[t])==null||r.call(e),Ud(Ur)},beginImageUpload(t,e){Ur.chain().focus().setImage({src:e,alt:t,width:pfe}).run();const r=Rs.querySelectorAll("img"),n=r[r.length-1];n&&(n.dataset.uploadId=t),Ud(Ur)},completeImageUpload(t,e,r){Ur.isEditable&&(Ur.view.dispatch(ZGt(Ur.state,{src:e,alt:r||t||"",width:pfe})),Ud(Ur))},groupImages(t){const e=JGt(Ur,t);return e&&Ud(Ur),e},cancelImageUpload(t){const e=Rs.querySelector(`img[data-upload-id="${t}"]`);e==null||e.remove(),Ud(Ur)},search(t,e){const r=vWt(Ur,t),n=r.length>0?Math.min(Math.max(Number.isFinite(e)?e:0,0),r.length-1):0,i=r[n];if(i){Ur.commands.setTextSelection({from:i.from,to:i.to});try{const a=Ur.view.domAtPos(i.from).node,s=a instanceof Element?a:a.parentElement;s==null||s.scrollIntoView({block:"center",behavior:"smooth"})}catch{}}fs({type:"searchResult",count:r.length,index:n})}};window.EdgeEverEditor=wWt;VRe(!1);fs({type:"ready",startupMs:Math.round(performance.now()-aWt)});class Gt extends Error{constructor(e,r){var n="KaTeX parse error: "+e,i,a,s=r&&r.loc;if(s&&s.start<=s.end){var o=s.lexer.input;i=s.start,a=s.end,i===o.length?n+=" at end of input: ":n+=" at position "+(i+1)+": ";var l=o.slice(i,a).replace(/[^]/g,"$&̲"),u;i>15?u="…"+o.slice(i-15,i):u=o.slice(0,i);var h;a+15t.replace(TWt,"-$1").toLowerCase(),SWt={"&":"&",">":">","<":"<",'"':""","'":"'"},kWt=/[&><"']/g,Qo=t=>String(t).replace(kWt,e=>SWt[e]),uM=t=>t.type==="ordgroup"||t.type==="color"?t.body.length===1?uM(t.body[0]):t:t.type==="font"?uM(t.body):t,EWt=new Set(["mathord","textord","atom"]),hp=t=>EWt.has(uM(t).type),AWt=t=>{var e=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(t);return e?e[2]!==":"||!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(e[1])?null:e[1].toLowerCase():"_relative"},PL={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format "},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color ",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:t=>"#"+t},macros:{type:"object",cli:"-m, --macro ",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:(t,e)=>(e.push(t),e)},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:t=>Math.max(0,t),cli:"--min-rule-thickness ",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily large",processor:t=>Math.max(0,t),cli:"-s, --max-size ",cliProcessor:parseInt},maxExpand:{type:"number",default:1e3,description:"Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to Infinity, the macro expander will try to fully expand as in LaTeX.",processor:t=>Math.max(0,t),cli:"-e, --max-expand ",cliProcessor:t=>t==="Infinity"?1/0:parseInt(t)},globalGroup:{type:"boolean",cli:!1}};function MWt(t){if(typeof t!="string")return t.enum[0];switch(t){case"boolean":return!1;case"string":return"";case"number":return 0;case"object":return{};default:throw new Error("Unexpected schema type; settings must declare an explicit default.")}}function RWt(t){if(t.default!==void 0)return t.default;var e=Array.isArray(t.type)?t.type[0]:t.type;return MWt(e)}function IWt(t,e,r,n){var i=r[e];t[e]=i!==void 0?n.processor?n.processor(i):i:RWt(n)}class Cte{constructor(e){e===void 0&&(e={}),this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,this.globalGroup=void 0,e=e||{};for(var r of Object.keys(PL)){var n=PL[r];n&&IWt(this,r,e,n)}}reportNonstrict(e,r,n){var i=this.strict;if(typeof i=="function"&&(i=i(e,r,n)),!(!i||i==="ignore")){if(i===!0||i==="error")throw new Gt("LaTeX-incompatible input and strict mode is set to 'error': "+(r+" ["+e+"]"),n);i==="warn"?typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(r+" ["+e+"]")):typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+i+"': "+r+" ["+e+"]"))}}useStrictBehavior(e,r,n){var i=this.strict;if(typeof i=="function")try{i=i(e,r,n)}catch{i="error"}return!i||i==="ignore"?!1:i===!0||i==="error"?!0:i==="warn"?(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(r+" ["+e+"]")),!1):(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+i+"': "+r+" ["+e+"]")),!1)}isTrusted(e){if("url"in e&&e.url&&!e.protocol){var r=AWt(e.url);if(r==null)return!1;e.protocol=r}var n=typeof this.trust=="function"?this.trust(e):this.trust;return!!n}}class Pp{constructor(e,r,n){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=e,this.size=r,this.cramped=n}sup(){return Ed[LWt[this.id]]}sub(){return Ed[NWt[this.id]]}fracNum(){return Ed[OWt[this.id]]}fracDen(){return Ed[DWt[this.id]]}cramp(){return Ed[PWt[this.id]]}text(){return Ed[_Wt[this.id]]}isTight(){return this.size>=2}}var Ste=0,_L=1,Ub=2,S0=3,HT=4,Du=5,s4=6,Dl=7,Ed=[new Pp(Ste,0,!1),new Pp(_L,0,!0),new Pp(Ub,1,!1),new Pp(S0,1,!0),new Pp(HT,2,!1),new Pp(Du,2,!0),new Pp(s4,3,!1),new Pp(Dl,3,!0)],LWt=[HT,Du,HT,Du,s4,Dl,s4,Dl],NWt=[Du,Du,Du,Du,Dl,Dl,Dl,Dl],OWt=[Ub,S0,HT,Du,s4,Dl,s4,Dl],DWt=[S0,S0,Du,Du,Dl,Dl,Dl,Dl],PWt=[_L,_L,S0,S0,Du,Du,Dl,Dl],_Wt=[Ste,_L,Ub,S0,Ub,S0,Ub,S0],pn={DISPLAY:Ed[Ste],TEXT:Ed[Ub],SCRIPT:Ed[HT],SCRIPTSCRIPT:Ed[s4]},Yq=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"armenian",blocks:[[1328,1423]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];function BWt(t){for(var e=0;e=i[0]&&t<=i[1])return r.name}return null}var hM=[];Yq.forEach(t=>t.blocks.forEach(e=>hM.push(...e)));function GRe(t){for(var e=0;e=hM[e]&&t<=hM[e+1])return!0;return!1}var Cs=t=>t+" "+t,ky=80,$Wt=function(e,r){return"M95,"+(622+e+r)+` c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14 c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54 c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10 diff --git a/apps/ios/EditorSource/src/main.ts b/apps/ios/EditorSource/src/main.ts index 26a242b5..13ac4460 100644 --- a/apps/ios/EditorSource/src/main.ts +++ b/apps/ios/EditorSource/src/main.ts @@ -1437,7 +1437,9 @@ const api: EdgeEverEditorAPI = { const modeChanged = nextMode !== mode; mode = nextMode; locale = opts.locale === "en-US" || opts.locale === "ja" || opts.locale === "pl" ? opts.locale : "zh-CN"; - editor.setEditable(mode === "editor"); + // Configuring a reused WebView is not a document edit. TipTap otherwise emits + // an update for its old (often empty) body before native pushes the share seed. + editor.setEditable(mode === "editor", false); setToolbarVisible(mode === "editor"); document.documentElement.dataset.theme = opts.theme || "light"; document.body.classList.toggle("viewer-mode", mode === "viewer"); diff --git a/apps/ios/Tests/EdgeEverTests/EditorBundleTests.swift b/apps/ios/Tests/EdgeEverTests/EditorBundleTests.swift index 4f34f60d..d8fdeb27 100644 --- a/apps/ios/Tests/EdgeEverTests/EditorBundleTests.swift +++ b/apps/ios/Tests/EdgeEverTests/EditorBundleTests.swift @@ -4,6 +4,112 @@ import WebKit /// Ensures the TipTap editor HTML is actually shipped in the app (not the plain-text fallback). final class EditorBundleTests: XCTestCase { + @MainActor + func testSharedFallbackSurvivesEditorConfigurationAndReuse() async throws { + let url = try XCTUnwrap(TipTapWebView.Coordinator.packagedEditorHTMLURL()) + let loaded = expectation(description: "editor bridge ready") + final class Loader: NSObject, WKScriptMessageHandler { + let loaded: XCTestExpectation + var changes: [[String: Any]] = [] + init(_ loaded: XCTestExpectation) { self.loaded = loaded } + func userContentController(_ controller: WKUserContentController, didReceive message: WKScriptMessage) { + guard let body = message.body as? [String: Any] else { return } + if body["type"] as? String == "ready" { loaded.fulfill() } + if body["type"] as? String == "change" { changes.append(body) } + } + } + let loader = Loader(loaded) + let config = WKWebViewConfiguration() + config.userContentController.add(loader, name: "edgeever") + defer { config.userContentController.removeScriptMessageHandler(forName: "edgeever") } + let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 390, height: 844), configuration: config) + webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent()) + await fulfillment(of: [loaded], timeout: 30) + + for path in ["first", "second"] { + // This used to emit the previous body (empty on first use) to native, + // replacing the share seed even though no user edit had occurred. + _ = try await webView.evaluateJavaScript("window.EdgeEverEditor.configure({mode:'editor',locale:'en-US',theme:'light'}); true") + try await Task.sleep(nanoseconds: 100_000_000) + XCTAssertTrue(loader.changes.isEmpty, "configuration must not overwrite the native share seed") + + let source = try XCTUnwrap(URL(string: "https://x.com/example/status/\(path)")) + let draft = WebClipper.fallback(source) + let decision = TipTapContentSource.resolve( + mode: .editor, documentJSON: MemoEditViewModel.emptyDocJSON, markdown: draft.contentMarkdown + ) + XCTAssertFalse(decision.useJSON) + let acknowledged = try await webView.evaluateJavaScript( + TipTapResourceLoader.jsCall(fn: "setMarkdown", arg: decision.payload) + ) + XCTAssertEqual(acknowledged as? Bool, true) + let snapshot = try await webView.evaluateJavaScript("({markdown:window.EdgeEverEditor.getMarkdown(),json:window.EdgeEverEditor.getDocument(),href:document.querySelector('.ProseMirror a')?.getAttribute('href')})") as? [String: String] + XCTAssertTrue(snapshot?["markdown"]?.contains(source.absoluteString) == true) + XCTAssertTrue(snapshot?["json"]?.contains(source.absoluteString) == true) + XCTAssertEqual(snapshot?["href"], source.absoluteString) + } + try await Task.sleep(nanoseconds: 100_000_000) + XCTAssertTrue(loader.changes.isEmpty, "native content pushes must not act as user edits") + } + + @MainActor + func testContentPushRequiresBridgeAcknowledgement() async throws { + let webView = WKWebView() + let script = TipTapResourceLoader.jsCall(fn: "setMarkdown", arg: "Source: https://example.com") + let missing = try await webView.evaluateJavaScript(script) + XCTAssertEqual(missing as? Bool, false) + _ = try await webView.evaluateJavaScript("window.EdgeEverEditor = {setMarkdown(){throw new Error('load failed')}}; true") + let failed = try await webView.evaluateJavaScript(script) + XCTAssertEqual(failed as? Bool, false) + _ = try await webView.evaluateJavaScript("window.EdgeEverEditor.setMarkdown = value => {window.receivedMarkdown = value}; true") + let succeeded = try await webView.evaluateJavaScript(script) + XCTAssertEqual(succeeded as? Bool, true) + let received = try await webView.evaluateJavaScript("window.receivedMarkdown") + XCTAssertEqual(received as? String, "Source: https://example.com") + } + + @MainActor + func testFailedContentPushCannotReplaceShareWithEmptySnapshot() async throws { + let runtime = SharedTipTapRuntime.editor + let host = TipTapHostView() + runtime.attach(to: host) + defer { + runtime.webView.evaluateJavaScript("if(window.savedEditorAPI){window.EdgeEverEditor=window.savedEditorAPI;delete window.savedEditorAPI;window.EdgeEverEditor.configure({mode:'editor',locale:'en-US',theme:'light'})}", completionHandler: nil) + runtime.detach(from: host) + } + for _ in 0..<100 { + if (try? await runtime.webView.evaluateJavaScript("!!window.EdgeEverEditor")) as? Bool == true { break } + try await Task.sleep(nanoseconds: 100_000_000) + } + _ = try await runtime.webView.evaluateJavaScript(""" + window.savedEditorAPI = window.EdgeEverEditor; + window.EdgeEverEditor = { + configure(){}, + setMarkdown(){throw new Error('content push failed')}, + getMarkdown(){return ''}, + getDocument(){return '{"type":"doc","content":[{"type":"paragraph"}]}'}, + focusEnd(){} + }; true + """) + var readyCount = 0 + var changeCount = 0 + runtime.bind(TipTapSession( + mode: .editor, + documentJSON: MemoEditViewModel.emptyDocJSON, + markdown: "Source: https://example.com/shared", + baseURL: nil, token: nil, locale: "en-US", theme: "light", placeholder: "", + onChange: { _, _ in changeCount += 1 }, + onBodyReady: { readyCount += 1 } + )) + try await Task.sleep(nanoseconds: 100_000_000) + _ = try await runtime.webView.evaluateJavaScript("window.webkit.messageHandlers.edgeever.postMessage({type:'change',contentMarkdown:'',contentJson:'{\"type\":\"doc\",\"content\":[]}'}); true") + try await Task.sleep(nanoseconds: 100_000_000) + let snapshot = await runtime.snapshotContent() + XCTAssertNil(snapshot, "failed initialization must leave the native share body intact") + XCTAssertEqual(readyCount, 0) + XCTAssertEqual(changeCount, 0) + } + @MainActor func testNativeImageBatchCreatesAnEditableGallery() async throws { let url = try XCTUnwrap(TipTapWebView.Coordinator.packagedEditorHTMLURL()) diff --git a/apps/ios/Tests/EdgeEverTests/WebClipperTests.swift b/apps/ios/Tests/EdgeEverTests/WebClipperTests.swift index aeecb6d8..bd9e8865 100644 --- a/apps/ios/Tests/EdgeEverTests/WebClipperTests.swift +++ b/apps/ios/Tests/EdgeEverTests/WebClipperTests.swift @@ -2,6 +2,21 @@ import XCTest @testable import EdgeEver final class WebClipperTests: XCTestCase { + func testFetchFailuresAlwaysKeepSourceLink() async throws { + let configuration = URLSessionConfiguration.ephemeral + configuration.protocolClasses = [ClipFailureProtocol.self] + let session = URLSession(configuration: configuration) + defer { session.invalidateAndCancel() } + + for path in ["network-error", "forbidden", "empty-body"] { + let source = try XCTUnwrap(URL(string: "https://example.com/\(path)")) + let draft = await WebClipper.build(source, session: session) + XCTAssertTrue(draft.contentMarkdown.contains("[\(source.absoluteString)](\(source.absoluteString))")) + XCTAssertTrue(draft.contentMarkdown.contains(WebClipLabels.chinese.unavailableBody)) + XCTAssertEqual(draft.tagsText, "web-clip") + } + } + @MainActor func testSharedImagesResolveOnlyFilesInsideHandoffDirectory() throws { let directory = FileManager.default.temporaryDirectory @@ -148,3 +163,25 @@ final class WebClipperTests: XCTestCase { XCTAssertTrue(draft.contentMarkdown.contains("本文を取り込めませんでした")) } } + +private final class ClipFailureProtocol: URLProtocol { + override class func canInit(with request: URLRequest) -> Bool { true } + override class func canonicalRequest(for request: URLRequest) -> URLRequest { request } + override func startLoading() { + guard let url = request.url else { return } + if url.lastPathComponent == "network-error" { + client?.urlProtocol(self, didFailWithError: URLError(.notConnectedToInternet)) + return + } + let response = HTTPURLResponse( + url: url, + statusCode: url.lastPathComponent == "forbidden" ? 403 : 200, + httpVersion: "HTTP/1.1", + headerFields: ["Content-Type": "text/html"] + )! + client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) + client?.urlProtocol(self, didLoad: Data("Shared post".utf8)) + client?.urlProtocolDidFinishLoading(self) + } + override func stopLoading() {} +}