prosemirror-completion 0.0.3 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs CHANGED
@@ -1,44 +1,44 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const L=require("prosemirror-state"),b=require("prosemirror-view"),F="prosemirror-completion",m=new L.PluginKey("prosemirror-completion");function E(e,t,c){const i=document.createElement("span");i.className=c.ghostClassName??"prosemirror-ghost-text";const s=C(t);return i.textContent=s,b.Decoration.widget(e,i,{side:1,stopEvent:()=>!0,key:"ghost-completion"})}function C(e){if(typeof e=="string")return e;if("plain"in e&&e.plain!==void 0)return e.plain;if("html"in e&&e.html!==void 0){const t=document.createElement("div");return t.innerHTML=e.html,t.textContent??""}return"prosemirror"in e&&e.prosemirror?e.prosemirror.textContent??"":""}function H(e,t){return typeof e=="string"?{text:e}:"prosemirror"in e?{text:e.prosemirror.textContent??"",node:e.prosemirror}:"plain"in e&&"html"in e?{text:e.plain,html:e.html}:"html"in e?{text:C(e),html:e.html}:"plain"in e?{text:e.plain}:{text:""}}function y(e){return e?b.DecorationSet.create(e.doc,[]):b.DecorationSet.empty}function N(e,t,c,i,s){if(!(s.showGhost??!0)||!i||!e)return s.debug&&console.log("[prosemirror-completion] Skip ghost decoration",{hasResult:!!i,hasState:!!e,showGhost:s.showGhost}),y(e);const r=E(c,i,s);return s.debug&&console.log("[prosemirror-completion] Create ghost decoration",{pos:c}),b.DecorationSet.create(e.doc,[r])}function w(e){return y(e)}function _(e){if(typeof e!="string"&&"html"in e&&e.html!==void 0)return e.html}function J(e){if(typeof e!="string"&&"prosemirror"in e&&e.prosemirror)return e.prosemirror}function I(e,t){const c=new DOMParser().parseFromString(e,"text/html");try{const i=[],s=c.body;for(let n=0;n<s.childNodes.length;n++){const r=s.childNodes[n];if(r.nodeType===3){const o=r.textContent??"";o&&i.push(t.schema.text(o))}else if(r.nodeType===1){const l=$(r,t);l&&i.push(l)}}return i.length===0?null:t.schema.nodes.doc.create(null,i)}catch{return null}}function $(e,t){var i,s,n,r;switch(e.tagName.toLowerCase()){case"p":return(i=t.schema.nodes.paragraph)==null?void 0:i.create(null,W(e,t));case"strong":case"b":return t.schema.text(e.textContent??"",[(s=t.schema.marks.strong)==null?void 0:s.create()]);case"em":case"i":return t.schema.text(e.textContent??"",[(n=t.schema.marks.em)==null?void 0:n.create()]);case"code":return t.schema.text(e.textContent??"",[(r=t.schema.marks.code)==null?void 0:r.create()]);case"br":return t.schema.text(`
2
- `);default:return t.schema.text(e.textContent??"")}}function W(e,t){const c=[];for(let i=0;i<e.childNodes.length;i++){const s=e.childNodes[i];if(s.nodeType===3){const n=s.textContent??"";n&&c.push(t.schema.text(n))}else if(s.nodeType===1){const n=$(s,t);n&&c.push(n)}}return c}function A(e,t){const c=m.getState(e);if(!c||c.triggerPos===null)return e.tr;const i=J(t),s=_(t),n=C(t),r=e.selection.from;let o,l=0;if(i)o=e.tr.insert(r,i.content),l=o.doc.content.size-e.doc.content.size;else if(s){const a=I(s,e);a&&a.childCount>0?(o=e.tr.insert(r,a.content),l=o.doc.content.size-e.doc.content.size):(o=e.tr.insertText(n,r),l=n.length)}else o=e.tr.insertText(n,r),l=n.length;const d=r+l;return o.setSelection(L.TextSelection.create(o.doc,d)),o.setMeta("prosemirror-completion",{type:"apply"}),o}function U(e){const t=e.tr;return t.setMeta("prosemirror-completion",{type:"cancel"}),t}function V(e){const t=m.getState(e);return!!(t!=null&&t.activeSuggestion&&(t==null?void 0:t.triggerPos)!==null)}function X(e){const t=m.getState(e);return(t==null?void 0:t.activeSuggestion)??null}function K(e,t,c){var r,o;const{activeSuggestion:i,triggerPos:s,options:n}=c;if(!i||s===null)return!1;if(t.key==="Tab"&&!t.shiftKey){t.preventDefault(),t.stopPropagation(),e.focus();const l=A(e.state,i);return e.dispatch(l),(r=n==null?void 0:n.onApply)==null||r.call(n,i,e),!0}if(t.key==="Escape"){t.preventDefault(),t.stopPropagation();const l={type:"cancel"};return e.dispatch(e.state.tr.setMeta("prosemirror-completion",l)),(o=n==null?void 0:n.onExit)==null||o.call(n,e),e.focus(),!0}return!1}function Y(e,t){const{activeSuggestion:c,triggerPos:i}=t;if(!c||i===null)return!1;const{from:s,to:n}=e.state.selection;return s<i}function S(e,t){const c=e.doc.resolve(t),i=c.parent;let s="",n=c.parentOffset;for(let r=0;r<i.childCount;r++){const o=i.child(r);if(o.isText){const l=o.text??"";if(n<=l.length){s+=l.slice(0,n);break}else s+=l,n-=l.length}else if(s+=" ",n-=1,n<=0)break}return s}function z(e,t){const c=e.doc.resolve(t),i=c.parent;let s="",n=c.parentOffset,r=!1;for(let o=0;o<i.childCount;o++){const l=i.child(o);if(l.isText){const d=l.text??"";r?s+=d:n<d.length?(s+=d.slice(n),r=!0):n-=d.length}else r||n<=0?(s+=" ",r=!0):n-=1}return s}function v(e){const{beforeText:t}=e;return t.includes("```")||/^\s*(function|const|let|var|class|import|export|if|for|while)/.test(t.slice(-50))?"code":/^\s*(#{1,6}\s|>|\*|\d+\.|\[|!\[)/.test(t.slice(-20))||t.includes("**")||t.includes("__")?"markdown":"common"}function B(e,t){const{from:c}=e.selection,i=S(e,c),s=t.minTriggerLength??3;return(i.split(/[\s\n]+/).pop()??"").length>=s}function Z(e,t){let c=null;return(...i)=>{c&&clearTimeout(c),c=setTimeout(()=>{e(...i),c=null},t)}}function Q(e){return{debounceMs:e.debounceMs??300,minTriggerLength:e.minTriggerLength??3,callCompletion:e.callCompletion,getPromptType:e.getPromptType??v,onChange:e.onChange??(()=>{}),onExit:e.onExit??(()=>{}),onApply:e.onApply??(()=>{}),ghostClassName:e.ghostClassName??"prosemirror-ghost-text",showGhost:e.showGhost??!0,debug:e.debug??!1}}function ee(e){const t=Q(e),c=t.debounceMs,i=t.minTriggerLength,s=t.debug,n=(...r)=>{s&&console.log("[prosemirror-completion]",...r)};return new L.Plugin({key:m,state:{init(){return{activeSuggestion:null,triggerPos:null,isLoading:!1,abortController:null,decorations:y(null),debounceTimer:null,options:t}},apply(r,o){const l=r.getMeta("prosemirror-completion");if(l)switch(l.type){case"suggest":return{...o,activeSuggestion:l.result,triggerPos:l.pos,isLoading:!1,abortController:null,decorations:N({doc:r.doc},o,l.pos,l.result,t),debounceTimer:null};case"apply":return{...o,activeSuggestion:null,triggerPos:null,isLoading:!1,abortController:null,decorations:w({doc:r.doc}),debounceTimer:null};case"cancel":return o.abortController&&o.abortController.abort(),o.debounceTimer&&clearTimeout(o.debounceTimer),{...o,activeSuggestion:null,triggerPos:null,isLoading:!1,abortController:null,decorations:w({doc:r.doc}),debounceTimer:null};case"loading":return{...o,isLoading:l.isLoading}}return!o.activeSuggestion||o.triggerPos===null?{...o,decorations:o.decorations.map(r.mapping,r.doc)}:{...o,decorations:o.decorations.map(r.mapping,r.doc)}}},props:{decorations(r){var o;return((o=this.getState(r))==null?void 0:o.decorations)??null},handleKeyDown(r,o){const l=m.getState(r.state);return l?K(r,o,l):!1}},view(r){const o=r,l=(()=>{let a=null,p=null;return u=>{a&&clearTimeout(a),p&&p.abort(),a=setTimeout(async()=>{const f=new AbortController;p=f,o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"loading",isLoading:!0}));try{const T=o.state.doc.resolve(u).parent,h=S(o.state,u),P=z(o.state,u),D=t.getPromptType?t.getPromptType({abortController:f,parent:T,pos:u,beforeText:h,afterText:P,promptType:"common",state:o.state}):v({abortController:f,parent:T,pos:u,beforeText:h,afterText:P,promptType:"common",state:o.state}),k={abortController:f,parent:T,pos:u,beforeText:h,afterText:P,promptType:D,state:o.state};n("Trigger completion",{pos:u,promptType:D,beforePreview:h.slice(-40)}),t.onChange&&t.onChange(k,o);const G=await Promise.resolve(t.callCompletion(k));if(n("Completion result",G),f.signal.aborted){n("Completion aborted",{pos:u});return}o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"suggest",result:G,pos:u}))}catch(x){if(f.signal.aborted)return;console.error("[prosemirror-completion] Completion error:",x)}finally{o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"loading",isLoading:!1}))}a=null,p=null},c);const g=m.getState(o.state);g&&g.debounceTimer&&clearTimeout(g.debounceTimer)}})(),d=a=>{B(o.state,{...t,minTriggerLength:i})&&l(a)};return{update:(a,p)=>{const u=m.getState(a.state);if(u){if(u.activeSuggestion){const{from:g}=a.state.selection,f=a.state.selection.eq(p.selection)===!1;(a.state.doc!==p.doc||f)&&a.dispatch(a.state.tr.setMeta("prosemirror-completion",{type:"cancel"}))}a.state.doc!==p.doc&&d(a.state.selection.from)}},destroy:()=>{const a=m.getState(o.state);a!=null&&a.abortController&&a.abortController.abort(),a!=null&&a.debounceTimer&&clearTimeout(a.debounceTimer)}}}})}function M(e){const t=e.beforeText.trim()||"(无内容)",c=e.afterText.trim();return["你是一名中文写作助手,请延续作者的语气继续写 1~2 句。","只输出中文内容,不要重复已存在的句子,也不要加解释。",`光标前内容:
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=require("prosemirror-state"),T=require("prosemirror-view"),B="prosemirror-completion",h=new w.PluginKey("prosemirror-completion");function S(e,t,n){const r=document.createElement("span");r.className=n.ghostClassName??"prosemirror-ghost-text";const s=b(t);return r.textContent=s,T.Decoration.widget(e,r,{side:1,stopEvent:()=>!0,key:"ghost-completion"})}function b(e){if(typeof e=="string")return e;if("plain"in e&&e.plain!==void 0)return e.plain;if("html"in e&&e.html!==void 0){const t=document.createElement("div");return t.innerHTML=e.html,t.textContent??""}return"prosemirror"in e&&e.prosemirror?e.prosemirror.textContent??"":""}function O(e,t){return typeof e=="string"?{text:e}:"prosemirror"in e?{text:e.prosemirror.textContent??"",node:e.prosemirror}:"plain"in e&&"html"in e?{text:e.plain,html:e.html}:"html"in e?{text:b(e),html:e.html}:"plain"in e?{text:e.plain}:{text:""}}function v(e){return e?T.DecorationSet.create(e.doc,[]):T.DecorationSet.empty}function D(e,t,n,r,s){if(!(s.showGhost??!0)||!r||!e)return s.debug&&console.log("[prosemirror-completion] Skip ghost decoration",{hasResult:!!r,hasState:!!e,showGhost:s.showGhost}),v(e);const i=S(n,r,s);return s.debug&&console.log("[prosemirror-completion] Create ghost decoration",{pos:n}),T.DecorationSet.create(e.doc,[i])}function y(e){return v(e)}function M(e,t){const n=e.doc.resolve(t),r=n.parent;let s="",c=n.parentOffset;for(let i=0;i<r.childCount;i++){const o=r.child(i);if(o.isText){const l=o.text??"";if(c<=l.length){s+=l.slice(0,c);break}else s+=l,c-=l.length}else if(s+=" ",c-=1,c<=0)break}return s}function E(e,t){const n=e.doc.resolve(t),r=n.parent;let s="",c=n.parentOffset,i=!1;for(let o=0;o<r.childCount;o++){const l=r.child(o);if(l.isText){const p=l.text??"";i?s+=p:c<p.length?(s+=p.slice(c),i=!0):c-=p.length}else i||c<=0?(s+=" ",i=!0):c-=1}return s}function L(e){const{beforeText:t}=e;return t.includes("```")||/^\s*(function|const|let|var|class|import|export|if|for|while)/.test(t.slice(-50))?"code":/^\s*(#{1,6}\s|>|\*|\d+\.|\[|!\[)/.test(t.slice(-20))||t.includes("**")||t.includes("__")?"markdown":"common"}function G(e,t){const{from:n}=e.selection,r=M(e,n),s=t.minTriggerLength??3;return(r.split(/[\s\n]+/).pop()??"").length>=s}function j(e,t){let n=null;return(...r)=>{n&&clearTimeout(n),n=setTimeout(()=>{e(...r),n=null},t)}}function F(e){if(typeof e!="string"&&"html"in e&&e.html!==void 0)return e.html}function I(e){if(typeof e!="string"&&"prosemirror"in e&&e.prosemirror)return e.prosemirror}function K(e,t){const n=new DOMParser().parseFromString(e,"text/html");try{const r=[],s=n.body;for(let c=0;c<s.childNodes.length;c++){const i=s.childNodes[c];if(i.nodeType===3){const o=i.textContent??"";o&&r.push(t.schema.text(o))}else if(i.nodeType===1){const l=$(i,t);l&&r.push(l)}}return r.length===0?null:t.schema.nodes.doc.create(null,r)}catch{return null}}function $(e,t){var r,s,c,i;switch(e.tagName.toLowerCase()){case"p":return((r=t.schema.nodes.paragraph)==null?void 0:r.create(null,_(e,t)))??null;case"strong":case"b":return t.schema.text(e.textContent??"",[(s=t.schema.marks.strong)==null?void 0:s.create()]);case"em":case"i":return t.schema.text(e.textContent??"",[(c=t.schema.marks.em)==null?void 0:c.create()]);case"code":return t.schema.text(e.textContent??"",[(i=t.schema.marks.code)==null?void 0:i.create()]);case"br":return t.schema.text(`
2
+ `);default:return t.schema.text(e.textContent??"")}}function _(e,t){const n=[];for(let r=0;r<e.childNodes.length;r++){const s=e.childNodes[r];if(s.nodeType===3){const c=s.textContent??"";c&&n.push(t.schema.text(c))}else if(s.nodeType===1){const c=$(s,t);c&&n.push(c)}}return n}function N(e,t){const n=h.getState(e);if(!n||n.triggerPos===null)return e.tr;const r=I(t),s=F(t),c=b(t),i=e.selection.from;let o,l=0;if(r)o=e.tr.insert(i,r.content),l=o.doc.content.size-e.doc.content.size;else if(s){const a=K(s,e);a&&a.childCount>0?(o=e.tr.insert(i,a.content),l=o.doc.content.size-e.doc.content.size):(o=e.tr.insertText(c,i),l=c.length)}else o=e.tr.insertText(c,i),l=c.length;const p=i+l;return o.setSelection(w.TextSelection.create(o.doc,p)),o.setMeta("prosemirror-completion",{type:"apply"}),o}function H(e){const t=e.tr;return t.setMeta("prosemirror-completion",{type:"cancel"}),t}function J(e){const t=h.getState(e);return!!(t!=null&&t.activeSuggestion&&(t==null?void 0:t.triggerPos)!==null)}function W(e){const t=h.getState(e);return(t==null?void 0:t.activeSuggestion)??null}const P="[prosemirror-completion]";function X(e){const t=e.debug??!1,n=e.logger??{},r={info:n.info??(t?(...c)=>console.info(P,...c):()=>{}),warn:n.warn??(t?(...c)=>console.warn(P,...c):()=>{}),error:n.error??((...c)=>console.error(P,...c))},s=e.onError??((c,i)=>{r.error("Completion error",c,i)});return{debounceMs:e.debounceMs??300,minTriggerLength:e.minTriggerLength??3,callCompletion:e.callCompletion,getPromptType:e.getPromptType??L,onChange:e.onChange??(()=>{}),onError:s,fallbackResult:e.fallbackResult??null,logger:r,ghostClassName:e.ghostClassName??"prosemirror-ghost-text",showGhost:e.showGhost??!0,debug:t}}function U(e){const t=X(e),n=t.debounceMs;t.minTriggerLength;let r=0;const s=()=>`pmc-${Date.now().toString(36)}-${r+=1}`,c=(i,o,l,p)=>{var x;const d=i.state.doc.resolve(o).parent,m=M(i.state,o),g=E(i.state,o),C={startedAt:Date.now(),beforeChars:m.length,afterChars:g.length},u={abortController:l,parent:d,pos:o,beforeText:m,afterText:g,promptType:"common",state:i.state,requestId:p,metrics:C},f=((x=t.getPromptType)==null?void 0:x.call(t,u))??L(u);return{...u,promptType:f}};return new w.Plugin({key:h,state:{init(){return{activeSuggestion:null,triggerPos:null,isLoading:!1,abortController:null,decorations:v(null),debounceTimer:null,options:t,activeContext:null,pendingContext:null,lastError:void 0}},apply(i,o){const l=i.getMeta("prosemirror-completion");if(l)switch(l.type){case"request-start":return{...o,isLoading:!0,abortController:l.controller,pendingContext:l.context,triggerPos:l.pos,activeSuggestion:null,lastError:void 0};case"suggest":return{...o,activeSuggestion:l.result,triggerPos:l.pos,isLoading:!1,abortController:null,decorations:D({doc:i.doc},o,l.pos,l.result,t),debounceTimer:null,activeContext:l.context??o.pendingContext??null,pendingContext:null,lastError:void 0};case"apply":return{...o,activeSuggestion:null,triggerPos:null,isLoading:!1,abortController:null,decorations:y({doc:i.doc}),debounceTimer:null,activeContext:null,pendingContext:null};case"cancel":return o.abortController&&o.abortController.abort(),o.debounceTimer&&clearTimeout(o.debounceTimer),{...o,activeSuggestion:null,triggerPos:null,isLoading:!1,abortController:null,decorations:y({doc:i.doc}),debounceTimer:null,activeContext:null,pendingContext:null};case"loading":return{...o,isLoading:l.isLoading};case"error":return{...o,isLoading:!1,abortController:null,activeSuggestion:null,triggerPos:null,decorations:y({doc:i.doc}),activeContext:null,pendingContext:null,lastError:l.error};case"timer":return{...o,debounceTimer:l.timer}}return!o.activeSuggestion||o.triggerPos===null?{...o,decorations:o.decorations.map(i.mapping,i.doc)}:{...o,decorations:o.decorations.map(i.mapping,i.doc)}}},props:{decorations(i){var o;return((o=this.getState(i))==null?void 0:o.decorations)??null}},view(i){const o=i,l=(()=>{let a=null,d=null;return m=>{a&&(clearTimeout(a),a=null),d&&(d.abort(),d=null),a=setTimeout(async()=>{const g=new AbortController;d=g;const C=s(),u=c(o,m,g,C);t.logger.info("Trigger completion",{requestId:C,pos:m,promptType:u.promptType}),t.onChange(u,o),o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"request-start",context:u,controller:g,pos:m}));try{const f=await Promise.resolve(t.callCompletion(u));if(g.signal.aborted){t.logger.info("Completion aborted",{requestId:C,pos:m});return}u.metrics.durationMs=Date.now()-u.metrics.startedAt,u.metrics.resultChars=b(f).length,o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"suggest",result:f,pos:m,context:u}))}catch(f){if(g.signal.aborted){t.logger.info("Completion aborted",{requestId:C,pos:m});return}t.onError(f,u);const x=f instanceof Error?f.message:String(f);o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"error",error:x})),t.fallbackResult&&(u.metrics.durationMs=Date.now()-u.metrics.startedAt,u.metrics.usedFallback=!0,u.metrics.resultChars=b(t.fallbackResult).length,o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"suggest",result:t.fallbackResult,pos:m,context:u})))}finally{o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"loading",isLoading:!1})),d=null,a=null,o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"timer",timer:null}))}},n),o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"timer",timer:a}))}})(),p=a=>{G(o.state,t)&&l(a)};return{update:(a,d)=>{const m=h.getState(a.state);if(m){if(m.activeSuggestion){const{from:g}=a.state.selection,C=a.state.selection.eq(d.selection)===!1;(a.state.doc!==d.doc||C)&&a.dispatch(a.state.tr.setMeta("prosemirror-completion",{type:"cancel"}))}a.state.doc!==d.doc&&p(a.state.selection.from)}},destroy:()=>{const a=h.getState(o.state);a!=null&&a.abortController&&a.abortController.abort(),a!=null&&a.debounceTimer&&clearTimeout(a.debounceTimer)}}}})}function V(e,t){const{activeSuggestion:n,triggerPos:r}=t;if(!n||r===null)return!1;const{from:s,to:c}=e.state.selection;return s<r}const Y=(e,t,n)=>{const r=h.getState(e);if(!(r!=null&&r.activeSuggestion)||r.triggerPos===null)return!1;const s=N(e,r.activeSuggestion);return t==null||t(s),!0},Z=(e,t,n)=>{const r=h.getState(e);return r!=null&&r.activeSuggestion?(t==null||t(e.tr.setMeta("prosemirror-completion",{type:"cancel"})),!0):!1};function k(e){const t=e.beforeText.trim()||"(无内容)",n=e.afterText.trim();return["你是一名中文写作助手,请延续作者的语气继续写 1~2 句。","只输出中文内容,不要重复已存在的句子,也不要加解释。",`光标前内容:
3
3
 
4
4
  ${t}
5
- `+(c?`
5
+ `+(n?`
6
6
  光标后内容:
7
7
 
8
- ${c}
8
+ ${n}
9
9
  `:""),"续写:"].join(`
10
10
 
11
- `)}const te=/^(zh|zh-.*|cn|中文)$/i;function j(e){const t=e.beforeText.trim(),c=e.afterText.trim();return["You are an inline writing assistant.","Continue the user's text in the same language and tone with 1-2 sentences.","Do not repeat existing content or add explanations.",t?`Context before cursor:
11
+ `)}const Q=/^(zh|zh-.*|cn|中文)$/i;function A(e){const t=e.beforeText.trim(),n=e.afterText.trim();return["You are an inline writing assistant.","Continue the user's text in the same language and tone with 1-2 sentences.","Do not repeat existing content or add explanations.",t?`Context before cursor:
12
12
 
13
13
  ${t}
14
- `:"Context before cursor is empty.",c?`Context after cursor:
14
+ `:"Context before cursor is empty.",n?`Context after cursor:
15
15
 
16
- ${c}
16
+ ${n}
17
17
  `:"","Continuation:"].filter(Boolean).join(`
18
18
 
19
- `)}function O(e){return`Complete the following code snippet.
19
+ `)}function R(e){return`Complete the following code snippet.
20
20
  Only provide the completion, no explanations:
21
21
 
22
22
  \`\`\`
23
23
  ${e.beforeText}
24
24
  \`\`\`
25
25
 
26
- Completion:`}function R(e){const t=e.beforeText.trim();return["Continue the following markdown document in a natural style.","Use valid markdown syntax (headings, lists, code blocks, etc.).",t?`Existing markdown:
26
+ Completion:`}function q(e){const t=e.beforeText.trim();return["Continue the following markdown document in a natural style.","Use valid markdown syntax (headings, lists, code blocks, etc.).",t?`Existing markdown:
27
27
 
28
28
  ${t}
29
29
  `:"Existing document is empty.","Continuation:"].filter(Boolean).join(`
30
30
 
31
- `)}function oe(e){return`Complete the following JavaScript/TypeScript code:
31
+ `)}function ee(e){return`Complete the following JavaScript/TypeScript code:
32
32
 
33
33
  \`\`\`javascript
34
34
  ${e.beforeText}
35
35
  \`\`\`
36
36
 
37
- Completion:`}function ne(e){return`Complete the following Python code:
37
+ Completion:`}function te(e){return`Complete the following Python code:
38
38
 
39
39
  \`\`\`python
40
40
  ${e.beforeText}
41
41
  \`\`\`
42
42
 
43
- Completion:`}function q(e){switch(e){case"code":return O;case"markdown":return R;case"zh":case"chinese":case"中文":return M;case"common":default:return j}}function re(e,t){const c=t==null?void 0:t.lang;if(c&&te.test(c))return M(e);const i=(t==null?void 0:t.type)??e.promptType;return q(i)(e)}function ie(e){const t=e.slice(-100);return/:\s*(string|number|boolean|any|void|interface|type)\b/.test(t)||/\b(const|let|var)\s+\w+:\s*\w+/.test(t)?"typescript":/\b(def|class|import|from)\b/.test(t)&&/:\s*\n/.test(t)?"python":/<\w+/.test(t)&&/>/.test(t)?"html":/\{[\s\S]*?:[\s\S]*?;/.test(t)?"css":"javascript"}exports.buildChinesePrompt=M;exports.buildCodePrompt=O;exports.buildCommonPrompt=j;exports.buildJavaScriptPrompt=oe;exports.buildMarkdownPrompt=R;exports.buildPrompt=re;exports.buildPythonPrompt=ne;exports.cancelCompletion=U;exports.clearDecorations=w;exports.completionMetaKey=F;exports.completionPluginKey=m;exports.createCompletionFragment=H;exports.createCompletionPlugin=ee;exports.createGhostDecoration=E;exports.debounce=Z;exports.defaultGetPromptType=v;exports.detectLanguage=ie;exports.emptyDecorations=y;exports.getActiveSuggestion=X;exports.getPromptBuilder=q;exports.getTextAfterCursor=z;exports.getTextBeforeCursor=S;exports.handleKeyDown=K;exports.hasActiveCompletion=V;exports.insertCompletion=A;exports.parseCompletionResult=C;exports.shouldCancelCompletion=Y;exports.shouldTriggerCompletion=B;exports.updateGhostDecoration=N;
43
+ Completion:`}function z(e){switch(e){case"code":return R;case"markdown":return q;case"zh":case"chinese":case"中文":return k;case"common":default:return A}}function oe(e,t){const n=t==null?void 0:t.lang;if(n&&Q.test(n))return k(e);const r=(t==null?void 0:t.type)??e.promptType;return z(r)(e)}function re(e){const t=e.slice(-100);return/:\s*(string|number|boolean|any|void|interface|type)\b/.test(t)||/\b(const|let|var)\s+\w+:\s*\w+/.test(t)?"typescript":/\b(def|class|import|from)\b/.test(t)&&/:\s*\n/.test(t)?"python":/<\w+/.test(t)&&/>/.test(t)?"html":/\{[\s\S]*?:[\s\S]*?;/.test(t)?"css":"javascript"}exports.approveCompletion=Y;exports.buildChinesePrompt=k;exports.buildCodePrompt=R;exports.buildCommonPrompt=A;exports.buildJavaScriptPrompt=ee;exports.buildMarkdownPrompt=q;exports.buildPrompt=oe;exports.buildPythonPrompt=te;exports.cancelCompletion=H;exports.clearDecorations=y;exports.completion=U;exports.completionMetaKey=B;exports.completionPluginKey=h;exports.createCompletionFragment=O;exports.createGhostDecoration=S;exports.debounce=j;exports.defaultGetPromptType=L;exports.detectLanguage=re;exports.emptyDecorations=v;exports.exitCompletion=Z;exports.getActiveSuggestion=W;exports.getPromptBuilder=z;exports.getTextAfterCursor=E;exports.getTextBeforeCursor=M;exports.hasActiveCompletion=J;exports.insertCompletion=N;exports.parseCompletionResult=b;exports.shouldCancelCompletion=V;exports.shouldTriggerCompletion=G;exports.updateGhostDecoration=D;
44
44
  //# sourceMappingURL=index.cjs.map
package/index.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/types.ts","../src/decorations.ts","../src/commands.ts","../src/keymap.ts","../src/utils.ts","../src/plugin.ts","../src/prompts.ts"],"sourcesContent":["import type { Node } from \"prosemirror-model\";\nimport type { EditorState, Transaction } from \"prosemirror-state\";\nimport type { EditorView, DecorationSet } from \"prosemirror-view\";\nimport { PluginKey } from \"prosemirror-state\";\n\n/**\n * 补全返回结果格式\n */\nexport type CompletionResult =\n | string\n | { plain: string; html?: string }\n | { html: string }\n | { prosemirror: Node };\n\n/**\n * Prompt 类型建议\n */\nexport type PromptType = \"common\" | \"code\" | \"markdown\" | string;\n\n/**\n * 补全上下文\n */\nexport interface CompletionContext {\n /** 用于取消请求的 AbortController */\n abortController: AbortController;\n /** 当前位置的父节点 */\n parent: Node;\n /** 当前位置 */\n pos: number;\n /** 光标前的文本 */\n beforeText: string;\n /** 光标后的文本 */\n afterText: string;\n /** 建议的 prompt 类型 */\n promptType: PromptType;\n /** 编辑器状态 */\n state: EditorState;\n}\n\n/**\n * 补全插件配置选项\n */\nexport interface CompletionOptions {\n /**\n * 防抖时间(毫秒)\n * @default 300\n */\n debounceMs?: number;\n\n /**\n * 触发补全的最小字符数\n * @default 3\n */\n minTriggerLength?: number;\n\n /**\n * 补全函数 - 接收上下文,返回补全结果\n */\n callCompletion: (\n context: CompletionContext,\n ) => Promise<CompletionResult> | CompletionResult;\n\n /**\n * 确定当前上下文适合的 prompt 类型\n */\n getPromptType?: (context: CompletionContext) => PromptType;\n\n /**\n * 用户打字时的回调\n */\n onChange?: (context: CompletionContext, view: EditorView) => void;\n\n /**\n * 补全退出时的回调(按 Esc 或点击别处)\n */\n onExit?: (view: EditorView) => void;\n\n /**\n * 补全应用时的回调(按 Tab)\n */\n onApply?: (result: CompletionResult, view: EditorView) => void;\n\n /**\n * 自定义 ghost text 的 CSS 类名\n * @default \"prosemirror-ghost-text\"\n */\n ghostClassName?: string;\n\n /**\n * 是否显示 ghost text\n * @default true\n */\n showGhost?: boolean;\n\n /**\n * 调试模式,开启后会在控制台输出关键日志\n * @default false\n */\n debug?: boolean;\n}\n\nexport interface ResolvedCompletionOptions extends Required<CompletionOptions> {}\n\nexport interface PromptOptions {\n type?: PromptType;\n lang?: string;\n}\n\n/**\n * 内部插件状态\n */\nexport interface CompletionPluginState {\n /** 当前激活的补全建议 */\n activeSuggestion: CompletionResult | null;\n /** 补全触发位置 */\n triggerPos: number | null;\n /** 是否正在加载 */\n isLoading: boolean;\n /** 当前 AbortController */\n abortController: AbortController | null;\n /** DecorationSet */\n decorations: DecorationSet;\n /** 防抖计时器 */\n debounceTimer: ReturnType<typeof setTimeout> | null;\n /** 插件配置 */\n options: CompletionOptions;\n}\n\n/**\n * Transaction meta key\n */\nexport const completionMetaKey = \"prosemirror-completion\";\n\n/**\n * 插件 Key\n */\nexport const completionPluginKey = new PluginKey<CompletionPluginState>(\n \"prosemirror-completion\",\n);\n\n/**\n * Action 类型\n */\nexport type CompletionAction =\n | { type: \"start\"; pos: number }\n | { type: \"suggest\"; result: CompletionResult; pos: number }\n | { type: \"apply\" }\n | { type: \"cancel\" }\n | { type: \"loading\"; isLoading: boolean };\n","import { Decoration, DecorationSet } from \"prosemirror-view\";\nimport type { EditorState } from \"prosemirror-state\";\nimport type { Node } from \"prosemirror-model\";\nimport type {\n CompletionOptions,\n CompletionResult,\n CompletionPluginState,\n} from \"./types\";\n\n/**\n * 创建 ghost text 的 decoration\n */\nexport function createGhostDecoration(\n pos: number,\n result: CompletionResult,\n options: CompletionOptions,\n): Decoration {\n const dom = document.createElement(\"span\");\n dom.className = options.ghostClassName ?? \"prosemirror-ghost-text\";\n\n // 解析补全结果\n const text = parseCompletionResult(result);\n dom.textContent = text;\n\n // side: 1 确保它在光标后,stopEvent 阻止用户点击灰字导致光标偏移\n return Decoration.widget(pos, dom, {\n side: 1,\n stopEvent: () => true,\n key: \"ghost-completion\",\n });\n}\n\n/**\n * 解析补全结果为纯文本\n */\nexport function parseCompletionResult(result: CompletionResult): string {\n if (typeof result === \"string\") {\n return result;\n }\n if (\"plain\" in result && result.plain !== undefined) {\n return result.plain;\n }\n if (\"html\" in result && result.html !== undefined) {\n // 简单去除 HTML 标签获取纯文本\n const tmp = document.createElement(\"div\");\n tmp.innerHTML = result.html;\n return tmp.textContent ?? \"\";\n }\n if (\"prosemirror\" in result && result.prosemirror) {\n // 从 ProseMirror Node 提取文本内容\n return result.prosemirror.textContent ?? \"\";\n }\n return \"\";\n}\n\n/**\n * 将补全结果转换为 ProseMirror 可以插入的片段\n */\nexport function createCompletionFragment(\n result: CompletionResult,\n state: EditorState,\n): { text: string; html?: string; node?: Node } {\n if (typeof result === \"string\") {\n return { text: result };\n }\n if (\"prosemirror\" in result) {\n return {\n text: result.prosemirror.textContent ?? \"\",\n node: result.prosemirror,\n };\n }\n if (\"plain\" in result && \"html\" in result) {\n return { text: result.plain, html: result.html };\n }\n if (\"html\" in result) {\n return {\n text: parseCompletionResult(result),\n html: result.html,\n };\n }\n if (\"plain\" in result) {\n return { text: result.plain };\n }\n return { text: \"\" };\n}\n\n/**\n * 创建空的 DecorationSet\n */\nexport function emptyDecorations(state: EditorState | null): DecorationSet {\n if (!state) {\n return DecorationSet.empty;\n }\n return DecorationSet.create(state.doc, []);\n}\n\n/**\n * 更新 DecorationSet,添加 ghost decoration\n */\nexport function updateGhostDecoration(\n state: EditorState | null,\n pluginState: CompletionPluginState,\n pos: number,\n result: CompletionResult,\n options: CompletionOptions,\n): DecorationSet {\n const shouldShowGhost = options.showGhost ?? true;\n if (!shouldShowGhost || !result || !state) {\n if (options.debug) {\n console.log(\"[prosemirror-completion] Skip ghost decoration\", {\n hasResult: Boolean(result),\n hasState: Boolean(state),\n showGhost: options.showGhost,\n });\n }\n return emptyDecorations(state);\n }\n\n const deco = createGhostDecoration(pos, result, options);\n if (options.debug) {\n console.log(\"[prosemirror-completion] Create ghost decoration\", { pos });\n }\n return DecorationSet.create(state.doc, [deco]);\n}\n\n/**\n * 创建空的 DecorationSet\n */\nexport function clearDecorations(state: EditorState | null): DecorationSet {\n return emptyDecorations(state);\n}\n","import type { EditorState, Transaction } from \"prosemirror-state\";\nimport { TextSelection } from \"prosemirror-state\";\nimport { Node as PMNode } from \"prosemirror-model\";\nimport type {\n CompletionResult,\n CompletionPluginState,\n} from \"./types\";\nimport { completionPluginKey } from \"./types\";\nimport { parseCompletionResult } from \"./decorations\";\n\n/**\n * 获取补全结果的 HTML 内容\n */\nfunction getCompletionHTML(result: CompletionResult): string | undefined {\n if (typeof result === \"string\") {\n return undefined;\n }\n if (\"html\" in result && result.html !== undefined) {\n return result.html;\n }\n return undefined;\n}\n\n/**\n * 获取补全结果的 ProseMirror Node\n */\nfunction getCompletionNode(result: CompletionResult): PMNode | undefined {\n if (typeof result === \"string\") {\n return undefined;\n }\n if (\"prosemirror\" in result && result.prosemirror) {\n return result.prosemirror;\n }\n return undefined;\n}\n\n/**\n * 将 HTML 字符串解析为 ProseMirror 文档片段\n */\nfunction parseHTMLToFragment(\n html: string,\n state: EditorState\n): PMNode | null {\n // 使用 DOMParser 将 HTML 解析为 DOM\n const dom = new DOMParser().parseFromString(html, \"text/html\");\n\n try {\n const nodes: PMNode[] = [];\n const body = dom.body;\n\n for (let i = 0; i < body.childNodes.length; i++) {\n const child = body.childNodes[i];\n if (child.nodeType === 3) { // TEXT_NODE\n const text = child.textContent ?? \"\";\n if (text) {\n nodes.push(state.schema.text(text));\n }\n } else if (child.nodeType === 1) { // ELEMENT_NODE\n const element = child as HTMLElement;\n const parsed = parseElement(element, state);\n if (parsed) {\n nodes.push(parsed);\n }\n }\n }\n\n if (nodes.length === 0) {\n return null;\n }\n\n // 创建文档片段\n return state.schema.nodes.doc.create(null, nodes);\n } catch {\n return null;\n }\n}\n\n/**\n * 解析单个 HTML 元素为 ProseMirror 节点\n */\nfunction parseElement(element: HTMLElement, state: EditorState): PMNode | null {\n const tagName = element.tagName.toLowerCase();\n\n // 处理常见标签\n switch (tagName) {\n case \"p\":\n return state.schema.nodes.paragraph?.create(\n null,\n parseInlineContent(element, state)\n );\n case \"strong\":\n case \"b\":\n return state.schema.text(\n element.textContent ?? \"\",\n [state.schema.marks.strong?.create()]\n );\n case \"em\":\n case \"i\":\n return state.schema.text(\n element.textContent ?? \"\",\n [state.schema.marks.em?.create()]\n );\n case \"code\":\n return state.schema.text(\n element.textContent ?? \"\",\n [state.schema.marks.code?.create()]\n );\n case \"br\":\n return state.schema.text(\"\\n\");\n default:\n // 对于未知标签,尝试解析其文本内容\n return state.schema.text(element.textContent ?? \"\");\n }\n}\n\n/**\n * 解析内联内容\n */\nfunction parseInlineContent(element: HTMLElement, state: EditorState): PMNode[] {\n const nodes: PMNode[] = [];\n\n for (let i = 0; i < element.childNodes.length; i++) {\n const child = element.childNodes[i];\n if (child.nodeType === 3) { // TEXT_NODE\n const text = child.textContent ?? \"\";\n if (text) {\n nodes.push(state.schema.text(text));\n }\n } else if (child.nodeType === 1) { // ELEMENT_NODE\n const parsed = parseElement(child as HTMLElement, state);\n if (parsed) {\n nodes.push(parsed);\n }\n }\n }\n\n return nodes;\n}\n\n/**\n * 插入补全内容 - 支持 HTML 直接渲染和 ProseMirror Node\n */\nexport function insertCompletion(\n state: EditorState,\n result: CompletionResult\n): Transaction {\n const pluginState = completionPluginKey.getState(state) as\n | CompletionPluginState\n | undefined;\n\n if (!pluginState || pluginState.triggerPos === null) {\n return state.tr;\n }\n\n const node = getCompletionNode(result);\n const html = getCompletionHTML(result);\n const text = parseCompletionResult(result);\n const pos = state.selection.from;\n\n let tr: Transaction;\n let insertedSize = 0;\n\n // 优先处理 prosemirror 类型\n if (node) {\n // 直接插入 ProseMirror Node\n tr = state.tr.insert(pos, node.content);\n insertedSize = tr.doc.content.size - state.doc.content.size;\n } else if (html) {\n // 如果有 HTML 内容,尝试插入富文本\n const fragment = parseHTMLToFragment(html, state);\n if (fragment && fragment.childCount > 0) {\n tr = state.tr.insert(pos, fragment.content);\n insertedSize = tr.doc.content.size - state.doc.content.size;\n } else {\n // 回退到纯文本插入\n tr = state.tr.insertText(text, pos);\n insertedSize = text.length;\n }\n } else {\n // 纯文本插入\n tr = state.tr.insertText(text, pos);\n insertedSize = text.length;\n }\n\n // 将光标移到插入内容之后\n const newPos = pos + insertedSize;\n tr.setSelection(TextSelection.create(tr.doc, newPos));\n\n // 添加 meta 标记已应用\n tr.setMeta(\"prosemirror-completion\", { type: \"apply\" });\n\n return tr;\n}\n\n/**\n * 取消补全\n */\nexport function cancelCompletion(state: EditorState): Transaction {\n const tr = state.tr;\n tr.setMeta(\"prosemirror-completion\", { type: \"cancel\" });\n return tr;\n}\n\n/**\n * 检查是否有活动的补全\n */\nexport function hasActiveCompletion(state: EditorState): boolean {\n const pluginState = completionPluginKey.getState(state);\n return !!(\n pluginState?.activeSuggestion && pluginState?.triggerPos !== null\n );\n}\n\n/**\n * 获取当前补全建议\n */\nexport function getActiveSuggestion(\n state: EditorState\n): CompletionResult | null {\n const pluginState = completionPluginKey.getState(state);\n return pluginState?.activeSuggestion ?? null;\n}\n","import type { EditorView } from \"prosemirror-view\";\nimport type { CompletionPluginState, CompletionAction } from \"./types\";\nimport { insertCompletion } from \"./commands\";\n\n/**\n * 键盘事件处理器\n * 处理 Tab(应用)、Esc(取消)、和其他按键\n */\nexport function handleKeyDown(\n view: EditorView,\n event: KeyboardEvent,\n pluginState: CompletionPluginState,\n): boolean {\n const { activeSuggestion, triggerPos, options } = pluginState;\n\n // 没有激活的补全时,不拦截\n if (!activeSuggestion || triggerPos === null) {\n return false;\n }\n\n // Tab: 应用补全\n if (event.key === \"Tab\" && !event.shiftKey) {\n event.preventDefault();\n event.stopPropagation();\n view.focus();\n // 使用 insertCompletion 实际插入文本\n const tr = insertCompletion(view.state, activeSuggestion);\n view.dispatch(tr);\n options?.onApply?.(activeSuggestion, view);\n return true;\n }\n\n // Escape: 取消补全\n if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n const action: CompletionAction = { type: \"cancel\" };\n view.dispatch(view.state.tr.setMeta(\"prosemirror-completion\", action));\n options?.onExit?.(view);\n view.focus();\n return true;\n }\n\n // 其他按键:更新状态,让 state.apply 处理\n return false;\n}\n\n/**\n * 检查是否需要取消补全(光标移出触发区域等)\n */\nexport function shouldCancelCompletion(\n view: EditorView,\n pluginState: CompletionPluginState,\n): boolean {\n const { activeSuggestion, triggerPos } = pluginState;\n\n if (!activeSuggestion || triggerPos === null) {\n return false;\n }\n\n const { from, to } = view.state.selection;\n\n // 如果光标不在触发位置之后,取消补全\n if (from < triggerPos) {\n return true;\n }\n\n return false;\n}\n","import type { EditorState } from \"prosemirror-state\";\nimport type {\n CompletionContext,\n CompletionOptions,\n PromptType,\n} from \"./types\";\n\n/**\n * 获取光标前的文本内容\n */\nexport function getTextBeforeCursor(state: EditorState, pos: number): string {\n const $pos = state.doc.resolve(pos);\n const node = $pos.parent;\n\n // 获取从节点开始到光标位置的文本\n let text = \"\";\n let offset = $pos.parentOffset;\n\n // 遍历子节点收集文本\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.isText) {\n const childText = child.text ?? \"\";\n if (offset <= childText.length) {\n text += childText.slice(0, offset);\n break;\n } else {\n text += childText;\n offset -= childText.length;\n }\n } else {\n // 非文本节点,记录为空格或换行\n text += \" \";\n offset -= 1;\n if (offset <= 0) break;\n }\n }\n\n return text;\n}\n\n/**\n * 获取光标后的文本内容\n */\nexport function getTextAfterCursor(state: EditorState, pos: number): string {\n const $pos = state.doc.resolve(pos);\n const node = $pos.parent;\n\n let text = \"\";\n let offset = $pos.parentOffset;\n let started = false;\n\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.isText) {\n const childText = child.text ?? \"\";\n if (!started) {\n if (offset < childText.length) {\n text += childText.slice(offset);\n started = true;\n } else {\n offset -= childText.length;\n }\n } else {\n text += childText;\n }\n } else {\n if (started || offset <= 0) {\n text += \" \";\n started = true;\n } else {\n offset -= 1;\n }\n }\n }\n\n return text;\n}\n\n/**\n * 默认的 prompt 类型检测\n */\nexport function defaultGetPromptType(context: CompletionContext): PromptType {\n const { beforeText } = context;\n\n // 检测代码块\n if (\n beforeText.includes(\"```\") ||\n /^\\s*(function|const|let|var|class|import|export|if|for|while)/.test(\n beforeText.slice(-50)\n )\n ) {\n return \"code\";\n }\n\n // 检测 markdown 特征\n if (\n /^\\s*(#{1,6}\\s|>|\\*|\\d+\\.|\\[|!\\[)/.test(beforeText.slice(-20)) ||\n beforeText.includes(\"**\") ||\n beforeText.includes(\"__\")\n ) {\n return \"markdown\";\n }\n\n return \"common\";\n}\n\n/**\n * 检查是否应该触发补全\n */\nexport function shouldTriggerCompletion(\n state: EditorState,\n options: CompletionOptions\n): boolean {\n const { from } = state.selection;\n const beforeText = getTextBeforeCursor(state, from);\n\n // 检查最小触发长度\n const minLength = options.minTriggerLength ?? 3;\n\n // 获取最后一个\"词\"的长度(以空格或标点分隔)\n const lastWord = beforeText.split(/[\\s\\n]+/).pop() ?? \"\";\n\n return lastWord.length >= minLength;\n}\n\n/**\n * 防抖函数\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n fn: T,\n ms: number\n): (...args: Parameters<T>) => void {\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n return (...args: Parameters<T>) => {\n if (timer) {\n clearTimeout(timer);\n }\n timer = setTimeout(() => {\n fn(...args);\n timer = null;\n }, ms);\n };\n}\n","import { Plugin } from \"prosemirror-state\";\nimport type { EditorView } from \"prosemirror-view\";\nimport type {\n CompletionOptions,\n CompletionPluginState,\n CompletionAction,\n ResolvedCompletionOptions,\n} from \"./types\";\nimport { completionPluginKey } from \"./types\";\nimport {\n emptyDecorations,\n clearDecorations,\n updateGhostDecoration,\n} from \"./decorations\";\nimport { handleKeyDown, shouldCancelCompletion } from \"./keymap\";\nimport {\n getTextBeforeCursor,\n getTextAfterCursor,\n defaultGetPromptType,\n shouldTriggerCompletion,\n} from \"./utils\";\nimport { insertCompletion } from \"./commands\";\n\n/**\n * 创建补全插件\n */\nfunction resolveOptions(options: CompletionOptions): ResolvedCompletionOptions {\n return {\n debounceMs: options.debounceMs ?? 300,\n minTriggerLength: options.minTriggerLength ?? 3,\n callCompletion: options.callCompletion,\n getPromptType: options.getPromptType ?? defaultGetPromptType,\n onChange: options.onChange ?? (() => {}),\n onExit: options.onExit ?? (() => {}),\n onApply: options.onApply ?? (() => {}),\n ghostClassName: options.ghostClassName ?? \"prosemirror-ghost-text\",\n showGhost: options.showGhost ?? true,\n debug: options.debug ?? false,\n };\n}\n\nexport function createCompletionPlugin(\n initOptions: CompletionOptions,\n): Plugin<CompletionPluginState> {\n const options = resolveOptions(initOptions);\n const debounceMs = options.debounceMs;\n const minTriggerLength = options.minTriggerLength;\n const debugEnabled = options.debug;\n const debugLog = (...args: unknown[]) => {\n if (!debugEnabled) return;\n console.log(\"[prosemirror-completion]\", ...args);\n };\n\n return new Plugin<CompletionPluginState>({\n key: completionPluginKey,\n\n state: {\n init(): CompletionPluginState {\n return {\n activeSuggestion: null,\n triggerPos: null,\n isLoading: false,\n abortController: null,\n decorations: emptyDecorations(null),\n debounceTimer: null,\n options,\n };\n },\n\n apply(tr, pluginState): CompletionPluginState {\n const action = tr.getMeta(\n \"prosemirror-completion\",\n ) as CompletionAction | null;\n\n // 处理 action\n if (action) {\n switch (action.type) {\n case \"suggest\":\n return {\n ...pluginState,\n activeSuggestion: action.result,\n triggerPos: action.pos,\n isLoading: false,\n abortController: null,\n decorations: updateGhostDecoration(\n { doc: tr.doc } as import(\"prosemirror-state\").EditorState,\n pluginState,\n action.pos,\n action.result,\n options,\n ),\n debounceTimer: null,\n };\n\n case \"apply\": {\n // 清理 decoration,补全内容已插入\n return {\n ...pluginState,\n activeSuggestion: null,\n triggerPos: null,\n isLoading: false,\n abortController: null,\n decorations: clearDecorations({\n doc: tr.doc,\n } as import(\"prosemirror-state\").EditorState),\n debounceTimer: null,\n };\n }\n\n case \"cancel\":\n // 取消之前的请求\n if (pluginState.abortController) {\n pluginState.abortController.abort();\n }\n if (pluginState.debounceTimer) {\n clearTimeout(pluginState.debounceTimer);\n }\n return {\n ...pluginState,\n activeSuggestion: null,\n triggerPos: null,\n isLoading: false,\n abortController: null,\n decorations: clearDecorations({\n doc: tr.doc,\n } as import(\"prosemirror-state\").EditorState),\n debounceTimer: null,\n };\n\n case \"loading\":\n return {\n ...pluginState,\n isLoading: action.isLoading,\n };\n }\n }\n\n // 如果没有 active suggestion,正常返回\n if (!pluginState.activeSuggestion || pluginState.triggerPos === null) {\n return {\n ...pluginState,\n decorations: pluginState.decorations.map(tr.mapping, tr.doc),\n };\n }\n\n // 映射 decorations\n return {\n ...pluginState,\n decorations: pluginState.decorations.map(tr.mapping, tr.doc),\n };\n },\n },\n\n props: {\n decorations(state) {\n return this.getState(state)?.decorations ?? null;\n },\n\n handleKeyDown(view: EditorView, event: KeyboardEvent): boolean {\n const pluginState = completionPluginKey.getState(view.state);\n if (!pluginState) return false;\n\n return handleKeyDown(view, event, pluginState);\n },\n },\n\n view(editorView) {\n const view = editorView;\n\n // 防抖处理补全请求\n const debouncedRequest = (() => {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let lastAbortController: AbortController | null = null;\n\n return (pos: number) => {\n if (timer) {\n clearTimeout(timer);\n }\n\n // 取消之前的请求\n if (lastAbortController) {\n lastAbortController.abort();\n }\n\n timer = setTimeout(async () => {\n const abortController = new AbortController();\n lastAbortController = abortController;\n\n // 更新 loading 状态\n view.dispatch(\n view.state.tr.setMeta(\"prosemirror-completion\", {\n type: \"loading\",\n isLoading: true,\n }),\n );\n\n try {\n const $pos = view.state.doc.resolve(pos);\n const parent = $pos.parent;\n const beforeText = getTextBeforeCursor(view.state, pos);\n const afterText = getTextAfterCursor(view.state, pos);\n const promptType = options.getPromptType\n ? options.getPromptType({\n abortController,\n parent,\n pos,\n beforeText,\n afterText,\n promptType: \"common\",\n state: view.state,\n })\n : defaultGetPromptType({\n abortController,\n parent,\n pos,\n beforeText,\n afterText,\n promptType: \"common\",\n state: view.state,\n });\n\n const context = {\n abortController,\n parent,\n pos,\n beforeText,\n afterText,\n promptType,\n state: view.state,\n };\n\n debugLog(\"Trigger completion\", {\n pos,\n promptType,\n beforePreview: beforeText.slice(-40),\n });\n\n if (options.onChange) {\n options.onChange(context, view);\n }\n\n // 调用补全函数\n const result = await Promise.resolve(\n options.callCompletion(context),\n );\n\n debugLog(\"Completion result\", result);\n\n // 如果已经被取消,不更新\n if (abortController.signal.aborted) {\n debugLog(\"Completion aborted\", { pos });\n return;\n }\n\n // 更新建议\n view.dispatch(\n view.state.tr.setMeta(\"prosemirror-completion\", {\n type: \"suggest\",\n result,\n pos,\n }),\n );\n } catch (error) {\n if (abortController.signal.aborted) {\n return;\n }\n console.error(\n \"[prosemirror-completion] Completion error:\",\n error,\n );\n } finally {\n // 清除 loading 状态\n view.dispatch(\n view.state.tr.setMeta(\"prosemirror-completion\", {\n type: \"loading\",\n isLoading: false,\n }),\n );\n }\n\n timer = null;\n lastAbortController = null;\n }, debounceMs);\n\n // 保存 timer 到插件状态\n const currentState = completionPluginKey.getState(view.state);\n if (currentState && currentState.debounceTimer) {\n clearTimeout(currentState.debounceTimer);\n }\n };\n })();\n\n // 监听文档和选择变化\n const handleInput = (from: number) => {\n // 检查是否应该触发补全\n if (\n !shouldTriggerCompletion(view.state, { ...options, minTriggerLength })\n ) {\n return;\n }\n // 触发新的补全请求\n debouncedRequest(from);\n };\n\n // 使用 ProseMirror 的 update 监听文档和选择变化\n return {\n update: (view, prevState) => {\n const pluginState = completionPluginKey.getState(view.state);\n if (!pluginState) return;\n\n // 检查是否有 active suggestion\n if (pluginState.activeSuggestion) {\n const { from } = view.state.selection;\n const selectionChanged =\n view.state.selection.eq(prevState.selection) === false;\n const docChanged = view.state.doc !== prevState.doc;\n\n // 如果有补全,检查是否需要取消\n if (docChanged || selectionChanged) {\n // 取消当前补全\n view.dispatch(\n view.state.tr.setMeta(\"prosemirror-completion\", {\n type: \"cancel\",\n }),\n );\n }\n }\n\n // 检查是否应该触发新的补全\n if (view.state.doc !== prevState.doc) {\n handleInput(view.state.selection.from);\n }\n },\n destroy: () => {\n // 清理\n const pluginState = completionPluginKey.getState(view.state);\n if (pluginState?.abortController) {\n pluginState.abortController.abort();\n }\n if (pluginState?.debounceTimer) {\n clearTimeout(pluginState.debounceTimer);\n }\n },\n };\n },\n });\n}\n","export function buildChinesePrompt(context: CompletionContext): string {\n const before = context.beforeText.trim() || \"(无内容)\";\n const after = context.afterText.trim();\n return [\n \"你是一名中文写作助手,请延续作者的语气继续写 1~2 句。\",\n \"只输出中文内容,不要重复已存在的句子,也不要加解释。\",\n `光标前内容:\\n\\n${before}\\n` +\n (after ? `\\n光标后内容:\\n\\n${after}\\n` : \"\"),\n \"续写:\",\n ].join(\"\\n\\n\");\n}\n\nimport type { CompletionContext, PromptType, PromptOptions } from \"./types\";\n\nconst ZH_LANG_REGEX = /^(zh|zh-.*|cn|中文)$/i;\n\n/**\n * 通用文本补全 prompt\n */\nexport function buildCommonPrompt(context: CompletionContext): string {\n const before = context.beforeText.trim();\n const after = context.afterText.trim();\n return [\n \"You are an inline writing assistant.\",\n \"Continue the user's text in the same language and tone with 1-2 sentences.\",\n \"Do not repeat existing content or add explanations.\",\n before\n ? `Context before cursor:\\n\\n${before}\\n`\n : \"Context before cursor is empty.\",\n after ? `Context after cursor:\\n\\n${after}\\n` : \"\",\n \"Continuation:\",\n ]\n .filter(Boolean)\n .join(\"\\n\\n\");\n}\n\n/**\n * 代码补全 prompt\n */\nexport function buildCodePrompt(context: CompletionContext): string {\n return `Complete the following code snippet. \nOnly provide the completion, no explanations:\n\n\\`\\`\\`\n${context.beforeText}\n\\`\\`\\`\n\nCompletion:`;\n}\n\n/**\n * Markdown 补全 prompt\n */\nexport function buildMarkdownPrompt(context: CompletionContext): string {\n const before = context.beforeText.trim();\n return [\n \"Continue the following markdown document in a natural style.\",\n \"Use valid markdown syntax (headings, lists, code blocks, etc.).\",\n before\n ? `Existing markdown:\\n\\n${before}\\n`\n : \"Existing document is empty.\",\n \"Continuation:\",\n ]\n .filter(Boolean)\n .join(\"\\n\\n\");\n}\n\n/**\n * JavaScript/TypeScript 代码补全\n */\nexport function buildJavaScriptPrompt(context: CompletionContext): string {\n return `Complete the following JavaScript/TypeScript code:\n\n\\`\\`\\`javascript\n${context.beforeText}\n\\`\\`\\`\n\nCompletion:`;\n}\n\n/**\n * Python 代码补全\n */\nexport function buildPythonPrompt(context: CompletionContext): string {\n return `Complete the following Python code:\n\n\\`\\`\\`python\n${context.beforeText}\n\\`\\`\\`\n\nCompletion:`;\n}\n\n/**\n * 根据 prompt 类型选择对应的 builder\n */\nexport function getPromptBuilder(type: PromptType | undefined) {\n switch (type) {\n case \"code\":\n return buildCodePrompt;\n case \"markdown\":\n return buildMarkdownPrompt;\n case \"zh\":\n case \"chinese\":\n case \"中文\":\n return buildChinesePrompt;\n case \"common\":\n default:\n return buildCommonPrompt;\n }\n}\n\n/**\n * 构建完整的 prompt\n */\nexport function buildPrompt(\n context: CompletionContext,\n options?: PromptOptions,\n): string {\n const lang = options?.lang;\n if (lang && ZH_LANG_REGEX.test(lang)) {\n return buildChinesePrompt(context);\n }\n\n const explicitType = options?.type ?? context.promptType;\n const builder = getPromptBuilder(explicitType);\n return builder(context);\n}\n\n/**\n * 检测代码语言\n */\nexport function detectLanguage(beforeText: string): string {\n const text = beforeText.slice(-100);\n\n // TypeScript/JavaScript\n if (\n /:\\s*(string|number|boolean|any|void|interface|type)\\b/.test(text) ||\n /\\b(const|let|var)\\s+\\w+:\\s*\\w+/.test(text)\n ) {\n return \"typescript\";\n }\n\n // Python\n if (/\\b(def|class|import|from)\\b/.test(text) && /:\\s*\\n/.test(text)) {\n return \"python\";\n }\n\n // HTML\n if (/<\\w+/.test(text) && />/.test(text)) {\n return \"html\";\n }\n\n // CSS\n if (/\\{[\\s\\S]*?:[\\s\\S]*?;/.test(text)) {\n return \"css\";\n }\n\n return \"javascript\";\n}\n"],"names":["completionMetaKey","completionPluginKey","PluginKey","createGhostDecoration","pos","result","options","dom","text","parseCompletionResult","Decoration","tmp","createCompletionFragment","state","emptyDecorations","DecorationSet","updateGhostDecoration","pluginState","deco","clearDecorations","getCompletionHTML","getCompletionNode","parseHTMLToFragment","html","nodes","body","i","child","parsed","parseElement","element","_a","parseInlineContent","_b","_c","_d","insertCompletion","node","tr","insertedSize","fragment","newPos","TextSelection","cancelCompletion","hasActiveCompletion","getActiveSuggestion","handleKeyDown","view","event","activeSuggestion","triggerPos","action","shouldCancelCompletion","from","to","getTextBeforeCursor","$pos","offset","childText","getTextAfterCursor","started","defaultGetPromptType","context","beforeText","shouldTriggerCompletion","minLength","debounce","fn","ms","timer","args","resolveOptions","createCompletionPlugin","initOptions","debounceMs","minTriggerLength","debugEnabled","debugLog","Plugin","editorView","debouncedRequest","lastAbortController","abortController","parent","afterText","promptType","error","currentState","handleInput","prevState","selectionChanged","buildChinesePrompt","before","after","ZH_LANG_REGEX","buildCommonPrompt","buildCodePrompt","buildMarkdownPrompt","buildJavaScriptPrompt","buildPythonPrompt","getPromptBuilder","type","buildPrompt","lang","explicitType","detectLanguage"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/types.ts","../src/decorations.ts","../src/utils.ts","../src/plugin.ts","../src/keymap.ts","../src/prompts.ts"],"sourcesContent":["import type { Node } from \"prosemirror-model\";\nimport type { EditorState } from \"prosemirror-state\";\nimport type { EditorView, DecorationSet } from \"prosemirror-view\";\nimport { PluginKey } from \"prosemirror-state\";\n\n/**\n * 补全返回结果格式\n */\nexport type CompletionResult =\n | string\n | { plain: string; html?: string }\n | { html: string }\n | { prosemirror: Node };\n\n/**\n * Prompt 类型建议\n */\nexport type PromptType = \"common\" | \"code\" | \"markdown\" | string;\n\n/**\n * 补全上下文\n */\nexport interface CompletionMetrics {\n /** 请求开始时间戳(ms) */\n startedAt: number;\n /** 光标前文本长度 */\n beforeChars: number;\n /** 光标后文本长度 */\n afterChars: number;\n /** 请求耗时(ms) */\n durationMs?: number;\n /** 结果文本长度 */\n resultChars?: number;\n /** 是否使用 fallback 结果 */\n usedFallback?: boolean;\n}\n\nexport interface CompletionContext {\n /** 用于取消请求的 AbortController */\n abortController: AbortController;\n /** 当前位置的父节点 */\n parent: Node;\n /** 当前位置 */\n pos: number;\n /** 光标前的文本 */\n beforeText: string;\n /** 光标后的文本 */\n afterText: string;\n /** 建议的 prompt 类型 */\n promptType: PromptType;\n /** 编辑器状态 */\n state: EditorState;\n /** 请求链路 ID */\n requestId: string;\n /** 生命周期指标 */\n metrics: CompletionMetrics;\n}\n\n/**\n * 补全插件配置选项\n */\nexport interface CompletionOptions {\n /**\n * 防抖时间(毫秒)\n * @default 300\n */\n debounceMs?: number;\n\n /**\n * 触发补全的最小字符数\n * @default 3\n */\n minTriggerLength?: number;\n\n /**\n * 补全函数 - 接收上下文,返回补全结果\n */\n callCompletion: (\n context: CompletionContext,\n ) => Promise<CompletionResult> | CompletionResult;\n\n /**\n * 确定当前上下文适合的 prompt 类型\n */\n getPromptType?: (context: CompletionContext) => PromptType;\n\n /**\n * 用户打字时的回调\n */\n onChange?: (context: CompletionContext, view: EditorView) => void;\n\n /**\n * 补全出错时的回调\n */\n onError?: (error: unknown, context: CompletionContext) => void;\n\n /**\n * 当补全失败时的降级结果\n */\n fallbackResult?: CompletionResult | null;\n\n /**\n * 自定义日志输出\n */\n logger?: CompletionLogger;\n\n /**\n * 自定义 ghost text 的 CSS 类名\n * @default \"prosemirror-ghost-text\"\n */\n ghostClassName?: string;\n\n /**\n * 是否显示 ghost text\n * @default true\n */\n showGhost?: boolean;\n\n /**\n * 调试模式,开启后会在控制台输出关键日志\n * @default false\n */\n debug?: boolean;\n}\n\nexport interface ResolvedCompletionOptions extends Required<\n Omit<CompletionOptions, \"fallbackResult\" | \"logger\">\n> {\n fallbackResult: CompletionResult | null;\n logger: Required<CompletionLogger>;\n}\n\nexport interface CompletionLogger {\n info?: (...args: unknown[]) => void;\n warn?: (...args: unknown[]) => void;\n error?: (...args: unknown[]) => void;\n}\n\nexport interface PromptOptions {\n type?: PromptType;\n lang?: string;\n}\n\n/**\n * 内部插件状态\n */\nexport interface CompletionPluginState {\n /** 当前激活的补全建议 */\n activeSuggestion: CompletionResult | null;\n /** 补全触发位置 */\n triggerPos: number | null;\n /** 是否正在加载 */\n isLoading: boolean;\n /** 当前 AbortController */\n abortController: AbortController | null;\n /** DecorationSet */\n decorations: DecorationSet;\n /** 防抖计时器 */\n debounceTimer: ReturnType<typeof setTimeout> | null;\n /** 插件配置 */\n options: ResolvedCompletionOptions;\n /** 当前激活的上下文 */\n activeContext: CompletionContext | null;\n /** 等待中的上下文 */\n pendingContext: CompletionContext | null;\n /** 最近一次错误信息 */\n lastError?: string;\n}\n\n/**\n * Transaction meta key\n */\nexport const completionMetaKey = \"prosemirror-completion\";\n\n/**\n * 插件 Key\n */\nexport const completionPluginKey = new PluginKey<CompletionPluginState>(\n \"prosemirror-completion\",\n);\n\n/**\n * Action 类型\n */\nexport type CompletionAction =\n | {\n type: \"request-start\";\n context: CompletionContext;\n controller: AbortController;\n pos: number;\n }\n | {\n type: \"suggest\";\n result: CompletionResult;\n pos: number;\n context?: CompletionContext;\n }\n | { type: \"apply\" }\n | { type: \"cancel\"; reason?: string; error?: string }\n | { type: \"loading\"; isLoading: boolean }\n | { type: \"error\"; error: string }\n | { type: \"timer\"; timer: ReturnType<typeof setTimeout> | null };\n","import { Decoration, DecorationSet } from \"prosemirror-view\";\nimport type { EditorState } from \"prosemirror-state\";\nimport type { Node } from \"prosemirror-model\";\nimport type {\n CompletionOptions,\n CompletionResult,\n CompletionPluginState,\n} from \"./types\";\n\n/**\n * 创建 ghost text 的 decoration\n */\nexport function createGhostDecoration(\n pos: number,\n result: CompletionResult,\n options: CompletionOptions,\n): Decoration {\n const dom = document.createElement(\"span\");\n dom.className = options.ghostClassName ?? \"prosemirror-ghost-text\";\n\n // 解析补全结果\n const text = parseCompletionResult(result);\n dom.textContent = text;\n\n // side: 1 确保它在光标后,stopEvent 阻止用户点击灰字导致光标偏移\n return Decoration.widget(pos, dom, {\n side: 1,\n stopEvent: () => true,\n key: \"ghost-completion\",\n });\n}\n\n/**\n * 解析补全结果为纯文本\n */\nexport function parseCompletionResult(result: CompletionResult): string {\n if (typeof result === \"string\") {\n return result;\n }\n if (\"plain\" in result && result.plain !== undefined) {\n return result.plain;\n }\n if (\"html\" in result && result.html !== undefined) {\n // 简单去除 HTML 标签获取纯文本\n const tmp = document.createElement(\"div\");\n tmp.innerHTML = result.html;\n return tmp.textContent ?? \"\";\n }\n if (\"prosemirror\" in result && result.prosemirror) {\n // 从 ProseMirror Node 提取文本内容\n return result.prosemirror.textContent ?? \"\";\n }\n return \"\";\n}\n\n/**\n * 将补全结果转换为 ProseMirror 可以插入的片段\n */\nexport function createCompletionFragment(\n result: CompletionResult,\n state: EditorState,\n): { text: string; html?: string; node?: Node } {\n if (typeof result === \"string\") {\n return { text: result };\n }\n if (\"prosemirror\" in result) {\n return {\n text: result.prosemirror.textContent ?? \"\",\n node: result.prosemirror,\n };\n }\n if (\"plain\" in result && \"html\" in result) {\n return { text: result.plain, html: result.html };\n }\n if (\"html\" in result) {\n return {\n text: parseCompletionResult(result),\n html: result.html,\n };\n }\n if (\"plain\" in result) {\n return { text: result.plain };\n }\n return { text: \"\" };\n}\n\n/**\n * 创建空的 DecorationSet\n */\nexport function emptyDecorations(state: EditorState | null): DecorationSet {\n if (!state) {\n return DecorationSet.empty;\n }\n return DecorationSet.create(state.doc, []);\n}\n\n/**\n * 更新 DecorationSet,添加 ghost decoration\n */\nexport function updateGhostDecoration(\n state: EditorState | null,\n pluginState: CompletionPluginState,\n pos: number,\n result: CompletionResult,\n options: CompletionOptions,\n): DecorationSet {\n const shouldShowGhost = options.showGhost ?? true;\n if (!shouldShowGhost || !result || !state) {\n if (options.debug) {\n console.log(\"[prosemirror-completion] Skip ghost decoration\", {\n hasResult: Boolean(result),\n hasState: Boolean(state),\n showGhost: options.showGhost,\n });\n }\n return emptyDecorations(state);\n }\n\n const deco = createGhostDecoration(pos, result, options);\n if (options.debug) {\n console.log(\"[prosemirror-completion] Create ghost decoration\", { pos });\n }\n return DecorationSet.create(state.doc, [deco]);\n}\n\n/**\n * 创建空的 DecorationSet\n */\nexport function clearDecorations(state: EditorState | null): DecorationSet {\n return emptyDecorations(state);\n}\n","import type { EditorState, Transaction } from \"prosemirror-state\";\nimport { TextSelection } from \"prosemirror-state\";\nimport { Node as PMNode } from \"prosemirror-model\";\nimport type {\n CompletionContext,\n CompletionOptions,\n CompletionPluginState,\n CompletionResult,\n PromptType,\n} from \"./types\";\nimport { completionPluginKey } from \"./types\";\nimport { parseCompletionResult } from \"./decorations\";\n\n/**\n * 获取光标前的文本内容\n */\nexport function getTextBeforeCursor(state: EditorState, pos: number): string {\n const $pos = state.doc.resolve(pos);\n const node = $pos.parent;\n\n // 获取从节点开始到光标位置的文本\n let text = \"\";\n let offset = $pos.parentOffset;\n\n // 遍历子节点收集文本\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.isText) {\n const childText = child.text ?? \"\";\n if (offset <= childText.length) {\n text += childText.slice(0, offset);\n break;\n } else {\n text += childText;\n offset -= childText.length;\n }\n } else {\n // 非文本节点,记录为空格或换行\n text += \" \";\n offset -= 1;\n if (offset <= 0) break;\n }\n }\n\n return text;\n}\n\n/**\n * 获取光标后的文本内容\n */\nexport function getTextAfterCursor(state: EditorState, pos: number): string {\n const $pos = state.doc.resolve(pos);\n const node = $pos.parent;\n\n let text = \"\";\n let offset = $pos.parentOffset;\n let started = false;\n\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.isText) {\n const childText = child.text ?? \"\";\n if (!started) {\n if (offset < childText.length) {\n text += childText.slice(offset);\n started = true;\n } else {\n offset -= childText.length;\n }\n } else {\n text += childText;\n }\n } else {\n if (started || offset <= 0) {\n text += \" \";\n started = true;\n } else {\n offset -= 1;\n }\n }\n }\n\n return text;\n}\n\n/**\n * 默认的 prompt 类型检测\n */\nexport function defaultGetPromptType(context: CompletionContext): PromptType {\n const { beforeText } = context;\n\n // 检测代码块\n if (\n beforeText.includes(\"```\") ||\n /^\\s*(function|const|let|var|class|import|export|if|for|while)/.test(\n beforeText.slice(-50),\n )\n ) {\n return \"code\";\n }\n\n // 检测 markdown 特征\n if (\n /^\\s*(#{1,6}\\s|>|\\*|\\d+\\.|\\[|!\\[)/.test(beforeText.slice(-20)) ||\n beforeText.includes(\"**\") ||\n beforeText.includes(\"__\")\n ) {\n return \"markdown\";\n }\n\n return \"common\";\n}\n\n/**\n * 检查是否应该触发补全\n */\nexport function shouldTriggerCompletion(\n state: EditorState,\n options: CompletionOptions,\n): boolean {\n const { from } = state.selection;\n const beforeText = getTextBeforeCursor(state, from);\n\n // 检查最小触发长度\n const minLength = options.minTriggerLength ?? 3;\n\n // 获取最后一个\"词\"的长度(以空格或标点分隔)\n const lastWord = beforeText.split(/[\\s\\n]+/).pop() ?? \"\";\n\n return lastWord.length >= minLength;\n}\n\n/**\n * 防抖函数\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n fn: T,\n ms: number,\n): (...args: Parameters<T>) => void {\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n return (...args: Parameters<T>) => {\n if (timer) {\n clearTimeout(timer);\n }\n timer = setTimeout(() => {\n fn(...args);\n timer = null;\n }, ms);\n };\n}\n\n/**\n * ------------------ Completion Helpers (原 commands.ts) ------------------\n */\n\nfunction getCompletionHTML(result: CompletionResult): string | undefined {\n if (typeof result === \"string\") {\n return undefined;\n }\n if (\"html\" in result && result.html !== undefined) {\n return result.html;\n }\n return undefined;\n}\n\nfunction getCompletionNode(result: CompletionResult): PMNode | undefined {\n if (typeof result === \"string\") {\n return undefined;\n }\n if (\"prosemirror\" in result && result.prosemirror) {\n return result.prosemirror;\n }\n return undefined;\n}\n\nfunction parseHTMLToFragment(html: string, state: EditorState): PMNode | null {\n const dom = new DOMParser().parseFromString(html, \"text/html\");\n\n try {\n const nodes: PMNode[] = [];\n const body = dom.body;\n\n for (let i = 0; i < body.childNodes.length; i++) {\n const child = body.childNodes[i];\n if (child.nodeType === 3) {\n const text = child.textContent ?? \"\";\n if (text) {\n nodes.push(state.schema.text(text));\n }\n } else if (child.nodeType === 1) {\n const element = child as HTMLElement;\n const parsed = parseElement(element, state);\n if (parsed) {\n nodes.push(parsed);\n }\n }\n }\n\n if (nodes.length === 0) {\n return null;\n }\n\n return state.schema.nodes.doc.create(null, nodes);\n } catch {\n return null;\n }\n}\n\nfunction parseElement(element: HTMLElement, state: EditorState): PMNode | null {\n const tagName = element.tagName.toLowerCase();\n\n switch (tagName) {\n case \"p\":\n return (\n state.schema.nodes.paragraph?.create(\n null,\n parseInlineContent(element, state),\n ) ?? null\n );\n case \"strong\":\n case \"b\":\n return state.schema.text(element.textContent ?? \"\", [\n state.schema.marks.strong?.create(),\n ]);\n case \"em\":\n case \"i\":\n return state.schema.text(element.textContent ?? \"\", [\n state.schema.marks.em?.create(),\n ]);\n case \"code\":\n return state.schema.text(element.textContent ?? \"\", [\n state.schema.marks.code?.create(),\n ]);\n case \"br\":\n return state.schema.text(\"\\n\");\n default:\n return state.schema.text(element.textContent ?? \"\");\n }\n}\n\nfunction parseInlineContent(\n element: HTMLElement,\n state: EditorState,\n): PMNode[] {\n const nodes: PMNode[] = [];\n\n for (let i = 0; i < element.childNodes.length; i++) {\n const child = element.childNodes[i];\n if (child.nodeType === 3) {\n const text = child.textContent ?? \"\";\n if (text) {\n nodes.push(state.schema.text(text));\n }\n } else if (child.nodeType === 1) {\n const parsed = parseElement(child as HTMLElement, state);\n if (parsed) {\n nodes.push(parsed);\n }\n }\n }\n\n return nodes;\n}\n\nexport function insertCompletion(\n state: EditorState,\n result: CompletionResult,\n): Transaction {\n const pluginState = completionPluginKey.getState(state) as\n | CompletionPluginState\n | undefined;\n\n if (!pluginState || pluginState.triggerPos === null) {\n return state.tr;\n }\n\n const node = getCompletionNode(result);\n const html = getCompletionHTML(result);\n const text = parseCompletionResult(result);\n const pos = state.selection.from;\n\n let tr: Transaction;\n let insertedSize = 0;\n\n if (node) {\n tr = state.tr.insert(pos, node.content);\n insertedSize = tr.doc.content.size - state.doc.content.size;\n } else if (html) {\n const fragment = parseHTMLToFragment(html, state);\n if (fragment && fragment.childCount > 0) {\n tr = state.tr.insert(pos, fragment.content);\n insertedSize = tr.doc.content.size - state.doc.content.size;\n } else {\n tr = state.tr.insertText(text, pos);\n insertedSize = text.length;\n }\n } else {\n tr = state.tr.insertText(text, pos);\n insertedSize = text.length;\n }\n\n const newPos = pos + insertedSize;\n tr.setSelection(TextSelection.create(tr.doc, newPos));\n tr.setMeta(\"prosemirror-completion\", { type: \"apply\" });\n\n return tr;\n}\n\nexport function cancelCompletion(state: EditorState): Transaction {\n const tr = state.tr;\n tr.setMeta(\"prosemirror-completion\", { type: \"cancel\" });\n return tr;\n}\n\nexport function hasActiveCompletion(state: EditorState): boolean {\n const pluginState = completionPluginKey.getState(state);\n return !!(pluginState?.activeSuggestion && pluginState?.triggerPos !== null);\n}\n\nexport function getActiveSuggestion(\n state: EditorState,\n): CompletionResult | null {\n const pluginState = completionPluginKey.getState(state);\n return pluginState?.activeSuggestion ?? null;\n}\n","import { Plugin } from \"prosemirror-state\";\nimport type { EditorView } from \"prosemirror-view\";\nimport type {\n CompletionOptions,\n CompletionPluginState,\n CompletionAction,\n ResolvedCompletionOptions,\n CompletionContext,\n} from \"./types\";\nimport { completionPluginKey } from \"./types\";\nimport {\n emptyDecorations,\n clearDecorations,\n updateGhostDecoration,\n parseCompletionResult,\n} from \"./decorations\";\nimport {\n getTextBeforeCursor,\n getTextAfterCursor,\n defaultGetPromptType,\n shouldTriggerCompletion,\n} from \"./utils\";\n\nconst LOG_PREFIX = \"[prosemirror-completion]\";\n\n/**\n * 创建补全插件\n */\nfunction resolveOptions(options: CompletionOptions): ResolvedCompletionOptions {\n const debug = options.debug ?? false;\n\n const providedLogger = options.logger ?? {};\n const logger: ResolvedCompletionOptions[\"logger\"] = {\n info:\n providedLogger.info ??\n (debug\n ? (...args: unknown[]) => console.info(LOG_PREFIX, ...args)\n : () => {}),\n warn:\n providedLogger.warn ??\n (debug\n ? (...args: unknown[]) => console.warn(LOG_PREFIX, ...args)\n : () => {}),\n error:\n providedLogger.error ??\n ((...args: unknown[]) => console.error(LOG_PREFIX, ...args)),\n };\n\n const onError =\n options.onError ??\n ((error: unknown, context: CompletionContext) => {\n logger.error(\"Completion error\", error, context);\n });\n\n return {\n debounceMs: options.debounceMs ?? 300,\n minTriggerLength: options.minTriggerLength ?? 3,\n callCompletion: options.callCompletion,\n getPromptType: options.getPromptType ?? defaultGetPromptType,\n onChange: options.onChange ?? (() => {}),\n onError,\n fallbackResult: options.fallbackResult ?? null,\n logger,\n ghostClassName: options.ghostClassName ?? \"prosemirror-ghost-text\",\n showGhost: options.showGhost ?? true,\n debug,\n };\n}\n\nexport function completion(\n initOptions: CompletionOptions,\n): Plugin<CompletionPluginState> {\n const options = resolveOptions(initOptions);\n const debounceMs = options.debounceMs;\n const minTriggerLength = options.minTriggerLength;\n\n let requestSeq = 0;\n const nextRequestId = () =>\n `pmc-${Date.now().toString(36)}-${(requestSeq += 1)}`;\n\n const buildContext = (\n view: EditorView,\n pos: number,\n abortController: AbortController,\n requestId: string,\n ): CompletionContext => {\n const $pos = view.state.doc.resolve(pos);\n const parent = $pos.parent;\n const beforeText = getTextBeforeCursor(view.state, pos);\n const afterText = getTextAfterCursor(view.state, pos);\n const metrics = {\n startedAt: Date.now(),\n beforeChars: beforeText.length,\n afterChars: afterText.length,\n };\n\n const baseContext: CompletionContext = {\n abortController,\n parent,\n pos,\n beforeText,\n afterText,\n promptType: \"common\",\n state: view.state,\n requestId,\n metrics,\n };\n\n const promptType =\n options.getPromptType?.(baseContext) ?? defaultGetPromptType(baseContext);\n\n return {\n ...baseContext,\n promptType,\n };\n };\n\n return new Plugin<CompletionPluginState>({\n key: completionPluginKey,\n\n state: {\n init(): CompletionPluginState {\n return {\n activeSuggestion: null,\n triggerPos: null,\n isLoading: false,\n abortController: null,\n decorations: emptyDecorations(null),\n debounceTimer: null,\n options,\n activeContext: null,\n pendingContext: null,\n lastError: undefined,\n };\n },\n\n apply(tr, pluginState): CompletionPluginState {\n const action = tr.getMeta(\n \"prosemirror-completion\",\n ) as CompletionAction | null;\n\n // 处理 action\n if (action) {\n switch (action.type) {\n case \"request-start\":\n return {\n ...pluginState,\n isLoading: true,\n abortController: action.controller,\n pendingContext: action.context,\n triggerPos: action.pos,\n activeSuggestion: null,\n lastError: undefined,\n };\n\n case \"suggest\":\n return {\n ...pluginState,\n activeSuggestion: action.result,\n triggerPos: action.pos,\n isLoading: false,\n abortController: null,\n decorations: updateGhostDecoration(\n { doc: tr.doc } as import(\"prosemirror-state\").EditorState,\n pluginState,\n action.pos,\n action.result,\n options,\n ),\n debounceTimer: null,\n activeContext:\n action.context ?? pluginState.pendingContext ?? null,\n pendingContext: null,\n lastError: undefined,\n };\n\n case \"apply\": {\n // 清理 decoration,补全内容已插入\n return {\n ...pluginState,\n activeSuggestion: null,\n triggerPos: null,\n isLoading: false,\n abortController: null,\n decorations: clearDecorations({\n doc: tr.doc,\n } as import(\"prosemirror-state\").EditorState),\n debounceTimer: null,\n activeContext: null,\n pendingContext: null,\n };\n }\n\n case \"cancel\":\n // 取消之前的请求\n if (pluginState.abortController) {\n pluginState.abortController.abort();\n }\n if (pluginState.debounceTimer) {\n clearTimeout(pluginState.debounceTimer);\n }\n return {\n ...pluginState,\n activeSuggestion: null,\n triggerPos: null,\n isLoading: false,\n abortController: null,\n decorations: clearDecorations({\n doc: tr.doc,\n } as import(\"prosemirror-state\").EditorState),\n debounceTimer: null,\n activeContext: null,\n pendingContext: null,\n };\n\n case \"loading\":\n return {\n ...pluginState,\n isLoading: action.isLoading,\n };\n\n case \"error\":\n return {\n ...pluginState,\n isLoading: false,\n abortController: null,\n activeSuggestion: null,\n triggerPos: null,\n decorations: clearDecorations({\n doc: tr.doc,\n } as import(\"prosemirror-state\").EditorState),\n activeContext: null,\n pendingContext: null,\n lastError: action.error,\n };\n\n case \"timer\":\n return {\n ...pluginState,\n debounceTimer: action.timer,\n };\n }\n }\n\n // 如果没有 active suggestion,正常返回\n if (!pluginState.activeSuggestion || pluginState.triggerPos === null) {\n return {\n ...pluginState,\n decorations: pluginState.decorations.map(tr.mapping, tr.doc),\n };\n }\n\n // 映射 decorations\n return {\n ...pluginState,\n decorations: pluginState.decorations.map(tr.mapping, tr.doc),\n };\n },\n },\n\n props: {\n decorations(state) {\n return this.getState(state)?.decorations ?? null;\n },\n },\n\n view(editorView) {\n const view = editorView;\n\n // 防抖处理补全请求\n const debouncedRequest = (() => {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let lastAbortController: AbortController | null = null;\n\n return (pos: number) => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n\n if (lastAbortController) {\n lastAbortController.abort();\n lastAbortController = null;\n }\n\n timer = setTimeout(async () => {\n const abortController = new AbortController();\n lastAbortController = abortController;\n const requestId = nextRequestId();\n const context = buildContext(view, pos, abortController, requestId);\n\n options.logger.info(\"Trigger completion\", {\n requestId,\n pos,\n promptType: context.promptType,\n });\n\n options.onChange(context, view);\n\n view.dispatch(\n view.state.tr.setMeta(\"prosemirror-completion\", {\n type: \"request-start\",\n context,\n controller: abortController,\n pos,\n }),\n );\n\n try {\n const result = await Promise.resolve(\n options.callCompletion(context),\n );\n\n if (abortController.signal.aborted) {\n options.logger.info(\"Completion aborted\", { requestId, pos });\n return;\n }\n\n context.metrics.durationMs =\n Date.now() - context.metrics.startedAt;\n context.metrics.resultChars =\n parseCompletionResult(result).length;\n\n view.dispatch(\n view.state.tr.setMeta(\"prosemirror-completion\", {\n type: \"suggest\",\n result,\n pos,\n context,\n }),\n );\n } catch (error) {\n if (abortController.signal.aborted) {\n options.logger.info(\"Completion aborted\", { requestId, pos });\n return;\n }\n\n options.onError(error, context);\n const message =\n error instanceof Error ? error.message : String(error);\n\n view.dispatch(\n view.state.tr.setMeta(\"prosemirror-completion\", {\n type: \"error\",\n error: message,\n }),\n );\n\n if (options.fallbackResult) {\n context.metrics.durationMs =\n Date.now() - context.metrics.startedAt;\n context.metrics.usedFallback = true;\n context.metrics.resultChars = parseCompletionResult(\n options.fallbackResult,\n ).length;\n\n view.dispatch(\n view.state.tr.setMeta(\"prosemirror-completion\", {\n type: \"suggest\",\n result: options.fallbackResult,\n pos,\n context,\n }),\n );\n }\n } finally {\n view.dispatch(\n view.state.tr.setMeta(\"prosemirror-completion\", {\n type: \"loading\",\n isLoading: false,\n }),\n );\n lastAbortController = null;\n timer = null;\n view.dispatch(\n view.state.tr.setMeta(\"prosemirror-completion\", {\n type: \"timer\",\n timer: null,\n }),\n );\n }\n }, debounceMs);\n\n view.dispatch(\n view.state.tr.setMeta(\"prosemirror-completion\", {\n type: \"timer\",\n timer,\n }),\n );\n };\n })();\n\n // 监听文档和选择变化\n const handleInput = (from: number) => {\n // 检查是否应该触发补全\n if (!shouldTriggerCompletion(view.state, options)) {\n return;\n }\n // 触发新的补全请求\n debouncedRequest(from);\n };\n\n // 使用 ProseMirror 的 update 监听文档和选择变化\n return {\n update: (view, prevState) => {\n const pluginState = completionPluginKey.getState(view.state);\n if (!pluginState) return;\n\n // 检查是否有 active suggestion\n if (pluginState.activeSuggestion) {\n const { from } = view.state.selection;\n const selectionChanged =\n view.state.selection.eq(prevState.selection) === false;\n const docChanged = view.state.doc !== prevState.doc;\n\n if (docChanged || selectionChanged) {\n view.dispatch(\n view.state.tr.setMeta(\"prosemirror-completion\", {\n type: \"cancel\",\n }),\n );\n }\n }\n\n // 检查是否应该触发新的补全\n if (view.state.doc !== prevState.doc) {\n handleInput(view.state.selection.from);\n }\n },\n destroy: () => {\n // 清理\n const pluginState = completionPluginKey.getState(view.state);\n if (pluginState?.abortController) {\n pluginState.abortController.abort();\n }\n if (pluginState?.debounceTimer) {\n clearTimeout(pluginState.debounceTimer);\n }\n },\n };\n },\n });\n}\n","import type { Command } from \"prosemirror-state\";\nimport type { EditorView } from \"prosemirror-view\";\nimport type { CompletionPluginState } from \"./types\";\nimport { completionPluginKey } from \"./types\";\nimport { insertCompletion } from \"./utils\";\n\n/**\n * 检查是否需要取消补全(光标移出触发区域等)\n */\nexport function shouldCancelCompletion(\n view: EditorView,\n pluginState: CompletionPluginState,\n): boolean {\n const { activeSuggestion, triggerPos } = pluginState;\n\n if (!activeSuggestion || triggerPos === null) {\n return false;\n }\n\n const { from, to } = view.state.selection;\n\n // 如果光标不在触发位置之后,取消补全\n if (from < triggerPos) {\n return true;\n }\n\n return false;\n}\n\nexport const approveCompletion: Command = (state, dispatch, view) => {\n const pluginState = completionPluginKey.getState(state);\n if (!pluginState?.activeSuggestion || pluginState.triggerPos === null) {\n return false;\n }\n\n const tr = insertCompletion(state, pluginState.activeSuggestion);\n dispatch?.(tr);\n\n return true;\n};\n\nexport const exitCompletion: Command = (state, dispatch, view) => {\n const pluginState = completionPluginKey.getState(state);\n if (!pluginState?.activeSuggestion) {\n return false;\n }\n\n dispatch?.(state.tr.setMeta(\"prosemirror-completion\", { type: \"cancel\" }));\n\n return true;\n};\n","export function buildChinesePrompt(context: CompletionContext): string {\n const before = context.beforeText.trim() || \"(无内容)\";\n const after = context.afterText.trim();\n return [\n \"你是一名中文写作助手,请延续作者的语气继续写 1~2 句。\",\n \"只输出中文内容,不要重复已存在的句子,也不要加解释。\",\n `光标前内容:\\n\\n${before}\\n` +\n (after ? `\\n光标后内容:\\n\\n${after}\\n` : \"\"),\n \"续写:\",\n ].join(\"\\n\\n\");\n}\n\nimport type { CompletionContext, PromptType, PromptOptions } from \"./types\";\n\nconst ZH_LANG_REGEX = /^(zh|zh-.*|cn|中文)$/i;\n\n/**\n * 通用文本补全 prompt\n */\nexport function buildCommonPrompt(context: CompletionContext): string {\n const before = context.beforeText.trim();\n const after = context.afterText.trim();\n return [\n \"You are an inline writing assistant.\",\n \"Continue the user's text in the same language and tone with 1-2 sentences.\",\n \"Do not repeat existing content or add explanations.\",\n before\n ? `Context before cursor:\\n\\n${before}\\n`\n : \"Context before cursor is empty.\",\n after ? `Context after cursor:\\n\\n${after}\\n` : \"\",\n \"Continuation:\",\n ]\n .filter(Boolean)\n .join(\"\\n\\n\");\n}\n\n/**\n * 代码补全 prompt\n */\nexport function buildCodePrompt(context: CompletionContext): string {\n return `Complete the following code snippet. \nOnly provide the completion, no explanations:\n\n\\`\\`\\`\n${context.beforeText}\n\\`\\`\\`\n\nCompletion:`;\n}\n\n/**\n * Markdown 补全 prompt\n */\nexport function buildMarkdownPrompt(context: CompletionContext): string {\n const before = context.beforeText.trim();\n return [\n \"Continue the following markdown document in a natural style.\",\n \"Use valid markdown syntax (headings, lists, code blocks, etc.).\",\n before\n ? `Existing markdown:\\n\\n${before}\\n`\n : \"Existing document is empty.\",\n \"Continuation:\",\n ]\n .filter(Boolean)\n .join(\"\\n\\n\");\n}\n\n/**\n * JavaScript/TypeScript 代码补全\n */\nexport function buildJavaScriptPrompt(context: CompletionContext): string {\n return `Complete the following JavaScript/TypeScript code:\n\n\\`\\`\\`javascript\n${context.beforeText}\n\\`\\`\\`\n\nCompletion:`;\n}\n\n/**\n * Python 代码补全\n */\nexport function buildPythonPrompt(context: CompletionContext): string {\n return `Complete the following Python code:\n\n\\`\\`\\`python\n${context.beforeText}\n\\`\\`\\`\n\nCompletion:`;\n}\n\n/**\n * 根据 prompt 类型选择对应的 builder\n */\nexport function getPromptBuilder(type: PromptType | undefined) {\n switch (type) {\n case \"code\":\n return buildCodePrompt;\n case \"markdown\":\n return buildMarkdownPrompt;\n case \"zh\":\n case \"chinese\":\n case \"中文\":\n return buildChinesePrompt;\n case \"common\":\n default:\n return buildCommonPrompt;\n }\n}\n\n/**\n * 构建完整的 prompt\n */\nexport function buildPrompt(\n context: CompletionContext,\n options?: PromptOptions,\n): string {\n const lang = options?.lang;\n if (lang && ZH_LANG_REGEX.test(lang)) {\n return buildChinesePrompt(context);\n }\n\n const explicitType = options?.type ?? context.promptType;\n const builder = getPromptBuilder(explicitType);\n return builder(context);\n}\n\n/**\n * 检测代码语言\n */\nexport function detectLanguage(beforeText: string): string {\n const text = beforeText.slice(-100);\n\n // TypeScript/JavaScript\n if (\n /:\\s*(string|number|boolean|any|void|interface|type)\\b/.test(text) ||\n /\\b(const|let|var)\\s+\\w+:\\s*\\w+/.test(text)\n ) {\n return \"typescript\";\n }\n\n // Python\n if (/\\b(def|class|import|from)\\b/.test(text) && /:\\s*\\n/.test(text)) {\n return \"python\";\n }\n\n // HTML\n if (/<\\w+/.test(text) && />/.test(text)) {\n return \"html\";\n }\n\n // CSS\n if (/\\{[\\s\\S]*?:[\\s\\S]*?;/.test(text)) {\n return \"css\";\n }\n\n return \"javascript\";\n}\n"],"names":["completionMetaKey","completionPluginKey","PluginKey","createGhostDecoration","pos","result","options","dom","text","parseCompletionResult","Decoration","tmp","createCompletionFragment","state","emptyDecorations","DecorationSet","updateGhostDecoration","pluginState","deco","clearDecorations","getTextBeforeCursor","$pos","node","offset","child","childText","getTextAfterCursor","started","i","defaultGetPromptType","context","beforeText","shouldTriggerCompletion","from","minLength","debounce","fn","ms","timer","args","getCompletionHTML","getCompletionNode","parseHTMLToFragment","html","nodes","body","parsed","parseElement","element","_a","parseInlineContent","_b","_c","_d","insertCompletion","tr","insertedSize","fragment","newPos","TextSelection","cancelCompletion","hasActiveCompletion","getActiveSuggestion","LOG_PREFIX","resolveOptions","debug","providedLogger","logger","onError","error","completion","initOptions","debounceMs","requestSeq","nextRequestId","buildContext","view","abortController","requestId","parent","afterText","metrics","baseContext","promptType","Plugin","action","editorView","debouncedRequest","lastAbortController","message","handleInput","prevState","selectionChanged","shouldCancelCompletion","activeSuggestion","triggerPos","to","approveCompletion","dispatch","exitCompletion","buildChinesePrompt","before","after","ZH_LANG_REGEX","buildCommonPrompt","buildCodePrompt","buildMarkdownPrompt","buildJavaScriptPrompt","buildPythonPrompt","getPromptBuilder","type","buildPrompt","lang","explicitType","detectLanguage"],"mappings":"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"}
package/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export * from './types';
2
2
  export * from './plugin';
3
- export * from './commands';
4
3
  export * from './decorations';
5
4
  export * from './utils';
6
5
  export * from './keymap';
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC"}
package/index.iife.js CHANGED
@@ -1,44 +1,44 @@
1
- var ProseMirrorCompletion=function(a,P,h){"use strict";const _="prosemirror-completion",d=new P.PluginKey("prosemirror-completion");function k(e,t,c){const i=document.createElement("span");i.className=c.ghostClassName??"prosemirror-ghost-text";const s=b(t);return i.textContent=s,h.Decoration.widget(e,i,{side:1,stopEvent:()=>!0,key:"ghost-completion"})}function b(e){if(typeof e=="string")return e;if("plain"in e&&e.plain!==void 0)return e.plain;if("html"in e&&e.html!==void 0){const t=document.createElement("div");return t.innerHTML=e.html,t.textContent??""}return"prosemirror"in e&&e.prosemirror?e.prosemirror.textContent??"":""}function J(e,t){return typeof e=="string"?{text:e}:"prosemirror"in e?{text:e.prosemirror.textContent??"",node:e.prosemirror}:"plain"in e&&"html"in e?{text:e.plain,html:e.html}:"html"in e?{text:b(e),html:e.html}:"plain"in e?{text:e.plain}:{text:""}}function C(e){return e?h.DecorationSet.create(e.doc,[]):h.DecorationSet.empty}function G(e,t,c,i,s){if(!(s.showGhost??!0)||!i||!e)return s.debug&&console.log("[prosemirror-completion] Skip ghost decoration",{hasResult:!!i,hasState:!!e,showGhost:s.showGhost}),C(e);const r=k(c,i,s);return s.debug&&console.log("[prosemirror-completion] Create ghost decoration",{pos:c}),h.DecorationSet.create(e.doc,[r])}function x(e){return C(e)}function q(e){if(typeof e!="string"&&"html"in e&&e.html!==void 0)return e.html}function I(e){if(typeof e!="string"&&"prosemirror"in e&&e.prosemirror)return e.prosemirror}function W(e,t){const c=new DOMParser().parseFromString(e,"text/html");try{const i=[],s=c.body;for(let n=0;n<s.childNodes.length;n++){const r=s.childNodes[n];if(r.nodeType===3){const o=r.textContent??"";o&&i.push(t.schema.text(o))}else if(r.nodeType===1){const l=E(r,t);l&&i.push(l)}}return i.length===0?null:t.schema.nodes.doc.create(null,i)}catch{return null}}function E(e,t){var i,s,n,r;switch(e.tagName.toLowerCase()){case"p":return(i=t.schema.nodes.paragraph)==null?void 0:i.create(null,U(e,t));case"strong":case"b":return t.schema.text(e.textContent??"",[(s=t.schema.marks.strong)==null?void 0:s.create()]);case"em":case"i":return t.schema.text(e.textContent??"",[(n=t.schema.marks.em)==null?void 0:n.create()]);case"code":return t.schema.text(e.textContent??"",[(r=t.schema.marks.code)==null?void 0:r.create()]);case"br":return t.schema.text(`
2
- `);default:return t.schema.text(e.textContent??"")}}function U(e,t){const c=[];for(let i=0;i<e.childNodes.length;i++){const s=e.childNodes[i];if(s.nodeType===3){const n=s.textContent??"";n&&c.push(t.schema.text(n))}else if(s.nodeType===1){const n=E(s,t);n&&c.push(n)}}return c}function N(e,t){const c=d.getState(e);if(!c||c.triggerPos===null)return e.tr;const i=I(t),s=q(t),n=b(t),r=e.selection.from;let o,l=0;if(i)o=e.tr.insert(r,i.content),l=o.doc.content.size-e.doc.content.size;else if(s){const u=W(s,e);u&&u.childCount>0?(o=e.tr.insert(r,u.content),l=o.doc.content.size-e.doc.content.size):(o=e.tr.insertText(n,r),l=n.length)}else o=e.tr.insertText(n,r),l=n.length;const f=r+l;return o.setSelection(P.TextSelection.create(o.doc,f)),o.setMeta("prosemirror-completion",{type:"apply"}),o}function X(e){const t=e.tr;return t.setMeta("prosemirror-completion",{type:"cancel"}),t}function Y(e){const t=d.getState(e);return!!(t!=null&&t.activeSuggestion&&(t==null?void 0:t.triggerPos)!==null)}function Z(e){const t=d.getState(e);return(t==null?void 0:t.activeSuggestion)??null}function $(e,t,c){var r,o;const{activeSuggestion:i,triggerPos:s,options:n}=c;if(!i||s===null)return!1;if(t.key==="Tab"&&!t.shiftKey){t.preventDefault(),t.stopPropagation(),e.focus();const l=N(e.state,i);return e.dispatch(l),(r=n==null?void 0:n.onApply)==null||r.call(n,i,e),!0}if(t.key==="Escape"){t.preventDefault(),t.stopPropagation();const l={type:"cancel"};return e.dispatch(e.state.tr.setMeta("prosemirror-completion",l)),(o=n==null?void 0:n.onExit)==null||o.call(n,e),e.focus(),!0}return!1}function Q(e,t){const{activeSuggestion:c,triggerPos:i}=t;if(!c||i===null)return!1;const{from:s,to:n}=e.state.selection;return s<i}function w(e,t){const c=e.doc.resolve(t),i=c.parent;let s="",n=c.parentOffset;for(let r=0;r<i.childCount;r++){const o=i.child(r);if(o.isText){const l=o.text??"";if(n<=l.length){s+=l.slice(0,n);break}else s+=l,n-=l.length}else if(s+=" ",n-=1,n<=0)break}return s}function A(e,t){const c=e.doc.resolve(t),i=c.parent;let s="",n=c.parentOffset,r=!1;for(let o=0;o<i.childCount;o++){const l=i.child(o);if(l.isText){const f=l.text??"";r?s+=f:n<f.length?(s+=f.slice(n),r=!0):n-=f.length}else r||n<=0?(s+=" ",r=!0):n-=1}return s}function M(e){const{beforeText:t}=e;return t.includes("```")||/^\s*(function|const|let|var|class|import|export|if|for|while)/.test(t.slice(-50))?"code":/^\s*(#{1,6}\s|>|\*|\d+\.|\[|!\[)/.test(t.slice(-20))||t.includes("**")||t.includes("__")?"markdown":"common"}function K(e,t){const{from:c}=e.selection,i=w(e,c),s=t.minTriggerLength??3;return(i.split(/[\s\n]+/).pop()??"").length>=s}function V(e,t){let c=null;return(...i)=>{c&&clearTimeout(c),c=setTimeout(()=>{e(...i),c=null},t)}}function ee(e){return{debounceMs:e.debounceMs??300,minTriggerLength:e.minTriggerLength??3,callCompletion:e.callCompletion,getPromptType:e.getPromptType??M,onChange:e.onChange??(()=>{}),onExit:e.onExit??(()=>{}),onApply:e.onApply??(()=>{}),ghostClassName:e.ghostClassName??"prosemirror-ghost-text",showGhost:e.showGhost??!0,debug:e.debug??!1}}function te(e){const t=ee(e),c=t.debounceMs,i=t.minTriggerLength,s=t.debug,n=(...r)=>{s&&console.log("[prosemirror-completion]",...r)};return new P.Plugin({key:d,state:{init(){return{activeSuggestion:null,triggerPos:null,isLoading:!1,abortController:null,decorations:C(null),debounceTimer:null,options:t}},apply(r,o){const l=r.getMeta("prosemirror-completion");if(l)switch(l.type){case"suggest":return{...o,activeSuggestion:l.result,triggerPos:l.pos,isLoading:!1,abortController:null,decorations:G({doc:r.doc},o,l.pos,l.result,t),debounceTimer:null};case"apply":return{...o,activeSuggestion:null,triggerPos:null,isLoading:!1,abortController:null,decorations:x({doc:r.doc}),debounceTimer:null};case"cancel":return o.abortController&&o.abortController.abort(),o.debounceTimer&&clearTimeout(o.debounceTimer),{...o,activeSuggestion:null,triggerPos:null,isLoading:!1,abortController:null,decorations:x({doc:r.doc}),debounceTimer:null};case"loading":return{...o,isLoading:l.isLoading}}return!o.activeSuggestion||o.triggerPos===null?{...o,decorations:o.decorations.map(r.mapping,r.doc)}:{...o,decorations:o.decorations.map(r.mapping,r.doc)}}},props:{decorations(r){var o;return((o=this.getState(r))==null?void 0:o.decorations)??null},handleKeyDown(r,o){const l=d.getState(r.state);return l?$(r,o,l):!1}},view(r){const o=r,l=(()=>{let u=null,g=null;return m=>{u&&clearTimeout(u),g&&g.abort(),u=setTimeout(async()=>{const p=new AbortController;g=p,o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"loading",isLoading:!0}));try{const S=o.state.doc.resolve(m).parent,T=w(o.state,m),D=A(o.state,m),R=t.getPromptType?t.getPromptType({abortController:p,parent:S,pos:m,beforeText:T,afterText:D,promptType:"common",state:o.state}):M({abortController:p,parent:S,pos:m,beforeText:T,afterText:D,promptType:"common",state:o.state}),F={abortController:p,parent:S,pos:m,beforeText:T,afterText:D,promptType:R,state:o.state};n("Trigger completion",{pos:m,promptType:R,beforePreview:T.slice(-40)}),t.onChange&&t.onChange(F,o);const H=await Promise.resolve(t.callCompletion(F));if(n("Completion result",H),p.signal.aborted){n("Completion aborted",{pos:m});return}o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"suggest",result:H,pos:m}))}catch(L){if(p.signal.aborted)return;console.error("[prosemirror-completion] Completion error:",L)}finally{o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"loading",isLoading:!1}))}u=null,g=null},c);const y=d.getState(o.state);y&&y.debounceTimer&&clearTimeout(y.debounceTimer)}})(),f=u=>{K(o.state,{...t,minTriggerLength:i})&&l(u)};return{update:(u,g)=>{const m=d.getState(u.state);if(m){if(m.activeSuggestion){const{from:y}=u.state.selection,p=u.state.selection.eq(g.selection)===!1;(u.state.doc!==g.doc||p)&&u.dispatch(u.state.tr.setMeta("prosemirror-completion",{type:"cancel"}))}u.state.doc!==g.doc&&f(u.state.selection.from)}},destroy:()=>{const u=d.getState(o.state);u!=null&&u.abortController&&u.abortController.abort(),u!=null&&u.debounceTimer&&clearTimeout(u.debounceTimer)}}}})}function v(e){const t=e.beforeText.trim()||"(无内容)",c=e.afterText.trim();return["你是一名中文写作助手,请延续作者的语气继续写 1~2 句。","只输出中文内容,不要重复已存在的句子,也不要加解释。",`光标前内容:
1
+ var ProseMirrorCompletion=function(a,w,T){"use strict";const O="prosemirror-completion",p=new w.PluginKey("prosemirror-completion");function D(e,t,r){const n=document.createElement("span");n.className=r.ghostClassName??"prosemirror-ghost-text";const c=y(t);return n.textContent=c,T.Decoration.widget(e,n,{side:1,stopEvent:()=>!0,key:"ghost-completion"})}function y(e){if(typeof e=="string")return e;if("plain"in e&&e.plain!==void 0)return e.plain;if("html"in e&&e.html!==void 0){const t=document.createElement("div");return t.innerHTML=e.html,t.textContent??""}return"prosemirror"in e&&e.prosemirror?e.prosemirror.textContent??"":""}function j(e,t){return typeof e=="string"?{text:e}:"prosemirror"in e?{text:e.prosemirror.textContent??"",node:e.prosemirror}:"plain"in e&&"html"in e?{text:e.plain,html:e.html}:"html"in e?{text:y(e),html:e.html}:"plain"in e?{text:e.plain}:{text:""}}function x(e){return e?T.DecorationSet.create(e.doc,[]):T.DecorationSet.empty}function E(e,t,r,n,c){if(!(c.showGhost??!0)||!n||!e)return c.debug&&console.log("[prosemirror-completion] Skip ghost decoration",{hasResult:!!n,hasState:!!e,showGhost:c.showGhost}),x(e);const i=D(r,n,c);return c.debug&&console.log("[prosemirror-completion] Create ghost decoration",{pos:r}),T.DecorationSet.create(e.doc,[i])}function v(e){return x(e)}function M(e,t){const r=e.doc.resolve(t),n=r.parent;let c="",s=r.parentOffset;for(let i=0;i<n.childCount;i++){const o=n.child(i);if(o.isText){const l=o.text??"";if(s<=l.length){c+=l.slice(0,s);break}else c+=l,s-=l.length}else if(c+=" ",s-=1,s<=0)break}return c}function G(e,t){const r=e.doc.resolve(t),n=r.parent;let c="",s=r.parentOffset,i=!1;for(let o=0;o<n.childCount;o++){const l=n.child(o);if(l.isText){const g=l.text??"";i?c+=g:s<g.length?(c+=g.slice(s),i=!0):s-=g.length}else i||s<=0?(c+=" ",i=!0):s-=1}return c}function L(e){const{beforeText:t}=e;return t.includes("```")||/^\s*(function|const|let|var|class|import|export|if|for|while)/.test(t.slice(-50))?"code":/^\s*(#{1,6}\s|>|\*|\d+\.|\[|!\[)/.test(t.slice(-20))||t.includes("**")||t.includes("__")?"markdown":"common"}function $(e,t){const{from:r}=e.selection,n=M(e,r),c=t.minTriggerLength??3;return(n.split(/[\s\n]+/).pop()??"").length>=c}function F(e,t){let r=null;return(...n)=>{r&&clearTimeout(r),r=setTimeout(()=>{e(...n),r=null},t)}}function I(e){if(typeof e!="string"&&"html"in e&&e.html!==void 0)return e.html}function K(e){if(typeof e!="string"&&"prosemirror"in e&&e.prosemirror)return e.prosemirror}function _(e,t){const r=new DOMParser().parseFromString(e,"text/html");try{const n=[],c=r.body;for(let s=0;s<c.childNodes.length;s++){const i=c.childNodes[s];if(i.nodeType===3){const o=i.textContent??"";o&&n.push(t.schema.text(o))}else if(i.nodeType===1){const l=N(i,t);l&&n.push(l)}}return n.length===0?null:t.schema.nodes.doc.create(null,n)}catch{return null}}function N(e,t){var n,c,s,i;switch(e.tagName.toLowerCase()){case"p":return((n=t.schema.nodes.paragraph)==null?void 0:n.create(null,H(e,t)))??null;case"strong":case"b":return t.schema.text(e.textContent??"",[(c=t.schema.marks.strong)==null?void 0:c.create()]);case"em":case"i":return t.schema.text(e.textContent??"",[(s=t.schema.marks.em)==null?void 0:s.create()]);case"code":return t.schema.text(e.textContent??"",[(i=t.schema.marks.code)==null?void 0:i.create()]);case"br":return t.schema.text(`
2
+ `);default:return t.schema.text(e.textContent??"")}}function H(e,t){const r=[];for(let n=0;n<e.childNodes.length;n++){const c=e.childNodes[n];if(c.nodeType===3){const s=c.textContent??"";s&&r.push(t.schema.text(s))}else if(c.nodeType===1){const s=N(c,t);s&&r.push(s)}}return r}function A(e,t){const r=p.getState(e);if(!r||r.triggerPos===null)return e.tr;const n=K(t),c=I(t),s=y(t),i=e.selection.from;let o,l=0;if(n)o=e.tr.insert(i,n.content),l=o.doc.content.size-e.doc.content.size;else if(c){const u=_(c,e);u&&u.childCount>0?(o=e.tr.insert(i,u.content),l=o.doc.content.size-e.doc.content.size):(o=e.tr.insertText(s,i),l=s.length)}else o=e.tr.insertText(s,i),l=s.length;const g=i+l;return o.setSelection(w.TextSelection.create(o.doc,g)),o.setMeta("prosemirror-completion",{type:"apply"}),o}function J(e){const t=e.tr;return t.setMeta("prosemirror-completion",{type:"cancel"}),t}function W(e){const t=p.getState(e);return!!(t!=null&&t.activeSuggestion&&(t==null?void 0:t.triggerPos)!==null)}function X(e){const t=p.getState(e);return(t==null?void 0:t.activeSuggestion)??null}const k="[prosemirror-completion]";function U(e){const t=e.debug??!1,r=e.logger??{},n={info:r.info??(t?(...s)=>console.info(k,...s):()=>{}),warn:r.warn??(t?(...s)=>console.warn(k,...s):()=>{}),error:r.error??((...s)=>console.error(k,...s))},c=e.onError??((s,i)=>{n.error("Completion error",s,i)});return{debounceMs:e.debounceMs??300,minTriggerLength:e.minTriggerLength??3,callCompletion:e.callCompletion,getPromptType:e.getPromptType??L,onChange:e.onChange??(()=>{}),onError:c,fallbackResult:e.fallbackResult??null,logger:n,ghostClassName:e.ghostClassName??"prosemirror-ghost-text",showGhost:e.showGhost??!0,debug:t}}function Y(e){const t=U(e),r=t.debounceMs;t.minTriggerLength;let n=0;const c=()=>`pmc-${Date.now().toString(36)}-${n+=1}`,s=(i,o,l,g)=>{var P;const f=i.state.doc.resolve(o).parent,d=M(i.state,o),h=G(i.state,o),b={startedAt:Date.now(),beforeChars:d.length,afterChars:h.length},m={abortController:l,parent:f,pos:o,beforeText:d,afterText:h,promptType:"common",state:i.state,requestId:g,metrics:b},C=((P=t.getPromptType)==null?void 0:P.call(t,m))??L(m);return{...m,promptType:C}};return new w.Plugin({key:p,state:{init(){return{activeSuggestion:null,triggerPos:null,isLoading:!1,abortController:null,decorations:x(null),debounceTimer:null,options:t,activeContext:null,pendingContext:null,lastError:void 0}},apply(i,o){const l=i.getMeta("prosemirror-completion");if(l)switch(l.type){case"request-start":return{...o,isLoading:!0,abortController:l.controller,pendingContext:l.context,triggerPos:l.pos,activeSuggestion:null,lastError:void 0};case"suggest":return{...o,activeSuggestion:l.result,triggerPos:l.pos,isLoading:!1,abortController:null,decorations:E({doc:i.doc},o,l.pos,l.result,t),debounceTimer:null,activeContext:l.context??o.pendingContext??null,pendingContext:null,lastError:void 0};case"apply":return{...o,activeSuggestion:null,triggerPos:null,isLoading:!1,abortController:null,decorations:v({doc:i.doc}),debounceTimer:null,activeContext:null,pendingContext:null};case"cancel":return o.abortController&&o.abortController.abort(),o.debounceTimer&&clearTimeout(o.debounceTimer),{...o,activeSuggestion:null,triggerPos:null,isLoading:!1,abortController:null,decorations:v({doc:i.doc}),debounceTimer:null,activeContext:null,pendingContext:null};case"loading":return{...o,isLoading:l.isLoading};case"error":return{...o,isLoading:!1,abortController:null,activeSuggestion:null,triggerPos:null,decorations:v({doc:i.doc}),activeContext:null,pendingContext:null,lastError:l.error};case"timer":return{...o,debounceTimer:l.timer}}return!o.activeSuggestion||o.triggerPos===null?{...o,decorations:o.decorations.map(i.mapping,i.doc)}:{...o,decorations:o.decorations.map(i.mapping,i.doc)}}},props:{decorations(i){var o;return((o=this.getState(i))==null?void 0:o.decorations)??null}},view(i){const o=i,l=(()=>{let u=null,f=null;return d=>{u&&(clearTimeout(u),u=null),f&&(f.abort(),f=null),u=setTimeout(async()=>{const h=new AbortController;f=h;const b=c(),m=s(o,d,h,b);t.logger.info("Trigger completion",{requestId:b,pos:d,promptType:m.promptType}),t.onChange(m,o),o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"request-start",context:m,controller:h,pos:d}));try{const C=await Promise.resolve(t.callCompletion(m));if(h.signal.aborted){t.logger.info("Completion aborted",{requestId:b,pos:d});return}m.metrics.durationMs=Date.now()-m.metrics.startedAt,m.metrics.resultChars=y(C).length,o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"suggest",result:C,pos:d,context:m}))}catch(C){if(h.signal.aborted){t.logger.info("Completion aborted",{requestId:b,pos:d});return}t.onError(C,m);const P=C instanceof Error?C.message:String(C);o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"error",error:P})),t.fallbackResult&&(m.metrics.durationMs=Date.now()-m.metrics.startedAt,m.metrics.usedFallback=!0,m.metrics.resultChars=y(t.fallbackResult).length,o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"suggest",result:t.fallbackResult,pos:d,context:m})))}finally{o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"loading",isLoading:!1})),f=null,u=null,o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"timer",timer:null}))}},r),o.dispatch(o.state.tr.setMeta("prosemirror-completion",{type:"timer",timer:u}))}})(),g=u=>{$(o.state,t)&&l(u)};return{update:(u,f)=>{const d=p.getState(u.state);if(d){if(d.activeSuggestion){const{from:h}=u.state.selection,b=u.state.selection.eq(f.selection)===!1;(u.state.doc!==f.doc||b)&&u.dispatch(u.state.tr.setMeta("prosemirror-completion",{type:"cancel"}))}u.state.doc!==f.doc&&g(u.state.selection.from)}},destroy:()=>{const u=p.getState(o.state);u!=null&&u.abortController&&u.abortController.abort(),u!=null&&u.debounceTimer&&clearTimeout(u.debounceTimer)}}}})}function Z(e,t){const{activeSuggestion:r,triggerPos:n}=t;if(!r||n===null)return!1;const{from:c,to:s}=e.state.selection;return c<n}const Q=(e,t,r)=>{const n=p.getState(e);if(!(n!=null&&n.activeSuggestion)||n.triggerPos===null)return!1;const c=A(e,n.activeSuggestion);return t==null||t(c),!0},V=(e,t,r)=>{const n=p.getState(e);return n!=null&&n.activeSuggestion?(t==null||t(e.tr.setMeta("prosemirror-completion",{type:"cancel"})),!0):!1};function S(e){const t=e.beforeText.trim()||"(无内容)",r=e.afterText.trim();return["你是一名中文写作助手,请延续作者的语气继续写 1~2 句。","只输出中文内容,不要重复已存在的句子,也不要加解释。",`光标前内容:
3
3
 
4
4
  ${t}
5
- `+(c?`
5
+ `+(r?`
6
6
  光标后内容:
7
7
 
8
- ${c}
8
+ ${r}
9
9
  `:""),"续写:"].join(`
10
10
 
11
- `)}const oe=/^(zh|zh-.*|cn|中文)$/i;function z(e){const t=e.beforeText.trim(),c=e.afterText.trim();return["You are an inline writing assistant.","Continue the user's text in the same language and tone with 1-2 sentences.","Do not repeat existing content or add explanations.",t?`Context before cursor:
11
+ `)}const ee=/^(zh|zh-.*|cn|中文)$/i;function R(e){const t=e.beforeText.trim(),r=e.afterText.trim();return["You are an inline writing assistant.","Continue the user's text in the same language and tone with 1-2 sentences.","Do not repeat existing content or add explanations.",t?`Context before cursor:
12
12
 
13
13
  ${t}
14
- `:"Context before cursor is empty.",c?`Context after cursor:
14
+ `:"Context before cursor is empty.",r?`Context after cursor:
15
15
 
16
- ${c}
16
+ ${r}
17
17
  `:"","Continuation:"].filter(Boolean).join(`
18
18
 
19
- `)}function B(e){return`Complete the following code snippet.
19
+ `)}function q(e){return`Complete the following code snippet.
20
20
  Only provide the completion, no explanations:
21
21
 
22
22
  \`\`\`
23
23
  ${e.beforeText}
24
24
  \`\`\`
25
25
 
26
- Completion:`}function j(e){const t=e.beforeText.trim();return["Continue the following markdown document in a natural style.","Use valid markdown syntax (headings, lists, code blocks, etc.).",t?`Existing markdown:
26
+ Completion:`}function z(e){const t=e.beforeText.trim();return["Continue the following markdown document in a natural style.","Use valid markdown syntax (headings, lists, code blocks, etc.).",t?`Existing markdown:
27
27
 
28
28
  ${t}
29
29
  `:"Existing document is empty.","Continuation:"].filter(Boolean).join(`
30
30
 
31
- `)}function ne(e){return`Complete the following JavaScript/TypeScript code:
31
+ `)}function te(e){return`Complete the following JavaScript/TypeScript code:
32
32
 
33
33
  \`\`\`javascript
34
34
  ${e.beforeText}
35
35
  \`\`\`
36
36
 
37
- Completion:`}function re(e){return`Complete the following Python code:
37
+ Completion:`}function oe(e){return`Complete the following Python code:
38
38
 
39
39
  \`\`\`python
40
40
  ${e.beforeText}
41
41
  \`\`\`
42
42
 
43
- Completion:`}function O(e){switch(e){case"code":return B;case"markdown":return j;case"zh":case"chinese":case"中文":return v;case"common":default:return z}}function ie(e,t){const c=t==null?void 0:t.lang;if(c&&oe.test(c))return v(e);const i=(t==null?void 0:t.type)??e.promptType;return O(i)(e)}function ce(e){const t=e.slice(-100);return/:\s*(string|number|boolean|any|void|interface|type)\b/.test(t)||/\b(const|let|var)\s+\w+:\s*\w+/.test(t)?"typescript":/\b(def|class|import|from)\b/.test(t)&&/:\s*\n/.test(t)?"python":/<\w+/.test(t)&&/>/.test(t)?"html":/\{[\s\S]*?:[\s\S]*?;/.test(t)?"css":"javascript"}return a.buildChinesePrompt=v,a.buildCodePrompt=B,a.buildCommonPrompt=z,a.buildJavaScriptPrompt=ne,a.buildMarkdownPrompt=j,a.buildPrompt=ie,a.buildPythonPrompt=re,a.cancelCompletion=X,a.clearDecorations=x,a.completionMetaKey=_,a.completionPluginKey=d,a.createCompletionFragment=J,a.createCompletionPlugin=te,a.createGhostDecoration=k,a.debounce=V,a.defaultGetPromptType=M,a.detectLanguage=ce,a.emptyDecorations=C,a.getActiveSuggestion=Z,a.getPromptBuilder=O,a.getTextAfterCursor=A,a.getTextBeforeCursor=w,a.handleKeyDown=$,a.hasActiveCompletion=Y,a.insertCompletion=N,a.parseCompletionResult=b,a.shouldCancelCompletion=Q,a.shouldTriggerCompletion=K,a.updateGhostDecoration=G,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"}),a}({},ProseMirrorState,ProseMirrorView);
43
+ Completion:`}function B(e){switch(e){case"code":return q;case"markdown":return z;case"zh":case"chinese":case"中文":return S;case"common":default:return R}}function ne(e,t){const r=t==null?void 0:t.lang;if(r&&ee.test(r))return S(e);const n=(t==null?void 0:t.type)??e.promptType;return B(n)(e)}function re(e){const t=e.slice(-100);return/:\s*(string|number|boolean|any|void|interface|type)\b/.test(t)||/\b(const|let|var)\s+\w+:\s*\w+/.test(t)?"typescript":/\b(def|class|import|from)\b/.test(t)&&/:\s*\n/.test(t)?"python":/<\w+/.test(t)&&/>/.test(t)?"html":/\{[\s\S]*?:[\s\S]*?;/.test(t)?"css":"javascript"}return a.approveCompletion=Q,a.buildChinesePrompt=S,a.buildCodePrompt=q,a.buildCommonPrompt=R,a.buildJavaScriptPrompt=te,a.buildMarkdownPrompt=z,a.buildPrompt=ne,a.buildPythonPrompt=oe,a.cancelCompletion=J,a.clearDecorations=v,a.completion=Y,a.completionMetaKey=O,a.completionPluginKey=p,a.createCompletionFragment=j,a.createGhostDecoration=D,a.debounce=F,a.defaultGetPromptType=L,a.detectLanguage=re,a.emptyDecorations=x,a.exitCompletion=V,a.getActiveSuggestion=X,a.getPromptBuilder=B,a.getTextAfterCursor=G,a.getTextBeforeCursor=M,a.hasActiveCompletion=W,a.insertCompletion=A,a.parseCompletionResult=y,a.shouldCancelCompletion=Z,a.shouldTriggerCompletion=$,a.updateGhostDecoration=E,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"}),a}({},ProseMirrorState,ProseMirrorView);
44
44
  //# sourceMappingURL=index.iife.js.map