ai-helper-core 1.0.0

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.
@@ -0,0 +1,56 @@
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.AiHelperCore={}))})(this,function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t={apiBase:`/api/ai-chat`,branding:{name:`坤土智脑`,icon:`data:image/svg+xml,<svg viewBox=%220 0 1051 1024%22 xmlns=%22http://www.w3.org/2000/svg%22><path d=%22M451.99 1005.12a369.77 369.77 0 0 1-369.81-369.81 369.81 369.81 0 0 1 369.81-369.81h147.93a369.81 369.81 0 0 1 369.81 369.81 369.77 369.77 0 0 1-369.81 369.81zM208.96 709.25a197.25 197.25 0 0 0 197.25 197.25h239.48a197.25 197.25 0 0 0 197.25-197.25 197.22 197.22 0 0 0-197.25-197.22h-239.48a197.22 197.22 0 0 0-197.25 197.18z m475.86 98.65a31.99 31.99 0 0 1-31.99-31.99v-133.28a31.99 31.99 0 0 1 31.99-31.99h6.46a31.99 31.99 0 0 1 31.99 31.99v133.28a31.99 31.99 0 0 1-31.99 31.99z m-324.07 0a31.99 31.99 0 0 1-31.99-31.99v-133.28a31.99 31.99 0 0 1 31.99-31.99h6.5a31.99 31.99 0 0 1 31.99 31.99v133.28a31.99 31.99 0 0 1-31.99 31.99z m91.92-624.2a20.73 20.73 0 0 1-13.37-26.17 88 88 0 0 1 87.39-49.59 91.85 91.85 0 0 1 85.09 43.7 20.77 20.77 0 0 1-10.12 27.57 20.81 20.81 0 0 1-27.65-10.16 53.48 53.48 0 0 0-47.32-19.56 51.32 51.32 0 0 0-47.85 20.77 20.77 20.77 0 0 1-19.79 14.43 20.54 20.54 0 0 1-6.42-0.98z m-69.41-78.78a20.77 20.77 0 0 1-2.34-29.27 194.08 194.08 0 0 1 145.74-56.65 193.52 193.52 0 0 1 144.12 54.91 20.81 20.81 0 0 1-1.78 29.38 20.85 20.85 0 0 1-29.38-1.78 154.39 154.39 0 0 0-112.96-40.94 154.5 154.5 0 0 0-114.09 42.11 20.58 20.58 0 0 1-15.82 7.29 20.88 20.88 0 0 1-13.52-5.02z%22 fill=%22%23fff%22 p-id=%223539%22/><path d=%22M0.04 643.54a115.04 115.04 0 0 0 115.04 115.04v-230.11A115.04 115.04 0 0 0 0.04 643.54z%22 fill=%22%23fff%22 opacity=%22.74%22 p-id=%223540%22/><path d=%22M936.87 528.5v230.11a115.04 115.04 0 0 0 115.04-115.04 115.04 115.04 0 0 0-115.04-115.07z%22 fill=%22%23fff%22 opacity=%22.74%22 p-id=%223541%22/></svg>`,welcomeTitle:`你好,我是坤土智脑`,welcomeDesc:`我可以帮你查询数据、分析趋势、生成图表`},theme:`auto`,showQuickQuestions:!0,endpoints:{chat:`/chat`,history:`/history`,prompts:`/prompts`},closeOnRouteChange:!0},n=class{constructor(e){this.config=this.buildConfig(e)}set(e){this.config=this.buildConfig(e)}merge(e){this.config=this.buildConfig(e)}get(){return{...this.config}}getApiUrl(e){return`${this.config.apiBase.replace(/\/$/,``)}${e.startsWith(`/`)?e:`/${e}`}`}buildConfig(e){return e?{apiBase:e.apiBase??t.apiBase,branding:{name:e.branding?.name??t.branding.name,icon:e.branding?.icon??t.branding.icon,welcomeTitle:e.branding?.welcomeTitle??t.branding.welcomeTitle,welcomeDesc:e.branding?.welcomeDesc??t.branding.welcomeDesc},theme:e.theme??t.theme,getHeaders:e.getHeaders,showQuickQuestions:e.showQuickQuestions??t.showQuickQuestions,endpoints:{chat:e.endpoints?.chat??t.endpoints.chat,history:e.endpoints?.history??t.endpoints.history,prompts:e.endpoints?.prompts??t.endpoints.prompts},closeOnRouteChange:e.closeOnRouteChange??t.closeOnRouteChange,buildChatBody:e.buildChatBody,parseChatChunk:e.parseChatChunk,parseHistoryResponse:e.parseHistoryResponse,parsePromptResponse:e.parsePromptResponse}:{...t}}},r=new n,i=class{constructor(e){this.config=e}defaultBuildChatBody(e){return JSON.stringify({message:e.message})}defaultParseChatChunk(e){try{let t=JSON.parse(e);return t.content===``?{content:``,done:!0}:typeof t.content==`string`?{content:t.content}:{content:``}}catch{return e===`[DONE]`?{content:``,done:!0}:{content:e}}}defaultParseHistory(e){let t=e;return{messages:Array.isArray(t.messages)?t.messages.map(e=>({role:e.role??`assistant`,content:e.content??``,timestamp:e.timestamp??``})):[],total:typeof t.total==`number`?t.total:0}}defaultParsePrompts(e){let t=e;return{items:Array.isArray(t.items)?t.items:[],total:typeof t.total==`number`?t.total:0}}get buildChatBodyFn(){return this.config.buildChatBody??this.defaultBuildChatBody.bind(this)}get parseChatChunkFn(){return this.config.parseChatChunk??this.defaultParseChatChunk.bind(this)}get parseHistoryFn(){return this.config.parseHistoryResponse??this.defaultParseHistory.bind(this)}get parsePromptsFn(){return this.config.parsePromptResponse??this.defaultParsePrompts.bind(this)}async resolveHeaders(){return{"Content-Type":`application/json`,...this.config.getHeaders?await this.config.getHeaders():{}}}resolveEndpoint(e){let t=this.config.apiBase.replace(/\/$/,``),n=this.config.endpoints;return e===`/chat`?`${t}${n.chat}`:e===`/history`?`${t}${n.history}`:e===`/prompts`?`${t}${n.prompts}`:`${t}${e.startsWith(`/`)?e:`/${e}`}`}async request(e,t){let n=this.resolveEndpoint(e);if(t?.params){let e=new URLSearchParams;for(let[n,r]of Object.entries(t.params))r!==void 0&&e.set(n,String(r));let r=e.toString();r&&(n+=(n.includes(`?`)?`&`:`?`)+r)}let{params:r,...i}=t??{},a=await this.resolveHeaders(),o=await fetch(n,{credentials:`include`,...i,headers:{...a,...i.headers}});if(!o.ok)throw Error(`Request failed: ${o.status} ${o.statusText}`);return o.json()}async chatStream(e,t,n){let r=new AbortController,i=await this.resolveHeaders(),a=null,o=()=>{a!==null&&clearTimeout(a),a=setTimeout(()=>{n?.()},6e4)},s=e=>{o(),t(e)},c=()=>{a!==null&&clearTimeout(a),n?.()},l=this.parseChatChunkFn,u=this.buildChatBodyFn;return(async()=>{try{let n=await fetch(this.resolveEndpoint(`/chat`),{method:`POST`,headers:i,credentials:`include`,signal:r.signal,body:u({message:e})});if(!n.ok){t(`请求失败: ${n.status} ${n.statusText}`),c();return}let a=n.body?.getReader();if(!a){c();return}let o=new TextDecoder,d=``;for(;;){let{done:e,value:t}=await a.read();if(e){c();break}d+=o.decode(t,{stream:!0});let n=d.split(/\r?\n/);d=n.pop()||``;for(let e of n){let t=e.trim();if(!t)continue;let n=t.startsWith(`data:`)?t.slice(5).trim():t;if(!n||n.startsWith(`:`)||n.startsWith(`id:`)||n.startsWith(`event:`))continue;let r=l(n);if(r.content&&s(r.content),r.done){c();return}}}}catch(e){if(e instanceof DOMException&&e.name===`AbortError`)return;c()}})(),o(),r}async getPrompts(e){let t=await this.request(`/prompts`,{method:`GET`,params:e});return this.parsePromptsFn(t)}async getHistory(e){let t=await this.request(`/history`,{method:`GET`,params:e});return this.parseHistoryFn(t)}},a=class{constructor(){this.state={messages:[],isVisible:!1,isLoading:!1,isInitial:!0},this.listeners=new Map}on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.off(e,t)}off(e,t){this.listeners.get(e)?.delete(t)}emit(e){let t=this.listeners.get(e);if(t)for(let e of t)e(this.state);if(e!==`stateChange`){let e=this.listeners.get(`stateChange`);if(e)for(let t of e)t(this.state)}}getState(){return this.state}show(){this.state.isVisible=!0,this.emit(`stateChange`)}hide(){this.state.isVisible=!1,this.emit(`stateChange`)}toggleVisibility(){this.state.isVisible=!this.state.isVisible,this.emit(`stateChange`)}addUserMessage(e){let t={id:Date.now(),role:`user`,content:e};return this.state.messages=[...this.state.messages,t],this.state.isInitial=!1,this.emit(`messageChange`),t}startAssistantMessage(){let e={id:Date.now()+1,role:`assistant`,content:``,isStreaming:!0};return this.state.messages=[...this.state.messages,e],this.state.isLoading=!0,this.emit(`messageChange`),e}appendToAssistantMessage(e){let t=this.state.messages[this.state.messages.length-1];if(t&&t.role===`assistant`){let n=t.content+e,r={...t,content:n};this.state.messages=[...this.state.messages.slice(0,-1),r],this.emit(`messageChange`)}}finishAssistantMessage(){let e=this.state.messages[this.state.messages.length-1];e&&e.role===`assistant`&&(e.isStreaming=!1),this.state.isLoading=!1,this.emit(`messageChange`)}setMessages(e){this.state.messages=e,this.state.isInitial=e.length===0,this.emit(`messageChange`)}clearMessages(){this.state.messages=[],this.state.isInitial=!0,this.emit(`messageChange`)}setLoading(e){this.state.isLoading=e,this.emit(`stateChange`)}};function o(){return typeof document>`u`?`light`:document.documentElement.classList.contains(`dark`)?`dark`:`light`}function s(e){if(typeof document>`u`||typeof window>`u`)return()=>{};let t=new MutationObserver(()=>{e(o())});t.observe(document.documentElement,{attributes:!0,attributeFilter:[`class`]});let n=window.matchMedia(`(prefers-color-scheme: dark)`),r=()=>e(o());return n.addEventListener(`change`,r),()=>{t.disconnect(),n.removeEventListener(`change`,r)}}function c(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var l=c();function u(e){l=e}var d={exec:()=>null};function f(e,t=``){let n=typeof e==`string`?e:e.source,r={replace:(e,t)=>{let i=typeof t==`string`?t:t.source;return i=i.replace(p.caret,`$1`),n=n.replace(e,i),r},getRegex:()=>new RegExp(n,t)};return r}var p={codeRemoveIndent:/^(?: {1,4}| {0,3}\t)/gm,outputLinkReplace:/\\([\[\]])/g,indentCodeCompensation:/^(\s+)(?:```)/,beginningSpace:/^\s+/,endingHash:/#$/,startingSpaceChar:/^ /,endingSpaceChar:/ $/,nonSpaceChar:/[^ ]/,newLineCharGlobal:/\n/g,tabCharGlobal:/\t/g,multipleSpaceGlobal:/\s+/g,blankLine:/^[ \t]*$/,doubleBlankLine:/\n[ \t]*\n[ \t]*$/,blockquoteStart:/^ {0,3}>/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] /,listReplaceTask:/^\[[ xX]\] +/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^<a /i,endATag:/^<\/a>/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^</,endAngleBracket:/>$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:e=>RegExp(`^( {0,3}${e})((?:[ ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:e=>RegExp(`^ {0,${Math.min(3,e-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),hrRegex:e=>RegExp(`^ {0,${Math.min(3,e-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:e=>RegExp(`^ {0,${Math.min(3,e-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:e=>RegExp(`^ {0,${Math.min(3,e-1)}}#`),htmlBeginRegex:e=>RegExp(`^ {0,${Math.min(3,e-1)}}<(?:[a-z].*>|!--)`,`i`)},ee=/^(?:[ \t]*(?:\n|$))+/,te=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,ne=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,m=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,re=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,h=/(?:[*+-]|\d{1,9}[.)])/,g=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,_=f(g).replace(/bull/g,h).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,``).getRegex(),ie=f(g).replace(/bull/g,h).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),v=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,y=/^[^\n]+/,b=/(?!\s*\])(?:\\.|[^\[\]\\])+/,ae=f(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace(`label`,b).replace(`title`,/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),oe=f(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,h).getRegex(),x=`address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul`,S=/<!--(?:-?>|[\s\S]*?(?:-->|$))/,se=f(`^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|<![A-Z][\\s\\S]*?(?:>\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))`,`i`).replace(`comment`,S).replace(`tag`,x).replace(`attribute`,/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),C=f(v).replace(`hr`,m).replace(`heading`,` {0,3}#{1,6}(?:\\s|$)`).replace(`|lheading`,``).replace(`|table`,``).replace(`blockquote`,` {0,3}>`).replace(`fences`," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace(`list`,` {0,3}(?:[*+-]|1[.)]) `).replace(`html`,`</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)`).replace(`tag`,x).getRegex(),w={blockquote:f(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace(`paragraph`,C).getRegex(),code:te,def:ae,fences:ne,heading:re,hr:m,html:se,lheading:_,list:oe,newline:ee,paragraph:C,table:d,text:y},T=f(`^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)`).replace(`hr`,m).replace(`heading`,` {0,3}#{1,6}(?:\\s|$)`).replace(`blockquote`,` {0,3}>`).replace(`code`,`(?: {4}| {0,3} )[^\\n]`).replace(`fences`," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace(`list`,` {0,3}(?:[*+-]|1[.)]) `).replace(`html`,`</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)`).replace(`tag`,x).getRegex(),ce={...w,lheading:ie,table:T,paragraph:f(v).replace(`hr`,m).replace(`heading`,` {0,3}#{1,6}(?:\\s|$)`).replace(`|lheading`,``).replace(`table`,T).replace(`blockquote`,` {0,3}>`).replace(`fences`," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace(`list`,` {0,3}(?:[*+-]|1[.)]) `).replace(`html`,`</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)`).replace(`tag`,x).getRegex()},le={...w,html:f(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:"[^"]*"|'[^']*'|\\s[^'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace(`comment`,S).replace(/tag/g,`(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b`).getRegex(),def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:d,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:f(v).replace(`hr`,m).replace(`heading`,` *#{1,6} *[^
2
+ ]`).replace(`lheading`,_).replace(`|table`,``).replace(`blockquote`,` {0,3}>`).replace(`|fences`,``).replace(`|list`,``).replace(`|html`,``).replace(`|tag`,``).getRegex()},ue=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,de=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,E=/^( {2,}|\\)\n(?!\s*$)/,fe=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,D=/[\p{P}\p{S}]/u,O=/[\s\p{P}\p{S}]/u,k=/[^\s\p{P}\p{S}]/u,pe=f(/^((?![*_])punctSpace)/,`u`).replace(/punctSpace/g,O).getRegex(),A=/(?!~)[\p{P}\p{S}]/u,me=/(?!~)[\s\p{P}\p{S}]/u,he=/(?:[^\s\p{P}\p{S}]|~)/u,ge=/\[[^[\]]*?\]\((?:\\.|[^\\\(\)]|\((?:\\.|[^\\\(\)])*\))*\)|`[^`]*?`|<[^<>]*?>/g,j=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,_e=f(j,`u`).replace(/punct/g,D).getRegex(),ve=f(j,`u`).replace(/punct/g,A).getRegex(),M=`^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)`,ye=f(M,`gu`).replace(/notPunctSpace/g,k).replace(/punctSpace/g,O).replace(/punct/g,D).getRegex(),be=f(M,`gu`).replace(/notPunctSpace/g,he).replace(/punctSpace/g,me).replace(/punct/g,A).getRegex(),xe=f(`^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)`,`gu`).replace(/notPunctSpace/g,k).replace(/punctSpace/g,O).replace(/punct/g,D).getRegex(),Se=f(/\\(punct)/,`gu`).replace(/punct/g,D).getRegex(),Ce=f(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace(`scheme`,/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace(`email`,/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),we=f(S).replace(`(?:-->|$)`,`-->`).getRegex(),Te=f(`^comment|^</[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>`).replace(`comment`,we).replace(`attribute`,/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),N=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Ee=f(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]*(?:\n[ \t]*)?)(title))?\s*\)/).replace(`label`,N).replace(`href`,/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/).replace(`title`,/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),P=f(/^!?\[(label)\]\[(ref)\]/).replace(`label`,N).replace(`ref`,b).getRegex(),F=f(/^!?\[(ref)\](?:\[\])?/).replace(`ref`,b).getRegex(),I={_backpedal:d,anyPunctuation:Se,autolink:Ce,blockSkip:ge,br:E,code:de,del:d,emStrongLDelim:_e,emStrongRDelimAst:ye,emStrongRDelimUnd:xe,escape:ue,link:Ee,nolink:F,punctuation:pe,reflink:P,reflinkSearch:f(`reflink|nolink(?!\\()`,`g`).replace(`reflink`,P).replace(`nolink`,F).getRegex(),tag:Te,text:fe,url:d},De={...I,link:f(/^!?\[(label)\]\((.*?)\)/).replace(`label`,N).getRegex(),reflink:f(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace(`label`,N).getRegex()},L={...I,emStrongRDelimAst:be,emStrongLDelim:ve,url:f(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,`i`).replace(`email`,/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\.|[^\\])*?(?:\\.|[^\s~\\]))\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/},Oe={...L,br:f(E).replace(`{2,}`,`*`).getRegex(),text:f(L.text).replace(`\\b_`,`\\b_| {2,}\\n`).replace(/\{2,\}/g,`*`).getRegex()},R={normal:w,gfm:ce,pedantic:le},z={normal:I,gfm:L,breaks:Oe,pedantic:De},ke={"&":`&amp;`,"<":`&lt;`,">":`&gt;`,'"':`&quot;`,"'":`&#39;`},B=e=>ke[e];function V(e,t){if(t){if(p.escapeTest.test(e))return e.replace(p.escapeReplace,B)}else if(p.escapeTestNoEncode.test(e))return e.replace(p.escapeReplaceNoEncode,B);return e}function H(e){try{e=encodeURI(e).replace(p.percentDecode,`%`)}catch{return null}return e}function U(e,t){let n=e.replace(p.findPipe,(e,t,n)=>{let r=!1,i=t;for(;--i>=0&&n[i]===`\\`;)r=!r;return r?`|`:` |`}).split(p.splitPipe),r=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length<t;)n.push(``);for(;r<n.length;r++)n[r]=n[r].trim().replace(p.slashPipe,`|`);return n}function W(e,t,n){let r=e.length;if(r===0)return``;let i=0;for(;i<r;){let a=e.charAt(r-i-1);if(a===t&&!n)i++;else if(a!==t&&n)i++;else break}return e.slice(0,r-i)}function Ae(e,t){if(e.indexOf(t[1])===-1)return-1;let n=0;for(let r=0;r<e.length;r++)if(e[r]===`\\`)r++;else if(e[r]===t[0])n++;else if(e[r]===t[1]&&(n--,n<0))return r;return n>0?-2:-1}function G(e,t,n,r,i){let a=t.href,o=t.title||null,s=e[1].replace(i.other.outputLinkReplace,`$1`);r.state.inLink=!0;let c={type:e[0].charAt(0)===`!`?`image`:`link`,raw:n,href:a,title:o,text:s,tokens:r.inlineTokens(s)};return r.state.inLink=!1,c}function je(e,t,n){let r=e.match(n.other.indentCodeCompensation);if(r===null)return t;let i=r[1];return t.split(`
3
+ `).map(e=>{let t=e.match(n.other.beginningSpace);if(t===null)return e;let[r]=t;return r.length>=i.length?e.slice(i.length):e}).join(`
4
+ `)}var K=class{options;rules;lexer;constructor(e){this.options=e||l}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:`space`,raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let e=t[0].replace(this.rules.other.codeRemoveIndent,``);return{type:`code`,raw:t[0],codeBlockStyle:`indented`,text:this.options.pedantic?e:W(e,`
5
+ `)}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let e=t[0],n=je(e,t[3]||``,this.rules);return{type:`code`,raw:e,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,`$1`):t[2],text:n}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let e=t[2].trim();if(this.rules.other.endingHash.test(e)){let t=W(e,`#`);(this.options.pedantic||!t||this.rules.other.endingSpaceChar.test(t))&&(e=t.trim())}return{type:`heading`,raw:t[0],depth:t[1].length,text:e,tokens:this.lexer.inline(e)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:`hr`,raw:W(t[0],`
6
+ `)}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let e=W(t[0],`
7
+ `).split(`
8
+ `),n=``,r=``,i=[];for(;e.length>0;){let t=!1,a=[],o;for(o=0;o<e.length;o++)if(this.rules.other.blockquoteStart.test(e[o]))a.push(e[o]),t=!0;else if(!t)a.push(e[o]);else break;e=e.slice(o);let s=a.join(`
9
+ `),c=s.replace(this.rules.other.blockquoteSetextReplace,`
10
+ $1`).replace(this.rules.other.blockquoteSetextReplace2,``);n=n?`${n}
11
+ ${s}`:s,r=r?`${r}
12
+ ${c}`:c;let l=this.lexer.state.top;if(this.lexer.state.top=!0,this.lexer.blockTokens(c,i,!0),this.lexer.state.top=l,e.length===0)break;let u=i.at(-1);if(u?.type===`code`)break;if(u?.type===`blockquote`){let t=u,a=t.raw+`
13
+ `+e.join(`
14
+ `),o=this.blockquote(a);i[i.length-1]=o,n=n.substring(0,n.length-t.raw.length)+o.raw,r=r.substring(0,r.length-t.text.length)+o.text;break}else if(u?.type===`list`){let t=u,a=t.raw+`
15
+ `+e.join(`
16
+ `),o=this.list(a);i[i.length-1]=o,n=n.substring(0,n.length-u.raw.length)+o.raw,r=r.substring(0,r.length-t.raw.length)+o.raw,e=a.substring(i.at(-1).raw.length).split(`
17
+ `);continue}}return{type:`blockquote`,raw:n,tokens:i,text:r}}}list(e){let t=this.rules.block.list.exec(e);if(t){let n=t[1].trim(),r=n.length>1,i={type:`list`,raw:``,ordered:r,start:r?+n.slice(0,-1):``,loose:!1,items:[]};n=r?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=r?n:`[*+-]`);let a=this.rules.other.listItemRegex(n),o=!1;for(;e;){let n=!1,r=``,s=``;if(!(t=a.exec(e))||this.rules.block.hr.test(e))break;r=t[0],e=e.substring(r.length);let c=t[2].split(`
18
+ `,1)[0].replace(this.rules.other.listReplaceTabs,e=>` `.repeat(3*e.length)),l=e.split(`
19
+ `,1)[0],u=!c.trim(),d=0;if(this.options.pedantic?(d=2,s=c.trimStart()):u?d=t[1].length+1:(d=t[2].search(this.rules.other.nonSpaceChar),d=d>4?1:d,s=c.slice(d),d+=t[1].length),u&&this.rules.other.blankLine.test(l)&&(r+=l+`
20
+ `,e=e.substring(l.length+1),n=!0),!n){let t=this.rules.other.nextBulletRegex(d),n=this.rules.other.hrRegex(d),i=this.rules.other.fencesBeginRegex(d),a=this.rules.other.headingBeginRegex(d),o=this.rules.other.htmlBeginRegex(d);for(;e;){let f=e.split(`
21
+ `,1)[0],p;if(l=f,this.options.pedantic?(l=l.replace(this.rules.other.listReplaceNesting,` `),p=l):p=l.replace(this.rules.other.tabCharGlobal,` `),i.test(l)||a.test(l)||o.test(l)||t.test(l)||n.test(l))break;if(p.search(this.rules.other.nonSpaceChar)>=d||!l.trim())s+=`
22
+ `+p.slice(d);else{if(u||c.replace(this.rules.other.tabCharGlobal,` `).search(this.rules.other.nonSpaceChar)>=4||i.test(c)||a.test(c)||n.test(c))break;s+=`
23
+ `+l}!u&&!l.trim()&&(u=!0),r+=f+`
24
+ `,e=e.substring(f.length+1),c=p.slice(d)}}i.loose||(o?i.loose=!0:this.rules.other.doubleBlankLine.test(r)&&(o=!0));let f=null,p;this.options.gfm&&(f=this.rules.other.listIsTask.exec(s),f&&(p=f[0]!==`[ ] `,s=s.replace(this.rules.other.listReplaceTask,``))),i.items.push({type:`list_item`,raw:r,task:!!f,checked:p,loose:!1,text:s,tokens:[]}),i.raw+=r}let s=i.items.at(-1);if(s)s.raw=s.raw.trimEnd(),s.text=s.text.trimEnd();else return;i.raw=i.raw.trimEnd();for(let e=0;e<i.items.length;e++)if(this.lexer.state.top=!1,i.items[e].tokens=this.lexer.blockTokens(i.items[e].text,[]),!i.loose){let t=i.items[e].tokens.filter(e=>e.type===`space`);i.loose=t.length>0&&t.some(e=>this.rules.other.anyLine.test(e.raw))}if(i.loose)for(let e=0;e<i.items.length;e++)i.items[e].loose=!0;return i}}html(e){let t=this.rules.block.html.exec(e);if(t)return{type:`html`,block:!0,raw:t[0],pre:t[1]===`pre`||t[1]===`script`||t[1]===`style`,text:t[0]}}def(e){let t=this.rules.block.def.exec(e);if(t){let e=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal,` `),n=t[2]?t[2].replace(this.rules.other.hrefBrackets,`$1`).replace(this.rules.inline.anyPunctuation,`$1`):``,r=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,`$1`):t[3];return{type:`def`,tag:e,raw:t[0],href:n,title:r}}}table(e){let t=this.rules.block.table.exec(e);if(!t||!this.rules.other.tableDelimiter.test(t[2]))return;let n=U(t[1]),r=t[2].replace(this.rules.other.tableAlignChars,``).split(`|`),i=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,``).split(`
25
+ `):[],a={type:`table`,raw:t[0],header:[],align:[],rows:[]};if(n.length===r.length){for(let e of r)this.rules.other.tableAlignRight.test(e)?a.align.push(`right`):this.rules.other.tableAlignCenter.test(e)?a.align.push(`center`):this.rules.other.tableAlignLeft.test(e)?a.align.push(`left`):a.align.push(null);for(let e=0;e<n.length;e++)a.header.push({text:n[e],tokens:this.lexer.inline(n[e]),header:!0,align:a.align[e]});for(let e of i)a.rows.push(U(e,a.header.length).map((e,t)=>({text:e,tokens:this.lexer.inline(e),header:!1,align:a.align[t]})));return a}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t)return{type:`heading`,raw:t[0],depth:t[2].charAt(0)===`=`?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let e=t[1].charAt(t[1].length-1)===`
26
+ `?t[1].slice(0,-1):t[1];return{type:`paragraph`,raw:t[0],text:e,tokens:this.lexer.inline(e)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:`text`,raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:`escape`,raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:`html`,raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){let t=this.rules.inline.link.exec(e);if(t){let e=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(e)){if(!this.rules.other.endAngleBracket.test(e))return;let t=W(e.slice(0,-1),`\\`);if((e.length-t.length)%2==0)return}else{let e=Ae(t[2],`()`);if(e===-2)return;if(e>-1){let n=(t[0].indexOf(`!`)===0?5:4)+t[1].length+e;t[2]=t[2].substring(0,e),t[0]=t[0].substring(0,n).trim(),t[3]=``}}let n=t[2],r=``;if(this.options.pedantic){let e=this.rules.other.pedanticHrefTitle.exec(n);e&&(n=e[1],r=e[3])}else r=t[3]?t[3].slice(1,-1):``;return n=n.trim(),this.rules.other.startAngleBracket.test(n)&&(n=this.options.pedantic&&!this.rules.other.endAngleBracket.test(e)?n.slice(1):n.slice(1,-1)),G(t,{href:n&&n.replace(this.rules.inline.anyPunctuation,`$1`),title:r&&r.replace(this.rules.inline.anyPunctuation,`$1`)},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let e=t[(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal,` `).toLowerCase()];if(!e){let e=n[0].charAt(0);return{type:`text`,raw:e,text:e}}return G(n,e,n[0],this.lexer,this.rules)}}emStrong(e,t,n=``){let r=this.rules.inline.emStrongLDelim.exec(e);if(r&&!(r[3]&&n.match(this.rules.other.unicodeAlphaNumeric))&&(!(r[1]||r[2])||!n||this.rules.inline.punctuation.exec(n))){let n=[...r[0]].length-1,i,a,o=n,s=0,c=r[0][0]===`*`?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(c.lastIndex=0,t=t.slice(-1*e.length+n);(r=c.exec(t))!=null;){if(i=r[1]||r[2]||r[3]||r[4]||r[5]||r[6],!i)continue;if(a=[...i].length,r[3]||r[4]){o+=a;continue}else if((r[5]||r[6])&&n%3&&!((n+a)%3)){s+=a;continue}if(o-=a,o>0)continue;a=Math.min(a,a+o+s);let t=[...r[0]][0].length,c=e.slice(0,n+r.index+t+a);if(Math.min(n,a)%2){let e=c.slice(1,-1);return{type:`em`,raw:c,text:e,tokens:this.lexer.inlineTokens(e)}}let l=c.slice(2,-2);return{type:`strong`,raw:c,text:l,tokens:this.lexer.inlineTokens(l)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let e=t[2].replace(this.rules.other.newLineCharGlobal,` `),n=this.rules.other.nonSpaceChar.test(e),r=this.rules.other.startingSpaceChar.test(e)&&this.rules.other.endingSpaceChar.test(e);return n&&r&&(e=e.substring(1,e.length-1)),{type:`codespan`,raw:t[0],text:e}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:`br`,raw:t[0]}}del(e){let t=this.rules.inline.del.exec(e);if(t)return{type:`del`,raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let e,n;return t[2]===`@`?(e=t[1],n=`mailto:`+e):(e=t[1],n=e),{type:`link`,raw:t[0],text:e,href:n,tokens:[{type:`text`,raw:e,text:e}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let e,n;if(t[2]===`@`)e=t[0],n=`mailto:`+e;else{let r;do r=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??``;while(r!==t[0]);e=t[0],n=t[1]===`www.`?`http://`+t[0]:t[0]}return{type:`link`,raw:t[0],text:e,href:n,tokens:[{type:`text`,raw:e,text:e}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let e=this.lexer.state.inRawBlock;return{type:`text`,raw:t[0],text:t[0],escaped:e}}}},q=class e{tokens;options;state;tokenizer;inlineQueue;constructor(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||l,this.options.tokenizer=this.options.tokenizer||new K,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let t={other:p,block:R.normal,inline:z.normal};this.options.pedantic?(t.block=R.pedantic,t.inline=z.pedantic):this.options.gfm&&(t.block=R.gfm,this.options.breaks?t.inline=z.breaks:t.inline=z.gfm),this.tokenizer.rules=t}static get rules(){return{block:R,inline:z}}static lex(t,n){return new e(n).lex(t)}static lexInline(t,n){return new e(n).inlineTokens(t)}lex(e){e=e.replace(p.carriageReturn,`
27
+ `),this.blockTokens(e,this.tokens);for(let e=0;e<this.inlineQueue.length;e++){let t=this.inlineQueue[e];this.inlineTokens(t.src,t.tokens)}return this.inlineQueue=[],this.tokens}blockTokens(e,t=[],n=!1){for(this.options.pedantic&&(e=e.replace(p.tabCharGlobal,` `).replace(p.spaceLine,``));e;){let r;if(this.options.extensions?.block?.some(n=>(r=n.call({lexer:this},e,t))?(e=e.substring(r.raw.length),t.push(r),!0):!1))continue;if(r=this.tokenizer.space(e)){e=e.substring(r.raw.length);let n=t.at(-1);r.raw.length===1&&n!==void 0?n.raw+=`
28
+ `:t.push(r);continue}if(r=this.tokenizer.code(e)){e=e.substring(r.raw.length);let n=t.at(-1);n?.type===`paragraph`||n?.type===`text`?(n.raw+=`
29
+ `+r.raw,n.text+=`
30
+ `+r.text,this.inlineQueue.at(-1).src=n.text):t.push(r);continue}if(r=this.tokenizer.fences(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.heading(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.hr(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.blockquote(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.list(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.html(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.def(e)){e=e.substring(r.raw.length);let n=t.at(-1);n?.type===`paragraph`||n?.type===`text`?(n.raw+=`
31
+ `+r.raw,n.text+=`
32
+ `+r.raw,this.inlineQueue.at(-1).src=n.text):this.tokens.links[r.tag]||(this.tokens.links[r.tag]={href:r.href,title:r.title});continue}if(r=this.tokenizer.table(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.lheading(e)){e=e.substring(r.raw.length),t.push(r);continue}let i=e;if(this.options.extensions?.startBlock){let t=1/0,n=e.slice(1),r;this.options.extensions.startBlock.forEach(e=>{r=e.call({lexer:this},n),typeof r==`number`&&r>=0&&(t=Math.min(t,r))}),t<1/0&&t>=0&&(i=e.substring(0,t+1))}if(this.state.top&&(r=this.tokenizer.paragraph(i))){let a=t.at(-1);n&&a?.type===`paragraph`?(a.raw+=`
33
+ `+r.raw,a.text+=`
34
+ `+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=a.text):t.push(r),n=i.length!==e.length,e=e.substring(r.raw.length);continue}if(r=this.tokenizer.text(e)){e=e.substring(r.raw.length);let n=t.at(-1);n?.type===`text`?(n.raw+=`
35
+ `+r.raw,n.text+=`
36
+ `+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=n.text):t.push(r);continue}if(e){let t=`Infinite loop on byte: `+e.charCodeAt(0);if(this.options.silent){console.error(t);break}else throw Error(t)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n=e,r=null;if(this.tokens.links){let e=Object.keys(this.tokens.links);if(e.length>0)for(;(r=this.tokenizer.rules.inline.reflinkSearch.exec(n))!=null;)e.includes(r[0].slice(r[0].lastIndexOf(`[`)+1,-1))&&(n=n.slice(0,r.index)+`[`+`a`.repeat(r[0].length-2)+`]`+n.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(r=this.tokenizer.rules.inline.anyPunctuation.exec(n))!=null;)n=n.slice(0,r.index)+`++`+n.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);for(;(r=this.tokenizer.rules.inline.blockSkip.exec(n))!=null;)n=n.slice(0,r.index)+`[`+`a`.repeat(r[0].length-2)+`]`+n.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);let i=!1,a=``;for(;e;){i||(a=``),i=!1;let r;if(this.options.extensions?.inline?.some(n=>(r=n.call({lexer:this},e,t))?(e=e.substring(r.raw.length),t.push(r),!0):!1))continue;if(r=this.tokenizer.escape(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.tag(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.link(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(r.raw.length);let n=t.at(-1);r.type===`text`&&n?.type===`text`?(n.raw+=r.raw,n.text+=r.text):t.push(r);continue}if(r=this.tokenizer.emStrong(e,n,a)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.codespan(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.br(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.del(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.autolink(e)){e=e.substring(r.raw.length),t.push(r);continue}if(!this.state.inLink&&(r=this.tokenizer.url(e))){e=e.substring(r.raw.length),t.push(r);continue}let o=e;if(this.options.extensions?.startInline){let t=1/0,n=e.slice(1),r;this.options.extensions.startInline.forEach(e=>{r=e.call({lexer:this},n),typeof r==`number`&&r>=0&&(t=Math.min(t,r))}),t<1/0&&t>=0&&(o=e.substring(0,t+1))}if(r=this.tokenizer.inlineText(o)){e=e.substring(r.raw.length),r.raw.slice(-1)!==`_`&&(a=r.raw.slice(-1)),i=!0;let n=t.at(-1);n?.type===`text`?(n.raw+=r.raw,n.text+=r.text):t.push(r);continue}if(e){let t=`Infinite loop on byte: `+e.charCodeAt(0);if(this.options.silent){console.error(t);break}else throw Error(t)}}return t}},J=class{options;parser;constructor(e){this.options=e||l}space(e){return``}code({text:e,lang:t,escaped:n}){let r=(t||``).match(p.notSpaceStart)?.[0],i=e.replace(p.endingNewline,``)+`
37
+ `;return r?`<pre><code class="language-`+V(r)+`">`+(n?i:V(i,!0))+`</code></pre>
38
+ `:`<pre><code>`+(n?i:V(i,!0))+`</code></pre>
39
+ `}blockquote({tokens:e}){return`<blockquote>
40
+ ${this.parser.parse(e)}</blockquote>
41
+ `}html({text:e}){return e}heading({tokens:e,depth:t}){return`<h${t}>${this.parser.parseInline(e)}</h${t}>
42
+ `}hr(e){return`<hr>
43
+ `}list(e){let t=e.ordered,n=e.start,r=``;for(let t=0;t<e.items.length;t++){let n=e.items[t];r+=this.listitem(n)}let i=t?`ol`:`ul`,a=t&&n!==1?` start="`+n+`"`:``;return`<`+i+a+`>
44
+ `+r+`</`+i+`>
45
+ `}listitem(e){let t=``;if(e.task){let n=this.checkbox({checked:!!e.checked});e.loose?e.tokens[0]?.type===`paragraph`?(e.tokens[0].text=n+` `+e.tokens[0].text,e.tokens[0].tokens&&e.tokens[0].tokens.length>0&&e.tokens[0].tokens[0].type===`text`&&(e.tokens[0].tokens[0].text=n+` `+V(e.tokens[0].tokens[0].text),e.tokens[0].tokens[0].escaped=!0)):e.tokens.unshift({type:`text`,raw:n+` `,text:n+` `,escaped:!0}):t+=n+` `}return t+=this.parser.parse(e.tokens,!!e.loose),`<li>${t}</li>
46
+ `}checkbox({checked:e}){return`<input `+(e?`checked="" `:``)+`disabled="" type="checkbox">`}paragraph({tokens:e}){return`<p>${this.parser.parseInline(e)}</p>
47
+ `}table(e){let t=``,n=``;for(let t=0;t<e.header.length;t++)n+=this.tablecell(e.header[t]);t+=this.tablerow({text:n});let r=``;for(let t=0;t<e.rows.length;t++){let i=e.rows[t];n=``;for(let e=0;e<i.length;e++)n+=this.tablecell(i[e]);r+=this.tablerow({text:n})}return r&&=`<tbody>${r}</tbody>`,`<table>
48
+ <thead>
49
+ `+t+`</thead>
50
+ `+r+`</table>
51
+ `}tablerow({text:e}){return`<tr>
52
+ ${e}</tr>
53
+ `}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?`th`:`td`;return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`</${n}>
54
+ `}strong({tokens:e}){return`<strong>${this.parser.parseInline(e)}</strong>`}em({tokens:e}){return`<em>${this.parser.parseInline(e)}</em>`}codespan({text:e}){return`<code>${V(e,!0)}</code>`}br(e){return`<br>`}del({tokens:e}){return`<del>${this.parser.parseInline(e)}</del>`}link({href:e,title:t,tokens:n}){let r=this.parser.parseInline(n),i=H(e);if(i===null)return r;e=i;let a=`<a href="`+e+`"`;return t&&(a+=` title="`+V(t)+`"`),a+=`>`+r+`</a>`,a}image({href:e,title:t,text:n,tokens:r}){r&&(n=this.parser.parseInline(r,this.parser.textRenderer));let i=H(e);if(i===null)return V(n);e=i;let a=`<img src="${e}" alt="${n}"`;return t&&(a+=` title="${V(t)}"`),a+=`>`,a}text(e){return`tokens`in e&&e.tokens?this.parser.parseInline(e.tokens):`escaped`in e&&e.escaped?e.text:V(e.text)}},Y=class{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return``+e}image({text:e}){return``+e}br(){return``}},X=class e{options;renderer;textRenderer;constructor(e){this.options=e||l,this.options.renderer=this.options.renderer||new J,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new Y}static parse(t,n){return new e(n).parse(t)}static parseInline(t,n){return new e(n).parseInline(t)}parse(e,t=!0){let n=``;for(let r=0;r<e.length;r++){let i=e[r];if(this.options.extensions?.renderers?.[i.type]){let e=i,t=this.options.extensions.renderers[e.type].call({parser:this},e);if(t!==!1||![`space`,`hr`,`heading`,`code`,`table`,`blockquote`,`list`,`html`,`paragraph`,`text`].includes(e.type)){n+=t||``;continue}}let a=i;switch(a.type){case`space`:n+=this.renderer.space(a);continue;case`hr`:n+=this.renderer.hr(a);continue;case`heading`:n+=this.renderer.heading(a);continue;case`code`:n+=this.renderer.code(a);continue;case`table`:n+=this.renderer.table(a);continue;case`blockquote`:n+=this.renderer.blockquote(a);continue;case`list`:n+=this.renderer.list(a);continue;case`html`:n+=this.renderer.html(a);continue;case`paragraph`:n+=this.renderer.paragraph(a);continue;case`text`:{let i=a,o=this.renderer.text(i);for(;r+1<e.length&&e[r+1].type===`text`;)i=e[++r],o+=`
55
+ `+this.renderer.text(i);t?n+=this.renderer.paragraph({type:`paragraph`,raw:o,text:o,tokens:[{type:`text`,raw:o,text:o,escaped:!0}]}):n+=o;continue}default:{let e=`Token with "`+a.type+`" type was not found.`;if(this.options.silent)return console.error(e),``;throw Error(e)}}}return n}parseInline(e,t=this.renderer){let n=``;for(let r=0;r<e.length;r++){let i=e[r];if(this.options.extensions?.renderers?.[i.type]){let e=this.options.extensions.renderers[i.type].call({parser:this},i);if(e!==!1||![`escape`,`html`,`link`,`image`,`strong`,`em`,`codespan`,`br`,`del`,`text`].includes(i.type)){n+=e||``;continue}}let a=i;switch(a.type){case`escape`:n+=t.text(a);break;case`html`:n+=t.html(a);break;case`link`:n+=t.link(a);break;case`image`:n+=t.image(a);break;case`strong`:n+=t.strong(a);break;case`em`:n+=t.em(a);break;case`codespan`:n+=t.codespan(a);break;case`br`:n+=t.br(a);break;case`del`:n+=t.del(a);break;case`text`:n+=t.text(a);break;default:{let e=`Token with "`+a.type+`" type was not found.`;if(this.options.silent)return console.error(e),``;throw Error(e)}}}return n}},Z=class{options;block;constructor(e){this.options=e||l}static passThroughHooks=new Set([`preprocess`,`postprocess`,`processAllTokens`]);preprocess(e){return e}postprocess(e){return e}processAllTokens(e){return e}provideLexer(){return this.block?q.lex:q.lexInline}provideParser(){return this.block?X.parse:X.parseInline}},Q=new class{defaults=c();options=this.setOptions;parse=this.parseMarkdown(!0);parseInline=this.parseMarkdown(!1);Parser=X;Renderer=J;TextRenderer=Y;Lexer=q;Tokenizer=K;Hooks=Z;constructor(...e){this.use(...e)}walkTokens(e,t){let n=[];for(let r of e)switch(n=n.concat(t.call(this,r)),r.type){case`table`:{let e=r;for(let r of e.header)n=n.concat(this.walkTokens(r.tokens,t));for(let r of e.rows)for(let e of r)n=n.concat(this.walkTokens(e.tokens,t));break}case`list`:{let e=r;n=n.concat(this.walkTokens(e.items,t));break}default:{let e=r;this.defaults.extensions?.childTokens?.[e.type]?this.defaults.extensions.childTokens[e.type].forEach(r=>{let i=e[r].flat(1/0);n=n.concat(this.walkTokens(i,t))}):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(e=>{let n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach(e=>{if(!e.name)throw Error(`extension name required`);if(`renderer`in e){let n=t.renderers[e.name];n?t.renderers[e.name]=function(...t){let r=e.renderer.apply(this,t);return r===!1&&(r=n.apply(this,t)),r}:t.renderers[e.name]=e.renderer}if(`tokenizer`in e){if(!e.level||e.level!==`block`&&e.level!==`inline`)throw Error(`extension level must be 'block' or 'inline'`);let n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&(e.level===`block`?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:e.level===`inline`&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}`childTokens`in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)}),n.extensions=t),e.renderer){let t=this.defaults.renderer||new J(this.defaults);for(let n in e.renderer){if(!(n in t))throw Error(`renderer '${n}' does not exist`);if([`options`,`parser`].includes(n))continue;let r=n,i=e.renderer[r],a=t[r];t[r]=(...e)=>{let n=i.apply(t,e);return n===!1&&(n=a.apply(t,e)),n||``}}n.renderer=t}if(e.tokenizer){let t=this.defaults.tokenizer||new K(this.defaults);for(let n in e.tokenizer){if(!(n in t))throw Error(`tokenizer '${n}' does not exist`);if([`options`,`rules`,`lexer`].includes(n))continue;let r=n,i=e.tokenizer[r],a=t[r];t[r]=(...e)=>{let n=i.apply(t,e);return n===!1&&(n=a.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){let t=this.defaults.hooks||new Z;for(let n in e.hooks){if(!(n in t))throw Error(`hook '${n}' does not exist`);if([`options`,`block`].includes(n))continue;let r=n,i=e.hooks[r],a=t[r];Z.passThroughHooks.has(n)?t[r]=e=>{if(this.defaults.async)return Promise.resolve(i.call(t,e)).then(e=>a.call(t,e));let n=i.call(t,e);return a.call(t,n)}:t[r]=(...e)=>{let n=i.apply(t,e);return n===!1&&(n=a.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){let t=this.defaults.walkTokens,r=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(r.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return q.lex(e,t??this.defaults)}parser(e,t){return X.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{let r={...n},i={...this.defaults,...r},a=this.onError(!!i.silent,!!i.async);if(this.defaults.async===!0&&r.async===!1)return a(Error(`marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise.`));if(t==null)return a(Error(`marked(): input parameter is undefined or null`));if(typeof t!=`string`)return a(Error(`marked(): input parameter is of type `+Object.prototype.toString.call(t)+`, string expected`));i.hooks&&(i.hooks.options=i,i.hooks.block=e);let o=i.hooks?i.hooks.provideLexer():e?q.lex:q.lexInline,s=i.hooks?i.hooks.provideParser():e?X.parse:X.parseInline;if(i.async)return Promise.resolve(i.hooks?i.hooks.preprocess(t):t).then(e=>o(e,i)).then(e=>i.hooks?i.hooks.processAllTokens(e):e).then(e=>i.walkTokens?Promise.all(this.walkTokens(e,i.walkTokens)).then(()=>e):e).then(e=>s(e,i)).then(e=>i.hooks?i.hooks.postprocess(e):e).catch(a);try{i.hooks&&(t=i.hooks.preprocess(t));let e=o(t,i);i.hooks&&(e=i.hooks.processAllTokens(e)),i.walkTokens&&this.walkTokens(e,i.walkTokens);let n=s(e,i);return i.hooks&&(n=i.hooks.postprocess(n)),n}catch(e){return a(e)}}}onError(e,t){return n=>{if(n.message+=`
56
+ Please report this to https://github.com/markedjs/marked.`,e){let e=`<p>An error occurred:</p><pre>`+V(n.message+``,!0)+`</pre>`;return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}};function $(e,t){return Q.parse(e,t)}$.options=$.setOptions=function(e){return Q.setOptions(e),$.defaults=Q.defaults,u($.defaults),$},$.getDefaults=c,$.defaults=l,$.use=function(...e){return Q.use(...e),$.defaults=Q.defaults,u($.defaults),$},$.walkTokens=function(e,t){return Q.walkTokens(e,t)},$.parseInline=Q.parseInline,$.Parser=X,$.parser=X.parse,$.Renderer=J,$.TextRenderer=Y,$.Lexer=q,$.lexer=q.lex,$.Tokenizer=K,$.Hooks=Z,$.parse=$,$.options,$.setOptions,$.use,$.walkTokens,$.parseInline,X.parse,q.lex;function Me(e,t){return $.parse(e,{breaks:!0,gfm:!0})}e.AiHelperApiClient=i,e.AiHelperConfigManager=n,e.AiHelperStateManager=a,e.configManager=r,e.detectTheme=o,e.onThemeChange=s,e.renderMarkdown=Me});
@@ -0,0 +1,23 @@
1
+ import type { AiHelperConfig, HistoryParams, HistoryResponse, PromptListResponse, PromptQueryParams } from '../types';
2
+ export declare class AiHelperApiClient {
3
+ private config;
4
+ constructor(config: AiHelperConfig);
5
+ /** Default request body builder: JSON.stringify({ message }) */
6
+ private defaultBuildChatBody;
7
+ /** Default SSE chunk parser: expects { content?: string } — done when content === '' or stream ends */
8
+ private defaultParseChatChunk;
9
+ /** Default history response adapter */
10
+ private defaultParseHistory;
11
+ /** Default prompt response adapter */
12
+ private defaultParsePrompts;
13
+ private get buildChatBodyFn();
14
+ private get parseChatChunkFn();
15
+ private get parseHistoryFn();
16
+ private get parsePromptsFn();
17
+ private resolveHeaders;
18
+ private resolveEndpoint;
19
+ private request;
20
+ chatStream(message: string, onChunk: (chunk: string) => void, onDone?: () => void): Promise<AbortController>;
21
+ getPrompts(params?: PromptQueryParams): Promise<PromptListResponse>;
22
+ getHistory(params?: HistoryParams): Promise<HistoryResponse>;
23
+ }
@@ -0,0 +1,11 @@
1
+ import type { AiHelperConfig, AiHelperOptions } from './types';
2
+ export declare class AiHelperConfigManager {
3
+ private config;
4
+ constructor(options?: AiHelperOptions);
5
+ set(options: AiHelperOptions): void;
6
+ merge(options: AiHelperOptions): void;
7
+ get(): AiHelperConfig;
8
+ getApiUrl(path: string): string;
9
+ private buildConfig;
10
+ }
11
+ export declare const configManager: AiHelperConfigManager;
@@ -0,0 +1,6 @@
1
+ export { AiHelperConfigManager, configManager } from './config';
2
+ export { AiHelperApiClient } from './api/client';
3
+ export { AiHelperStateManager } from './state/manager';
4
+ export { detectTheme, onThemeChange } from './theme/detector';
5
+ export { renderMarkdown } from './markdown/renderer';
6
+ export type { AiHelperOptions, AiHelperConfig, AiHelperBranding, AiHelperEndpoints, ChatParseResult, ChatRequestParams, Message, AiHelperState, PromptRow, PromptListResponse, PromptQueryParams, HistoryMessage, HistoryResponse, HistoryParams } from './types';
@@ -0,0 +1 @@
1
+ export declare function renderMarkdown(content: string, theme?: 'light' | 'dark'): string;
@@ -0,0 +1,21 @@
1
+ import type { AiHelperState, Message } from '../types';
2
+ type StateListener = (state: Readonly<AiHelperState>) => void;
3
+ export declare class AiHelperStateManager {
4
+ private state;
5
+ private listeners;
6
+ on(event: 'stateChange' | 'messageChange', listener: StateListener): () => void;
7
+ off(event: string, listener: StateListener): void;
8
+ private emit;
9
+ getState(): Readonly<AiHelperState>;
10
+ show(): void;
11
+ hide(): void;
12
+ toggleVisibility(): void;
13
+ addUserMessage(content: string): Message;
14
+ startAssistantMessage(): Message;
15
+ appendToAssistantMessage(chunk: string): void;
16
+ finishAssistantMessage(): void;
17
+ setMessages(messages: Message[]): void;
18
+ clearMessages(): void;
19
+ setLoading(loading: boolean): void;
20
+ }
21
+ export {};
@@ -0,0 +1,2 @@
1
+ export declare function detectTheme(): 'light' | 'dark';
2
+ export declare function onThemeChange(callback: (theme: 'light' | 'dark') => void): () => void;
@@ -0,0 +1,115 @@
1
+ export interface AiHelperBranding {
2
+ name?: string;
3
+ icon?: string;
4
+ welcomeTitle?: string;
5
+ welcomeDesc?: string;
6
+ }
7
+ export interface AiHelperEndpoints {
8
+ chat?: string;
9
+ history?: string;
10
+ prompts?: string;
11
+ }
12
+ /**
13
+ * 流式聊天响应解析结果
14
+ * content: 从当前 chunk 中提取的文本(追加到 assistant 消息)
15
+ * done: 是否已结束(设为 true 时终止流,不再调用 onChunk)
16
+ */
17
+ export interface ChatParseResult {
18
+ content: string;
19
+ done?: boolean;
20
+ }
21
+ /**
22
+ * 聊天请求构建参数
23
+ */
24
+ export interface ChatRequestParams {
25
+ message: string;
26
+ }
27
+ export interface AiHelperOptions {
28
+ apiBase?: string;
29
+ branding?: AiHelperBranding;
30
+ theme?: 'light' | 'dark' | 'auto';
31
+ getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
32
+ showQuickQuestions?: boolean;
33
+ endpoints?: AiHelperEndpoints;
34
+ closeOnRouteChange?: boolean;
35
+ /**
36
+ * 自定义聊天请求体构建函数
37
+ * 返回将作为 POST body 发送的字符串
38
+ * 默认: JSON.stringify({ message })
39
+ */
40
+ buildChatBody?: (params: ChatRequestParams) => string;
41
+ /**
42
+ * 自定义流式响应解析函数
43
+ * 每个 SSE chunk(或 NDJSON 行)调用一次
44
+ * 返回 { content, done }
45
+ * 默认: 解析为 { content: string } 格式
46
+ */
47
+ parseChatChunk?: (rawChunk: string) => ChatParseResult;
48
+ /**
49
+ * 自定义历史响应解析函数
50
+ * 接收 fetch().json() 的原始结果,返回 HistoryResponse
51
+ * 默认: 期望原始结果为 { messages: [{role, content}], total }
52
+ */
53
+ parseHistoryResponse?: (raw: unknown) => HistoryResponse;
54
+ /**
55
+ * 自定义提示词响应解析函数
56
+ * 接收 fetch().json() 的原始结果,返回 PromptListResponse
57
+ * 默认: 期望原始结果为 { items: [{id, name, content}], total }
58
+ */
59
+ parsePromptResponse?: (raw: unknown) => PromptListResponse;
60
+ }
61
+ export interface AiHelperConfig {
62
+ apiBase: string;
63
+ branding: Required<AiHelperBranding>;
64
+ theme: 'light' | 'dark' | 'auto';
65
+ getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
66
+ showQuickQuestions: boolean;
67
+ endpoints: Required<AiHelperEndpoints>;
68
+ closeOnRouteChange: boolean;
69
+ buildChatBody?: (params: ChatRequestParams) => string;
70
+ parseChatChunk?: (rawChunk: string) => ChatParseResult;
71
+ parseHistoryResponse?: (raw: unknown) => HistoryResponse;
72
+ parsePromptResponse?: (raw: unknown) => PromptListResponse;
73
+ }
74
+ export interface Message {
75
+ id: number;
76
+ role: 'user' | 'assistant';
77
+ content: string;
78
+ isStreaming?: boolean;
79
+ }
80
+ export interface PromptRow {
81
+ id: number;
82
+ name: string;
83
+ content: string;
84
+ description?: string;
85
+ created_at: string;
86
+ updated_at: string;
87
+ }
88
+ export interface PromptListResponse {
89
+ items: PromptRow[];
90
+ total: number;
91
+ }
92
+ export interface PromptQueryParams {
93
+ keyword?: string;
94
+ limit?: number;
95
+ offset?: number;
96
+ }
97
+ export interface HistoryMessage {
98
+ role: 'user' | 'assistant';
99
+ content: string;
100
+ timestamp: string;
101
+ }
102
+ export interface HistoryResponse {
103
+ messages: HistoryMessage[];
104
+ total: number;
105
+ }
106
+ export interface HistoryParams {
107
+ limit?: number;
108
+ offset?: number;
109
+ }
110
+ export interface AiHelperState {
111
+ messages: Message[];
112
+ isVisible: boolean;
113
+ isLoading: boolean;
114
+ isInitial: boolean;
115
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "ai-helper-core",
3
+ "version": "1.0.0",
4
+ "description": "Framework-agnostic AI chat helper core library",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "main": "./dist/ai-helper-core.umd.cjs",
8
+ "module": "./dist/ai-helper-core.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "development": "./src/index.ts",
14
+ "import": "./dist/ai-helper-core.js",
15
+ "require": "./dist/ai-helper-core.umd.cjs"
16
+ }
17
+ },
18
+ "files": ["dist", "src"],
19
+ "scripts": {
20
+ "build": "vite build && npm run build:types",
21
+ "build:types": "tsc --emitDeclarationOnly",
22
+ "dev": "vite build --watch"
23
+ },
24
+ "peerDependencies": {},
25
+ "dependencies": {
26
+ "marked": "^15.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "typescript": "~5.9.3",
30
+ "vite": "^8.0.1"
31
+ }
32
+ }
@@ -0,0 +1,221 @@
1
+ import type { AiHelperConfig, HistoryParams, HistoryResponse, PromptListResponse, PromptQueryParams, ChatParseResult } from '../types'
2
+
3
+ export class AiHelperApiClient {
4
+ constructor(private config: AiHelperConfig) {}
5
+
6
+ // ── Default parsers ──────────────────────────────────────────
7
+
8
+ /** Default request body builder: JSON.stringify({ message }) */
9
+ private defaultBuildChatBody(params: { message: string }): string {
10
+ return JSON.stringify({ message: params.message })
11
+ }
12
+
13
+ /** Default SSE chunk parser: expects { content?: string } — done when content === '' or stream ends */
14
+ private defaultParseChatChunk(raw: string): ChatParseResult {
15
+ try {
16
+ const parsed = JSON.parse(raw)
17
+ if (parsed.content === '') return { content: '', done: true }
18
+ if (typeof parsed.content === 'string') return { content: parsed.content }
19
+ // If the parsed object has no content field at all, skip it
20
+ return { content: '' }
21
+ } catch {
22
+ // Not JSON — return as-is
23
+ if (raw === '[DONE]') return { content: '', done: true }
24
+ return { content: raw }
25
+ }
26
+ }
27
+
28
+ /** Default history response adapter */
29
+ private defaultParseHistory(raw: unknown): HistoryResponse {
30
+ const r = raw as any
31
+ return {
32
+ messages: Array.isArray(r.messages) ? r.messages.map((m: any) => ({
33
+ role: m.role ?? 'assistant',
34
+ content: m.content ?? '',
35
+ timestamp: m.timestamp ?? ''
36
+ })) : [],
37
+ total: typeof r.total === 'number' ? r.total : 0
38
+ }
39
+ }
40
+
41
+ /** Default prompt response adapter */
42
+ private defaultParsePrompts(raw: unknown): PromptListResponse {
43
+ const r = raw as any
44
+ return {
45
+ items: Array.isArray(r.items) ? r.items : [],
46
+ total: typeof r.total === 'number' ? r.total : 0
47
+ }
48
+ }
49
+
50
+ // ── Helpers ──────────────────────────────────────────────────
51
+
52
+ private get buildChatBodyFn() {
53
+ return this.config.buildChatBody ?? this.defaultBuildChatBody.bind(this)
54
+ }
55
+
56
+ private get parseChatChunkFn() {
57
+ return this.config.parseChatChunk ?? this.defaultParseChatChunk.bind(this)
58
+ }
59
+
60
+ private get parseHistoryFn() {
61
+ return this.config.parseHistoryResponse ?? this.defaultParseHistory.bind(this)
62
+ }
63
+
64
+ private get parsePromptsFn() {
65
+ return this.config.parsePromptResponse ?? this.defaultParsePrompts.bind(this)
66
+ }
67
+
68
+ private async resolveHeaders(): Promise<Record<string, string>> {
69
+ const extra = this.config.getHeaders ? await this.config.getHeaders() : {}
70
+ return { 'Content-Type': 'application/json', ...extra }
71
+ }
72
+
73
+ private resolveEndpoint(path: string): string {
74
+ const base = this.config.apiBase.replace(/\/$/, '')
75
+ const endpoints = this.config.endpoints
76
+ if (path === '/chat') return `${base}${endpoints.chat}`
77
+ if (path === '/history') return `${base}${endpoints.history}`
78
+ if (path === '/prompts') return `${base}${endpoints.prompts}`
79
+ return `${base}${path.startsWith('/') ? path : `/${path}`}`
80
+ }
81
+
82
+ private async request<T>(endpoint: string, init?: RequestInit & { params?: Record<string, string | number | undefined> }): Promise<T> {
83
+ let url = this.resolveEndpoint(endpoint)
84
+
85
+ if (init?.params) {
86
+ const search = new URLSearchParams()
87
+ for (const [key, value] of Object.entries(init.params)) {
88
+ if (value !== undefined) search.set(key, String(value))
89
+ }
90
+ const qs = search.toString()
91
+ if (qs) url += (url.includes('?') ? '&' : '?') + qs
92
+ }
93
+
94
+ const { params: _params, ...rest } = init ?? {}
95
+ const headers = await this.resolveHeaders()
96
+ const res = await fetch(url, {
97
+ credentials: 'include',
98
+ ...rest,
99
+ headers: { ...headers, ...(rest.headers as Record<string, string> | undefined) }
100
+ })
101
+
102
+ if (!res.ok) {
103
+ throw new Error(`Request failed: ${res.status} ${res.statusText}`)
104
+ }
105
+
106
+ return res.json() as Promise<T>
107
+ }
108
+
109
+ async chatStream(
110
+ message: string,
111
+ onChunk: (chunk: string) => void,
112
+ onDone?: () => void
113
+ ): Promise<AbortController> {
114
+ const controller = new AbortController()
115
+ const headers = await this.resolveHeaders()
116
+
117
+ // Timeout guard: if no chunk arrives within 60s, close the stream
118
+ const STREAM_TIMEOUT = 60_000
119
+ let inactivityTimer: ReturnType<typeof setTimeout> | null = null
120
+
121
+ const resetInactivityTimer = () => {
122
+ if (inactivityTimer !== null) clearTimeout(inactivityTimer)
123
+ inactivityTimer = setTimeout(() => {
124
+ onDone?.()
125
+ }, STREAM_TIMEOUT)
126
+ }
127
+
128
+ const wrappedOnChunk = (chunk: string) => {
129
+ resetInactivityTimer()
130
+ onChunk(chunk)
131
+ }
132
+
133
+ const safeOnDone = () => {
134
+ if (inactivityTimer !== null) clearTimeout(inactivityTimer)
135
+ onDone?.()
136
+ }
137
+
138
+ const parseFn = this.parseChatChunkFn
139
+ const buildBodyFn = this.buildChatBodyFn
140
+
141
+ void (async () => {
142
+ try {
143
+ const response = await fetch(this.resolveEndpoint('/chat'), {
144
+ method: 'POST',
145
+ headers,
146
+ credentials: 'include',
147
+ signal: controller.signal,
148
+ body: buildBodyFn({ message })
149
+ })
150
+
151
+ if (!response.ok) {
152
+ onChunk(`请求失败: ${response.status} ${response.statusText}`)
153
+ safeOnDone()
154
+ return
155
+ }
156
+
157
+ const reader = response.body?.getReader()
158
+ if (!reader) {
159
+ safeOnDone()
160
+ return
161
+ }
162
+
163
+ const decoder = new TextDecoder()
164
+ let buffer = ''
165
+
166
+ while (true) {
167
+ const { done, value } = await reader.read()
168
+ if (done) {
169
+ safeOnDone()
170
+ break
171
+ }
172
+
173
+ buffer += decoder.decode(value, { stream: true })
174
+
175
+ // Split by newlines to handle NDJSON or SSE lines
176
+ const lines = buffer.split(/\r?\n/)
177
+ buffer = lines.pop() || ''
178
+
179
+ for (const line of lines) {
180
+ const trimmed = line.trim()
181
+ if (!trimmed) continue
182
+
183
+ // Strip SSE 'data:' prefix if present
184
+ const raw = trimmed.startsWith('data:') ? trimmed.slice(5).trim() : trimmed
185
+
186
+ // Skip SSE comments and metadata
187
+ if (!raw || raw.startsWith(':') || raw.startsWith('id:') || raw.startsWith('event:')) continue
188
+
189
+ const result = parseFn(raw)
190
+ if (result.content) {
191
+ wrappedOnChunk(result.content)
192
+ }
193
+ if (result.done) {
194
+ safeOnDone()
195
+ return
196
+ }
197
+ }
198
+ }
199
+ } catch (error) {
200
+ if (error instanceof DOMException && error.name === 'AbortError') {
201
+ return
202
+ }
203
+ safeOnDone()
204
+ }
205
+ })()
206
+
207
+ resetInactivityTimer()
208
+
209
+ return controller
210
+ }
211
+
212
+ async getPrompts(params?: PromptQueryParams): Promise<PromptListResponse> {
213
+ const raw = await this.request<unknown>('/prompts', { method: 'GET', params: params as Record<string, string | number | undefined> })
214
+ return this.parsePromptsFn(raw)
215
+ }
216
+
217
+ async getHistory(params?: HistoryParams): Promise<HistoryResponse> {
218
+ const raw = await this.request<unknown>('/history', { method: 'GET', params: params as Record<string, string | number | undefined> })
219
+ return this.parseHistoryFn(raw)
220
+ }
221
+ }