@vpxa/aikit 0.1.162 → 0.1.164
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/package.json +1 -1
- package/packages/blocks-core/dist/index.d.ts +2 -1
- package/packages/blocks-core/dist/index.js +6 -3
- package/packages/present/dist/index.html +1 -1
- package/packages/server/dist/index.js +1 -1
- package/packages/server/dist/{server-DLkC-unR.js → server-BXHUbkn9.js} +2436 -42
- package/packages/server/viewers/c4-viewer.html +27 -36
- package/packages/server/viewers/canvas.html +63 -73
- package/packages/server/viewers/report-template.html +596 -31
- package/packages/server/viewers/task-plan-static.html +38 -35
- package/packages/server/viewers/tour-viewer.html +601 -50
- package/scaffold/dist/definitions/skills/c4-architecture.mjs +1 -1
- package/scaffold/dist/definitions/skills/docs.mjs +1 -1
- package/scaffold/dist/definitions/skills/present.mjs +1 -1
|
@@ -238,7 +238,2383 @@ Complements: \`symbol\` (single lookup), \`trace\` (call-chain AST), \`blast_rad
|
|
|
238
238
|
{ path: 'docs/api.md', title: 'API Reference', status: 'stale' },
|
|
239
239
|
],
|
|
240
240
|
},
|
|
241
|
-
}`},{type:`text`,category:`content`,description:`Plain text content rendered through the markdown parser.`,valueShape:`string`},{type:`heading`,category:`content`,description:`Single heading with configurable level from h1 to h6.`,valueShape:`string`},{type:`paragraph`,category:`content`,description:`Single paragraph rendered inside a p tag.`,valueShape:`string`},{type:`separator`,category:`layout`,description:`Horizontal rule used to separate adjacent blocks.`,valueShape:`undefined`},{type:`actions`,category:`layout`,description:`Action bar containing button and select action definitions.`,valueShape:`Array<{ type: string; id: string; label: string; variant?: string; options?: Array<string | { label: string; value: string }> }>`}].map(e=>[e.type,{...e}]));function Pl(){let e={};for(let[t,n]of Nl)n.vendorScripts?.length&&(e[t]=n.vendorScripts);return e}const Fl=1024,Il=new Set([`user-closed`,`host-dismissed`,`replaced`]);function Ll(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function q(e,t,n){let r={code:e,message:t};return n!==void 0&&(r.details=n),{kind:`error`,error:r}}function J(e,t,n,r){e.writeHead(t,{"Content-Type":`application/json`,"Access-Control-Allow-Origin":r??`null`}),e.end(JSON.stringify(n))}async function Rl(e){return await new Promise((t,n)=>{let r=[],i=0;e.on(`data`,t=>{if(i+=t.length,i>65536){n(Error(`Callback payload exceeds 64KB limit`)),e.destroy();return}r.push(t)}),e.on(`end`,()=>t(Buffer.concat(r).toString(`utf8`))),e.on(`error`,n)})}function zl(e,t){if(typeof e!=`string`||e.length!==t.length)return!1;let n=Buffer.from(e,`utf8`),r=Buffer.from(t,`utf8`);return n.length===r.length?oe(n,r):!1}function Bl(e,t){let n=e.headers.origin;if(n!=null)return n===t;let r=e.headers.referer;return r==null?!1:r.startsWith(t)}function Vl(e){return(e.headers[`content-type`]??``).startsWith(`application/json`)}function Hl(e){for(let[t,n]of Object.entries(e)){let e=typeof n==`string`?n:JSON.stringify(n);if(Buffer.byteLength(e,`utf8`)>8192)return`Form field "${t}" exceeds 8KB limit`}return null}function Ul(e){return Array.isArray(e)&&e.length>Fl?`Selection exceeds ${Fl} items (got ${e.length})`:null}function Wl(e,t,n){let r=typeof e.actionId==`string`?e.actionId:``,i=n.find(e=>e.id===r);if(!i)return q(`INVALID_ACTION`,`Unknown actionId: ${r||`(missing)`}`);let a=typeof e.selection==`string`||Array.isArray(e.selection)?e.selection:void 0,o=Ll(e.formData)?e.formData:void 0,s={surfaceId:t,actionId:i.id,actionType:i.type,timestamp:typeof e.timestamp==`string`?e.timestamp:new Date().toISOString(),sourceTransport:`browser`};return e.value!==void 0&&(s.value=e.value),a!==void 0&&(s.selection=a),o!==void 0&&(s.formData=o),typeof e.label==`string`?s.label=e.label:i.label&&(s.label=i.label),{kind:`result`,result:s}}function Gl(e,t){let n=ie(16).toString(`hex`),r=!1,i=!1,a,o,s=new Promise(e=>{o=e}),c=e=>r?a??e:(r=!0,a=e,o?.(e),e);return{nonce:n,promise:s,settleTimeout(e){return c({kind:`timeout`,waitedMs:e})},settleCancelled(e){return c({kind:`cancelled`,reason:e})},settleError(e,t,n){return c(q(e,t,n))},async handle(r,a,o){if(!Vl(r)){let e=c(q(`INVALID_CONTENT_TYPE`,`Content-Type must be application/json`));return J(a,415,{ok:!1,error:`unsupported-media-type`},o),e}if(!Bl(r,o)){let e=c(q(`INVALID_ORIGIN`,`Unexpected callback origin: ${r.headers.origin??r.headers.referer??`(missing)`}`));return J(a,403,{ok:!1,error:`invalid-origin`},o),e}let s=``;try{s=await Rl(r)}catch(e){let t=c(q(`INVALID_PAYLOAD`,e instanceof Error?e.message:`Unable to read callback payload`));return J(a,413,{ok:!1,error:`payload-too-large`},o),t}let l;try{let e=JSON.parse(s);if(!Ll(e))throw Error(`Callback payload must be an object`);l=e}catch(e){let t=c(q(`INVALID_PAYLOAD`,e instanceof Error?e.message:`Malformed callback payload`));return J(a,400,{ok:!1,error:`invalid-json`},o),t}if(i){let e=q(`NONCE_CONSUMED`,`Nonce has already been used`);return J(a,403,{ok:!1,error:`nonce-consumed`},o),e}if(!zl(typeof l.nonce==`string`?l.nonce:``,n)){let e=c(q(`INVALID_NONCE`,`Callback nonce did not match`));return J(a,403,{ok:!1,error:`invalid-nonce`},o),e}i=!0;let u=Ul(l.selection);if(u){let e=c(q(`SELECTION_TOO_LARGE`,u));return J(a,413,{ok:!1,error:`selection-too-large`},o),e}if(Ll(l.formData)){let e=Hl(l.formData);if(e){let t=c(q(`PAYLOAD_FIELD_TOO_LARGE`,e));return J(a,413,{ok:!1,error:`field-too-large`},o),t}}if(l.kind===`cancelled`){let e=typeof l.reason==`string`?l.reason:``;if(!Il.has(e)){let t=c(q(`INVALID_CANCEL_REASON`,`Unsupported cancel reason: ${e||`(missing)`}`));return J(a,400,{ok:!1,error:`invalid-cancel-reason`},o),t}let t=c({kind:`cancelled`,reason:e});return J(a,200,{ok:!0,kind:`cancelled`},o),t}let d=c(Wl(l,e,t));return J(a,d.kind===`error`?400:200,{ok:d.kind!==`error`,kind:d.kind},o),d}}}const Kl=new Map;function ql(e){return e.replace(/[|\\{}()[\]^$+*?.]/g,`\\$&`)}function Jl(e){let t=`(?=[^>]*\\bid=['"]${ql(e)}['"])`;return RegExp(`(<script\\b(?=[^>]*\\btype=['"]application\\/json['"])${t}[^>]*>)[\\s\\S]*?(<\/script>)`,`i`)}function Yl(e){Kl.has(e.id)&&console.warn(`[viewer-registry] Overwriting viewer template "${e.id}"`),Kl.set(e.id,e)}function Xl(e){return Kl.get(e)}function Zl(e){return typeof e==`string`&&Kl.has(e)}function Ql(){return[...Kl.values()]}function $l(e,t,n,r){let i=Jl(n);if(!i.test(e))throw Error(`No <script type="application/json" id="${n}"> found in template`);let a=r?r(t):t,o=JSON.stringify(a??null).replace(/<\/script/gi,`<\\/script`).replace(/<script/gi,`\\u003Cscript`).replace(/-->/g,`--\\u003E`).replace(/<!--/g,`\\u003C!--`);return e.replace(i,`$1\n${o}\n$2`)}const Y=import.meta.dirname??h(x(import.meta.url)),eu={type:`object`,properties:{title:{type:`string`},description:{type:`string`},layout:{type:`object`,properties:{direction:{type:`string`},spacing:{type:`number`},layerSpacing:{type:`number`}}},nodes:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},type:{type:`string`,enum:[`person`,`system`,`container`,`component`,`database`,`queue`,`external`,`boundary`]},label:{type:`string`},technology:{type:`string`},icon:{type:`string`},description:{type:`string`}},required:[`id`,`type`,`label`]}},edges:{type:`array`,items:{type:`object`,properties:{source:{type:`string`},target:{type:`string`},label:{type:`string`},style:{type:`string`,enum:[`sync`,`async`,`dashed`,`dotted`,`solid`]}},required:[`source`,`target`]},default:[]}},required:[`nodes`]},tu={type:`object`,properties:{nodes:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},label:{type:`string`},type:{type:`string`,enum:[`start-end`,`manual`,`automated`,`integration`,`decision`,`prerequisite`]},description:{type:`string`}},required:[`id`,`label`]}},edges:{type:`array`,items:{type:`object`,properties:{source:{type:`string`},target:{type:`string`},label:{type:`string`},type:{type:`string`,enum:[`standard`,`loop-back`,`exception`]}},required:[`source`,`target`]}}},required:[`nodes`]},nu={type:`object`,properties:{title:{type:`string`},description:{type:`string`},phases:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},label:{type:`string`},outcome:{type:`string`},batches:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},order:{type:`number`},parallel:{type:`boolean`},label:{type:`string`},tasks:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},title:{type:`string`},agent:{type:`string`},files:{type:`array`,items:{type:`string`},default:[]},status:{type:`string`,enum:[`pending`,`in-progress`,`done`,`blocked`]},dependsOn:{type:`array`,items:{type:`string`},default:[]}},required:[`id`,`title`]},default:[]}},required:[`id`,`tasks`]},default:[]}},required:[`id`,`label`,`batches`]}}},required:[`phases`]};function ru(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function iu(e,t){return ru(t)?{...t,kind:e}:{kind:e}}function au(e){let t=[...e===`canvas.html`?[_(Y,`..`,`..`,`..`,`..`,`..`,`scaffold`,`general`,`viewers`,`src`,`canvas`,`index.html`),_(Y,`..`,`..`,`scaffold`,`general`,`viewers`,`src`,`canvas`,`index.html`)]:[],_(Y,`..`,`..`,`..`,`viewers`,e),_(Y,`..`,`..`,`..`,`..`,`..`,`scaffold`,`general`,`viewers`,`src`,`static`,e),_(Y,`..`,`..`,`..`,`..`,`..`,`scaffold`,`general`,`viewers`,`dist`,e),_(Y,`..`,`..`,`..`,`..`,`..`,`scaffold`,`general`,`viewers`,e),_(Y,`..`,`..`,`scaffold`,`general`,`viewers`,`dist`,e),_(Y,`..`,`..`,`scaffold`,`general`,`viewers`,e),_(Y,`..`,`viewers`,`dist`,e),_(Y,`..`,`viewers`,e)];for(let e of t)try{return c(e,`utf8`)}catch{}throw Error(`Viewer HTML not found: ${e}. Searched: ${t.join(`, `)}`)}let ou,su,cu,lu,uu,du;function fu(){return ou??=au(`c4-viewer.html`),ou}function pu(){return su??=au(`tour-viewer.html`),su}function mu(){return cu??=au(`canvas.html`),cu}function hu(){return lu??=au(`architecture-static.html`),lu}function gu(){return uu??=au(`process-flow-static.html`),uu}function _u(){return du??=au(`task-plan-static.html`),du}function vu(){Yl({id:`c4@1`,label:`C4 Architecture Diagram`,description:`Interactive C4 architecture diagram with zoom, pan, and ELK auto-layout`,inputSchema:eu,injectId:`diagram-data`,supportedTransports:[`browser`],resolveHtml:fu}),Yl({id:`c4-static@1`,label:`C4 Architecture Diagram (Static)`,description:`Static SVG architecture diagram for inline rendering`,inputSchema:eu,injectId:`diagram-data`,supportedTransports:[`mcp-app`],resolveHtml:hu}),Yl({id:`tour@1`,label:`Code Tour Viewer`,description:`Interactive code tour with step navigation and dependency graph`,inputSchema:{type:`object`,properties:{title:{type:`string`},description:{type:`string`},estimatedTime:{type:`string`},steps:{type:`array`,items:{type:`object`,properties:{stepNumber:{type:`number`},id:{type:`string`},title:{type:`string`},file:{type:`string`},explanation:{type:`string`},description:{type:`string`},learnsConcept:{type:`string`},duration:{type:`string`},line:{type:`number`},code:{type:`string`}},required:[`id`,`title`]}},dependencies:{type:`array`,items:{type:`object`,properties:{source:{type:`string`},target:{type:`string`}},required:[`source`,`target`]},default:[]}},required:[`steps`]},injectId:`tour-data`,supportedTransports:[`browser`],resolveHtml:pu}),Yl({id:`process-flow-static@1`,label:`Process Flow Diagram (Static)`,description:`Static SVG process flow diagram for inline rendering`,inputSchema:tu,injectId:`diagram-data`,supportedTransports:[`mcp-app`],resolveHtml:gu}),Yl({id:`task-plan-static@1`,label:`Task Execution Plan (Static)`,description:`Static SVG task execution plan for inline rendering`,inputSchema:nu,injectId:`diagram-data`,supportedTransports:[`mcp-app`],resolveHtml:_u}),Yl({id:`task-plan@1`,label:`Task Execution Plan (Interactive)`,description:`Interactive task execution plan with ReactFlow, phase grouping, and dependency edges`,inputSchema:nu,injectId:`diagram-data`,supportedTransports:[`browser`],resolveHtml:mu,transformData:e=>iu(`task-plan`,e)}),Yl({id:`process-flow@1`,label:`Process Flow Diagram (Interactive)`,description:`Interactive process flow diagram with ReactFlow`,inputSchema:tu,injectId:`diagram-data`,supportedTransports:[`browser`],resolveHtml:mu,transformData:e=>iu(`process-flow`,e)})}function yu(){let e=new zn;for(let t of Hn.list())e.register(t);return e.register(Vn),e.register(Kn),e.register(qn),e.register(Un),e}function bu(e,t){return e?.supportedTransports.includes(t)??!1}const xu=yu();function Su(e){if(e.template)return xu.get(e.template)}function Cu(e){if((e.actions?.length??0)>0)return!0;if(e.template&&Zl(e.template)){let t=Xl(e.template);return t?.supportedTransports.includes(`browser`)===!0&&!t.supportedTransports.includes(`mcp-app`)}let t=Su(e);return bu(t,`browser`)&&!bu(t,`mcp-app`)}vu();function wu(){let e;return()=>{if(e!==void 0)return e;try{e=c(a(import.meta.url).resolve(`mermaid/dist/mermaid.min.js`),`utf-8`)}catch{e=``}return e}}const Tu={id:`mermaid`,cdn:`https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js`,initScript:`window.__aikit_initMermaid = function() {
|
|
241
|
+
}`},{type:`text`,category:`content`,description:`Plain text content rendered through the markdown parser.`,valueShape:`string`},{type:`heading`,category:`content`,description:`Single heading with configurable level from h1 to h6.`,valueShape:`string`},{type:`paragraph`,category:`content`,description:`Single paragraph rendered inside a p tag.`,valueShape:`string`},{type:`separator`,category:`layout`,description:`Horizontal rule used to separate adjacent blocks.`,valueShape:`undefined`},{type:`actions`,category:`layout`,description:`Action bar containing button and select action definitions.`,valueShape:`Array<{ type: string; id: string; label: string; variant?: string; options?: Array<string | { label: string; value: string }> }>`}].map(e=>[e.type,{...e}]));function Pl(){let e={};for(let[t,n]of Nl)n.vendorScripts?.length&&(e[t]=n.vendorScripts);return e}const Fl=1024,Il=new Set([`user-closed`,`host-dismissed`,`replaced`]);function Ll(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function q(e,t,n){let r={code:e,message:t};return n!==void 0&&(r.details=n),{kind:`error`,error:r}}function J(e,t,n,r){e.writeHead(t,{"Content-Type":`application/json`,"Access-Control-Allow-Origin":r??`null`}),e.end(JSON.stringify(n))}async function Rl(e){return await new Promise((t,n)=>{let r=[],i=0;e.on(`data`,t=>{if(i+=t.length,i>65536){n(Error(`Callback payload exceeds 64KB limit`)),e.destroy();return}r.push(t)}),e.on(`end`,()=>t(Buffer.concat(r).toString(`utf8`))),e.on(`error`,n)})}function zl(e,t){if(typeof e!=`string`||e.length!==t.length)return!1;let n=Buffer.from(e,`utf8`),r=Buffer.from(t,`utf8`);return n.length===r.length?oe(n,r):!1}function Bl(e,t){let n=e.headers.origin;if(n!=null)return n===t;let r=e.headers.referer;return r==null?!1:r.startsWith(t)}function Vl(e){return(e.headers[`content-type`]??``).startsWith(`application/json`)}function Hl(e){for(let[t,n]of Object.entries(e)){let e=typeof n==`string`?n:JSON.stringify(n);if(Buffer.byteLength(e,`utf8`)>8192)return`Form field "${t}" exceeds 8KB limit`}return null}function Ul(e){return Array.isArray(e)&&e.length>Fl?`Selection exceeds ${Fl} items (got ${e.length})`:null}function Wl(e,t,n){let r=typeof e.actionId==`string`?e.actionId:``,i=n.find(e=>e.id===r);if(!i)return q(`INVALID_ACTION`,`Unknown actionId: ${r||`(missing)`}`);let a=typeof e.selection==`string`||Array.isArray(e.selection)?e.selection:void 0,o=Ll(e.formData)?e.formData:void 0,s={surfaceId:t,actionId:i.id,actionType:i.type,timestamp:typeof e.timestamp==`string`?e.timestamp:new Date().toISOString(),sourceTransport:`browser`};return e.value!==void 0&&(s.value=e.value),a!==void 0&&(s.selection=a),o!==void 0&&(s.formData=o),typeof e.label==`string`?s.label=e.label:i.label&&(s.label=i.label),{kind:`result`,result:s}}function Gl(e,t){let n=ie(16).toString(`hex`),r=!1,i=!1,a,o,s=new Promise(e=>{o=e}),c=e=>r?a??e:(r=!0,a=e,o?.(e),e);return{nonce:n,promise:s,settleTimeout(e){return c({kind:`timeout`,waitedMs:e})},settleCancelled(e){return c({kind:`cancelled`,reason:e})},settleError(e,t,n){return c(q(e,t,n))},async handle(r,a,o){if(!Vl(r)){let e=c(q(`INVALID_CONTENT_TYPE`,`Content-Type must be application/json`));return J(a,415,{ok:!1,error:`unsupported-media-type`},o),e}if(!Bl(r,o)){let e=c(q(`INVALID_ORIGIN`,`Unexpected callback origin: ${r.headers.origin??r.headers.referer??`(missing)`}`));return J(a,403,{ok:!1,error:`invalid-origin`},o),e}let s=``;try{s=await Rl(r)}catch(e){let t=c(q(`INVALID_PAYLOAD`,e instanceof Error?e.message:`Unable to read callback payload`));return J(a,413,{ok:!1,error:`payload-too-large`},o),t}let l;try{let e=JSON.parse(s);if(!Ll(e))throw Error(`Callback payload must be an object`);l=e}catch(e){let t=c(q(`INVALID_PAYLOAD`,e instanceof Error?e.message:`Malformed callback payload`));return J(a,400,{ok:!1,error:`invalid-json`},o),t}if(i){let e=q(`NONCE_CONSUMED`,`Nonce has already been used`);return J(a,403,{ok:!1,error:`nonce-consumed`},o),e}if(!zl(typeof l.nonce==`string`?l.nonce:``,n)){let e=c(q(`INVALID_NONCE`,`Callback nonce did not match`));return J(a,403,{ok:!1,error:`invalid-nonce`},o),e}i=!0;let u=Ul(l.selection);if(u){let e=c(q(`SELECTION_TOO_LARGE`,u));return J(a,413,{ok:!1,error:`selection-too-large`},o),e}if(Ll(l.formData)){let e=Hl(l.formData);if(e){let t=c(q(`PAYLOAD_FIELD_TOO_LARGE`,e));return J(a,413,{ok:!1,error:`field-too-large`},o),t}}if(l.kind===`cancelled`){let e=typeof l.reason==`string`?l.reason:``;if(!Il.has(e)){let t=c(q(`INVALID_CANCEL_REASON`,`Unsupported cancel reason: ${e||`(missing)`}`));return J(a,400,{ok:!1,error:`invalid-cancel-reason`},o),t}let t=c({kind:`cancelled`,reason:e});return J(a,200,{ok:!0,kind:`cancelled`},o),t}let d=c(Wl(l,e,t));return J(a,d.kind===`error`?400:200,{ok:d.kind!==`error`,kind:d.kind},o),d}}}const Kl=new Map;function ql(e){return e.replace(/[|\\{}()[\]^$+*?.]/g,`\\$&`)}function Jl(e){let t=`(?=[^>]*\\bid=['"]${ql(e)}['"])`;return RegExp(`(<script\\b(?=[^>]*\\btype=['"]application\\/json['"])${t}[^>]*>)[\\s\\S]*?(<\/script>)`,`i`)}function Yl(e){Kl.has(e.id)&&console.warn(`[viewer-registry] Overwriting viewer template "${e.id}"`),Kl.set(e.id,e)}function Xl(e){return Kl.get(e)}function Zl(e){return typeof e==`string`&&Kl.has(e)}function Ql(){return[...Kl.values()]}function $l(e,t,n,r){let i=Jl(n);if(!i.test(e))throw Error(`No <script type="application/json" id="${n}"> found in template`);let a=r?r(t):t,o=JSON.stringify(a??null).replace(/<\/script/gi,`<\\/script`).replace(/<script/gi,`\\u003Cscript`).replace(/-->/g,`--\\u003E`).replace(/<!--/g,`\\u003C!--`);return e.replace(i,`$1\n${o}\n$2`)}const Y=import.meta.dirname??h(x(import.meta.url)),eu={type:`object`,properties:{title:{type:`string`},description:{type:`string`},layout:{type:`object`,properties:{direction:{type:`string`},spacing:{type:`number`},layerSpacing:{type:`number`}}},nodes:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},type:{type:`string`,enum:[`person`,`system`,`container`,`component`,`database`,`queue`,`external`,`boundary`]},label:{type:`string`},technology:{type:`string`},icon:{type:`string`},description:{type:`string`}},required:[`id`,`type`,`label`]}},edges:{type:`array`,items:{type:`object`,properties:{source:{type:`string`},target:{type:`string`},label:{type:`string`},style:{type:`string`,enum:[`sync`,`async`,`dashed`,`dotted`,`solid`]}},required:[`source`,`target`]},default:[]}},required:[`nodes`]},tu={type:`object`,properties:{nodes:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},label:{type:`string`},type:{type:`string`,enum:[`start-end`,`manual`,`automated`,`integration`,`decision`,`prerequisite`]},description:{type:`string`}},required:[`id`,`label`]}},edges:{type:`array`,items:{type:`object`,properties:{source:{type:`string`},target:{type:`string`},label:{type:`string`},type:{type:`string`,enum:[`standard`,`loop-back`,`exception`]}},required:[`source`,`target`]}}},required:[`nodes`]},nu={type:`object`,properties:{title:{type:`string`},description:{type:`string`},phases:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},label:{type:`string`},outcome:{type:`string`},batches:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},order:{type:`number`},parallel:{type:`boolean`},label:{type:`string`},tasks:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},title:{type:`string`},agent:{type:`string`},files:{type:`array`,items:{type:`string`},default:[]},status:{type:`string`,enum:[`pending`,`in-progress`,`done`,`blocked`]},dependsOn:{type:`array`,items:{type:`string`},default:[]}},required:[`id`,`title`]},default:[]}},required:[`id`,`tasks`]},default:[]}},required:[`id`,`label`,`batches`]}}},required:[`phases`]};function ru(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function iu(e,t){return ru(t)?{...t,kind:e}:{kind:e}}function au(e){let t=[...e===`canvas.html`?[_(Y,`..`,`..`,`..`,`..`,`..`,`scaffold`,`general`,`viewers`,`src`,`canvas`,`index.html`),_(Y,`..`,`..`,`scaffold`,`general`,`viewers`,`src`,`canvas`,`index.html`)]:[],_(Y,`..`,`..`,`..`,`viewers`,e),_(Y,`..`,`..`,`..`,`..`,`..`,`scaffold`,`general`,`viewers`,`src`,`static`,e),_(Y,`..`,`..`,`..`,`..`,`..`,`scaffold`,`general`,`viewers`,`dist`,e),_(Y,`..`,`..`,`..`,`..`,`..`,`scaffold`,`general`,`viewers`,e),_(Y,`..`,`..`,`scaffold`,`general`,`viewers`,`dist`,e),_(Y,`..`,`..`,`scaffold`,`general`,`viewers`,e),_(Y,`..`,`viewers`,`dist`,e),_(Y,`..`,`viewers`,e)];for(let e of t)try{return c(e,`utf8`)}catch{}throw Error(`Viewer HTML not found: ${e}. Searched: ${t.join(`, `)}`)}let ou,su,cu,lu,uu,du;function fu(){return ou??=au(`c4-viewer.html`),ou}function pu(){return su??=au(`tour-viewer.html`),su}function mu(){return cu??=au(`canvas.html`),cu}function hu(){return lu??=au(`architecture-static.html`),lu}function gu(){return uu??=au(`process-flow-static.html`),uu}function _u(){return du??=au(`task-plan-static.html`),du}function vu(){Yl({id:`c4@1`,label:`C4 Architecture Diagram`,description:`Interactive C4 architecture diagram with zoom, pan, and ELK auto-layout`,inputSchema:eu,injectId:`diagram-data`,supportedTransports:[`browser`],resolveHtml:fu}),Yl({id:`c4-static@1`,label:`C4 Architecture Diagram (Static)`,description:`Static SVG architecture diagram for inline rendering`,inputSchema:eu,injectId:`diagram-data`,supportedTransports:[`mcp-app`],resolveHtml:hu}),Yl({id:`tour@1`,label:`Code Tour Viewer`,description:`Interactive code tour with step navigation and dependency graph`,inputSchema:{type:`object`,properties:{title:{type:`string`},description:{type:`string`},estimatedTime:{type:`string`},steps:{type:`array`,items:{type:`object`,properties:{stepNumber:{type:`number`},id:{type:`string`},title:{type:`string`},file:{type:`string`},explanation:{type:`string`},description:{type:`string`},learnsConcept:{type:`string`},duration:{type:`string`},line:{type:`number`},code:{type:`string`}},required:[`id`,`title`]}},dependencies:{type:`array`,items:{type:`object`,properties:{source:{type:`string`},target:{type:`string`}},required:[`source`,`target`]},default:[]}},required:[`steps`]},injectId:`tour-data`,supportedTransports:[`browser`],resolveHtml:pu}),Yl({id:`process-flow-static@1`,label:`Process Flow Diagram (Static)`,description:`Static SVG process flow diagram for inline rendering`,inputSchema:tu,injectId:`diagram-data`,supportedTransports:[`mcp-app`],resolveHtml:gu}),Yl({id:`task-plan-static@1`,label:`Task Execution Plan (Static)`,description:`Static SVG task execution plan for inline rendering`,inputSchema:nu,injectId:`diagram-data`,supportedTransports:[`mcp-app`],resolveHtml:_u}),Yl({id:`task-plan@1`,label:`Task Execution Plan (Interactive)`,description:`Interactive task execution plan with ReactFlow, phase grouping, and dependency edges`,inputSchema:nu,injectId:`diagram-data`,supportedTransports:[`browser`],resolveHtml:mu,transformData:e=>iu(`task-plan`,e)}),Yl({id:`process-flow@1`,label:`Process Flow Diagram (Interactive)`,description:`Interactive process flow diagram with ReactFlow`,inputSchema:tu,injectId:`diagram-data`,supportedTransports:[`browser`],resolveHtml:mu,transformData:e=>iu(`process-flow`,e)})}function yu(){let e=new zn;for(let t of Hn.list())e.register(t);return e.register(Vn),e.register(Kn),e.register(qn),e.register(Un),e}function bu(e,t){return e?.supportedTransports.includes(t)??!1}const xu=yu();function Su(e){if(e.template)return xu.get(e.template)}function Cu(e){if((e.actions?.length??0)>0)return!0;if(e.template&&Zl(e.template)){let t=Xl(e.template);return t?.supportedTransports.includes(`browser`)===!0&&!t.supportedTransports.includes(`mcp-app`)}let t=Su(e);return bu(t,`browser`)&&!bu(t,`mcp-app`)}vu();const wu=String.raw`(function(){
|
|
242
|
+
'use strict';
|
|
243
|
+
|
|
244
|
+
var SVG_NS = 'http://www.w3.org/2000/svg';
|
|
245
|
+
var DEFAULT_DIRECTION = 'TD';
|
|
246
|
+
var HORIZONTAL_GAP = 60;
|
|
247
|
+
var VERTICAL_GAP = 80;
|
|
248
|
+
var OUTER_PADDING = 32;
|
|
249
|
+
var GROUP_PADDING = 18;
|
|
250
|
+
var FONT_SIZE = 14;
|
|
251
|
+
var LINE_HEIGHT = 18;
|
|
252
|
+
var CHAR_WIDTH = 8;
|
|
253
|
+
var MAX_RENDER_PASSES = 1;
|
|
254
|
+
|
|
255
|
+
function createSvg(tag) {
|
|
256
|
+
return document.createElementNS(SVG_NS, tag);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function setAttrs(node, attrs) {
|
|
260
|
+
var keys = Object.keys(attrs);
|
|
261
|
+
for (var i = 0; i < keys.length; i += 1) {
|
|
262
|
+
var key = keys[i];
|
|
263
|
+
var value = attrs[key];
|
|
264
|
+
if (value !== undefined && value !== null) {
|
|
265
|
+
node.setAttribute(key, String(value));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return node;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function pushUnique(list, value) {
|
|
272
|
+
if (list.indexOf(value) === -1) {
|
|
273
|
+
list.push(value);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function stripQuotes(value) {
|
|
278
|
+
var text = String(value || '').trim();
|
|
279
|
+
if (text.length >= 2) {
|
|
280
|
+
var first = text.charAt(0);
|
|
281
|
+
var last = text.charAt(text.length - 1);
|
|
282
|
+
if ((first === '"' && last === '"') || (first === '\'' && last === '\'')) {
|
|
283
|
+
return text.slice(1, -1);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return text;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function normalizeLabel(value) {
|
|
290
|
+
var text = stripQuotes(value);
|
|
291
|
+
return text.replace(/<br\s*\/?>/gi, '\n').replace(/\\n/g, '\n').trim();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function isTopLevel(depths, quote) {
|
|
295
|
+
return !quote && depths.square === 0 && depths.round === 0 && depths.curly === 0;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function splitTopLevel(text, delimiter) {
|
|
299
|
+
var items = [];
|
|
300
|
+
var current = '';
|
|
301
|
+
var quote = '';
|
|
302
|
+
var depths = { square: 0, round: 0, curly: 0 };
|
|
303
|
+
|
|
304
|
+
for (var i = 0; i < text.length; i += 1) {
|
|
305
|
+
var ch = text.charAt(i);
|
|
306
|
+
var prev = i > 0 ? text.charAt(i - 1) : '';
|
|
307
|
+
|
|
308
|
+
if (quote) {
|
|
309
|
+
current += ch;
|
|
310
|
+
if (ch === quote && prev !== '\\') {
|
|
311
|
+
quote = '';
|
|
312
|
+
}
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (ch === '"' || ch === '\'') {
|
|
317
|
+
quote = ch;
|
|
318
|
+
current += ch;
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (ch === '[') depths.square += 1;
|
|
323
|
+
else if (ch === ']') depths.square = Math.max(0, depths.square - 1);
|
|
324
|
+
else if (ch === '(') depths.round += 1;
|
|
325
|
+
else if (ch === ')') depths.round = Math.max(0, depths.round - 1);
|
|
326
|
+
else if (ch === '{') depths.curly += 1;
|
|
327
|
+
else if (ch === '}') depths.curly = Math.max(0, depths.curly - 1);
|
|
328
|
+
|
|
329
|
+
if (ch === delimiter && isTopLevel(depths, quote)) {
|
|
330
|
+
if (current.trim()) {
|
|
331
|
+
items.push(current.trim());
|
|
332
|
+
}
|
|
333
|
+
current = '';
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
current += ch;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (current.trim()) {
|
|
341
|
+
items.push(current.trim());
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return items;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function stripComment(line) {
|
|
348
|
+
var quote = '';
|
|
349
|
+
var depths = { square: 0, round: 0, curly: 0 };
|
|
350
|
+
|
|
351
|
+
for (var i = 0; i < line.length; i += 1) {
|
|
352
|
+
var ch = line.charAt(i);
|
|
353
|
+
var next = i + 1 < line.length ? line.charAt(i + 1) : '';
|
|
354
|
+
var prev = i > 0 ? line.charAt(i - 1) : '';
|
|
355
|
+
|
|
356
|
+
if (quote) {
|
|
357
|
+
if (ch === quote && prev !== '\\') {
|
|
358
|
+
quote = '';
|
|
359
|
+
}
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (ch === '"' || ch === '\'') {
|
|
364
|
+
quote = ch;
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (ch === '[') depths.square += 1;
|
|
369
|
+
else if (ch === ']') depths.square = Math.max(0, depths.square - 1);
|
|
370
|
+
else if (ch === '(') depths.round += 1;
|
|
371
|
+
else if (ch === ')') depths.round = Math.max(0, depths.round - 1);
|
|
372
|
+
else if (ch === '{') depths.curly += 1;
|
|
373
|
+
else if (ch === '}') depths.curly = Math.max(0, depths.curly - 1);
|
|
374
|
+
|
|
375
|
+
if (ch === '%' && next === '%' && isTopLevel(depths, quote)) {
|
|
376
|
+
return line.slice(0, i);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return line;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function extractStatements(source) {
|
|
384
|
+
var lines = String(source || '').split(/\r?\n/);
|
|
385
|
+
var statements = [];
|
|
386
|
+
|
|
387
|
+
for (var i = 0; i < lines.length; i += 1) {
|
|
388
|
+
var line = stripComment(lines[i]).trim();
|
|
389
|
+
if (!line) {
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
var parts = splitTopLevel(line, ';');
|
|
394
|
+
for (var j = 0; j < parts.length; j += 1) {
|
|
395
|
+
if (parts[j]) {
|
|
396
|
+
statements.push(parts[j]);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return statements;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function readBalanced(text, index, open, close) {
|
|
405
|
+
var depth = 0;
|
|
406
|
+
var quote = '';
|
|
407
|
+
|
|
408
|
+
for (var i = index; i < text.length; i += 1) {
|
|
409
|
+
var ch = text.charAt(i);
|
|
410
|
+
var prev = i > 0 ? text.charAt(i - 1) : '';
|
|
411
|
+
|
|
412
|
+
if (quote) {
|
|
413
|
+
if (ch === quote && prev !== '\\') {
|
|
414
|
+
quote = '';
|
|
415
|
+
}
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (ch === '"' || ch === '\'') {
|
|
420
|
+
quote = ch;
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (text.slice(i, i + open.length) === open) {
|
|
425
|
+
depth += 1;
|
|
426
|
+
i += open.length - 1;
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (text.slice(i, i + close.length) === close) {
|
|
431
|
+
depth -= 1;
|
|
432
|
+
i += close.length - 1;
|
|
433
|
+
if (depth === 0) {
|
|
434
|
+
return {
|
|
435
|
+
value: text.slice(index + open.length, i - close.length + 1),
|
|
436
|
+
index: i + 1,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
throw new Error('Unterminated node shape');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function skipWhitespace(text, index) {
|
|
446
|
+
var cursor = index;
|
|
447
|
+
while (cursor < text.length && /\s/.test(text.charAt(cursor))) {
|
|
448
|
+
cursor += 1;
|
|
449
|
+
}
|
|
450
|
+
return cursor;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function readIdentifier(text, index) {
|
|
454
|
+
var cursor = skipWhitespace(text, index);
|
|
455
|
+
var start = cursor;
|
|
456
|
+
|
|
457
|
+
while (cursor < text.length) {
|
|
458
|
+
var ch = text.charAt(cursor);
|
|
459
|
+
if (/\s/.test(ch) || ch === '[' || ch === '(' || ch === '{' || ch === '-' || ch === '=' || ch === '.' || ch === '|' || ch === ';') {
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
cursor += 1;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (cursor === start) {
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return {
|
|
470
|
+
value: text.slice(start, cursor),
|
|
471
|
+
index: cursor,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function readNodeRef(text, index) {
|
|
476
|
+
var idToken = readIdentifier(text, index);
|
|
477
|
+
if (!idToken) {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
var id = idToken.value.trim();
|
|
482
|
+
var cursor = skipWhitespace(text, idToken.index);
|
|
483
|
+
var shape = 'rect';
|
|
484
|
+
var label = id;
|
|
485
|
+
|
|
486
|
+
if (text.slice(cursor, cursor + 2) === '((') {
|
|
487
|
+
var circle = readBalanced(text, cursor, '((', '))');
|
|
488
|
+
shape = 'circle';
|
|
489
|
+
label = normalizeLabel(circle.value);
|
|
490
|
+
cursor = circle.index;
|
|
491
|
+
} else if (text.slice(cursor, cursor + 2) === '([') {
|
|
492
|
+
var stadium = readBalanced(text, cursor, '([', '])');
|
|
493
|
+
shape = 'stadium';
|
|
494
|
+
label = normalizeLabel(stadium.value);
|
|
495
|
+
cursor = stadium.index;
|
|
496
|
+
} else if (text.slice(cursor, cursor + 2) === '{{') {
|
|
497
|
+
var hexagon = readBalanced(text, cursor, '{{', '}}');
|
|
498
|
+
shape = 'hexagon';
|
|
499
|
+
label = normalizeLabel(hexagon.value);
|
|
500
|
+
cursor = hexagon.index;
|
|
501
|
+
} else if (text.charAt(cursor) === '[') {
|
|
502
|
+
var rect = readBalanced(text, cursor, '[', ']');
|
|
503
|
+
shape = 'rect';
|
|
504
|
+
label = normalizeLabel(rect.value);
|
|
505
|
+
cursor = rect.index;
|
|
506
|
+
} else if (text.charAt(cursor) === '(') {
|
|
507
|
+
var rounded = readBalanced(text, cursor, '(', ')');
|
|
508
|
+
shape = 'rounded';
|
|
509
|
+
label = normalizeLabel(rounded.value);
|
|
510
|
+
cursor = rounded.index;
|
|
511
|
+
} else if (text.charAt(cursor) === '{') {
|
|
512
|
+
var diamond = readBalanced(text, cursor, '{', '}');
|
|
513
|
+
shape = 'diamond';
|
|
514
|
+
label = normalizeLabel(diamond.value);
|
|
515
|
+
cursor = diamond.index;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return {
|
|
519
|
+
node: {
|
|
520
|
+
id: id,
|
|
521
|
+
label: label || id,
|
|
522
|
+
shape: shape,
|
|
523
|
+
},
|
|
524
|
+
index: cursor,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function edgePattern(match, style, arrow) {
|
|
529
|
+
return {
|
|
530
|
+
match: match,
|
|
531
|
+
style: style,
|
|
532
|
+
arrow: arrow,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
var EDGE_PATTERNS = [
|
|
537
|
+
edgePattern(/^\s*-\.->\|([^|]+)\|\s*/, 'dotted', true),
|
|
538
|
+
edgePattern(/^\s*==>\|([^|]+)\|\s*/, 'thick', true),
|
|
539
|
+
edgePattern(/^\s*-->\|([^|]+)\|\s*/, 'solid', true),
|
|
540
|
+
edgePattern(/^\s*---\|([^|]+)\|\s*/, 'solid', false),
|
|
541
|
+
edgePattern(/^\s*--\s+(.+?)\s+-->\s*/, 'solid', true),
|
|
542
|
+
edgePattern(/^\s*-\.->\s*/, 'dotted', true),
|
|
543
|
+
edgePattern(/^\s*==>\s*/, 'thick', true),
|
|
544
|
+
edgePattern(/^\s*-->\s*/, 'solid', true),
|
|
545
|
+
edgePattern(/^\s*---\s*/, 'solid', false),
|
|
546
|
+
];
|
|
547
|
+
|
|
548
|
+
function readEdge(text, index) {
|
|
549
|
+
var remainder = text.slice(index);
|
|
550
|
+
|
|
551
|
+
for (var i = 0; i < EDGE_PATTERNS.length; i += 1) {
|
|
552
|
+
var pattern = EDGE_PATTERNS[i];
|
|
553
|
+
var match = remainder.match(pattern.match);
|
|
554
|
+
if (!match) {
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
return {
|
|
559
|
+
index: index + match[0].length,
|
|
560
|
+
style: pattern.style,
|
|
561
|
+
arrow: pattern.arrow,
|
|
562
|
+
label: match[1] ? normalizeLabel(match[1]) : '',
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function newGraph(direction) {
|
|
570
|
+
return {
|
|
571
|
+
direction: direction,
|
|
572
|
+
nodesById: Object.create(null),
|
|
573
|
+
nodeOrder: [],
|
|
574
|
+
edges: [],
|
|
575
|
+
subgraphs: [],
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function addNode(graph, ref, activeGroups) {
|
|
580
|
+
var node = graph.nodesById[ref.id];
|
|
581
|
+
|
|
582
|
+
if (!node) {
|
|
583
|
+
node = {
|
|
584
|
+
id: ref.id,
|
|
585
|
+
label: ref.label || ref.id,
|
|
586
|
+
shape: ref.shape || 'rect',
|
|
587
|
+
groups: [],
|
|
588
|
+
rank: 0,
|
|
589
|
+
width: 0,
|
|
590
|
+
height: 0,
|
|
591
|
+
lines: [],
|
|
592
|
+
x: 0,
|
|
593
|
+
y: 0,
|
|
594
|
+
order: graph.nodeOrder.length,
|
|
595
|
+
};
|
|
596
|
+
graph.nodesById[ref.id] = node;
|
|
597
|
+
graph.nodeOrder.push(ref.id);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (ref.label && ref.label !== ref.id) {
|
|
601
|
+
node.label = ref.label;
|
|
602
|
+
}
|
|
603
|
+
if (ref.shape && ref.shape !== 'rect') {
|
|
604
|
+
node.shape = ref.shape;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
for (var i = 0; i < activeGroups.length; i += 1) {
|
|
608
|
+
var groupId = activeGroups[i];
|
|
609
|
+
pushUnique(node.groups, groupId);
|
|
610
|
+
for (var j = 0; j < graph.subgraphs.length; j += 1) {
|
|
611
|
+
if (graph.subgraphs[j].id === groupId) {
|
|
612
|
+
pushUnique(graph.subgraphs[j].members, node.id);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return node;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function addEdge(graph, fromNode, edgeDef, toNode) {
|
|
621
|
+
graph.edges.push({
|
|
622
|
+
from: fromNode.id,
|
|
623
|
+
to: toNode.id,
|
|
624
|
+
label: edgeDef.label || '',
|
|
625
|
+
style: edgeDef.style,
|
|
626
|
+
arrow: edgeDef.arrow,
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function parseDirection(statement) {
|
|
631
|
+
var match = statement.match(/^(graph|flowchart)\s+(TD|TB|BT|LR|RL)\b/i);
|
|
632
|
+
if (!match) {
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
var direction = match[2].toUpperCase();
|
|
636
|
+
return direction === 'TB' ? 'TD' : direction;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function parseMermaid(source) {
|
|
640
|
+
var statements = extractStatements(source);
|
|
641
|
+
var direction = DEFAULT_DIRECTION;
|
|
642
|
+
|
|
643
|
+
if (statements.length > 0) {
|
|
644
|
+
var explicitDirection = parseDirection(statements[0]);
|
|
645
|
+
if (explicitDirection) {
|
|
646
|
+
direction = explicitDirection;
|
|
647
|
+
statements.shift();
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
var graph = newGraph(direction);
|
|
652
|
+
var activeGroups = [];
|
|
653
|
+
var groupCount = 0;
|
|
654
|
+
|
|
655
|
+
for (var i = 0; i < statements.length; i += 1) {
|
|
656
|
+
var statement = statements[i];
|
|
657
|
+
if (!statement) {
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (/^subgraph\b/i.test(statement)) {
|
|
662
|
+
groupCount += 1;
|
|
663
|
+
var title = normalizeLabel(statement.replace(/^subgraph\s+/i, '')) || ('Subgraph ' + groupCount);
|
|
664
|
+
var group = {
|
|
665
|
+
id: 'subgraph_' + groupCount,
|
|
666
|
+
title: title,
|
|
667
|
+
members: [],
|
|
668
|
+
};
|
|
669
|
+
graph.subgraphs.push(group);
|
|
670
|
+
activeGroups.push(group.id);
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (/^end$/i.test(statement)) {
|
|
675
|
+
activeGroups.pop();
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
var first = readNodeRef(statement, 0);
|
|
680
|
+
if (!first) {
|
|
681
|
+
throw new Error('Unable to parse node: ' + statement);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
var fromNode = addNode(graph, first.node, activeGroups.slice());
|
|
685
|
+
var cursor = first.index;
|
|
686
|
+
var consumedEdge = false;
|
|
687
|
+
|
|
688
|
+
while (true) {
|
|
689
|
+
var edge = readEdge(statement, cursor);
|
|
690
|
+
if (!edge) {
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
cursor = edge.index;
|
|
695
|
+
var next = readNodeRef(statement, cursor);
|
|
696
|
+
if (!next) {
|
|
697
|
+
throw new Error('Edge is missing a target node: ' + statement);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
var toNode = addNode(graph, next.node, activeGroups.slice());
|
|
701
|
+
addEdge(graph, fromNode, edge, toNode);
|
|
702
|
+
fromNode = toNode;
|
|
703
|
+
cursor = next.index;
|
|
704
|
+
consumedEdge = true;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (!consumedEdge) {
|
|
708
|
+
var tail = statement.slice(cursor).trim();
|
|
709
|
+
if (tail) {
|
|
710
|
+
throw new Error('Unexpected trailing content: ' + tail);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
return graph;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
function createMeasurer() {
|
|
719
|
+
var svg = null;
|
|
720
|
+
var text = null;
|
|
721
|
+
|
|
722
|
+
return function measureLine(line) {
|
|
723
|
+
var sample = String(line || '');
|
|
724
|
+
|
|
725
|
+
try {
|
|
726
|
+
if (!svg) {
|
|
727
|
+
svg = createSvg('svg');
|
|
728
|
+
setAttrs(svg, {
|
|
729
|
+
width: 0,
|
|
730
|
+
height: 0,
|
|
731
|
+
style: 'position:absolute;left:-9999px;top:-9999px;overflow:hidden;pointer-events:none',
|
|
732
|
+
'aria-hidden': 'true',
|
|
733
|
+
});
|
|
734
|
+
text = createSvg('text');
|
|
735
|
+
setAttrs(text, {
|
|
736
|
+
'font-size': FONT_SIZE,
|
|
737
|
+
'font-family': 'var(--dt-font-sans, ui-sans-serif, system-ui, sans-serif)',
|
|
738
|
+
});
|
|
739
|
+
svg.appendChild(text);
|
|
740
|
+
if (document.body) {
|
|
741
|
+
document.body.appendChild(svg);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
if (text && typeof text.getBBox === 'function') {
|
|
746
|
+
text.textContent = sample || ' ';
|
|
747
|
+
var box = text.getBBox();
|
|
748
|
+
if (box && box.width) {
|
|
749
|
+
return box.width;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
} catch (_error) {
|
|
753
|
+
return sample.length * CHAR_WIDTH;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
return Math.max(16, sample.length * CHAR_WIDTH);
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
var measureLine = createMeasurer();
|
|
761
|
+
|
|
762
|
+
function splitLabelLines(label) {
|
|
763
|
+
var lines = String(label || '').split(/\n+/).map(function(line) {
|
|
764
|
+
return line.trim();
|
|
765
|
+
}).filter(Boolean);
|
|
766
|
+
return lines.length ? lines : [''];
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
function sizeNode(node) {
|
|
770
|
+
if (node.stateKind === 'start') {
|
|
771
|
+
node.lines = [];
|
|
772
|
+
node.width = 18;
|
|
773
|
+
node.height = 18;
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if (node.stateKind === 'end') {
|
|
778
|
+
node.lines = [];
|
|
779
|
+
node.width = 24;
|
|
780
|
+
node.height = 24;
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
var lines = splitLabelLines(node.label || node.id);
|
|
785
|
+
var maxWidth = 0;
|
|
786
|
+
|
|
787
|
+
for (var i = 0; i < lines.length; i += 1) {
|
|
788
|
+
maxWidth = Math.max(maxWidth, measureLine(lines[i]));
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
var width = Math.max(72, Math.ceil(maxWidth + 28));
|
|
792
|
+
var height = Math.max(44, lines.length * LINE_HEIGHT + 18);
|
|
793
|
+
|
|
794
|
+
if (node.shape === 'circle') {
|
|
795
|
+
var diameter = Math.max(width, height);
|
|
796
|
+
width = diameter;
|
|
797
|
+
height = diameter;
|
|
798
|
+
} else if (node.shape === 'diamond') {
|
|
799
|
+
width = Math.max(width + 16, 88);
|
|
800
|
+
height = Math.max(height + 12, 56);
|
|
801
|
+
} else if (node.shape === 'hexagon') {
|
|
802
|
+
width = Math.max(width + 18, 92);
|
|
803
|
+
} else if (node.shape === 'stadium') {
|
|
804
|
+
width = Math.max(width + 12, 88);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
node.lines = lines;
|
|
808
|
+
node.width = width;
|
|
809
|
+
node.height = height;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
function buildAdjacency(graph) {
|
|
813
|
+
var incoming = Object.create(null);
|
|
814
|
+
var outgoing = Object.create(null);
|
|
815
|
+
var indegree = Object.create(null);
|
|
816
|
+
|
|
817
|
+
for (var i = 0; i < graph.nodeOrder.length; i += 1) {
|
|
818
|
+
var id = graph.nodeOrder[i];
|
|
819
|
+
incoming[id] = [];
|
|
820
|
+
outgoing[id] = [];
|
|
821
|
+
indegree[id] = 0;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
for (var j = 0; j < graph.edges.length; j += 1) {
|
|
825
|
+
var edge = graph.edges[j];
|
|
826
|
+
outgoing[edge.from].push(edge);
|
|
827
|
+
incoming[edge.to].push(edge);
|
|
828
|
+
if (edge.from !== edge.to) {
|
|
829
|
+
indegree[edge.to] += 1;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
return {
|
|
834
|
+
incoming: incoming,
|
|
835
|
+
outgoing: outgoing,
|
|
836
|
+
indegree: indegree,
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
function topologicalOrder(graph, adjacency) {
|
|
841
|
+
var queue = [];
|
|
842
|
+
var order = [];
|
|
843
|
+
var seen = Object.create(null);
|
|
844
|
+
|
|
845
|
+
for (var i = 0; i < graph.nodeOrder.length; i += 1) {
|
|
846
|
+
var id = graph.nodeOrder[i];
|
|
847
|
+
if (adjacency.indegree[id] === 0) {
|
|
848
|
+
queue.push(id);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
while (queue.length) {
|
|
853
|
+
var current = queue.shift();
|
|
854
|
+
if (seen[current]) {
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
seen[current] = true;
|
|
858
|
+
order.push(current);
|
|
859
|
+
|
|
860
|
+
var outgoing = adjacency.outgoing[current];
|
|
861
|
+
for (var j = 0; j < outgoing.length; j += 1) {
|
|
862
|
+
var edge = outgoing[j];
|
|
863
|
+
if (edge.from === edge.to) {
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
adjacency.indegree[edge.to] -= 1;
|
|
867
|
+
if (adjacency.indegree[edge.to] === 0) {
|
|
868
|
+
queue.push(edge.to);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
for (var k = 0; k < graph.nodeOrder.length; k += 1) {
|
|
874
|
+
var fallbackId = graph.nodeOrder[k];
|
|
875
|
+
if (!seen[fallbackId]) {
|
|
876
|
+
order.push(fallbackId);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
return order;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
function assignRanks(graph, adjacency, order) {
|
|
884
|
+
var ranks = Object.create(null);
|
|
885
|
+
|
|
886
|
+
for (var i = 0; i < graph.nodeOrder.length; i += 1) {
|
|
887
|
+
ranks[graph.nodeOrder[i]] = 0;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
for (var j = 0; j < order.length; j += 1) {
|
|
891
|
+
var id = order[j];
|
|
892
|
+
var edges = adjacency.outgoing[id];
|
|
893
|
+
for (var k = 0; k < edges.length; k += 1) {
|
|
894
|
+
var edge = edges[k];
|
|
895
|
+
if (edge.from === edge.to) {
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
ranks[edge.to] = Math.max(ranks[edge.to], ranks[edge.from] + 1);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
var layers = [];
|
|
903
|
+
for (var m = 0; m < graph.nodeOrder.length; m += 1) {
|
|
904
|
+
var node = graph.nodesById[graph.nodeOrder[m]];
|
|
905
|
+
node.rank = ranks[node.id] || 0;
|
|
906
|
+
if (!layers[node.rank]) {
|
|
907
|
+
layers[node.rank] = [];
|
|
908
|
+
}
|
|
909
|
+
layers[node.rank].push(node.id);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
return layers;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function barycenterSort(layers, graph, adjacency) {
|
|
916
|
+
function positions(layer) {
|
|
917
|
+
var map = Object.create(null);
|
|
918
|
+
for (var i = 0; i < layer.length; i += 1) {
|
|
919
|
+
map[layer[i]] = i;
|
|
920
|
+
}
|
|
921
|
+
return map;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
for (var pass = 0; pass < 2; pass += 1) {
|
|
925
|
+
for (var rank = 1; rank < layers.length; rank += 1) {
|
|
926
|
+
if (!layers[rank] || layers[rank].length < 2) {
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
var previous = layers[rank - 1] || [];
|
|
931
|
+
var prevPos = positions(previous);
|
|
932
|
+
layers[rank].sort(function(a, b) {
|
|
933
|
+
var aIncoming = adjacency.incoming[a];
|
|
934
|
+
var bIncoming = adjacency.incoming[b];
|
|
935
|
+
var aSum = 0;
|
|
936
|
+
var bSum = 0;
|
|
937
|
+
var aCount = 0;
|
|
938
|
+
var bCount = 0;
|
|
939
|
+
|
|
940
|
+
for (var i = 0; i < aIncoming.length; i += 1) {
|
|
941
|
+
if (prevPos[aIncoming[i].from] !== undefined) {
|
|
942
|
+
aSum += prevPos[aIncoming[i].from];
|
|
943
|
+
aCount += 1;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
for (var j = 0; j < bIncoming.length; j += 1) {
|
|
947
|
+
if (prevPos[bIncoming[j].from] !== undefined) {
|
|
948
|
+
bSum += prevPos[bIncoming[j].from];
|
|
949
|
+
bCount += 1;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
var aValue = aCount ? aSum / aCount : graph.nodesById[a].order;
|
|
954
|
+
var bValue = bCount ? bSum / bCount : graph.nodesById[b].order;
|
|
955
|
+
if (aValue === bValue) {
|
|
956
|
+
return graph.nodesById[a].order - graph.nodesById[b].order;
|
|
957
|
+
}
|
|
958
|
+
return aValue - bValue;
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
for (var reverseRank = layers.length - 2; reverseRank >= 0; reverseRank -= 1) {
|
|
963
|
+
if (!layers[reverseRank] || layers[reverseRank].length < 2) {
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
var next = layers[reverseRank + 1] || [];
|
|
968
|
+
var nextPos = positions(next);
|
|
969
|
+
layers[reverseRank].sort(function(a, b) {
|
|
970
|
+
var aOutgoing = adjacency.outgoing[a];
|
|
971
|
+
var bOutgoing = adjacency.outgoing[b];
|
|
972
|
+
var aSum = 0;
|
|
973
|
+
var bSum = 0;
|
|
974
|
+
var aCount = 0;
|
|
975
|
+
var bCount = 0;
|
|
976
|
+
|
|
977
|
+
for (var i = 0; i < aOutgoing.length; i += 1) {
|
|
978
|
+
if (nextPos[aOutgoing[i].to] !== undefined) {
|
|
979
|
+
aSum += nextPos[aOutgoing[i].to];
|
|
980
|
+
aCount += 1;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
for (var j = 0; j < bOutgoing.length; j += 1) {
|
|
984
|
+
if (nextPos[bOutgoing[j].to] !== undefined) {
|
|
985
|
+
bSum += nextPos[bOutgoing[j].to];
|
|
986
|
+
bCount += 1;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
var aValue = aCount ? aSum / aCount : graph.nodesById[a].order;
|
|
991
|
+
var bValue = bCount ? bSum / bCount : graph.nodesById[b].order;
|
|
992
|
+
if (aValue === bValue) {
|
|
993
|
+
return graph.nodesById[a].order - graph.nodesById[b].order;
|
|
994
|
+
}
|
|
995
|
+
return aValue - bValue;
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
function directionOrder(length, direction) {
|
|
1002
|
+
var items = [];
|
|
1003
|
+
if (direction === 'BT' || direction === 'RL') {
|
|
1004
|
+
for (var i = length - 1; i >= 0; i -= 1) {
|
|
1005
|
+
items.push(i);
|
|
1006
|
+
}
|
|
1007
|
+
return items;
|
|
1008
|
+
}
|
|
1009
|
+
for (var j = 0; j < length; j += 1) {
|
|
1010
|
+
items.push(j);
|
|
1011
|
+
}
|
|
1012
|
+
return items;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
function layoutGraph(graph) {
|
|
1016
|
+
for (var i = 0; i < graph.nodeOrder.length; i += 1) {
|
|
1017
|
+
sizeNode(graph.nodesById[graph.nodeOrder[i]]);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
var adjacency = buildAdjacency(graph);
|
|
1021
|
+
var order = topologicalOrder(graph, adjacency);
|
|
1022
|
+
var layers = assignRanks(graph, buildAdjacency(graph), order);
|
|
1023
|
+
barycenterSort(layers, graph, buildAdjacency(graph));
|
|
1024
|
+
|
|
1025
|
+
var direction = graph.direction;
|
|
1026
|
+
var mainAxis = direction === 'LR' || direction === 'RL' ? 'x' : 'y';
|
|
1027
|
+
var crossAxis = mainAxis === 'x' ? 'y' : 'x';
|
|
1028
|
+
var rankOrder = directionOrder(layers.length, direction);
|
|
1029
|
+
var layerSpans = [];
|
|
1030
|
+
var layerCross = [];
|
|
1031
|
+
var totalMain = OUTER_PADDING;
|
|
1032
|
+
var maxCross = 0;
|
|
1033
|
+
|
|
1034
|
+
for (var layerIndex = 0; layerIndex < rankOrder.length; layerIndex += 1) {
|
|
1035
|
+
var rank = rankOrder[layerIndex];
|
|
1036
|
+
var layer = layers[rank] || [];
|
|
1037
|
+
var mainSize = 0;
|
|
1038
|
+
var crossSize = 0;
|
|
1039
|
+
|
|
1040
|
+
for (var nodeIndex = 0; nodeIndex < layer.length; nodeIndex += 1) {
|
|
1041
|
+
var node = graph.nodesById[layer[nodeIndex]];
|
|
1042
|
+
mainSize = Math.max(mainSize, mainAxis === 'x' ? node.width : node.height);
|
|
1043
|
+
crossSize += (crossAxis === 'x' ? node.width : node.height);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
if (layer.length > 1) {
|
|
1047
|
+
crossSize += (layer.length - 1) * (crossAxis === 'x' ? HORIZONTAL_GAP : VERTICAL_GAP);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
layerSpans[rank] = mainSize;
|
|
1051
|
+
layerCross[rank] = crossSize;
|
|
1052
|
+
totalMain += mainSize;
|
|
1053
|
+
if (layerIndex < rankOrder.length - 1) {
|
|
1054
|
+
totalMain += mainAxis === 'x' ? HORIZONTAL_GAP : VERTICAL_GAP;
|
|
1055
|
+
}
|
|
1056
|
+
maxCross = Math.max(maxCross, crossSize);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
totalMain += OUTER_PADDING;
|
|
1060
|
+
maxCross += OUTER_PADDING * 2;
|
|
1061
|
+
|
|
1062
|
+
var currentMain = OUTER_PADDING;
|
|
1063
|
+
for (var ranked = 0; ranked < rankOrder.length; ranked += 1) {
|
|
1064
|
+
var currentRank = rankOrder[ranked];
|
|
1065
|
+
var currentLayer = layers[currentRank] || [];
|
|
1066
|
+
var layerMainSize = layerSpans[currentRank] || 0;
|
|
1067
|
+
var crossStart = OUTER_PADDING + (maxCross - OUTER_PADDING * 2 - (layerCross[currentRank] || 0)) / 2;
|
|
1068
|
+
|
|
1069
|
+
for (var entry = 0; entry < currentLayer.length; entry += 1) {
|
|
1070
|
+
var currentNode = graph.nodesById[currentLayer[entry]];
|
|
1071
|
+
var nodeMainSize = mainAxis === 'x' ? currentNode.width : currentNode.height;
|
|
1072
|
+
var nodeCrossSize = crossAxis === 'x' ? currentNode.width : currentNode.height;
|
|
1073
|
+
var centerMain = currentMain + layerMainSize / 2;
|
|
1074
|
+
var centerCross = crossStart + nodeCrossSize / 2;
|
|
1075
|
+
|
|
1076
|
+
if (mainAxis === 'x') {
|
|
1077
|
+
currentNode.x = centerMain;
|
|
1078
|
+
currentNode.y = centerCross;
|
|
1079
|
+
} else {
|
|
1080
|
+
currentNode.x = centerCross;
|
|
1081
|
+
currentNode.y = centerMain;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
crossStart += nodeCrossSize + (crossAxis === 'x' ? HORIZONTAL_GAP : VERTICAL_GAP);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
currentMain += layerMainSize + (mainAxis === 'x' ? HORIZONTAL_GAP : VERTICAL_GAP);
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
graph.bounds = {
|
|
1091
|
+
width: mainAxis === 'x' ? totalMain : maxCross,
|
|
1092
|
+
height: mainAxis === 'x' ? maxCross : totalMain,
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
function nodeBounds(node) {
|
|
1097
|
+
return {
|
|
1098
|
+
left: node.x - node.width / 2,
|
|
1099
|
+
right: node.x + node.width / 2,
|
|
1100
|
+
top: node.y - node.height / 2,
|
|
1101
|
+
bottom: node.y + node.height / 2,
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
function computeGroupBounds(graph) {
|
|
1106
|
+
for (var i = 0; i < graph.subgraphs.length; i += 1) {
|
|
1107
|
+
var group = graph.subgraphs[i];
|
|
1108
|
+
if (!group.members.length) {
|
|
1109
|
+
group.bounds = null;
|
|
1110
|
+
continue;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
var minX = Infinity;
|
|
1114
|
+
var minY = Infinity;
|
|
1115
|
+
var maxX = -Infinity;
|
|
1116
|
+
var maxY = -Infinity;
|
|
1117
|
+
|
|
1118
|
+
for (var j = 0; j < group.members.length; j += 1) {
|
|
1119
|
+
var node = graph.nodesById[group.members[j]];
|
|
1120
|
+
if (!node) {
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
var bounds = nodeBounds(node);
|
|
1124
|
+
minX = Math.min(minX, bounds.left);
|
|
1125
|
+
minY = Math.min(minY, bounds.top);
|
|
1126
|
+
maxX = Math.max(maxX, bounds.right);
|
|
1127
|
+
maxY = Math.max(maxY, bounds.bottom);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
group.bounds = {
|
|
1131
|
+
x: minX - GROUP_PADDING,
|
|
1132
|
+
y: minY - GROUP_PADDING - 12,
|
|
1133
|
+
width: Math.max(80, maxX - minX + GROUP_PADDING * 2),
|
|
1134
|
+
height: Math.max(50, maxY - minY + GROUP_PADDING * 2 + 12),
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
function attachText(parent, x, y, lines, className) {
|
|
1140
|
+
var text = createSvg('text');
|
|
1141
|
+
setAttrs(text, {
|
|
1142
|
+
x: x,
|
|
1143
|
+
y: y,
|
|
1144
|
+
fill: 'var(--dt-text-primary)',
|
|
1145
|
+
'font-size': FONT_SIZE,
|
|
1146
|
+
'font-family': 'var(--dt-font-sans, ui-sans-serif, system-ui, sans-serif)',
|
|
1147
|
+
'text-anchor': 'middle',
|
|
1148
|
+
'dominant-baseline': 'middle',
|
|
1149
|
+
class: className || '',
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
var offset = -((lines.length - 1) * LINE_HEIGHT) / 2;
|
|
1153
|
+
for (var i = 0; i < lines.length; i += 1) {
|
|
1154
|
+
var tspan = createSvg('tspan');
|
|
1155
|
+
setAttrs(tspan, {
|
|
1156
|
+
x: x,
|
|
1157
|
+
y: y + offset + i * LINE_HEIGHT,
|
|
1158
|
+
});
|
|
1159
|
+
tspan.textContent = lines[i];
|
|
1160
|
+
text.appendChild(tspan);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
parent.appendChild(text);
|
|
1164
|
+
return text;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
function drawNode(parent, node) {
|
|
1168
|
+
var group = createSvg('g');
|
|
1169
|
+
setAttrs(group, { class: 'mini-mermaid-node' });
|
|
1170
|
+
var fill = 'var(--dt-bg-secondary)';
|
|
1171
|
+
var stroke = 'var(--dt-border-default)';
|
|
1172
|
+
var bounds = nodeBounds(node);
|
|
1173
|
+
|
|
1174
|
+
if (node.stateKind === 'start') {
|
|
1175
|
+
var startCircle = createSvg('circle');
|
|
1176
|
+
setAttrs(startCircle, {
|
|
1177
|
+
cx: node.x,
|
|
1178
|
+
cy: node.y,
|
|
1179
|
+
r: node.width / 2,
|
|
1180
|
+
fill: 'var(--dt-text-primary)',
|
|
1181
|
+
stroke: 'var(--dt-text-primary)',
|
|
1182
|
+
'stroke-width': 1,
|
|
1183
|
+
});
|
|
1184
|
+
group.appendChild(startCircle);
|
|
1185
|
+
parent.appendChild(group);
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
if (node.stateKind === 'end') {
|
|
1190
|
+
var endOuter = createSvg('circle');
|
|
1191
|
+
setAttrs(endOuter, {
|
|
1192
|
+
cx: node.x,
|
|
1193
|
+
cy: node.y,
|
|
1194
|
+
r: node.width / 2,
|
|
1195
|
+
fill: 'var(--dt-bg-canvas)',
|
|
1196
|
+
stroke: 'var(--dt-text-primary)',
|
|
1197
|
+
'stroke-width': 1.5,
|
|
1198
|
+
});
|
|
1199
|
+
group.appendChild(endOuter);
|
|
1200
|
+
|
|
1201
|
+
var endInner = createSvg('circle');
|
|
1202
|
+
setAttrs(endInner, {
|
|
1203
|
+
cx: node.x,
|
|
1204
|
+
cy: node.y,
|
|
1205
|
+
r: Math.max(4, node.width / 2 - 5),
|
|
1206
|
+
fill: 'var(--dt-text-primary)',
|
|
1207
|
+
stroke: 'none',
|
|
1208
|
+
});
|
|
1209
|
+
group.appendChild(endInner);
|
|
1210
|
+
parent.appendChild(group);
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
if (node.shape === 'diamond') {
|
|
1215
|
+
var diamond = createSvg('polygon');
|
|
1216
|
+
var diamondPoints = [
|
|
1217
|
+
node.x + ',' + bounds.top,
|
|
1218
|
+
bounds.right + ',' + node.y,
|
|
1219
|
+
node.x + ',' + bounds.bottom,
|
|
1220
|
+
bounds.left + ',' + node.y,
|
|
1221
|
+
].join(' ');
|
|
1222
|
+
setAttrs(diamond, { points: diamondPoints, fill: fill, stroke: stroke, 'stroke-width': 1.5 });
|
|
1223
|
+
group.appendChild(diamond);
|
|
1224
|
+
} else if (node.shape === 'circle') {
|
|
1225
|
+
var circle = createSvg('ellipse');
|
|
1226
|
+
setAttrs(circle, {
|
|
1227
|
+
cx: node.x,
|
|
1228
|
+
cy: node.y,
|
|
1229
|
+
rx: node.width / 2,
|
|
1230
|
+
ry: node.height / 2,
|
|
1231
|
+
fill: fill,
|
|
1232
|
+
stroke: stroke,
|
|
1233
|
+
'stroke-width': 1.5,
|
|
1234
|
+
});
|
|
1235
|
+
group.appendChild(circle);
|
|
1236
|
+
} else if (node.shape === 'hexagon') {
|
|
1237
|
+
var inset = Math.max(14, node.width * 0.16);
|
|
1238
|
+
var hexagon = createSvg('polygon');
|
|
1239
|
+
var hexPoints = [
|
|
1240
|
+
(bounds.left + inset) + ',' + bounds.top,
|
|
1241
|
+
(bounds.right - inset) + ',' + bounds.top,
|
|
1242
|
+
bounds.right + ',' + node.y,
|
|
1243
|
+
(bounds.right - inset) + ',' + bounds.bottom,
|
|
1244
|
+
(bounds.left + inset) + ',' + bounds.bottom,
|
|
1245
|
+
bounds.left + ',' + node.y,
|
|
1246
|
+
].join(' ');
|
|
1247
|
+
setAttrs(hexagon, { points: hexPoints, fill: fill, stroke: stroke, 'stroke-width': 1.5 });
|
|
1248
|
+
group.appendChild(hexagon);
|
|
1249
|
+
} else {
|
|
1250
|
+
var rect = createSvg('rect');
|
|
1251
|
+
var radius = 8;
|
|
1252
|
+
if (node.shape === 'stadium') {
|
|
1253
|
+
radius = node.height / 2;
|
|
1254
|
+
} else if (node.shape !== 'rounded') {
|
|
1255
|
+
radius = 4;
|
|
1256
|
+
}
|
|
1257
|
+
setAttrs(rect, {
|
|
1258
|
+
x: bounds.left,
|
|
1259
|
+
y: bounds.top,
|
|
1260
|
+
width: node.width,
|
|
1261
|
+
height: node.height,
|
|
1262
|
+
rx: radius,
|
|
1263
|
+
ry: radius,
|
|
1264
|
+
fill: fill,
|
|
1265
|
+
stroke: stroke,
|
|
1266
|
+
'stroke-width': 1.5,
|
|
1267
|
+
});
|
|
1268
|
+
group.appendChild(rect);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
attachText(group, node.x, node.y, node.lines, 'mini-mermaid-node-label');
|
|
1272
|
+
parent.appendChild(group);
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
function edgeAnchors(source, target, direction) {
|
|
1276
|
+
if (source.id === target.id) {
|
|
1277
|
+
return {
|
|
1278
|
+
start: { x: source.x + source.width * 0.22, y: source.y - source.height / 2 },
|
|
1279
|
+
end: { x: source.x + source.width / 2, y: source.y - source.height * 0.18 },
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
var vertical = direction === 'TD' || direction === 'BT';
|
|
1284
|
+
|
|
1285
|
+
if (vertical) {
|
|
1286
|
+
if (Math.abs(target.y - source.y) >= Math.abs(target.x - source.x)) {
|
|
1287
|
+
return target.y >= source.y ? {
|
|
1288
|
+
start: { x: source.x, y: source.y + source.height / 2 },
|
|
1289
|
+
end: { x: target.x, y: target.y - target.height / 2 },
|
|
1290
|
+
} : {
|
|
1291
|
+
start: { x: source.x, y: source.y - source.height / 2 },
|
|
1292
|
+
end: { x: target.x, y: target.y + target.height / 2 },
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
return target.x >= source.x ? {
|
|
1297
|
+
start: { x: source.x + source.width / 2, y: source.y },
|
|
1298
|
+
end: { x: target.x - target.width / 2, y: target.y },
|
|
1299
|
+
} : {
|
|
1300
|
+
start: { x: source.x - source.width / 2, y: source.y },
|
|
1301
|
+
end: { x: target.x + target.width / 2, y: target.y },
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
if (Math.abs(target.x - source.x) >= Math.abs(target.y - source.y)) {
|
|
1306
|
+
return target.x >= source.x ? {
|
|
1307
|
+
start: { x: source.x + source.width / 2, y: source.y },
|
|
1308
|
+
end: { x: target.x - target.width / 2, y: target.y },
|
|
1309
|
+
} : {
|
|
1310
|
+
start: { x: source.x - source.width / 2, y: source.y },
|
|
1311
|
+
end: { x: target.x + target.width / 2, y: target.y },
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
return target.y >= source.y ? {
|
|
1316
|
+
start: { x: source.x, y: source.y + source.height / 2 },
|
|
1317
|
+
end: { x: target.x, y: target.y - target.height / 2 },
|
|
1318
|
+
} : {
|
|
1319
|
+
start: { x: source.x, y: source.y - source.height / 2 },
|
|
1320
|
+
end: { x: target.x, y: target.y + target.height / 2 },
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
function buildEdgePath(edge, source, target, direction) {
|
|
1325
|
+
var anchors = edgeAnchors(source, target, direction);
|
|
1326
|
+
var start = anchors.start;
|
|
1327
|
+
var end = anchors.end;
|
|
1328
|
+
|
|
1329
|
+
if (edge.from === edge.to) {
|
|
1330
|
+
return [
|
|
1331
|
+
'M', start.x, start.y,
|
|
1332
|
+
'C', start.x + 34, start.y - 36, end.x + 22, end.y - 34, end.x, end.y,
|
|
1333
|
+
].join(' ');
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
var dx = end.x - start.x;
|
|
1337
|
+
var dy = end.y - start.y;
|
|
1338
|
+
var vertical = direction === 'TD' || direction === 'BT';
|
|
1339
|
+
var radius = 16;
|
|
1340
|
+
|
|
1341
|
+
if (vertical) {
|
|
1342
|
+
var midY = start.y + dy / 2;
|
|
1343
|
+
if (Math.abs(dx) < 8) {
|
|
1344
|
+
return ['M', start.x, start.y, 'L', end.x, end.y].join(' ');
|
|
1345
|
+
}
|
|
1346
|
+
return [
|
|
1347
|
+
'M', start.x, start.y,
|
|
1348
|
+
'L', start.x, midY - radius,
|
|
1349
|
+
'Q', start.x, midY, start.x + (dx > 0 ? radius : -radius), midY,
|
|
1350
|
+
'L', end.x - (dx > 0 ? radius : -radius), midY,
|
|
1351
|
+
'Q', end.x, midY, end.x, midY + radius,
|
|
1352
|
+
'L', end.x, end.y,
|
|
1353
|
+
].join(' ');
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
var midX = start.x + dx / 2;
|
|
1357
|
+
if (Math.abs(dy) < 8) {
|
|
1358
|
+
return ['M', start.x, start.y, 'L', end.x, end.y].join(' ');
|
|
1359
|
+
}
|
|
1360
|
+
return [
|
|
1361
|
+
'M', start.x, start.y,
|
|
1362
|
+
'L', midX - radius, start.y,
|
|
1363
|
+
'Q', midX, start.y, midX, start.y + (dy > 0 ? radius : -radius),
|
|
1364
|
+
'L', midX, end.y - (dy > 0 ? radius : -radius),
|
|
1365
|
+
'Q', midX, end.y, midX + radius, end.y,
|
|
1366
|
+
'L', end.x, end.y,
|
|
1367
|
+
].join(' ');
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
function edgeMidpoint(edge, source, target) {
|
|
1371
|
+
if (edge.from === edge.to) {
|
|
1372
|
+
return {
|
|
1373
|
+
x: source.x + source.width * 0.4,
|
|
1374
|
+
y: source.y - source.height * 0.9,
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
return {
|
|
1378
|
+
x: (source.x + target.x) / 2,
|
|
1379
|
+
y: (source.y + target.y) / 2,
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
function drawLabel(parent, x, y, textValue) {
|
|
1384
|
+
var label = String(textValue || '').trim();
|
|
1385
|
+
if (!label) {
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
var width = Math.max(24, measureLine(label) + 12);
|
|
1390
|
+
var group = createSvg('g');
|
|
1391
|
+
var rect = createSvg('rect');
|
|
1392
|
+
setAttrs(rect, {
|
|
1393
|
+
x: x - width / 2,
|
|
1394
|
+
y: y - 10,
|
|
1395
|
+
width: width,
|
|
1396
|
+
height: 20,
|
|
1397
|
+
rx: 6,
|
|
1398
|
+
ry: 6,
|
|
1399
|
+
fill: 'var(--dt-bg-canvas)',
|
|
1400
|
+
stroke: 'var(--dt-border-default)',
|
|
1401
|
+
'stroke-width': 0.75,
|
|
1402
|
+
});
|
|
1403
|
+
group.appendChild(rect);
|
|
1404
|
+
|
|
1405
|
+
var text = createSvg('text');
|
|
1406
|
+
setAttrs(text, {
|
|
1407
|
+
x: x,
|
|
1408
|
+
y: y + 1,
|
|
1409
|
+
fill: 'var(--dt-text-primary)',
|
|
1410
|
+
'font-size': 12,
|
|
1411
|
+
'font-family': 'var(--dt-font-sans, ui-sans-serif, system-ui, sans-serif)',
|
|
1412
|
+
'text-anchor': 'middle',
|
|
1413
|
+
'dominant-baseline': 'middle',
|
|
1414
|
+
});
|
|
1415
|
+
text.textContent = label;
|
|
1416
|
+
group.appendChild(text);
|
|
1417
|
+
parent.appendChild(group);
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
function drawEdges(parent, graph) {
|
|
1421
|
+
for (var i = 0; i < graph.edges.length; i += 1) {
|
|
1422
|
+
var edge = graph.edges[i];
|
|
1423
|
+
var source = graph.nodesById[edge.from];
|
|
1424
|
+
var target = graph.nodesById[edge.to];
|
|
1425
|
+
if (!source || !target) {
|
|
1426
|
+
continue;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
var path = createSvg('path');
|
|
1430
|
+
setAttrs(path, {
|
|
1431
|
+
d: buildEdgePath(edge, source, target, graph.direction),
|
|
1432
|
+
fill: 'none',
|
|
1433
|
+
stroke: 'var(--dt-text-primary)',
|
|
1434
|
+
'stroke-width': edge.style === 'thick' ? 2.5 : 1.6,
|
|
1435
|
+
'stroke-dasharray': edge.style === 'dotted' ? '4 5' : '',
|
|
1436
|
+
'stroke-linecap': 'round',
|
|
1437
|
+
'stroke-linejoin': 'round',
|
|
1438
|
+
'marker-end': edge.arrow ? 'url(#mini-mermaid-arrow)' : '',
|
|
1439
|
+
});
|
|
1440
|
+
parent.appendChild(path);
|
|
1441
|
+
|
|
1442
|
+
if (edge.label) {
|
|
1443
|
+
var midpoint = edgeMidpoint(edge, source, target);
|
|
1444
|
+
drawLabel(parent, midpoint.x, midpoint.y, edge.label);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
function drawSubgraphs(parent, graph) {
|
|
1450
|
+
for (var i = 0; i < graph.subgraphs.length; i += 1) {
|
|
1451
|
+
var group = graph.subgraphs[i];
|
|
1452
|
+
if (!group.bounds) {
|
|
1453
|
+
continue;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
var rect = createSvg('rect');
|
|
1457
|
+
setAttrs(rect, {
|
|
1458
|
+
x: group.bounds.x,
|
|
1459
|
+
y: group.bounds.y,
|
|
1460
|
+
width: group.bounds.width,
|
|
1461
|
+
height: group.bounds.height,
|
|
1462
|
+
rx: 12,
|
|
1463
|
+
ry: 12,
|
|
1464
|
+
fill: 'rgba(127, 127, 127, 0.06)',
|
|
1465
|
+
stroke: 'var(--dt-border-default)',
|
|
1466
|
+
'stroke-width': 1,
|
|
1467
|
+
'stroke-dasharray': '6 5',
|
|
1468
|
+
});
|
|
1469
|
+
parent.appendChild(rect);
|
|
1470
|
+
|
|
1471
|
+
var label = createSvg('text');
|
|
1472
|
+
setAttrs(label, {
|
|
1473
|
+
x: group.bounds.x + 14,
|
|
1474
|
+
y: group.bounds.y + 18,
|
|
1475
|
+
fill: 'var(--dt-text-primary)',
|
|
1476
|
+
'font-size': 12,
|
|
1477
|
+
'font-family': 'var(--dt-font-sans, ui-sans-serif, system-ui, sans-serif)',
|
|
1478
|
+
'font-weight': '600',
|
|
1479
|
+
});
|
|
1480
|
+
label.textContent = group.title;
|
|
1481
|
+
parent.appendChild(label);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
function ensureMarkers(defs) {
|
|
1486
|
+
var marker = createSvg('marker');
|
|
1487
|
+
setAttrs(marker, {
|
|
1488
|
+
id: 'mini-mermaid-arrow',
|
|
1489
|
+
viewBox: '0 0 10 10',
|
|
1490
|
+
refX: 9,
|
|
1491
|
+
refY: 5,
|
|
1492
|
+
markerWidth: 8,
|
|
1493
|
+
markerHeight: 8,
|
|
1494
|
+
orient: 'auto-start-reverse',
|
|
1495
|
+
markerUnits: 'strokeWidth',
|
|
1496
|
+
});
|
|
1497
|
+
var polygon = createSvg('polygon');
|
|
1498
|
+
setAttrs(polygon, {
|
|
1499
|
+
points: '0,0 10,5 0,10 2.5,5',
|
|
1500
|
+
fill: 'var(--dt-text-primary)',
|
|
1501
|
+
});
|
|
1502
|
+
marker.appendChild(polygon);
|
|
1503
|
+
defs.appendChild(marker);
|
|
1504
|
+
|
|
1505
|
+
var openArrow = createSvg('marker');
|
|
1506
|
+
setAttrs(openArrow, {
|
|
1507
|
+
id: 'mini-mermaid-open-arrow',
|
|
1508
|
+
viewBox: '0 0 10 10',
|
|
1509
|
+
refX: 9,
|
|
1510
|
+
refY: 5,
|
|
1511
|
+
markerWidth: 8,
|
|
1512
|
+
markerHeight: 8,
|
|
1513
|
+
orient: 'auto-start-reverse',
|
|
1514
|
+
markerUnits: 'strokeWidth',
|
|
1515
|
+
});
|
|
1516
|
+
var openPath = createSvg('path');
|
|
1517
|
+
setAttrs(openPath, {
|
|
1518
|
+
d: 'M 1 1 L 9 5 L 1 9',
|
|
1519
|
+
fill: 'none',
|
|
1520
|
+
stroke: 'var(--dt-text-primary)',
|
|
1521
|
+
'stroke-width': 1.4,
|
|
1522
|
+
'stroke-linecap': 'round',
|
|
1523
|
+
'stroke-linejoin': 'round',
|
|
1524
|
+
});
|
|
1525
|
+
openArrow.appendChild(openPath);
|
|
1526
|
+
defs.appendChild(openArrow);
|
|
1527
|
+
|
|
1528
|
+
var triangleArrow = createSvg('marker');
|
|
1529
|
+
setAttrs(triangleArrow, {
|
|
1530
|
+
id: 'mini-mermaid-triangle-arrow',
|
|
1531
|
+
viewBox: '0 0 10 10',
|
|
1532
|
+
refX: 9,
|
|
1533
|
+
refY: 5,
|
|
1534
|
+
markerWidth: 9,
|
|
1535
|
+
markerHeight: 9,
|
|
1536
|
+
orient: 'auto-start-reverse',
|
|
1537
|
+
markerUnits: 'strokeWidth',
|
|
1538
|
+
});
|
|
1539
|
+
var triangle = createSvg('polygon');
|
|
1540
|
+
setAttrs(triangle, {
|
|
1541
|
+
points: '0,0 10,5 0,10',
|
|
1542
|
+
fill: 'var(--dt-bg-canvas)',
|
|
1543
|
+
stroke: 'var(--dt-text-primary)',
|
|
1544
|
+
'stroke-width': 1.1,
|
|
1545
|
+
'stroke-linejoin': 'round',
|
|
1546
|
+
});
|
|
1547
|
+
triangleArrow.appendChild(triangle);
|
|
1548
|
+
defs.appendChild(triangleArrow);
|
|
1549
|
+
|
|
1550
|
+
var filledDiamond = createSvg('marker');
|
|
1551
|
+
setAttrs(filledDiamond, {
|
|
1552
|
+
id: 'mini-mermaid-diamond-arrow',
|
|
1553
|
+
viewBox: '0 0 12 10',
|
|
1554
|
+
refX: 10,
|
|
1555
|
+
refY: 5,
|
|
1556
|
+
markerWidth: 11,
|
|
1557
|
+
markerHeight: 9,
|
|
1558
|
+
orient: 'auto-start-reverse',
|
|
1559
|
+
markerUnits: 'strokeWidth',
|
|
1560
|
+
});
|
|
1561
|
+
var filledDiamondShape = createSvg('polygon');
|
|
1562
|
+
setAttrs(filledDiamondShape, {
|
|
1563
|
+
points: '0,5 5,0 10,5 5,10',
|
|
1564
|
+
fill: 'var(--dt-text-primary)',
|
|
1565
|
+
stroke: 'var(--dt-text-primary)',
|
|
1566
|
+
'stroke-width': 1,
|
|
1567
|
+
'stroke-linejoin': 'round',
|
|
1568
|
+
});
|
|
1569
|
+
filledDiamond.appendChild(filledDiamondShape);
|
|
1570
|
+
defs.appendChild(filledDiamond);
|
|
1571
|
+
|
|
1572
|
+
var openDiamond = createSvg('marker');
|
|
1573
|
+
setAttrs(openDiamond, {
|
|
1574
|
+
id: 'mini-mermaid-open-diamond-arrow',
|
|
1575
|
+
viewBox: '0 0 12 10',
|
|
1576
|
+
refX: 10,
|
|
1577
|
+
refY: 5,
|
|
1578
|
+
markerWidth: 11,
|
|
1579
|
+
markerHeight: 9,
|
|
1580
|
+
orient: 'auto-start-reverse',
|
|
1581
|
+
markerUnits: 'strokeWidth',
|
|
1582
|
+
});
|
|
1583
|
+
var openDiamondShape = createSvg('polygon');
|
|
1584
|
+
setAttrs(openDiamondShape, {
|
|
1585
|
+
points: '0,5 5,0 10,5 5,10',
|
|
1586
|
+
fill: 'var(--dt-bg-canvas)',
|
|
1587
|
+
stroke: 'var(--dt-text-primary)',
|
|
1588
|
+
'stroke-width': 1,
|
|
1589
|
+
'stroke-linejoin': 'round',
|
|
1590
|
+
});
|
|
1591
|
+
openDiamond.appendChild(openDiamondShape);
|
|
1592
|
+
defs.appendChild(openDiamond);
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
function createRootSvg(width, height, label) {
|
|
1596
|
+
var svg = createSvg('svg');
|
|
1597
|
+
setAttrs(svg, {
|
|
1598
|
+
width: '100%',
|
|
1599
|
+
viewBox: '0 0 ' + Math.max(120, Math.ceil(width)) + ' ' + Math.max(80, Math.ceil(height)),
|
|
1600
|
+
role: 'img',
|
|
1601
|
+
'aria-label': label,
|
|
1602
|
+
style: 'display:block;overflow:visible;background:var(--dt-bg-canvas)',
|
|
1603
|
+
preserveAspectRatio: 'xMidYMid meet',
|
|
1604
|
+
});
|
|
1605
|
+
|
|
1606
|
+
var defs = createSvg('defs');
|
|
1607
|
+
ensureMarkers(defs);
|
|
1608
|
+
svg.appendChild(defs);
|
|
1609
|
+
return svg;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
function renderSvg(graph) {
|
|
1613
|
+
layoutGraph(graph);
|
|
1614
|
+
computeGroupBounds(graph);
|
|
1615
|
+
|
|
1616
|
+
var svg = createRootSvg(graph.bounds.width, graph.bounds.height, 'Mermaid flowchart diagram');
|
|
1617
|
+
|
|
1618
|
+
if (!graph.nodeOrder.length) {
|
|
1619
|
+
var empty = createSvg('text');
|
|
1620
|
+
setAttrs(empty, {
|
|
1621
|
+
x: 24,
|
|
1622
|
+
y: 32,
|
|
1623
|
+
fill: 'var(--dt-text-secondary, var(--dt-text-primary))',
|
|
1624
|
+
'font-size': FONT_SIZE,
|
|
1625
|
+
'font-family': 'var(--dt-font-sans, ui-sans-serif, system-ui, sans-serif)',
|
|
1626
|
+
});
|
|
1627
|
+
empty.textContent = 'Empty diagram';
|
|
1628
|
+
svg.appendChild(empty);
|
|
1629
|
+
return svg;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
var groupLayer = createSvg('g');
|
|
1633
|
+
drawSubgraphs(groupLayer, graph);
|
|
1634
|
+
svg.appendChild(groupLayer);
|
|
1635
|
+
|
|
1636
|
+
var edgeLayer = createSvg('g');
|
|
1637
|
+
setAttrs(edgeLayer, { class: 'mini-mermaid-edges' });
|
|
1638
|
+
drawEdges(edgeLayer, graph);
|
|
1639
|
+
svg.appendChild(edgeLayer);
|
|
1640
|
+
|
|
1641
|
+
var nodeLayer = createSvg('g');
|
|
1642
|
+
setAttrs(nodeLayer, { class: 'mini-mermaid-nodes' });
|
|
1643
|
+
for (var i = 0; i < graph.nodeOrder.length; i += 1) {
|
|
1644
|
+
drawNode(nodeLayer, graph.nodesById[graph.nodeOrder[i]]);
|
|
1645
|
+
}
|
|
1646
|
+
svg.appendChild(nodeLayer);
|
|
1647
|
+
return svg;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
function appendText(parent, x, y, textValue, options) {
|
|
1651
|
+
var text = createSvg('text');
|
|
1652
|
+
setAttrs(text, {
|
|
1653
|
+
x: x,
|
|
1654
|
+
y: y,
|
|
1655
|
+
fill: options && options.fill ? options.fill : 'var(--dt-text-primary)',
|
|
1656
|
+
'font-size': options && options.size ? options.size : FONT_SIZE,
|
|
1657
|
+
'font-family': 'var(--dt-font-sans, ui-sans-serif, system-ui, sans-serif)',
|
|
1658
|
+
'font-weight': options && options.weight ? options.weight : '400',
|
|
1659
|
+
'text-anchor': options && options.anchor ? options.anchor : 'start',
|
|
1660
|
+
'dominant-baseline': options && options.baseline ? options.baseline : 'middle',
|
|
1661
|
+
class: options && options.className ? options.className : '',
|
|
1662
|
+
});
|
|
1663
|
+
text.textContent = textValue;
|
|
1664
|
+
parent.appendChild(text);
|
|
1665
|
+
return text;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
function appendLeftText(parent, x, y, lines, options) {
|
|
1669
|
+
for (var i = 0; i < lines.length; i += 1) {
|
|
1670
|
+
appendText(parent, x, y + i * LINE_HEIGHT, lines[i], {
|
|
1671
|
+
anchor: 'start',
|
|
1672
|
+
baseline: 'hanging',
|
|
1673
|
+
weight: options && options.weight ? options.weight : '400',
|
|
1674
|
+
size: options && options.size ? options.size : FONT_SIZE,
|
|
1675
|
+
fill: options && options.fill ? options.fill : 'var(--dt-text-primary)',
|
|
1676
|
+
className: options && options.className ? options.className : '',
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
function cleanedLines(source) {
|
|
1682
|
+
var raw = String(source || '').split(/\r?\n/);
|
|
1683
|
+
var lines = [];
|
|
1684
|
+
|
|
1685
|
+
for (var i = 0; i < raw.length; i += 1) {
|
|
1686
|
+
var line = stripComment(raw[i]).trim();
|
|
1687
|
+
if (line) {
|
|
1688
|
+
lines.push(line);
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
return lines;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
function boxAnchor(source, target) {
|
|
1696
|
+
var dx = target.x - source.x;
|
|
1697
|
+
var dy = target.y - source.y;
|
|
1698
|
+
|
|
1699
|
+
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
1700
|
+
return dx >= 0 ? { x: source.x + source.width / 2, y: source.y } : { x: source.x - source.width / 2, y: source.y };
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
return dy >= 0 ? { x: source.x, y: source.y + source.height / 2 } : { x: source.x, y: source.y - source.height / 2 };
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
function drawStraightLine(parent, x1, y1, x2, y2, options) {
|
|
1707
|
+
var line = createSvg('line');
|
|
1708
|
+
setAttrs(line, {
|
|
1709
|
+
x1: x1,
|
|
1710
|
+
y1: y1,
|
|
1711
|
+
x2: x2,
|
|
1712
|
+
y2: y2,
|
|
1713
|
+
fill: 'none',
|
|
1714
|
+
stroke: options && options.stroke ? options.stroke : 'var(--dt-text-primary)',
|
|
1715
|
+
'stroke-width': options && options.strokeWidth ? options.strokeWidth : 1.6,
|
|
1716
|
+
'stroke-dasharray': options && options.dash ? options.dash : '',
|
|
1717
|
+
'stroke-linecap': 'round',
|
|
1718
|
+
'stroke-linejoin': 'round',
|
|
1719
|
+
'marker-start': options && options.markerStart ? options.markerStart : '',
|
|
1720
|
+
'marker-end': options && options.markerEnd ? options.markerEnd : '',
|
|
1721
|
+
});
|
|
1722
|
+
parent.appendChild(line);
|
|
1723
|
+
return line;
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
function ensureSequenceParticipant(participants, participantsById, id, label) {
|
|
1727
|
+
if (!participantsById[id]) {
|
|
1728
|
+
participantsById[id] = { id: id, label: label || id, width: 0, x: 0 };
|
|
1729
|
+
participants.push(participantsById[id]);
|
|
1730
|
+
}
|
|
1731
|
+
if (label && (!participantsById[id].label || participantsById[id].label === id || label !== id)) {
|
|
1732
|
+
participantsById[id].label = label;
|
|
1733
|
+
}
|
|
1734
|
+
return participantsById[id];
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
function renderSequenceDiagram(source) {
|
|
1738
|
+
var lines = cleanedLines(source);
|
|
1739
|
+
var participants = [];
|
|
1740
|
+
var participantsById = Object.create(null);
|
|
1741
|
+
var entries = [];
|
|
1742
|
+
|
|
1743
|
+
for (var i = 0; i < lines.length; i += 1) {
|
|
1744
|
+
var line = lines[i];
|
|
1745
|
+
if (i === 0 && /^sequenceDiagram\b/i.test(line)) {
|
|
1746
|
+
continue;
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
var participantMatch = line.match(/^participant\s+([A-Za-z0-9_.-]+)(?:\s+as\s+(.+))?$/i);
|
|
1750
|
+
if (participantMatch) {
|
|
1751
|
+
ensureSequenceParticipant(participants, participantsById, participantMatch[1], normalizeLabel(participantMatch[2] || participantMatch[1]));
|
|
1752
|
+
continue;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
var noteMatch = line.match(/^Note\s+over\s+([^:]+):\s*(.+)$/i);
|
|
1756
|
+
if (noteMatch) {
|
|
1757
|
+
var refs = noteMatch[1].split(',');
|
|
1758
|
+
var noteRefs = [];
|
|
1759
|
+
for (var refIndex = 0; refIndex < refs.length; refIndex += 1) {
|
|
1760
|
+
var refId = refs[refIndex].trim();
|
|
1761
|
+
if (!refId) {
|
|
1762
|
+
continue;
|
|
1763
|
+
}
|
|
1764
|
+
ensureSequenceParticipant(participants, participantsById, refId, refId);
|
|
1765
|
+
noteRefs.push(refId);
|
|
1766
|
+
}
|
|
1767
|
+
if (noteRefs.length) {
|
|
1768
|
+
entries.push({ type: 'note', refs: noteRefs, text: normalizeLabel(noteMatch[2]) });
|
|
1769
|
+
}
|
|
1770
|
+
continue;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
var messageMatch = line.match(/^([A-Za-z0-9_.-]+)\s*(-->>|->>|-->|->)\s*([A-Za-z0-9_.-]+)(?:\s*:\s*(.+))?$/);
|
|
1774
|
+
if (messageMatch) {
|
|
1775
|
+
ensureSequenceParticipant(participants, participantsById, messageMatch[1], messageMatch[1]);
|
|
1776
|
+
ensureSequenceParticipant(participants, participantsById, messageMatch[3], messageMatch[3]);
|
|
1777
|
+
entries.push({
|
|
1778
|
+
type: 'message',
|
|
1779
|
+
from: messageMatch[1],
|
|
1780
|
+
to: messageMatch[3],
|
|
1781
|
+
arrow: messageMatch[2],
|
|
1782
|
+
text: normalizeLabel(messageMatch[4] || ''),
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
if (!participants.length) {
|
|
1788
|
+
throw new Error('Sequence diagram is missing participants');
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
var totalParticipantWidth = 0;
|
|
1792
|
+
for (var participantIndex = 0; participantIndex < participants.length; participantIndex += 1) {
|
|
1793
|
+
var participant = participants[participantIndex];
|
|
1794
|
+
participant.width = Math.max(96, Math.ceil(measureLine(participant.label) + 28));
|
|
1795
|
+
totalParticipantWidth += participant.width;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
var gap = Math.max(HORIZONTAL_GAP, 48);
|
|
1799
|
+
var width = OUTER_PADDING * 2 + totalParticipantWidth + gap * Math.max(0, participants.length - 1);
|
|
1800
|
+
var boxHeight = 34;
|
|
1801
|
+
var topY = OUTER_PADDING + 18;
|
|
1802
|
+
var cursorX = OUTER_PADDING;
|
|
1803
|
+
|
|
1804
|
+
for (var position = 0; position < participants.length; position += 1) {
|
|
1805
|
+
participants[position].x = cursorX + participants[position].width / 2;
|
|
1806
|
+
cursorX += participants[position].width + gap;
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
var contentY = topY + boxHeight + 26;
|
|
1810
|
+
for (var entryIndex = 0; entryIndex < entries.length; entryIndex += 1) {
|
|
1811
|
+
var entry = entries[entryIndex];
|
|
1812
|
+
entry.y = contentY;
|
|
1813
|
+
if (entry.type === 'note') {
|
|
1814
|
+
entry.lines = splitLabelLines(entry.text);
|
|
1815
|
+
entry.height = Math.max(42, entry.lines.length * LINE_HEIGHT + 18);
|
|
1816
|
+
contentY += entry.height + 14;
|
|
1817
|
+
} else {
|
|
1818
|
+
contentY += 42;
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
var height = contentY + OUTER_PADDING;
|
|
1823
|
+
var svg = createRootSvg(width, height, 'Mermaid sequence diagram');
|
|
1824
|
+
|
|
1825
|
+
for (var drawParticipantIndex = 0; drawParticipantIndex < participants.length; drawParticipantIndex += 1) {
|
|
1826
|
+
var drawParticipant = participants[drawParticipantIndex];
|
|
1827
|
+
var left = drawParticipant.x - drawParticipant.width / 2;
|
|
1828
|
+
|
|
1829
|
+
var participantRect = createSvg('rect');
|
|
1830
|
+
setAttrs(participantRect, {
|
|
1831
|
+
x: left,
|
|
1832
|
+
y: topY,
|
|
1833
|
+
width: drawParticipant.width,
|
|
1834
|
+
height: boxHeight,
|
|
1835
|
+
rx: 8,
|
|
1836
|
+
ry: 8,
|
|
1837
|
+
fill: 'var(--dt-bg-secondary)',
|
|
1838
|
+
stroke: 'var(--dt-border-default)',
|
|
1839
|
+
'stroke-width': 1.5,
|
|
1840
|
+
});
|
|
1841
|
+
svg.appendChild(participantRect);
|
|
1842
|
+
attachText(svg, drawParticipant.x, topY + boxHeight / 2 + 1, [drawParticipant.label], '');
|
|
1843
|
+
|
|
1844
|
+
var lifeline = createSvg('line');
|
|
1845
|
+
setAttrs(lifeline, {
|
|
1846
|
+
x1: drawParticipant.x,
|
|
1847
|
+
y1: topY + boxHeight,
|
|
1848
|
+
x2: drawParticipant.x,
|
|
1849
|
+
y2: height - OUTER_PADDING / 2,
|
|
1850
|
+
stroke: 'var(--dt-text-secondary, var(--dt-text-primary))',
|
|
1851
|
+
'stroke-width': 1.2,
|
|
1852
|
+
'stroke-dasharray': '6 6',
|
|
1853
|
+
});
|
|
1854
|
+
svg.appendChild(lifeline);
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
for (var drawEntryIndex = 0; drawEntryIndex < entries.length; drawEntryIndex += 1) {
|
|
1858
|
+
var drawEntry = entries[drawEntryIndex];
|
|
1859
|
+
|
|
1860
|
+
if (drawEntry.type === 'note') {
|
|
1861
|
+
var leftParticipant = participantsById[drawEntry.refs[0]];
|
|
1862
|
+
var rightParticipant = participantsById[drawEntry.refs[drawEntry.refs.length - 1]];
|
|
1863
|
+
var noteWidth = Math.max(measureLine(drawEntry.text) + 26, Math.abs(rightParticipant.x - leftParticipant.x) + 40);
|
|
1864
|
+
var noteX = (leftParticipant.x + rightParticipant.x) / 2;
|
|
1865
|
+
var noteRect = createSvg('rect');
|
|
1866
|
+
setAttrs(noteRect, {
|
|
1867
|
+
x: noteX - noteWidth / 2,
|
|
1868
|
+
y: drawEntry.y - 12,
|
|
1869
|
+
width: noteWidth,
|
|
1870
|
+
height: drawEntry.height,
|
|
1871
|
+
rx: 10,
|
|
1872
|
+
ry: 10,
|
|
1873
|
+
fill: 'var(--dt-bg-secondary)',
|
|
1874
|
+
stroke: 'var(--dt-border-default)',
|
|
1875
|
+
'stroke-width': 1.2,
|
|
1876
|
+
});
|
|
1877
|
+
svg.appendChild(noteRect);
|
|
1878
|
+
attachText(svg, noteX, drawEntry.y - 12 + drawEntry.height / 2, drawEntry.lines, '');
|
|
1879
|
+
continue;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
var fromParticipant = participantsById[drawEntry.from];
|
|
1883
|
+
var toParticipant = participantsById[drawEntry.to];
|
|
1884
|
+
var messageY = drawEntry.y;
|
|
1885
|
+
var dashed = drawEntry.arrow === '-->>' || drawEntry.arrow === '-->';
|
|
1886
|
+
var closedArrow = drawEntry.arrow === '->>' || drawEntry.arrow === '-->>';
|
|
1887
|
+
|
|
1888
|
+
if (fromParticipant.id === toParticipant.id) {
|
|
1889
|
+
var loopPath = createSvg('path');
|
|
1890
|
+
var loopX = fromParticipant.x + 34;
|
|
1891
|
+
setAttrs(loopPath, {
|
|
1892
|
+
d: ['M', fromParticipant.x, messageY, 'L', loopX, messageY, 'L', loopX, messageY + 20, 'L', fromParticipant.x + 8, messageY + 20].join(' '),
|
|
1893
|
+
fill: 'none',
|
|
1894
|
+
stroke: 'var(--dt-text-primary)',
|
|
1895
|
+
'stroke-width': 1.6,
|
|
1896
|
+
'stroke-dasharray': dashed ? '6 5' : '',
|
|
1897
|
+
'marker-end': closedArrow ? 'url(#mini-mermaid-arrow)' : 'url(#mini-mermaid-open-arrow)',
|
|
1898
|
+
'stroke-linecap': 'round',
|
|
1899
|
+
'stroke-linejoin': 'round',
|
|
1900
|
+
});
|
|
1901
|
+
svg.appendChild(loopPath);
|
|
1902
|
+
if (drawEntry.text) {
|
|
1903
|
+
appendText(svg, loopX + 6, messageY + 10, drawEntry.text, { anchor: 'start', baseline: 'middle', size: 12 });
|
|
1904
|
+
}
|
|
1905
|
+
continue;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
drawStraightLine(svg, fromParticipant.x, messageY, toParticipant.x, messageY, {
|
|
1909
|
+
dash: dashed ? '6 5' : '',
|
|
1910
|
+
markerEnd: closedArrow ? 'url(#mini-mermaid-arrow)' : 'url(#mini-mermaid-open-arrow)',
|
|
1911
|
+
});
|
|
1912
|
+
|
|
1913
|
+
if (drawEntry.text) {
|
|
1914
|
+
appendText(svg, (fromParticipant.x + toParticipant.x) / 2, messageY - 11, drawEntry.text, { anchor: 'middle', baseline: 'middle', size: 12 });
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
return svg;
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
function parseClassDiagram(source) {
|
|
1922
|
+
var lines = cleanedLines(source);
|
|
1923
|
+
var classes = [];
|
|
1924
|
+
var classesById = Object.create(null);
|
|
1925
|
+
var relations = [];
|
|
1926
|
+
var currentClass = null;
|
|
1927
|
+
|
|
1928
|
+
function ensureClass(id) {
|
|
1929
|
+
if (!classesById[id]) {
|
|
1930
|
+
classesById[id] = { id: id, name: id, attributes: [], methods: [], width: 0, height: 0, x: 0, y: 0 };
|
|
1931
|
+
classes.push(classesById[id]);
|
|
1932
|
+
}
|
|
1933
|
+
return classesById[id];
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
for (var i = 0; i < lines.length; i += 1) {
|
|
1937
|
+
var line = lines[i];
|
|
1938
|
+
if (i === 0 && /^classDiagram\b/i.test(line)) {
|
|
1939
|
+
continue;
|
|
1940
|
+
}
|
|
1941
|
+
if (currentClass) {
|
|
1942
|
+
if (line === '}') {
|
|
1943
|
+
currentClass = null;
|
|
1944
|
+
continue;
|
|
1945
|
+
}
|
|
1946
|
+
if (/\([^)]*\)/.test(line)) {
|
|
1947
|
+
currentClass.methods.push(line);
|
|
1948
|
+
} else {
|
|
1949
|
+
currentClass.attributes.push(line);
|
|
1950
|
+
}
|
|
1951
|
+
continue;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
var classBlockMatch = line.match(/^class\s+([A-Za-z0-9_.-]+)\s*\{$/i);
|
|
1955
|
+
if (classBlockMatch) {
|
|
1956
|
+
currentClass = ensureClass(classBlockMatch[1]);
|
|
1957
|
+
continue;
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
var classMatch = line.match(/^class\s+([A-Za-z0-9_.-]+)$/i);
|
|
1961
|
+
if (classMatch) {
|
|
1962
|
+
ensureClass(classMatch[1]);
|
|
1963
|
+
continue;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
var relationMatch = line.match(/^([A-Za-z0-9_.-]+)\s+(<\|--|\*--|o--|-->|--|\.\.>)\s+([A-Za-z0-9_.-]+)(?:\s*:\s*(.+))?$/);
|
|
1967
|
+
if (relationMatch) {
|
|
1968
|
+
ensureClass(relationMatch[1]);
|
|
1969
|
+
ensureClass(relationMatch[3]);
|
|
1970
|
+
relations.push({ left: relationMatch[1], right: relationMatch[3], type: relationMatch[2], label: normalizeLabel(relationMatch[4] || '') });
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
return { classes: classes, classesById: classesById, relations: relations };
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
function renderClassDiagram(source) {
|
|
1978
|
+
var diagram = parseClassDiagram(source);
|
|
1979
|
+
if (!diagram.classes.length) {
|
|
1980
|
+
throw new Error('Class diagram is empty');
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
var maxWidth = 0;
|
|
1984
|
+
var maxHeight = 0;
|
|
1985
|
+
for (var i = 0; i < diagram.classes.length; i += 1) {
|
|
1986
|
+
var classDef = diagram.classes[i];
|
|
1987
|
+
var classWidth = Math.max(120, measureLine(classDef.name) + 28);
|
|
1988
|
+
var maxMemberWidth = classWidth;
|
|
1989
|
+
var lineIndex;
|
|
1990
|
+
|
|
1991
|
+
for (lineIndex = 0; lineIndex < classDef.attributes.length; lineIndex += 1) {
|
|
1992
|
+
maxMemberWidth = Math.max(maxMemberWidth, measureLine(classDef.attributes[lineIndex]) + 24);
|
|
1993
|
+
}
|
|
1994
|
+
for (lineIndex = 0; lineIndex < classDef.methods.length; lineIndex += 1) {
|
|
1995
|
+
maxMemberWidth = Math.max(maxMemberWidth, measureLine(classDef.methods[lineIndex]) + 24);
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
var attrHeight = classDef.attributes.length ? classDef.attributes.length * LINE_HEIGHT + 12 : 0;
|
|
1999
|
+
var methodHeight = classDef.methods.length ? classDef.methods.length * LINE_HEIGHT + 12 : 0;
|
|
2000
|
+
classDef.width = Math.ceil(maxMemberWidth);
|
|
2001
|
+
classDef.height = 34 + attrHeight + methodHeight + (classDef.attributes.length && classDef.methods.length ? 6 : 0) + 12;
|
|
2002
|
+
maxWidth = Math.max(maxWidth, classDef.width);
|
|
2003
|
+
maxHeight = Math.max(maxHeight, classDef.height);
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
var columns = Math.min(4, Math.max(1, Math.ceil(Math.sqrt(diagram.classes.length))));
|
|
2007
|
+
var rows = Math.ceil(diagram.classes.length / columns);
|
|
2008
|
+
var width = OUTER_PADDING * 2 + columns * maxWidth + Math.max(0, columns - 1) * HORIZONTAL_GAP;
|
|
2009
|
+
var height = OUTER_PADDING * 2 + rows * maxHeight + Math.max(0, rows - 1) * VERTICAL_GAP;
|
|
2010
|
+
var svg = createRootSvg(width, height, 'Mermaid class diagram');
|
|
2011
|
+
|
|
2012
|
+
for (var classIndex = 0; classIndex < diagram.classes.length; classIndex += 1) {
|
|
2013
|
+
var current = diagram.classes[classIndex];
|
|
2014
|
+
var column = classIndex % columns;
|
|
2015
|
+
var row = Math.floor(classIndex / columns);
|
|
2016
|
+
current.x = OUTER_PADDING + column * (maxWidth + HORIZONTAL_GAP) + maxWidth / 2;
|
|
2017
|
+
current.y = OUTER_PADDING + row * (maxHeight + VERTICAL_GAP) + maxHeight / 2;
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
for (var relationIndex = 0; relationIndex < diagram.relations.length; relationIndex += 1) {
|
|
2021
|
+
var relation = diagram.relations[relationIndex];
|
|
2022
|
+
var fromClass = diagram.classesById[relation.left];
|
|
2023
|
+
var toClass = diagram.classesById[relation.right];
|
|
2024
|
+
var relationFrom = fromClass;
|
|
2025
|
+
var relationTo = toClass;
|
|
2026
|
+
var markerEnd = '';
|
|
2027
|
+
var dash = '';
|
|
2028
|
+
|
|
2029
|
+
if (relation.type === '<|--' || relation.type === '*--' || relation.type === 'o--') {
|
|
2030
|
+
relationFrom = toClass;
|
|
2031
|
+
relationTo = fromClass;
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
if (relation.type === '<|--') {
|
|
2035
|
+
markerEnd = 'url(#mini-mermaid-triangle-arrow)';
|
|
2036
|
+
} else if (relation.type === '*--') {
|
|
2037
|
+
markerEnd = 'url(#mini-mermaid-diamond-arrow)';
|
|
2038
|
+
} else if (relation.type === 'o--') {
|
|
2039
|
+
markerEnd = 'url(#mini-mermaid-open-diamond-arrow)';
|
|
2040
|
+
} else if (relation.type === '-->') {
|
|
2041
|
+
markerEnd = 'url(#mini-mermaid-open-arrow)';
|
|
2042
|
+
} else if (relation.type === '..>') {
|
|
2043
|
+
markerEnd = 'url(#mini-mermaid-open-arrow)';
|
|
2044
|
+
dash = '6 5';
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
var relationStart = boxAnchor(relationFrom, relationTo);
|
|
2048
|
+
var relationEnd = boxAnchor(relationTo, relationFrom);
|
|
2049
|
+
drawStraightLine(svg, relationStart.x, relationStart.y, relationEnd.x, relationEnd.y, { dash: dash, markerEnd: markerEnd });
|
|
2050
|
+
if (relation.label) {
|
|
2051
|
+
drawLabel(svg, (relationStart.x + relationEnd.x) / 2, (relationStart.y + relationEnd.y) / 2 - 10, relation.label);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
for (var drawClassIndex = 0; drawClassIndex < diagram.classes.length; drawClassIndex += 1) {
|
|
2056
|
+
var drawClass = diagram.classes[drawClassIndex];
|
|
2057
|
+
var bounds = { left: drawClass.x - drawClass.width / 2, top: drawClass.y - drawClass.height / 2 };
|
|
2058
|
+
|
|
2059
|
+
var outer = createSvg('rect');
|
|
2060
|
+
setAttrs(outer, {
|
|
2061
|
+
x: bounds.left,
|
|
2062
|
+
y: bounds.top,
|
|
2063
|
+
width: drawClass.width,
|
|
2064
|
+
height: drawClass.height,
|
|
2065
|
+
rx: 8,
|
|
2066
|
+
ry: 8,
|
|
2067
|
+
fill: 'var(--dt-bg-secondary)',
|
|
2068
|
+
stroke: 'var(--dt-border-default)',
|
|
2069
|
+
'stroke-width': 1.5,
|
|
2070
|
+
});
|
|
2071
|
+
svg.appendChild(outer);
|
|
2072
|
+
|
|
2073
|
+
var headerHeight = 34;
|
|
2074
|
+
drawStraightLine(svg, bounds.left, bounds.top + headerHeight, bounds.left + drawClass.width, bounds.top + headerHeight, {
|
|
2075
|
+
stroke: 'var(--dt-border-default)',
|
|
2076
|
+
strokeWidth: 1,
|
|
2077
|
+
});
|
|
2078
|
+
attachText(svg, drawClass.x, bounds.top + headerHeight / 2 + 1, [drawClass.name], '');
|
|
2079
|
+
|
|
2080
|
+
var contentY = bounds.top + headerHeight + 10;
|
|
2081
|
+
if (drawClass.attributes.length) {
|
|
2082
|
+
appendLeftText(svg, bounds.left + 10, contentY, drawClass.attributes, { size: 13 });
|
|
2083
|
+
contentY += drawClass.attributes.length * LINE_HEIGHT + 8;
|
|
2084
|
+
}
|
|
2085
|
+
if (drawClass.methods.length) {
|
|
2086
|
+
drawStraightLine(svg, bounds.left, contentY - 4, bounds.left + drawClass.width, contentY - 4, {
|
|
2087
|
+
stroke: 'var(--dt-border-default)',
|
|
2088
|
+
strokeWidth: 1,
|
|
2089
|
+
});
|
|
2090
|
+
appendLeftText(svg, bounds.left + 10, contentY + 4, drawClass.methods, { size: 13 });
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
return svg;
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
function ensureStateNode(graph, id, label, shape, stateKind) {
|
|
2098
|
+
var node = graph.nodesById[id];
|
|
2099
|
+
if (!node) {
|
|
2100
|
+
node = {
|
|
2101
|
+
id: id,
|
|
2102
|
+
label: label,
|
|
2103
|
+
shape: shape,
|
|
2104
|
+
groups: [],
|
|
2105
|
+
rank: 0,
|
|
2106
|
+
width: 0,
|
|
2107
|
+
height: 0,
|
|
2108
|
+
lines: [],
|
|
2109
|
+
x: 0,
|
|
2110
|
+
y: 0,
|
|
2111
|
+
order: graph.nodeOrder.length,
|
|
2112
|
+
stateKind: stateKind || '',
|
|
2113
|
+
};
|
|
2114
|
+
graph.nodesById[id] = node;
|
|
2115
|
+
graph.nodeOrder.push(id);
|
|
2116
|
+
}
|
|
2117
|
+
node.label = label;
|
|
2118
|
+
node.shape = shape;
|
|
2119
|
+
node.stateKind = stateKind || node.stateKind || '';
|
|
2120
|
+
return node;
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
function parseStateDiagram(source) {
|
|
2124
|
+
var lines = cleanedLines(source);
|
|
2125
|
+
var graph = newGraph('TD');
|
|
2126
|
+
|
|
2127
|
+
for (var i = 0; i < lines.length; i += 1) {
|
|
2128
|
+
var line = lines[i];
|
|
2129
|
+
if (i === 0 && /^stateDiagram(?:-v2)?\b/i.test(line)) {
|
|
2130
|
+
continue;
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
var match = line.match(/^(.+?)\s*-->\s*(.+?)(?:\s*:\s*(.+))?$/);
|
|
2134
|
+
if (!match) {
|
|
2135
|
+
continue;
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
var fromToken = match[1].trim();
|
|
2139
|
+
var toToken = match[2].trim();
|
|
2140
|
+
var fromNode = fromToken === '[*]' ? ensureStateNode(graph, '__state_start__', '', 'circle', 'start') : ensureStateNode(graph, fromToken, normalizeLabel(fromToken), 'rounded', '');
|
|
2141
|
+
var toNode = toToken === '[*]' ? ensureStateNode(graph, '__state_end__', '', 'circle', 'end') : ensureStateNode(graph, toToken, normalizeLabel(toToken), 'rounded', '');
|
|
2142
|
+
|
|
2143
|
+
addEdge(graph, fromNode, { label: normalizeLabel(match[3] || ''), style: 'solid', arrow: true }, toNode);
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
return graph;
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
function renderStateDiagram(source) {
|
|
2150
|
+
var svg = renderSvg(parseStateDiagram(source));
|
|
2151
|
+
svg.setAttribute('aria-label', 'Mermaid state diagram');
|
|
2152
|
+
return svg;
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
function parseGanttChart(source) {
|
|
2156
|
+
var lines = cleanedLines(source);
|
|
2157
|
+
var title = '';
|
|
2158
|
+
var sections = [];
|
|
2159
|
+
var sectionsByName = Object.create(null);
|
|
2160
|
+
var tasks = [];
|
|
2161
|
+
var tasksById = Object.create(null);
|
|
2162
|
+
var currentSection = null;
|
|
2163
|
+
var nextDateSlot = 0;
|
|
2164
|
+
var dateSlots = Object.create(null);
|
|
2165
|
+
|
|
2166
|
+
function ensureSection(name) {
|
|
2167
|
+
if (!sectionsByName[name]) {
|
|
2168
|
+
sectionsByName[name] = { name: name, tasks: [], index: sections.length };
|
|
2169
|
+
sections.push(sectionsByName[name]);
|
|
2170
|
+
}
|
|
2171
|
+
return sectionsByName[name];
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
for (var i = 0; i < lines.length; i += 1) {
|
|
2175
|
+
var line = lines[i];
|
|
2176
|
+
if (i === 0 && /^gantt\b/i.test(line)) {
|
|
2177
|
+
continue;
|
|
2178
|
+
}
|
|
2179
|
+
var titleMatch = line.match(/^title\s+(.+)$/i);
|
|
2180
|
+
if (titleMatch) {
|
|
2181
|
+
title = normalizeLabel(titleMatch[1]);
|
|
2182
|
+
continue;
|
|
2183
|
+
}
|
|
2184
|
+
if (/^dateFormat\b/i.test(line)) {
|
|
2185
|
+
continue;
|
|
2186
|
+
}
|
|
2187
|
+
var sectionMatch = line.match(/^section\s+(.+)$/i);
|
|
2188
|
+
if (sectionMatch) {
|
|
2189
|
+
currentSection = ensureSection(normalizeLabel(sectionMatch[1]));
|
|
2190
|
+
continue;
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
var separator = line.indexOf(':');
|
|
2194
|
+
if (separator === -1) {
|
|
2195
|
+
continue;
|
|
2196
|
+
}
|
|
2197
|
+
if (!currentSection) {
|
|
2198
|
+
currentSection = ensureSection('Tasks');
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
var taskName = normalizeLabel(line.slice(0, separator));
|
|
2202
|
+
var fields = line.slice(separator + 1).split(',');
|
|
2203
|
+
var taskId = stripQuotes((fields[0] || '').trim()) || ('task_' + (tasks.length + 1));
|
|
2204
|
+
var startExpr = (fields[1] || '').trim();
|
|
2205
|
+
var durationField = (fields[2] || fields[fields.length - 1] || '').trim();
|
|
2206
|
+
var durationMatch = durationField.match(/(\d+)\s*d/i);
|
|
2207
|
+
var task = {
|
|
2208
|
+
id: taskId,
|
|
2209
|
+
name: taskName,
|
|
2210
|
+
startExpr: startExpr,
|
|
2211
|
+
duration: durationMatch ? Math.max(1, Number(durationMatch[1])) : 1,
|
|
2212
|
+
section: currentSection,
|
|
2213
|
+
start: null,
|
|
2214
|
+
end: null,
|
|
2215
|
+
};
|
|
2216
|
+
tasks.push(task);
|
|
2217
|
+
tasksById[task.id] = task;
|
|
2218
|
+
currentSection.tasks.push(task);
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
var autoCursor = 0;
|
|
2222
|
+
for (var pass = 0; pass < tasks.length + 1; pass += 1) {
|
|
2223
|
+
for (var taskIndex = 0; taskIndex < tasks.length; taskIndex += 1) {
|
|
2224
|
+
var task = tasks[taskIndex];
|
|
2225
|
+
if (task.start !== null) {
|
|
2226
|
+
continue;
|
|
2227
|
+
}
|
|
2228
|
+
var afterMatch = task.startExpr.match(/^after\s+(.+)$/i);
|
|
2229
|
+
if (afterMatch) {
|
|
2230
|
+
var dependency = tasksById[afterMatch[1].trim()];
|
|
2231
|
+
if (!dependency || dependency.end === null) {
|
|
2232
|
+
continue;
|
|
2233
|
+
}
|
|
2234
|
+
task.start = dependency.end + 1;
|
|
2235
|
+
} else if (task.startExpr) {
|
|
2236
|
+
if (dateSlots[task.startExpr] === undefined) {
|
|
2237
|
+
dateSlots[task.startExpr] = nextDateSlot;
|
|
2238
|
+
nextDateSlot += task.duration + 1;
|
|
2239
|
+
}
|
|
2240
|
+
task.start = dateSlots[task.startExpr];
|
|
2241
|
+
} else {
|
|
2242
|
+
task.start = autoCursor;
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
task.end = task.start + task.duration;
|
|
2246
|
+
autoCursor = Math.max(autoCursor, task.end + 1);
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
for (var fallbackIndex = 0; fallbackIndex < tasks.length; fallbackIndex += 1) {
|
|
2251
|
+
if (tasks[fallbackIndex].start === null) {
|
|
2252
|
+
tasks[fallbackIndex].start = autoCursor;
|
|
2253
|
+
tasks[fallbackIndex].end = autoCursor + tasks[fallbackIndex].duration;
|
|
2254
|
+
autoCursor = tasks[fallbackIndex].end + 1;
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
return { title: title, sections: sections, tasks: tasks };
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
function renderGanttChart(source) {
|
|
2262
|
+
var gantt = parseGanttChart(source);
|
|
2263
|
+
if (!gantt.tasks.length) {
|
|
2264
|
+
throw new Error('Gantt chart is empty');
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
var labelWidth = 0;
|
|
2268
|
+
var maxEnd = 0;
|
|
2269
|
+
for (var i = 0; i < gantt.tasks.length; i += 1) {
|
|
2270
|
+
labelWidth = Math.max(labelWidth, measureLine(gantt.tasks[i].name) + 18);
|
|
2271
|
+
maxEnd = Math.max(maxEnd, gantt.tasks[i].end || 0);
|
|
2272
|
+
}
|
|
2273
|
+
for (var sectionIndex = 0; sectionIndex < gantt.sections.length; sectionIndex += 1) {
|
|
2274
|
+
labelWidth = Math.max(labelWidth, measureLine(gantt.sections[sectionIndex].name) + 18);
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
labelWidth = Math.max(120, labelWidth);
|
|
2278
|
+
var scaleX = 28;
|
|
2279
|
+
var titleHeight = gantt.title ? 30 : 0;
|
|
2280
|
+
var rows = gantt.tasks.length + gantt.sections.length;
|
|
2281
|
+
var width = OUTER_PADDING * 2 + labelWidth + 24 + Math.max(4, maxEnd + 1) * scaleX;
|
|
2282
|
+
var height = OUTER_PADDING * 2 + titleHeight + 28 + rows * 28 + 20;
|
|
2283
|
+
var svg = createRootSvg(width, height, 'Mermaid gantt chart');
|
|
2284
|
+
var chartLeft = OUTER_PADDING + labelWidth + 20;
|
|
2285
|
+
var currentY = OUTER_PADDING;
|
|
2286
|
+
|
|
2287
|
+
if (gantt.title) {
|
|
2288
|
+
appendText(svg, width / 2, currentY + 10, gantt.title, { anchor: 'middle', baseline: 'middle', weight: '600' });
|
|
2289
|
+
currentY += titleHeight;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
var axisY = currentY + 14;
|
|
2293
|
+
drawStraightLine(svg, chartLeft, axisY + 10, width - OUTER_PADDING, axisY + 10, { stroke: 'var(--dt-border-default)', strokeWidth: 1 });
|
|
2294
|
+
for (var tick = 0; tick <= maxEnd + 1; tick += 1) {
|
|
2295
|
+
var tickX = chartLeft + tick * scaleX;
|
|
2296
|
+
drawStraightLine(svg, tickX, axisY + 10, tickX, height - OUTER_PADDING, { stroke: 'var(--dt-border-default)', strokeWidth: 0.8 });
|
|
2297
|
+
appendText(svg, tickX + scaleX / 2, axisY, String(tick + 1), {
|
|
2298
|
+
anchor: 'middle',
|
|
2299
|
+
baseline: 'middle',
|
|
2300
|
+
size: 11,
|
|
2301
|
+
fill: 'var(--dt-text-secondary, var(--dt-text-primary))',
|
|
2302
|
+
});
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
currentY = axisY + 28;
|
|
2306
|
+
for (var drawSectionIndex = 0; drawSectionIndex < gantt.sections.length; drawSectionIndex += 1) {
|
|
2307
|
+
var section = gantt.sections[drawSectionIndex];
|
|
2308
|
+
appendText(svg, OUTER_PADDING, currentY, section.name, { anchor: 'start', baseline: 'middle', weight: '600', size: 13 });
|
|
2309
|
+
drawStraightLine(svg, OUTER_PADDING, currentY + 10, width - OUTER_PADDING, currentY + 10, {
|
|
2310
|
+
stroke: 'var(--dt-border-default)',
|
|
2311
|
+
strokeWidth: 1,
|
|
2312
|
+
});
|
|
2313
|
+
currentY += 18;
|
|
2314
|
+
|
|
2315
|
+
for (var taskIndex = 0; taskIndex < section.tasks.length; taskIndex += 1) {
|
|
2316
|
+
var task = section.tasks[taskIndex];
|
|
2317
|
+
var barX = chartLeft + task.start * scaleX + 2;
|
|
2318
|
+
var barWidth = Math.max(18, task.duration * scaleX - 4);
|
|
2319
|
+
appendText(svg, OUTER_PADDING + 4, currentY + 8, task.name, { anchor: 'start', baseline: 'middle', size: 13 });
|
|
2320
|
+
|
|
2321
|
+
var bar = createSvg('rect');
|
|
2322
|
+
setAttrs(bar, {
|
|
2323
|
+
x: barX,
|
|
2324
|
+
y: currentY - 2,
|
|
2325
|
+
width: barWidth,
|
|
2326
|
+
height: 18,
|
|
2327
|
+
rx: 6,
|
|
2328
|
+
ry: 6,
|
|
2329
|
+
fill: 'var(--dt-bg-secondary)',
|
|
2330
|
+
'fill-opacity': String(Math.max(0.45, 0.95 - drawSectionIndex * 0.12)),
|
|
2331
|
+
stroke: 'var(--dt-text-primary)',
|
|
2332
|
+
'stroke-width': 1,
|
|
2333
|
+
});
|
|
2334
|
+
svg.appendChild(bar);
|
|
2335
|
+
|
|
2336
|
+
if (barWidth > measureLine(task.name) + 12) {
|
|
2337
|
+
appendText(svg, barX + barWidth / 2, currentY + 7, task.name, { anchor: 'middle', baseline: 'middle', size: 11 });
|
|
2338
|
+
}
|
|
2339
|
+
currentY += 28;
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
return svg;
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
function parseErDiagram(source) {
|
|
2347
|
+
var lines = cleanedLines(source);
|
|
2348
|
+
var entities = [];
|
|
2349
|
+
var entitiesById = Object.create(null);
|
|
2350
|
+
var relationships = [];
|
|
2351
|
+
var currentEntity = null;
|
|
2352
|
+
|
|
2353
|
+
function ensureEntity(id) {
|
|
2354
|
+
if (!entitiesById[id]) {
|
|
2355
|
+
entitiesById[id] = { id: id, name: id, attributes: [], width: 0, height: 0, x: 0, y: 0 };
|
|
2356
|
+
entities.push(entitiesById[id]);
|
|
2357
|
+
}
|
|
2358
|
+
return entitiesById[id];
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
for (var i = 0; i < lines.length; i += 1) {
|
|
2362
|
+
var line = lines[i];
|
|
2363
|
+
if (i === 0 && /^erDiagram\b/i.test(line)) {
|
|
2364
|
+
continue;
|
|
2365
|
+
}
|
|
2366
|
+
if (currentEntity) {
|
|
2367
|
+
if (line === '}') {
|
|
2368
|
+
currentEntity = null;
|
|
2369
|
+
continue;
|
|
2370
|
+
}
|
|
2371
|
+
var attrMatch = line.match(/^([A-Za-z0-9_.-]+)\s+([A-Za-z0-9_.-]+)(?:\s+(PK|FK))?$/i);
|
|
2372
|
+
if (attrMatch) {
|
|
2373
|
+
currentEntity.attributes.push({ type: attrMatch[1], name: attrMatch[2], key: attrMatch[3] || '' });
|
|
2374
|
+
}
|
|
2375
|
+
continue;
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
var entityMatch = line.match(/^([A-Za-z0-9_.-]+)\s*\{$/);
|
|
2379
|
+
if (entityMatch) {
|
|
2380
|
+
currentEntity = ensureEntity(entityMatch[1]);
|
|
2381
|
+
continue;
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
var relationMatch = line.match(/^([A-Za-z0-9_.-]+)\s+(\|\||o\{|\|\{|o\|)\-\-(\|\||o\{|\|\{|o\|)\s+([A-Za-z0-9_.-]+)(?:\s*:\s*(.+))?$/);
|
|
2385
|
+
if (relationMatch) {
|
|
2386
|
+
ensureEntity(relationMatch[1]);
|
|
2387
|
+
ensureEntity(relationMatch[4]);
|
|
2388
|
+
relationships.push({
|
|
2389
|
+
from: relationMatch[1],
|
|
2390
|
+
to: relationMatch[4],
|
|
2391
|
+
fromCardinality: relationMatch[2],
|
|
2392
|
+
toCardinality: relationMatch[3],
|
|
2393
|
+
label: normalizeLabel(relationMatch[5] || ''),
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
return { entities: entities, entitiesById: entitiesById, relationships: relationships };
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
function drawErToken(parent, token, x, y, angle) {
|
|
2402
|
+
var dx = Math.cos(angle);
|
|
2403
|
+
var dy = Math.sin(angle);
|
|
2404
|
+
var nx = -dy;
|
|
2405
|
+
var ny = dx;
|
|
2406
|
+
|
|
2407
|
+
function drawPart(kind, offset) {
|
|
2408
|
+
var cx = x + dx * offset;
|
|
2409
|
+
var cy = y + dy * offset;
|
|
2410
|
+
|
|
2411
|
+
if (kind === 'o') {
|
|
2412
|
+
var circle = createSvg('circle');
|
|
2413
|
+
setAttrs(circle, {
|
|
2414
|
+
cx: cx,
|
|
2415
|
+
cy: cy,
|
|
2416
|
+
r: 4,
|
|
2417
|
+
fill: 'var(--dt-bg-canvas)',
|
|
2418
|
+
stroke: 'var(--dt-text-primary)',
|
|
2419
|
+
'stroke-width': 1.2,
|
|
2420
|
+
});
|
|
2421
|
+
parent.appendChild(circle);
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
if (kind === '|') {
|
|
2426
|
+
drawStraightLine(parent, cx - nx * 7, cy - ny * 7, cx + nx * 7, cy + ny * 7, { stroke: 'var(--dt-text-primary)', strokeWidth: 1.6 });
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
if (kind === '{') {
|
|
2431
|
+
var baseX = cx - dx * 5;
|
|
2432
|
+
var baseY = cy - dy * 5;
|
|
2433
|
+
drawStraightLine(parent, baseX, baseY, cx + nx * 7, cy + ny * 7, { stroke: 'var(--dt-text-primary)', strokeWidth: 1.3 });
|
|
2434
|
+
drawStraightLine(parent, baseX, baseY, cx, cy, { stroke: 'var(--dt-text-primary)', strokeWidth: 1.3 });
|
|
2435
|
+
drawStraightLine(parent, baseX, baseY, cx - nx * 7, cy - ny * 7, { stroke: 'var(--dt-text-primary)', strokeWidth: 1.3 });
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
drawPart(token.charAt(0), 6);
|
|
2440
|
+
drawPart(token.charAt(1), 15);
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
function renderErDiagram(source) {
|
|
2444
|
+
var diagram = parseErDiagram(source);
|
|
2445
|
+
if (!diagram.entities.length) {
|
|
2446
|
+
throw new Error('ER diagram is empty');
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
var maxWidth = 0;
|
|
2450
|
+
var maxHeight = 0;
|
|
2451
|
+
for (var i = 0; i < diagram.entities.length; i += 1) {
|
|
2452
|
+
var entity = diagram.entities[i];
|
|
2453
|
+
var entityWidth = Math.max(140, measureLine(entity.name) + 28);
|
|
2454
|
+
for (var attributeIndex = 0; attributeIndex < entity.attributes.length; attributeIndex += 1) {
|
|
2455
|
+
var attribute = entity.attributes[attributeIndex];
|
|
2456
|
+
var attributeLabel = attribute.type + ' ' + attribute.name + (attribute.key ? ' ' + attribute.key : '');
|
|
2457
|
+
entityWidth = Math.max(entityWidth, measureLine(attributeLabel) + 24);
|
|
2458
|
+
}
|
|
2459
|
+
entity.width = Math.ceil(entityWidth);
|
|
2460
|
+
entity.height = 36 + Math.max(1, entity.attributes.length) * LINE_HEIGHT + 18;
|
|
2461
|
+
maxWidth = Math.max(maxWidth, entity.width);
|
|
2462
|
+
maxHeight = Math.max(maxHeight, entity.height);
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
var columns = Math.min(3, Math.max(1, Math.ceil(Math.sqrt(diagram.entities.length))));
|
|
2466
|
+
var rows = Math.ceil(diagram.entities.length / columns);
|
|
2467
|
+
var width = OUTER_PADDING * 2 + columns * maxWidth + Math.max(0, columns - 1) * HORIZONTAL_GAP;
|
|
2468
|
+
var height = OUTER_PADDING * 2 + rows * maxHeight + Math.max(0, rows - 1) * VERTICAL_GAP;
|
|
2469
|
+
var svg = createRootSvg(width, height, 'Mermaid ER diagram');
|
|
2470
|
+
|
|
2471
|
+
for (var entityIndex = 0; entityIndex < diagram.entities.length; entityIndex += 1) {
|
|
2472
|
+
var currentEntity = diagram.entities[entityIndex];
|
|
2473
|
+
var column = entityIndex % columns;
|
|
2474
|
+
var row = Math.floor(entityIndex / columns);
|
|
2475
|
+
currentEntity.x = OUTER_PADDING + column * (maxWidth + HORIZONTAL_GAP) + maxWidth / 2;
|
|
2476
|
+
currentEntity.y = OUTER_PADDING + row * (maxHeight + VERTICAL_GAP) + maxHeight / 2;
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2479
|
+
for (var relationIndex = 0; relationIndex < diagram.relationships.length; relationIndex += 1) {
|
|
2480
|
+
var relation = diagram.relationships[relationIndex];
|
|
2481
|
+
var fromEntity = diagram.entitiesById[relation.from];
|
|
2482
|
+
var toEntity = diagram.entitiesById[relation.to];
|
|
2483
|
+
var start = boxAnchor(fromEntity, toEntity);
|
|
2484
|
+
var end = boxAnchor(toEntity, fromEntity);
|
|
2485
|
+
|
|
2486
|
+
drawStraightLine(svg, start.x, start.y, end.x, end.y, { stroke: 'var(--dt-text-primary)', strokeWidth: 1.4 });
|
|
2487
|
+
|
|
2488
|
+
var forwardAngle = Math.atan2(end.y - start.y, end.x - start.x);
|
|
2489
|
+
drawErToken(svg, relation.fromCardinality, start.x, start.y, forwardAngle);
|
|
2490
|
+
drawErToken(svg, relation.toCardinality, end.x, end.y, forwardAngle + Math.PI);
|
|
2491
|
+
if (relation.label) {
|
|
2492
|
+
drawLabel(svg, (start.x + end.x) / 2, (start.y + end.y) / 2 - 10, relation.label);
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
for (var drawEntityIndex = 0; drawEntityIndex < diagram.entities.length; drawEntityIndex += 1) {
|
|
2497
|
+
var drawEntity = diagram.entities[drawEntityIndex];
|
|
2498
|
+
var bounds = { left: drawEntity.x - drawEntity.width / 2, top: drawEntity.y - drawEntity.height / 2 };
|
|
2499
|
+
|
|
2500
|
+
var outer = createSvg('rect');
|
|
2501
|
+
setAttrs(outer, {
|
|
2502
|
+
x: bounds.left,
|
|
2503
|
+
y: bounds.top,
|
|
2504
|
+
width: drawEntity.width,
|
|
2505
|
+
height: drawEntity.height,
|
|
2506
|
+
rx: 8,
|
|
2507
|
+
ry: 8,
|
|
2508
|
+
fill: 'var(--dt-bg-canvas)',
|
|
2509
|
+
stroke: 'var(--dt-border-default)',
|
|
2510
|
+
'stroke-width': 1.5,
|
|
2511
|
+
});
|
|
2512
|
+
svg.appendChild(outer);
|
|
2513
|
+
|
|
2514
|
+
var header = createSvg('rect');
|
|
2515
|
+
setAttrs(header, {
|
|
2516
|
+
x: bounds.left,
|
|
2517
|
+
y: bounds.top,
|
|
2518
|
+
width: drawEntity.width,
|
|
2519
|
+
height: 34,
|
|
2520
|
+
rx: 8,
|
|
2521
|
+
ry: 8,
|
|
2522
|
+
fill: 'var(--dt-bg-secondary)',
|
|
2523
|
+
stroke: 'var(--dt-border-default)',
|
|
2524
|
+
'stroke-width': 1.2,
|
|
2525
|
+
});
|
|
2526
|
+
svg.appendChild(header);
|
|
2527
|
+
|
|
2528
|
+
drawStraightLine(svg, bounds.left, bounds.top + 34, bounds.left + drawEntity.width, bounds.top + 34, {
|
|
2529
|
+
stroke: 'var(--dt-border-default)',
|
|
2530
|
+
strokeWidth: 1,
|
|
2531
|
+
});
|
|
2532
|
+
attachText(svg, drawEntity.x, bounds.top + 18, [drawEntity.name], '');
|
|
2533
|
+
|
|
2534
|
+
var attrLines = [];
|
|
2535
|
+
for (var attrIndex = 0; attrIndex < drawEntity.attributes.length; attrIndex += 1) {
|
|
2536
|
+
var entityAttr = drawEntity.attributes[attrIndex];
|
|
2537
|
+
attrLines.push(entityAttr.type + ' ' + entityAttr.name + (entityAttr.key ? ' ' + entityAttr.key : ''));
|
|
2538
|
+
}
|
|
2539
|
+
appendLeftText(svg, bounds.left + 10, bounds.top + 42, attrLines.length ? attrLines : ['(no attributes)'], { size: 13 });
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
return svg;
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
function renderBlock(pre) {
|
|
2546
|
+
var source = pre.textContent || '';
|
|
2547
|
+
if (!source.trim()) {
|
|
2548
|
+
return;
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
var firstLine = source.trim().split('\n')[0].trim();
|
|
2552
|
+
var svg;
|
|
2553
|
+
|
|
2554
|
+
if (/^sequenceDiagram/i.test(firstLine)) {
|
|
2555
|
+
svg = renderSequenceDiagram(source);
|
|
2556
|
+
} else if (/^classDiagram/i.test(firstLine)) {
|
|
2557
|
+
svg = renderClassDiagram(source);
|
|
2558
|
+
} else if (/^stateDiagram/i.test(firstLine)) {
|
|
2559
|
+
svg = renderStateDiagram(source);
|
|
2560
|
+
} else if (/^gantt/i.test(firstLine)) {
|
|
2561
|
+
svg = renderGanttChart(source);
|
|
2562
|
+
} else if (/^erDiagram/i.test(firstLine)) {
|
|
2563
|
+
svg = renderErDiagram(source);
|
|
2564
|
+
} else {
|
|
2565
|
+
svg = renderSvg(parseMermaid(source));
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
pre.innerHTML = '';
|
|
2569
|
+
pre.style.whiteSpace = 'normal';
|
|
2570
|
+
pre.style.overflowX = 'auto';
|
|
2571
|
+
pre.setAttribute('data-mini-mermaid-rendered', 'true');
|
|
2572
|
+
pre.appendChild(svg);
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
function renderAll() {
|
|
2576
|
+
if (!document || typeof document.querySelectorAll !== 'function') {
|
|
2577
|
+
return;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
var blocks = document.querySelectorAll('pre.mermaid');
|
|
2581
|
+
for (var i = 0; i < blocks.length; i += 1) {
|
|
2582
|
+
var block = blocks[i];
|
|
2583
|
+
if (!block || block.getAttribute('data-mini-mermaid-rendered') === 'true') {
|
|
2584
|
+
continue;
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
try {
|
|
2588
|
+
renderBlock(block);
|
|
2589
|
+
} catch (_error) { console.error("[mini-mermaid] renderBlock error:", _error, "source:", block.textContent && block.textContent.substring(0, 80)); block.removeAttribute('data-mini-mermaid-rendered');
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
function init() {
|
|
2595
|
+
if (document.readyState === 'loading') {
|
|
2596
|
+
document.addEventListener('DOMContentLoaded', function onReady() {
|
|
2597
|
+
document.removeEventListener('DOMContentLoaded', onReady);
|
|
2598
|
+
renderAll();
|
|
2599
|
+
});
|
|
2600
|
+
return;
|
|
2601
|
+
}
|
|
2602
|
+
renderAll();
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
window.__aikit_initMermaid = function() {
|
|
2606
|
+
for (var i = 0; i < MAX_RENDER_PASSES; i += 1) {
|
|
2607
|
+
init();
|
|
2608
|
+
}
|
|
2609
|
+
};
|
|
2610
|
+
|
|
2611
|
+
window.__aikitMiniMermaid = {
|
|
2612
|
+
renderAll: renderAll,
|
|
2613
|
+
parse: parseMermaid,
|
|
2614
|
+
};
|
|
2615
|
+
|
|
2616
|
+
init();
|
|
2617
|
+
})();`;function Tu(){let e;return()=>{if(e!==void 0)return e;try{e=c(a(import.meta.url).resolve(`mermaid/dist/mermaid.min.js`),`utf-8`)}catch{e=``}return e}}const Eu={id:`mermaid`,cdn:`https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js`,inlineSource:wu,initScript:`window.__aikit_initMermaid = function() {
|
|
242
2618
|
if (typeof mermaid === 'undefined') return;
|
|
243
2619
|
if (document.readyState === 'loading') {
|
|
244
2620
|
document.addEventListener('DOMContentLoaded', window.__aikit_initMermaid);
|
|
@@ -251,8 +2627,8 @@ Complements: \`symbol\` (single lookup), \`trace\` (call-chain AST), \`blast_rad
|
|
|
251
2627
|
}
|
|
252
2628
|
mermaid.initialize({ startOnLoad: false, theme: theme });
|
|
253
2629
|
mermaid.run();
|
|
254
|
-
};`,onload:`window.__aikit_initMermaid&&window.__aikit_initMermaid()`,local:{route:`/vendor/mermaid.min.js`,getSource:
|
|
255
|
-
`)}function
|
|
2630
|
+
};`,onload:`window.__aikit_initMermaid&&window.__aikit_initMermaid()`,local:{route:`/vendor/mermaid.min.js`,getSource:Tu()}},Du=new Map([[`mermaid`,Eu]]);function Ou(e,t){if(!e?.length)return;let n=[];for(let r of e){let e=Du.get(r);if(e){if(t===`browser`){n.push({inlineSource:e.inlineSource});continue}if(t===`mcp-app`){n.push({inlineSource:e.inlineSource});continue}n.push({src:e.cdn,initScript:e.initScript,onload:e.onload})}}return n.length>0?n:void 0}function ku(){let e=[];for(let t of Du.values())t.local&&e.push({route:t.local.route,getSource:t.local.getSource,cdn:t.cdn});return e}const Au=300*1e3;let X=null;process.on(`exit`,()=>{let e=X;if(e){try{e.close()}catch{}X=null}});function ju(e){let t=typeof e.description==`string`&&e.description.trim().length>0?`<p class="present-surface-description">${G(e.description)}</p>`:``;return[`<h1 class="present-surface-title">${G(e.title)}</h1>`,t].filter(Boolean).join(`
|
|
2631
|
+
`)}function Mu(e,t){return e.length===0?``:`
|
|
256
2632
|
<section class="present-surface-actions">
|
|
257
2633
|
<div class="present-action-bar">${e.map(e=>{let t=G(e.id),n=G(e.label);return e.type===`select`||e.type===`multi-select`?`<label class="present-action-field"><span>${n}</span><select data-action-id="${t}"${e.type===`multi-select`?` multiple`:``}>${(e.options??[]).map(e=>`<option value="${G(e.value)}">${G(e.label)}</option>`).join(``)}</select></label>`:`<button type="button" class="present-action-btn present-action-${G(e.variant??`default`)}" data-action-id="${t}" data-action-label="${n}">${n}</button>`}).join(``)}</div>
|
|
258
2634
|
<p id="present-action-status" class="present-action-status" aria-live="polite"></p>
|
|
@@ -379,8 +2755,8 @@ Complements: \`symbol\` (single lookup), \`trace\` (call-chain AST), \`blast_rad
|
|
|
379
2755
|
navigator.sendBeacon('/callback', new Blob([body], { type: 'application/json' }));
|
|
380
2756
|
});
|
|
381
2757
|
})();
|
|
382
|
-
<\/script>`}function
|
|
383
|
-
`)}function
|
|
2758
|
+
<\/script>`}function Nu(e){try{In(process.platform===`win32`?`start "" "${e}"`:process.platform===`darwin`?`open "${e}"`:`xdg-open "${e}"`)}catch{}}function Pu(e,t,n){let r=[e.title];return e.description&&r.push(e.description),r.push(``,`Opened in browser at ${t}`,n),r.join(`
|
|
2759
|
+
`)}function Fu(e){return e.some(e=>e.id!==`__dismiss`)}function Iu(e){return e.replace(`</head>`,`<style>
|
|
384
2760
|
.toolbar, .badge, body > header, body > footer { display: none !important; }
|
|
385
2761
|
body { padding-top: 0 !important; margin-top: 0 !important; }
|
|
386
2762
|
</style>
|
|
@@ -391,8 +2767,8 @@ window.addEventListener('message', function(e) {
|
|
|
391
2767
|
}
|
|
392
2768
|
});
|
|
393
2769
|
<\/script>
|
|
394
|
-
</body>`)}function
|
|
395
|
-
`);return{content:[{type:`text`,text:`[ERROR:VALIDATION] Template "${t.id}" data validation failed:\n${e}\n\nExpected schema: ${JSON.stringify(t.inputSchema,null,2)}`}],structuredContent:{kind:`error`,error:{code:`VIEWER_DATA_INVALID`,message:`Template "${t.id}" data validation failed:\n${e}\n\nExpected schema: ${JSON.stringify(t.inputSchema,null,2)}`}},isError:!0}}}let n=
|
|
2770
|
+
</body>`)}function Lu(){let e=X;e&&(e.close(),X=null)}async function Ru(e,t){if(Uu(`<html><body style="font-family:system-ui;padding:2rem;color:#888;text-align:center"><p>Content opened in browser window.</p></body></html>`),e.data!=null&&t.inputSchema){let n=jt({data:e.data,schema:t.inputSchema});if(!n.valid){let e=n.errors.map(e=>` ${e.path}: ${e.message}${e.expected?` (expected: ${e.expected}, got: ${e.received})`:``}`).join(`
|
|
2771
|
+
`);return{content:[{type:`text`,text:`[ERROR:VALIDATION] Template "${t.id}" data validation failed:\n${e}\n\nExpected schema: ${JSON.stringify(t.inputSchema,null,2)}`}],structuredContent:{kind:`error`,error:{code:`VIEWER_DATA_INVALID`,message:`Template "${t.id}" data validation failed:\n${e}\n\nExpected schema: ${JSON.stringify(t.inputSchema,null,2)}`}},isError:!0}}}let n=Iu($l(t.resolveHtml(),e.data??null,t.injectId,t.transformData)),r=Bn({title:e.title,html:[ju(e),`<div class="present-viewer-container">`,` <iframe class="present-viewer-iframe" src="/viewer" sandbox="allow-scripts allow-same-origin"></iframe>`,` <script>
|
|
396
2772
|
(function() {
|
|
397
2773
|
const iframe = document.querySelector('.present-viewer-iframe');
|
|
398
2774
|
if (!iframe) return;
|
|
@@ -408,21 +2784,32 @@ window.addEventListener('message', function(e) {
|
|
|
408
2784
|
})();
|
|
409
2785
|
<\/script>`,`</div>`].join(`
|
|
410
2786
|
`),css:[[`.present-viewer-container {`,` flex: 1;`,` min-height: calc(100vh - 12rem);`,` position: relative;`,` border-radius: 0.75rem;`,` overflow: hidden;`,` border: none;`,`}`,`.present-viewer-iframe {`,` border: none;`,` width: 100%;`,` height: 100%;`,` position: absolute;`,` inset: 0;`,` border-radius: inherit;`,`}`].join(`
|
|
411
|
-
`)],islands:[],payload:void 0,nonce:``,generatedAt:new Date().toISOString(),colorScheme:e.colorScheme,lang:e.lang,dir:e.dir,transport:`browser`,exportPolicy:`local-interactive`}),i=``;try{
|
|
412
|
-
`),css:t.css,islands:t.islands,payload:t.payload,nonce:t.nonce,generatedAt:new Date().toISOString(),colorScheme:e.colorScheme,lang:e.lang,dir:e.dir,transport:`browser`,exportPolicy:t.exportPolicy,headScripts:r}),o=``,s=``;try{let e=X;e&&(e.close(),X=null),o=await new Promise((e,t)=>{let r=Yn((e,t)=>{if(e.method===`OPTIONS`){t.writeHead(204,{"Access-Control-Allow-Origin":s||`*`,"Access-Control-Allow-Methods":`POST`,"Access-Control-Allow-Headers":`Content-Type`}),t.end();return}if(e.method===`POST`&&e.url===`/callback`){n.handle(e,t,s);return}let r=i.find(t=>e.url===t.route);if(r){let e=r.getSource();if(e){t.writeHead(200,{"Content-Type":`application/javascript; charset=utf-8`,"Cache-Control":`public, max-age=86400`}),t.end(e);return}t.writeHead(302,{Location:r.cdn}),t.end();return}t.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),t.end(a)});r.listen(0,`127.0.0.1`,()=>{let n=r.address();if(typeof n!=`object`||!n){t(Error(`Failed to resolve local browser address`));return}X=r,s=`http://127.0.0.1:${n.port}`,e(s)})})}catch(t){return{content:[{type:`text`,text:`${e.title}\n\nUnable to start local browser transport.`}],structuredContent:n.settleError(`BROWSER_START_FAILED`,t instanceof Error?t.message:`Unable to start browser transport`),isError:!0}}if(
|
|
413
|
-
`)}function
|
|
414
|
-
`)}const
|
|
2787
|
+
`)],islands:[],payload:void 0,nonce:``,generatedAt:new Date().toISOString(),colorScheme:e.colorScheme,lang:e.lang,dir:e.dir,transport:`browser`,exportPolicy:`local-interactive`}),i=``;try{Lu(),i=await new Promise((e,t)=>{let i=Yn((e,t)=>{if(e.url===`/viewer`){t.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),t.end(n);return}t.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),t.end(r)});i.listen(0,`127.0.0.1`,()=>{let n=i.address();if(typeof n!=`object`||!n){t(Error(`Failed to resolve local browser address`));return}X=i,e(`http://127.0.0.1:${n.port}`)})})}catch(t){return{content:[{type:`text`,text:`${e.title}\n\nUnable to start local browser transport.`}],structuredContent:{kind:`error`,error:{code:`BROWSER_START_FAILED`,message:t instanceof Error?t.message:`Unable to start browser transport`}},isError:!0}}return Nu(i),{content:[{type:`text`,text:Pu(e,i,`Rendered.`)}],structuredContent:{kind:`rendered`,reason:`no-response-required`}}}async function zu(e){Uu(`<html><body style="font-family:system-ui;padding:2rem;color:#888;text-align:center"><p>Content opened in browser window.</p></body></html>`);let t=Jn(e,{transport:`browser`,registry:xu,blockVendorScripts:Pl()}),n=Gl(t.surfaceId,t.actions),r=Ou(t.vendorScripts,`browser`),i=ku(),a=Bn({title:e.title,html:[ju(e),t.html,Mu(t.actions,n.nonce)].filter(Boolean).join(`
|
|
2788
|
+
`),css:t.css,islands:t.islands,payload:t.payload,nonce:t.nonce,generatedAt:new Date().toISOString(),colorScheme:e.colorScheme,lang:e.lang,dir:e.dir,transport:`browser`,exportPolicy:t.exportPolicy,headScripts:r}),o=``,s=``;try{let e=X;e&&(e.close(),X=null),o=await new Promise((e,t)=>{let r=Yn((e,t)=>{if(e.method===`OPTIONS`){t.writeHead(204,{"Access-Control-Allow-Origin":s||`*`,"Access-Control-Allow-Methods":`POST`,"Access-Control-Allow-Headers":`Content-Type`}),t.end();return}if(e.method===`POST`&&e.url===`/callback`){n.handle(e,t,s);return}let r=i.find(t=>e.url===t.route);if(r){let e=r.getSource();if(e){t.writeHead(200,{"Content-Type":`application/javascript; charset=utf-8`,"Cache-Control":`public, max-age=86400`}),t.end(e);return}t.writeHead(302,{Location:r.cdn}),t.end();return}t.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),t.end(a)});r.listen(0,`127.0.0.1`,()=>{let n=r.address();if(typeof n!=`object`||!n){t(Error(`Failed to resolve local browser address`));return}X=r,s=`http://127.0.0.1:${n.port}`,e(s)})})}catch(t){return{content:[{type:`text`,text:`${e.title}\n\nUnable to start local browser transport.`}],structuredContent:n.settleError(`BROWSER_START_FAILED`,t instanceof Error?t.message:`Unable to start browser transport`),isError:!0}}if(Nu(o),!Fu(t.actions)){let t=X;return t&&(n.promise.finally(()=>{X===t&&(t.close(),X=null)}),setTimeout(()=>{X===t&&(t.close(),X=null)},Au)),{content:[{type:`text`,text:Pu(e,o,`Rendered.`)}],structuredContent:{kind:`rendered`,reason:`no-response-required`}}}let c=await Promise.race([n.promise,new Promise(e=>{setTimeout(()=>e(n.settleTimeout(Au)),Au)})]),l=X;l&&(l.close(),X=null);let u=c.kind===`result`?`Selected action: ${c.result.actionId}`:c.kind===`timeout`?`Timed out after ${c.waitedMs}ms.`:c.kind===`cancelled`?`Cancelled: ${c.reason}.`:c.kind===`error`?`Error: ${c.error.message}`:`Rendered.`;return{content:[{type:`text`,text:Pu(e,o,u)}],structuredContent:c,isError:c.kind===`error`}}const Bu=`ui://aikit/present.html`;let Vu=``;function Hu(){return Vu}function Uu(e){Vu=e}function Wu(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Gu(e){let t=typeof e.description==`string`&&e.description.trim().length>0?`<p class="present-surface-description">${G(e.description)}</p>`:``;return[`<section class="present-surface-header">`,` <h1>${G(e.title)}</h1>`,t,`</section>`,`<style>`,` .present-surface-header { display: grid; gap: 0.75rem; margin-bottom: 1.5rem; }`,` .present-surface-header h1 { margin: 0; font-size: 1.875rem; }`,` .present-surface-description { margin: 0; color: var(--dt-text-secondary, #475467); }`,`</style>`].filter(Boolean).join(`
|
|
2789
|
+
`)}function Ku(e,t){let n=[e.title];return e.description&&n.push(e.description),Gn(t)?n.push(``,`Selected action: ${t.result.actionId}`):Wn(t)?n.push(``,`Error: ${t.error.message}`):t.kind===`timeout`?n.push(``,`Timed out after ${t.waitedMs}ms.`):t.kind===`cancelled`&&n.push(``,`Cancelled: ${t.reason}.`),n.join(`
|
|
2790
|
+
`)}const qu={id:`__dismiss`,type:`button`,label:`Done`,variant:`default`};function Ju(e){if((e.actions?.length??0)>0||!e.template)return e;let t=Su(e);return t?.supportedTransports.includes(`browser`)===!0&&!t.supportedTransports.includes(`mcp-app`)?{...e,actions:[qu]}:e}function Yu(e){return e.replace(`</head>`,`<style>
|
|
415
2791
|
.toolbar, .badge, body > header, body > footer { display: none !important; }
|
|
416
2792
|
body { padding-top: 0 !important; margin-top: 0 !important; }
|
|
417
2793
|
</style>
|
|
418
2794
|
</head>`).replace(`</body>`,`<script>
|
|
2795
|
+
(function() {
|
|
2796
|
+
function report() {
|
|
2797
|
+
var h = document.documentElement.scrollHeight;
|
|
2798
|
+
window.parent.postMessage({ type: 'aikit-viewer-height', height: h }, '*');
|
|
2799
|
+
}
|
|
2800
|
+
var ro = new ResizeObserver(report);
|
|
2801
|
+
ro.observe(document.documentElement);
|
|
2802
|
+
report();
|
|
2803
|
+
})();
|
|
2804
|
+
<\/script>
|
|
2805
|
+
<script>
|
|
419
2806
|
window.addEventListener('message', function(e) {
|
|
420
2807
|
if (e.data && e.data.type === 'aikit-theme-change') {
|
|
421
2808
|
document.documentElement.setAttribute('data-theme', e.data.theme);
|
|
422
2809
|
}
|
|
423
2810
|
});
|
|
424
2811
|
<\/script>
|
|
425
|
-
</body>`)}function
|
|
2812
|
+
</body>`)}function Xu(e){let t=`<script>
|
|
426
2813
|
(function(){
|
|
427
2814
|
var last=0;
|
|
428
2815
|
function notify(){
|
|
@@ -433,8 +2820,8 @@ window.addEventListener('message', function(e) {
|
|
|
433
2820
|
if(typeof ResizeObserver!=="undefined"){new ResizeObserver(notify).observe(document.documentElement)}
|
|
434
2821
|
else{setInterval(notify,500)}
|
|
435
2822
|
})();
|
|
436
|
-
<\/script>`;return e.includes(`</body>`)?e.replace(`</body>`,`${t}</body>`):`${e}${t}`}function
|
|
437
|
-
`);return
|
|
2823
|
+
<\/script>`;return e.includes(`</body>`)?e.replace(`</body>`,`${t}</body>`):`${e}${t}`}function Zu(e,t,n){return{content:[{type:`text`,text:`[ERROR:VALIDATION] ${t}`}],structuredContent:{kind:`error`,error:{code:e,message:t,...n===void 0?{}:{details:n}}},isError:!0}}function Qu(e){return!Wu(e)||!(`schemaVersion`in e)?{ok:!1,response:Zu(`INVALID_CHANNEL_SURFACE`,`present expects a ChannelSurface object with schemaVersion: 1`)}:e.schemaVersion===1?{ok:!0,surface:e}:{ok:!1,response:Zu(`UNSUPPORTED_SCHEMA_VERSION`,`Unsupported ChannelSurface schemaVersion: ${String(e.schemaVersion)}`)}}async function $u(e){if(e.template){let t=Xl(e.template);if(t)return await ed(e,t)}let t=Ju(e);return Cu(t)?await zu(t):td(t)}async function ed(e,t){if(e.data!=null&&t.inputSchema){let n=jt({data:e.data,schema:t.inputSchema});if(!n.valid){let e=n.errors.map(e=>` ${e.path}: ${e.message}${e.expected?` (expected: ${e.expected}, got: ${e.received})`:``}`).join(`
|
|
2824
|
+
`);return Zu(`VIEWER_DATA_INVALID`,`Template "${t.id}" data validation failed:\n${e}\n\nExpected schema: ${JSON.stringify(t.inputSchema,null,2)}`)}}let n=t.supportedTransports.includes(`browser`),r=t.supportedTransports.includes(`mcp-app`);if(n&&!r)return await Ru(e,t);if(!r)return Zu(`VIEWER_TRANSPORT_UNSUPPORTED`,`Viewer template "${t.id}" does not support an available transport`);try{let n=Yu($l(t.resolveHtml(),e.data??null,t.injectId,t.transformData)).replace(/&/g,`&`).replace(/"/g,`"`),r={kind:`rendered`,reason:`no-response-required`};return Uu(Xu(Bn({title:e.title,html:[Gu(e),`<div class="present-viewer-container">`,` <iframe class="present-viewer-iframe" srcdoc="${n}" sandbox="allow-scripts allow-same-origin"></iframe>`,` <script>
|
|
438
2825
|
(function() {
|
|
439
2826
|
const iframe = document.querySelector('.present-viewer-iframe');
|
|
440
2827
|
if (!iframe) return;
|
|
@@ -448,18 +2835,25 @@ window.addEventListener('message', function(e) {
|
|
|
448
2835
|
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
|
|
449
2836
|
iframe.addEventListener('load', sendTheme);
|
|
450
2837
|
})();
|
|
2838
|
+
<\/script>`,` <script>
|
|
2839
|
+
window.addEventListener('message', function(e) {
|
|
2840
|
+
if (e.data && e.data.type === 'aikit-viewer-height') {
|
|
2841
|
+
var container = document.querySelector('.present-viewer-container');
|
|
2842
|
+
if (container) { container.style.height = e.data.height + 'px'; }
|
|
2843
|
+
}
|
|
2844
|
+
});
|
|
451
2845
|
<\/script>`,`</div>`].join(`
|
|
452
|
-
`),css:[[`.present-viewer-container {`,` flex: 1;`,` min-height:
|
|
453
|
-
`)],islands:[],payload:void 0,nonce:``,generatedAt:new Date().toISOString(),colorScheme:e.colorScheme,lang:e.lang,dir:e.dir,transport:`mcp-app`,exportPolicy:`local-interactive`}))),{content:[{type:`text`,text:[
|
|
2846
|
+
`),css:[[`.present-viewer-container {`,` flex: 1;`,` min-height: 640px;`,` position: relative;`,` border-radius: 0.75rem;`,` overflow: hidden;`,` border: none;`,`}`,`.present-viewer-iframe {`,` border: none;`,` width: 100%;`,` height: 100%;`,` position: absolute;`,` inset: 0;`,` border-radius: inherit;`,`}`].join(`
|
|
2847
|
+
`)],islands:[],payload:void 0,nonce:``,generatedAt:new Date().toISOString(),colorScheme:e.colorScheme,lang:e.lang,dir:e.dir,transport:`mcp-app`,exportPolicy:`local-interactive`}))),{content:[{type:`text`,text:[Ku(e,r),`If this content doesn't render inline, retry with format: 'browser'.`].join(`
|
|
454
2848
|
|
|
455
|
-
`)}],ui:{type:`resource`,uri:
|
|
456
|
-
`),css:t.css,islands:t.islands,payload:t.payload,nonce:t.nonce,colorScheme:e.colorScheme,lang:e.lang,dir:e.dir,transport:`mcp-app`,exportPolicy:t.exportPolicy,headScripts:n}),i={kind:`rendered`,reason:`no-response-required`};return
|
|
2849
|
+
`)}],ui:{type:`resource`,uri:Bu},structuredContent:r}}catch(e){return Zu(`VIEWER_RENDER_FAILED`,e instanceof Error?e.message:`Failed to render viewer template`)}}function td(e){try{let t=Jn(e,{transport:`mcp-app`,registry:xu,blockVendorScripts:Pl()}),n=Ou(t.vendorScripts,`mcp-app`),r=Bn({title:e.title,html:[Gu(e),t.html].filter(Boolean).join(`
|
|
2850
|
+
`),css:t.css,islands:t.islands,payload:t.payload,nonce:t.nonce,colorScheme:e.colorScheme,lang:e.lang,dir:e.dir,transport:`mcp-app`,exportPolicy:t.exportPolicy,headScripts:n}),i={kind:`rendered`,reason:`no-response-required`};return Vu=Xu(r),{content:[{type:`text`,text:[Ku(e,i),`If this content doesn't render inline, retry with format: 'browser'.`].join(`
|
|
457
2851
|
|
|
458
|
-
`)}],ui:{type:`resource`,uri:
|
|
459
|
-
`);Mn(e,`present`,{title:n.title,description:r,inputSchema:
|
|
2852
|
+
`)}],ui:{type:`resource`,uri:Bu},structuredContent:i}}catch(e){return Zu(`RENDER_FAILED`,e instanceof Error?e.message:`Failed to render ChannelSurface`)}}const nd=N.object({label:N.string(),value:N.string(),description:N.string().optional()}),rd=N.object({id:N.string().describe(`Unique action identifier`),type:N.enum([`button`,`select`,`multi-select`,`form-submit`,`text-submit`,`confirm`,`custom`]).describe(`Interactive action type`),label:N.string().describe(`Visible action label`),variant:N.enum([`primary`,`danger`,`default`]).optional(),options:N.array(nd).optional(),schema:N.record(N.string(),N.unknown()).optional()}),id=N.object({type:N.string(),title:N.string().optional(),value:N.unknown().optional(),language:N.string().optional()}).catchall(N.unknown()),ad={schemaVersion:N.literal(1).describe(`ChannelSurface schema version. Must be 1.`),title:N.string().describe(`Surface title shown to the user.`),description:N.string().optional().describe(`Optional supporting copy for the surface.`),template:N.string().optional().describe(`Optional template id such as report@1.`),layout:N.object({maxWidth:N.string().optional(),padding:N.string().optional(),columns:N.number().optional()}).optional(),blocks:N.array(id).optional().describe(`Typed block content for the surface.`),data:N.unknown().optional().describe(`Template data payload, if the template expands data.`),actions:N.array(rd).optional().describe(`Interactive actions. Presence triggers browser transport.`),response:N.object({timeout:N.number().optional(),required:N.boolean().optional()}).optional(),metadata:N.object({surfaceId:N.string().optional(),createdAt:N.string().optional(),source:N.string().optional()}).optional(),colorScheme:N.enum([`light`,`dark`,`auto`]).optional(),lang:N.string().optional(),dir:N.enum([`ltr`,`rtl`,`auto`]).optional()};N.object({surfaceId:N.string(),actionId:N.string(),actionType:N.enum([`button`,`select`,`multi-select`,`form-submit`,`text-submit`,`confirm`,`custom`]),value:N.unknown().optional(),formData:N.record(N.string(),N.unknown()).optional(),selection:N.union([N.string(),N.array(N.string())]).optional(),label:N.string().optional(),timestamp:N.string(),sourceTransport:N.enum([`mcp-app`,`browser`,`export`])});function od(e,t){let n=V(`present`),r=[`Render a ChannelSurface for the user.`,`Quick ref:`,`- Full schema: aikit://schemas/channel-surface`,`- Required: schemaVersion: 1, title`,`- Content: blocks[] for direct rendering, or template + data for registry-backed rendering`,`- Actions: actions[] of SurfaceAction; interactive surfaces use browser transport and return ChannelOutcome`,`- Optional fields: description, layout, response, metadata, colorScheme, lang, dir`,`- Transports: mcp-app, browser, export`,`- Use schema resource for block enums and action details.`].join(`
|
|
2853
|
+
`);Mn(e,`present`,{title:n.title,description:r,inputSchema:ad,annotations:n.annotations,_meta:{ui:{resourceUri:Bu}}},async e=>{let t=Qu(e);return t.ok?await $u(t.surface):t.response});try{jn(e,`Present View`,Bu,{description:`AI Kit present tool viewer`},async()=>({contents:[{uri:Bu,mimeType:An,text:Hu()||`<html><body><p>No content generated yet. Call the present tool first.</p></body></html>`}]}))}catch{}try{e.registerResource(`present-templates`,`aikit://present/templates`,{description:`List all registered present viewer templates with their input schemas`},async()=>{let e=Ql().map(e=>({id:e.id,label:e.label,description:e.description,transports:e.supportedTransports,inputSchema:e.inputSchema}));return{contents:[{uri:`aikit://present/templates`,mimeType:`application/json`,text:JSON.stringify(e,null,2)}]}})}catch{}}const sd=A(`tools`);function cd(e,t){let n=new _n({structure:new yn,dependencies:new mn,symbols:new bn,patterns:new vn,entryPoints:new gn,diagrams:new hn}),r=V(`produce_knowledge`);e.registerTool(`produce_knowledge`,{title:r.title,description:`Run automated codebase analysis and produce synthesis instructions. Executes Tier 1 deterministic analyzers, then returns structured baselines and instructions for you to synthesize knowledge using remember.`,inputSchema:{scope:N.string().optional().describe(`Root path to analyze (defaults to workspace root)`),aspects:N.array(N.enum([`all`,`structure`,`dependencies`,`symbols`,`patterns`,`entry-points`,`diagrams`])).default([`all`]).describe(`Which analysis aspects to run`)},annotations:r.annotations},async({scope:e,aspects:r})=>{try{let i=e??`.`;sd.info(`Running knowledge production`,{rootPath:i,aspects:r});let a=await n.runExtraction(i,r);try{let e=t?.onboardDir??_(i,`.ai`,`context`);s(e,{recursive:!0});let n=`<!-- Generated by produce_knowledge at ${new Date().toISOString()} -->\n\n`;for(let[t,r]of Object.entries(a))r&&typeof r==`string`&&p(_(e,`${t}.md`),n+r,`utf-8`);sd.info(`Knowledge persisted to .ai/context/`,{files:Object.keys(a).length})}catch(e){sd.warn(`Failed to persist knowledge to .ai/context/`,{error:j(e)})}return{content:[{type:`text`,text:n.buildSynthesisInstructions(a,r)+`
|
|
460
2854
|
|
|
461
2855
|
---
|
|
462
|
-
_Next: Review the baselines above and use \`remember\` to store synthesized knowledge entries._`}]}}catch(e){return
|
|
2856
|
+
_Next: Review the baselines above and use \`remember\` to store synthesized knowledge entries._`}]}}catch(e){return sd.error(`Knowledge production failed`,j(e)),W(`INTERNAL`,`Knowledge production failed: ${e instanceof Error?e.message:String(e)}`)}})}const ld=A(`tools`);function ud(e,t,n,r,i,a,o){let s=V(`reindex`);e.registerTool(`reindex`,{title:s.title,description:`Trigger re-indexing of the AI Kit index. Can do incremental (only changed files) or full re-index. When smart indexing is active, use force: true to override the automatic trickle indexer.`,inputSchema:{full:N.boolean().default(!1).describe(`If true, force full re-index ignoring file hashes`),force:N.boolean().default(!1).describe(`If true, override smart indexing guard and run reindex anyway`)},annotations:s.annotations},async({full:e,force:s},c)=>{try{if(t.isIndexing)return{content:[{type:`text`,text:`## Reindex Already in Progress
|
|
463
2857
|
|
|
464
2858
|
A reindex operation is currently running. Search and other tools continue to work with existing data. Use \`status({})\` to check when it completes.`}]};if(o===`smart`&&!s)return{content:[{type:`text`,text:`## Smart Indexing Active
|
|
465
2859
|
|
|
@@ -467,39 +2861,39 @@ Smart indexing (trickle mode) is enabled — files are automatically indexed as
|
|
|
467
2861
|
|
|
468
2862
|
**If the index is severely outdated**, use \`reindex({ force: true })\` to override.
|
|
469
2863
|
|
|
470
|
-
Use \`status({})\` to check smart indexing queue status.`}]};let l=wo(c).createTask(`Reindex`,1);l.progress(0,`Starting ${e?`full`:`incremental`} reindex`),
|
|
2864
|
+
Use \`status({})\` to check smart indexing queue status.`}]};let l=wo(c).createTask(`Reindex`,1);l.progress(0,`Starting ${e?`full`:`incremental`} reindex`),ld.info(`Starting background re-index`,{mode:e?`full`:`incremental`});let u=e=>t=>{t.phase===`chunking`&&t.currentFile&&ld.debug(`Re-index progress`,{prefix:e,current:t.filesProcessed+1,total:t.filesTotal,file:t.currentFile})};return(e?t.reindexAll(n,u(`Reindex`)):t.index(n,u(`Index`))).then(async e=>{if(ld.info(`Background re-index complete`,{filesProcessed:e.filesProcessed,chunksCreated:e.chunksCreated,durationMs:e.durationMs}),l.complete(`Reindex complete: ${e.filesProcessed} files, ${e.chunksCreated} chunks in ${e.durationMs}ms`),i)try{await i.createFtsIndex(),ld.info(`FTS index rebuilt after reindex`)}catch(e){ld.warn(`FTS index rebuild failed`,j(e))}try{let e=await r.reindexAll();ld.info(`Curated re-index complete`,{indexed:e.indexed})}catch(e){ld.warn(`Curated re-index failed`,j(e))}if(a)try{await a.notifyAfterReindex()}catch(e){ld.warn(`Post-reindex resource notification failed`,j(e))}}).catch(e=>{l.fail(`Reindex failed: ${e instanceof Error?e.message:String(e)}`),ld.error(`Background reindex failed`,j(e))}),{content:[{type:`text`,text:`## Reindex Started (Background)\n\n- **Mode**: ${e?`Full`:`Incremental`}\n- Search and other tools continue to work with existing data during reindex.\n- Completion will be logged. Use \`status({})\` to check index stats afterward.\n\n---\n_Next: Continue working — the reindex runs in the background._`}]}}catch(e){return ld.error(`Reindex failed`,j(e)),W(`INTERNAL`,`Reindex failed: ${e instanceof Error?e.message:String(e)}`)}})}const dd=A(`tools`);function fd(e){let t=V(`replay`);e.registerTool(`replay`,{title:t.title,description:`View or clear the audit trail of recent MCP tool and CLI invocations. Shows tool name, duration, status, and input/output summaries.`,inputSchema:{action:N.enum([`list`,`clear`]).default(`list`).describe(`Action: "list" (default) to view entries, "clear" to wipe the log`),last:N.number().optional().describe(`Number of entries to return (default: 20, list only)`),tool:N.string().optional().describe(`Filter by tool name (list only)`),source:N.enum([`mcp`,`cli`]).optional().describe(`Filter by source: "mcp" or "cli" (list only)`),since:N.string().optional().describe(`ISO timestamp — only show entries after this time (list only)`)},annotations:t.annotations},async({action:e,last:t,tool:n,source:r,since:i})=>{try{if(e===`clear`)return Et(),{content:[{type:`text`,text:`Replay log cleared.`}]};let a=Dt({last:t,tool:n,source:r,since:i});if(a.length===0)return{content:[{type:`text`,text:`No replay entries found. Activity is logged when tools are invoked via MCP or CLI.`}]};let o=a.map(e=>`${e.ts.split(`T`)[1]?.split(`.`)[0]??e.ts} ${e.status===`ok`?`✓`:`✗`} ${e.tool} (${e.durationMs}ms) [${e.source}]\n in: ${e.input}\n out: ${e.output}`);return Ot().catch(()=>{}),{content:[{type:`text`,text:`**Replay Log** (${a.length} entries)\n\n${o.join(`
|
|
471
2865
|
|
|
472
|
-
`)}`}]}}catch(e){return
|
|
2866
|
+
`)}`}]}}catch(e){return dd.error(`Replay failed`,j(e)),W(`INTERNAL`,`Replay failed: ${e instanceof Error?e.message:String(e)}`)}})}const pd=A(`tools`);function md(e){let t=V(`restore`);e.registerTool(`restore`,{title:t.title,description:`List and restore file snapshots taken before destructive operations (codemod, rename, forget). Use action=list to see available restore points, action=restore with an id to undo.`,inputSchema:{action:N.enum([`list`,`restore`]).describe(`list: show restore points, restore: apply a restore point`),id:N.string().optional().describe(`Restore point ID (required for action=restore)`),limit:N.number().min(1).max(50).default(10).describe(`Max restore points to list`)},annotations:t.annotations},async({action:e,id:t,limit:n})=>{try{if(e===`list`){let e=tt().slice(0,n);return e.length===0?{content:[{type:`text`,text:`No restore points found.`}]}:{content:[{type:`text`,text:`## Restore Points\n\n${e.map(e=>`- **${e.id}** (${e.timestamp}) — ${e.operation}: ${e.description} (${e.files.length} files)`).join(`
|
|
473
2867
|
`)}`}]}}if(!t)throw Error(`id is required for restore action`);let r=await kt(t);return{content:[{type:`text`,text:`Restored ${r.length} files:\n${r.map(e=>`- \`${e}\``).join(`
|
|
474
|
-
`)}`}]}}catch(e){return
|
|
475
|
-
`)[0].slice(0,500);if(r&&r!==e){let i=await t.embedQuery(r),a=await n.search(i,y);a.length>0&&(b=a,C=`> _Original query "${e}" returned 0 results. Auto-reformulated to "${r}"._\n\n`,
|
|
2868
|
+
`)}`}]}}catch(e){return pd.error(`Restore failed`,j(e)),W(`INTERNAL`,`Restore failed: ${e instanceof Error?e.message:String(e)}`)}})}const hd=/<\/?curated-context>/gi;function gd(e){return e.replace(hd,``)}function _d(e){return`<curated-context>\n${gd(e)}\n</curated-context>`}const vd=A(`tools`);function yd(e){let t=[],n=m(process.cwd());n&&t.push(`[project: ${n}]`);let r=Lt(`__context_boost`);return r&&t.push(`[focus: ${r.value}]`),t.length===0?e:`${t.join(` `)} ${e}`}async function bd(e,t,n,r,i){if(!e||t>=e.config.fallbackThreshold&&n.length>0)return{results:n,triggered:!1,cacheHit:!1};let a=!1;try{let t=e.cache.get(r);return t?a=!0:(t=await e.client.search(r,i),t.length>0&&e.cache.set(r,t)),t.length>0?{results:kn(n,t,i).map(e=>({record:{id:`er:${e.sourcePath}`,content:e.content,sourcePath:e.source===`er`?`[ER] ${e.sourcePath}`:e.sourcePath,startLine:e.startLine??0,endLine:e.endLine??0,contentType:e.contentType??`documentation`,headingPath:e.headingPath,origin:e.source===`er`?`curated`:e.origin??`indexed`,category:e.category,tags:e.tags??[],chunkIndex:0,totalChunks:1,fileHash:``,indexedAt:new Date().toISOString(),version:1},score:e.score})),triggered:!0,cacheHit:a}:{results:n,triggered:!0,cacheHit:a}}catch(e){return vd.warn(`ER fallback failed`,j(e)),{results:n,triggered:!0,cacheHit:a}}}function xd(e,t,n=60){let r=new Map,i=e[0]?.score||1,a=t[0]?.score||1;for(let t=0;t<e.length;t++){let a=e[t],o=a.score/i;r.set(a.record.id,{record:a.record,score:1/(n+t+1)*o})}for(let e=0;e<t.length;e++){let i=t[e],o=i.score/a,s=r.get(i.record.id);s?s.score+=1/(n+e+1)*o:r.set(i.record.id,{record:i.record,score:1/(n+e+1)*o})}return[...r.values()].sort((e,t)=>t.score-e.score).map(({record:e,score:t})=>({record:e,score:t}))}function Sd(e,t){let n=t.toLowerCase().split(/\s+/).filter(e=>e.length>=2);return n.length<2?e:e.map(e=>{let t=e.record.content.toLowerCase();if(t.length>5e3)return e;let r=n.map(e=>{let n=[],r=t.indexOf(e);for(;r!==-1&&n.length<10;)n.push(r),r=t.indexOf(e,r+1);return n});if(r.some(e=>e.length===0))return e;let i=t.length;for(let e of r[0]){let t=e,a=e+n[0].length;for(let i=1;i<r.length;i++){let o=r[i][0],s=Math.abs(o-e);for(let t=1;t<r[i].length;t++){let n=Math.abs(r[i][t]-e);n<s&&(s=n,o=r[i][t])}t=Math.min(t,o),a=Math.max(a,o+n[i].length)}i=Math.min(i,a-t)}let a=1+.25/(1+i/200);return{record:e.record,score:e.score*a}}).sort((e,t)=>t.score-e.score)}function Cd(e){if(e.length<=1)return e;let t=new Set;return e.filter(e=>{let n=e.record.contentHash;return n?t.has(n)?!1:(t.add(n),!0):!0})}function wd(e,t,n=8){let r=new Set(t.toLowerCase().split(/\s+/).filter(e=>e.length>=2)),i=new Map,a=e.length;for(let t of e){let e=t.record.content.split(/[^a-zA-Z0-9_]+/).filter(e=>e.length>=3&&!Td.has(e.toLowerCase())),n=new Set;for(let t of e){let e=t.toLowerCase();/[_A-Z]/.test(t)&&i.set(`__id__${e}`,1),n.has(e)||(n.add(e),i.set(e,(i.get(e)??0)+1))}}let o=[];for(let[e,t]of i){if(e.startsWith(`__id__`)||r.has(e)||t>a*.8)continue;let n=Math.log(a/t),s=+!!i.has(`__id__${e}`),c=e.length>8?.5:0;o.push({term:e,score:n+s+c})}return o.sort((e,t)=>t.score-e.score).slice(0,n).map(e=>e.term)}const Td=new Set(`the.and.for.are.but.not.you.all.can.had.her.was.one.our.out.has.have.from.this.that.with.they.been.said.each.which.their.will.other.about.many.then.them.these.some.would.make.like.into.could.time.very.when.come.just.know.take.people.also.back.after.only.more.than.over.such.import.export.const.function.return.true.false.null.undefined.string.number.boolean.void.type.interface`.split(`.`));async function Ed(e,t){try{let n=await e.getStats();if(!n.lastIndexedAt)return;let r=new Date(n.lastIndexedAt).getTime(),i=Date.now(),a=[...new Set(t.map(e=>e.record.sourcePath))].filter(e=>!e.startsWith(`[ER]`)).slice(0,5);if(a.length===0)return;let o=0;for(let e of a)try{(await nn(e)).mtimeMs>r&&o++}catch{o++}if(o>0){let e=i-r,t=Math.floor(e/6e4),n=t<1?`<1 min`:`${t} min`;return`> ⚠️ **Index may be stale** — ${o} file(s) modified since last index (${n} ago). Use \`reindex\` to refresh.`}}catch{}}function Dd(e,t,n,r,i,a,o){let s=V(`search`);e.registerTool(`search`,{title:s.title,description:`Search AI Kit for code, docs, and prior decisions. Default choice for discovery. Modes: hybrid (default), semantic, keyword. For multi-strategy precision queries use find; for a known file path use lookup.`,outputSchema:Ca,inputSchema:{query:N.string().max(5e3).describe(`Natural language search query`),limit:N.number().min(1).max(20).default(5).describe(`Maximum results to return`),search_mode:N.enum([`hybrid`,`semantic`,`keyword`]).default(`hybrid`).describe(`Search strategy: hybrid (vector + FTS + RRF fusion, default), semantic (vector only), keyword (FTS only)`),content_type:N.enum(C).optional().describe(`Filter by content type`),source_type:N.enum(E).optional().describe(`Coarse filter: "source" (code only), "documentation" (md, curated), "test", "config". Overrides content_type if both set.`),origin:N.enum(T).optional().describe(`Filter by knowledge origin`),category:N.string().optional().describe(`Filter by category (e.g., decisions, patterns, conventions)`),tags:N.array(N.string()).optional().describe(`Filter by tags (returns results matching ANY of the specified tags)`),min_score:N.number().min(0).max(1).default(.25).describe(`Minimum similarity score`),graph_hops:N.number().min(0).max(3).default(1).describe(`Number of graph hops to augment results with connected entities (0 = disabled, 1 = direct connections, 2-3 = deeper traversal). Default 1 provides module/symbol context automatically.`),max_tokens:N.number().min(100).max(5e4).optional().describe(`Maximum token budget for the response. When set, output is truncated to fit.`),dedup:N.enum([`file`,`chunk`]).default(`chunk`).describe(`Deduplication mode: "chunk" (default, show all matching chunks) or "file" (collapse chunks from same file into single result with merged line ranges)`),workspaces:N.array(N.string()).optional().describe(`Cross-workspace search: partition names or folder basenames to include. Use ["*"] for all registered workspaces. Only works in user-level install mode.`)},annotations:s.annotations},async({query:e,limit:s,search_mode:c,content_type:l,source_type:u,origin:d,category:f,tags:p,min_score:m,graph_hops:h,max_tokens:g,dedup:_,workspaces:v})=>{try{let y={limit:s,minScore:m,contentType:l,sourceType:u,origin:d,category:f,tags:p},b,x=!1,S=!1,C=``,w=yd(e);if(c===`keyword`)b=await n.ftsSearch(e,y),b=b.slice(0,s);else if(c===`semantic`){let r=await t.embedQuery(w);b=await n.search(r,y);let a=await bd(i,b[0]?.score??0,b,e,s);b=a.results,x=a.triggered,S=a.cacheHit}else{let r=await t.embedQuery(w),a=n.coarseSearch,o=a?a.bind(n):n.search.bind(n),[c,l]=await Promise.all([o(r,{...y,limit:s*2}),n.ftsSearch(e,{...y,limit:s*2}).catch(()=>[])]);c.length===0&&l.length>0?(b=l.slice(0,s),C=` (FTS-only: vector index rebuilding)`):b=l.length===0&&c.length>0?c.slice(0,s):xd(c,l).slice(0,s);let u=await bd(i,c[0]?.score??0,b,e,s);b=u.results,x=u.triggered,S=u.cacheHit}a&&a.recordSearch(e,x,S),b.length>1&&(b=Sd(b,e)),b=Cd(b);let T=``;if(v&&v.length>0){let n=es(v,k(process.cwd()));if(n.length>0){let{stores:r,closeAll:i}=await ts(n);try{let i;i=c===`keyword`?await rs(r,e,{...y,limit:s}):await ns(r,await t.embedQuery(e),{...y,limit:s});for(let e of i)b.push({record:{...e.record,sourcePath:`[${e.workspace}] ${e.record.sourcePath}`},score:e.score});b=b.sort((e,t)=>t.score-e.score).slice(0,s),T=` + ${n.length} workspace(s)`}finally{await i()}}}if(_===`file`&&b.length>1){let e=new Map;for(let t of b){let n=t.record.sourcePath,r=e.get(n);r?(t.score>r.best.score&&(r.best=t),r.ranges.push({start:t.record.startLine,end:t.record.endLine})):e.set(n,{best:t,ranges:[{start:t.record.startLine,end:t.record.endLine}]})}b=[...e.values()].sort((e,t)=>t.best.score-e.best.score).map(({best:e,ranges:t})=>({record:{...e.record,content:t.length>1?`${e.record.content}\n\n_Matched ${t.length} sections: ${t.sort((e,t)=>e.start-t.start).map(e=>`L${e.start}-${e.end}`).join(`, `)}_`:e.record.content},score:e.score}))}if(b.length>1){let e=b[0].score,t=Math.max(e*.4,.1);b=b.filter(e=>e.score>=t)}if(b.length===0){if(o?.available)try{let r=(await o.createMessage({prompt:`The search query "${e}" returned 0 results in AI Kit code search. Suggest ONE alternative search query that might find relevant results. Reply with ONLY the alternative query, nothing else.`,systemPrompt:`You are a search query optimizer for AI Kit code search. Generate a single alternative query.`,maxTokens:100})).text.trim().split(`
|
|
2869
|
+
`)[0].slice(0,500);if(r&&r!==e){let i=await t.embedQuery(r),a=await n.search(i,y);a.length>0&&(b=a,C=`> _Original query "${e}" returned 0 results. Auto-reformulated to "${r}"._\n\n`,vd.info(`Smart search fallback succeeded`,{originalQuery:e,altQuery:r,resultCount:a.length}))}}catch(e){vd.debug(`Smart search fallback failed`,{error:String(e)})}if(b.length===0)return{content:[{type:`text`,text:`No results found for the given query.`}],structuredContent:{results:[],totalResults:0,searchMode:c,query:e}}}let E,D;if(h>0&&!r&&(D="> **Note:** `graph_hops` was set but no graph store is available. Graph augmentation skipped."),h>0&&r)try{let e=await Ue(r,b.map(e=>({recordId:e.record.id,score:e.score,sourcePath:e.record.sourcePath})),{hops:h,maxPerHit:5});E=new Map;for(let t of e)if(t.graphContext.nodes.length>0){let e=t.graphContext.nodes.slice(0,5).map(e=>` - **${e.name}** (${e.type})`).join(`
|
|
476
2870
|
`),n=t.graphContext.edges.slice(0,5).map(e=>` - ${e.fromId} —[${e.type}]→ ${e.toId}`).join(`
|
|
477
2871
|
`),r=[`- **Graph Context** (${h} hop${h>1?`s`:``}):`];e&&r.push(` Entities:\n${e}`),n&&r.push(` Relationships:\n${n}`),E.set(t.recordId,r.join(`
|
|
478
|
-
`))}}catch(e){
|
|
479
|
-
`)}\n\n${n.origin===`curated`?
|
|
2872
|
+
`))}}catch(e){vd.warn(`Graph augmentation failed`,j(e)),D=`> **Note:** Graph augmentation failed. Results shown without graph context.`}let O=Date.now();for(let e of b)if(e.record.origin===`curated`&&e.record.indexedAt){let t=O-new Date(e.record.indexedAt).getTime(),n=Math.max(0,t/864e5);e.score*=.95**n}b.sort((e,t)=>t.score-e.score),b=de(b);let A=b.map((e,t)=>{let n=e.record;return`${`### Result ${t+1} (score: ${e.score.toFixed(3)})`}\n${[`- **Source**: ${n.sourcePath}`,n.headingPath?`- **Section**: ${n.headingPath}`:null,`- **Type**: ${n.contentType}`,n.startLine?`- **Lines**: ${n.startLine}-${n.endLine}`:null,n.origin===`indexed`?null:`- **Origin**: ${n.origin}`,n.category?`- **Category**: ${n.category}`:null,n.tags?.length?`- **Tags**: ${n.tags.join(`, `)}`:null,E?.get(n.id)??null].filter(Boolean).join(`
|
|
2873
|
+
`)}\n\n${n.origin===`curated`?_d(n.content):n.content}`}).join(`
|
|
480
2874
|
|
|
481
2875
|
---
|
|
482
2876
|
|
|
483
|
-
`),ee=(c===`hybrid`?`hybrid (vector + keyword RRF)`:c===`keyword`?`keyword (FTS)`:`semantic (vector)`)+T,te=
|
|
2877
|
+
`),ee=(c===`hybrid`?`hybrid (vector + keyword RRF)`:c===`keyword`?`keyword (FTS)`:`semantic (vector)`)+T,te=wd(b,e),ne=te.length>0?`\n_Distinctive terms: ${te.map(e=>`\`${e}\``).join(`, `)}_`:``,re=await Ed(n,b),M=[];if(b.length===0)M.push("`reindex` — no results found, index may be stale"),M.push("`find` — try federated search with glob/regex");else{let e=b[0]?.record.sourcePath;e&&M.push(`\`lookup\` — see all chunks from \`${e}\``),M.push("`symbol` — resolve a specific symbol from the results"),M.push("`compact` — compress a result file for focused reading")}let ie=[D?`${D}\n\n`:``,re?`${re}\n\n`:``,A,`\n\n---\n_Search mode: ${ee} | ${b.length} results_${ne}`,`\n_Next: ${M.join(` | `)}_`],ae=C+ie.join(``);g&&(ae=Kt(ae,g));let oe=new Set,se=[];for(let e of b){if(e.record.origin!==`curated`||e.record.sourcePath.startsWith(`[`))continue;let t=Ec(e.record.sourcePath);if(!t)continue;let n=wc(t,e.record.headingPath??t);n&&!oe.has(n.uri)&&(oe.add(n.uri),se.push(n))}return{content:[{type:`text`,text:ae},...se],structuredContent:{results:b.map(e=>({sourcePath:e.record.sourcePath,contentType:e.record.contentType,score:e.score,...e.record.headingPath?{headingPath:e.record.headingPath}:{},...e.record.startLine?{startLine:e.record.startLine}:{},...e.record.endLine?{endLine:e.record.endLine}:{},...e.record.origin===`indexed`?{}:{origin:e.record.origin},...e.record.category?{category:e.record.category}:{},...e.record.tags?.length?{tags:e.record.tags}:{}})),totalResults:b.length,searchMode:c,query:e}}}catch(e){return vd.error(`Search failed`,j(e)),W(`INTERNAL`,`Search failed: ${e instanceof Error?e.message:String(e)}`)}})}const Od=A(`tools`);function kd(e,t){let n=V(`session_digest`);e.registerTool(`session_digest`,{title:n.title,description:`Compress the current session's tool trajectory into a focused digest. Aggregates replay log, stash state, and checkpoints. Supports deterministic and LLM-assisted (sampling) modes with configurable token budget.`,inputSchema:{scope:N.enum([`tools`,`stash`,`all`]).default(`all`).describe(`What to include: tools (replay only), stash (stash + checkpoints only), or all`),since:N.string().optional().describe(`ISO timestamp — only include activity after this time`),last:N.number().min(1).max(500).optional().describe(`Max replay entries to consider (default: 50)`),focus:N.string().optional().describe(`Focus query — prioritize entries matching this topic`),mode:N.enum([`deterministic`,`sampling`]).default(`deterministic`).describe(`Summary mode: deterministic (fast, template-based) or sampling (LLM-assisted, richer)`),token_budget:N.number().min(100).max(8e3).default(2e3).describe(`Target token budget for the digest output`),persist:N.boolean().default(!0).describe(`Auto-save digest to stash for crash recovery`)},annotations:n.annotations},async({scope:e,since:n,last:r,focus:i,mode:a,token_budget:o,persist:s})=>{try{let c={scope:e,since:n,last:r,focus:i,mode:a,tokenBudget:o,persist:s};return{content:[{type:`text`,text:(a===`sampling`&&t?.available?await Pt(c,async(e,n,r)=>(await t.createMessage({prompt:e,systemPrompt:n,maxTokens:r})).text):Nt(c)).digest}]}}catch(e){return Od.error(`Session digest failed`,j(e)),W(`INTERNAL`,`Session digest failed: ${e instanceof Error?e.message:String(e)}`)}})}const Ad=A(`tools`);function jd(e,t,n,r=15e3){let i,a=new Promise(e=>{i=setTimeout(()=>{Ad.warn(`Status sub-operation "${n}" timed out after ${r}ms`),e({value:t,timedOut:!0})},r)});return Promise.race([e.then(e=>(clearTimeout(i),{value:e,timedOut:!1}),e=>(clearTimeout(i),Ad.warn(`Status sub-operation "${n}" failed: ${e instanceof Error?e.message:String(e)}`),{value:t,timedOut:!1})),a])}function Md(){let e=Sn.get(),t=xn.get();if(e)return`- **Tree-sitter (WASM)**: ✅ Available (${t.grammarCount} grammars, dir: ${t.wasmDir})`;let n=t.pathsChecked.length?t.pathsChecked.map(e=>` ${e.exists?`✓`:`✗`} ${e.path}`).join(`
|
|
484
2878
|
`):` none`,r=t.healAttempted?` Auto-heal: ${t.healSuccess?`✓ succeeded`:`✗ failed (${t.healError??`unknown`})`}`:` Auto-heal: not attempted`;return[`- **Tree-sitter (WASM)**: ⚠ Unavailable (regex fallback)`,` Reason: ${t.reason||`unknown`}`,` Paths checked:`,n,r,` OS: ${t.os} ${t.arch} | Node: ${t.nodeVersion}`,` Fix: Reinstall package or run vendor:wasm script`].join(`
|
|
485
|
-
`)}const
|
|
486
|
-
`)}],structuredContent:l}})}function
|
|
487
|
-
`),ie={totalRecords:h.totalRecords,totalFiles:h.totalFiles,lastIndexedAt:h.lastIndexedAt??null,onboarded:w,onboardDir:x,contentTypes:h.contentTypeBreakdown,wasmAvailable:!!Sn.get(),wasmDiagnostics:xn.get(),graphStats:_,curatedCount:v,serverVersion:ee,scaffoldVersion:k??null,workspaceScaffoldVersion:A??null,upgradeAvailable:te||ne,storeBackend:l?.store?.backend,storeDiagnostics:D??null,contextPressure:T};return{content:[{type:`text`,text:M+(u===`smart`?"\n\n---\n_Next: Use `search` to query indexed content or `graph({action:'find_nodes', name_pattern:'<top-level-module>'})` → then `graph({action:'neighbors', node_id})` for relationships. Smart indexing handles updates automatically._":"\n\n---\n_Next: Use `search` to query indexed content, `graph({action:'find_nodes', name_pattern:'<top-level-module>'})` → then `graph({action:'neighbors', node_id})` for relationships, or `reindex` to refresh the index._")}],structuredContent:ie}}catch(e){return
|
|
488
|
-
`)};if(i.length===1)try{return{content:[{type:`text`,text:`${await a(i[0])}\n---\n_Next: Use \`web_fetch\` to read any of these pages in full._`}]}}catch(e){return
|
|
2879
|
+
`)}const Nd=5*6e4;let Pd=null,Fd=null;function Id(e,t,n,r){let i=Math.min(e/2e4,1),a=Math.min((t+n)/5e4,1),o=Math.min(r/200,1);return Math.round(i*40+a*35+o*25)}function Ld(){let e=Date.now();if(Pd&&e-Pd.ts<Nd)return Pd.value;try{let t=y(Rn(),`.copilot`,`.aikit-scaffold.json`);if(!o(t))return Pd={value:null,ts:e},null;let n=JSON.parse(c(t,`utf-8`)).version??null;return Pd={value:n,ts:e},n}catch{return Pd={value:null,ts:e},null}}function Rd(){let e=Date.now();if(Fd&&e-Fd.ts<Nd)return Fd.value;try{let t=y(process.cwd(),`.github`,`.aikit-scaffold.json`);if(!o(t))return Fd={value:null,ts:e},null;let n=JSON.parse(c(t,`utf-8`)).version??null;return Fd={value:n,ts:e},n}catch{return Fd={value:null,ts:e},null}}function zd(e){let t=V(`status`);e.registerTool(`status`,{title:t.title,description:`Get the current status and statistics of the AI Kit index.`,outputSchema:ma,annotations:t.annotations},async()=>{let e=r(),t=Ld(),a=Rd(),o=t!=null&&t!==e,s=a!=null&&a!==e,c=[`## AI Kit Status`,``,`⏳ **AI Kit is initializing** — index stats will be available shortly.`,``,`### Runtime`,`- **Tree-sitter (WASM)**: ${Sn.get()?`✅ Available (AST analysis)`:`⚠ Unavailable (regex fallback)`}`,``,`### Version`,`- **Server**: ${e}`,`- **Scaffold (user)**: ${t??`not installed`}`,`- **Scaffold (workspace)**: ${a??`not installed`}`];if(o||s){let r=i(),l=[];o&&l.push(`user scaffold v${t}`),s&&l.push(`workspace scaffold v${a}`);let u=l.join(`, `);r.state===`success`?c.push(``,`### ✅ Upgrade Applied`,`- Server v${e} — ${u} auto-upgraded successfully.`,`- _Restart the MCP server to use the updated version._`):r.state===`pending`?c.push(``,`### ⏳ Upgrade In Progress`,`- Server v${e} ≠ ${u}`,`- Auto-upgrade is running in the background…`):r.state===`failed`?(n(),c.push(``,`### ⬆ Upgrade Available (auto-upgrade failed, retrying)`,`- Server v${e} ≠ ${u}`,`- Error: ${r.error??`unknown`}`)):(n(),c.push(``,`### ⬆ Upgrade Available`,`- Server v${e} ≠ ${u}`,`- Auto-upgrade triggered — check again shortly.`))}let l={totalRecords:0,totalFiles:0,lastIndexedAt:null,onboarded:!1,onboardDir:``,contentTypes:{},wasmAvailable:!!Sn.get(),wasmDiagnostics:xn.get(),graphStats:null,curatedCount:0,serverVersion:e,scaffoldVersion:t??null,workspaceScaffoldVersion:a??null,upgradeAvailable:o||s,contextPressure:0};return{content:[{type:`text`,text:c.join(`
|
|
2880
|
+
`)}],structuredContent:l}})}function Bd(e,t,a,s,c,l,u,d){let p=V(`status`);e.registerTool(`status`,{title:p.title,description:`Get the current status and statistics of the AI Kit index.`,outputSchema:ma,annotations:p.annotations},async()=>{let e=[];try{let[p,m]=await Promise.all([jd(t.getStats(),{totalRecords:0,totalFiles:0,lastIndexedAt:null,contentTypeBreakdown:{}},`store.getStats`),jd(t.listSourcePaths(),[],`store.listSourcePaths`)]),h=p.value;p.timedOut&&e.push(`⚠ Index stats timed out — values may be incomplete`);let g=m.value;m.timedOut&&e.push(`⚠ File listing timed out`);let _=null,v=0,b=[`## AI Kit Status`,``,`- **Total Records**: ${h.totalRecords}`,`- **Total Files**: ${h.totalFiles}`,`- **Last Indexed**: ${h.lastIndexedAt??`Never`}`,``,`### Content Types`,...Object.entries(h.contentTypeBreakdown).map(([e,t])=>`- ${e}: ${t}`),``,`### Indexed Files`,...g.slice(0,50).map(e=>`- ${e}`),g.length>50?`\n... and ${g.length-50} more files`:``];if(a)try{let t=await jd(a.getStats(),{nodeCount:0,edgeCount:0,nodeTypes:{},edgeTypes:{}},`graphStore.getStats`);if(t.timedOut)e.push(`⚠ Graph stats timed out`),b.push(``,`### Knowledge Graph`,`- Graph stats timed out`);else{let e=t.value;_={nodes:e.nodeCount,edges:e.edgeCount},b.push(``,`### Knowledge Graph`,`- **Nodes**: ${e.nodeCount}`,`- **Edges**: ${e.edgeCount}`,...Object.entries(e.nodeTypes).map(([e,t])=>` - ${e}: ${t}`));try{let e=await jd(a.validate(),{valid:!0,danglingEdges:[],orphanNodes:[],stats:{nodeCount:0,edgeCount:0,nodeTypes:{},edgeTypes:{}}},`graphStore.validate`);if(!e.timedOut){let t=e.value;t.valid||b.push(`- **⚠ Integrity Issues**: ${t.danglingEdges.length} dangling edges`),t.orphanNodes.length>0&&b.push(`- **Orphan nodes**: ${t.orphanNodes.length}`)}}catch{}}}catch{b.push(``,`### Knowledge Graph`,`- Graph store unavailable`)}let x=l?.onboardDir??y(process.cwd(),S.aiContext),C=o(x),w=c?.onboardComplete||C;if(b.push(``,`### Onboard Status`,w?`- ✅ Complete${c?.onboardTimestamp?` (last: ${c.onboardTimestamp})`:``}`:'- ❌ Not run — call `onboard({ path: "." })` to analyze the codebase',`- **Onboard Directory**: \`${x}\``),s)try{let t=await jd(s.list(),[],`curated.list`);if(t.timedOut)e.push(`⚠ Curated knowledge listing timed out`),b.push(``,`### Curated Knowledge`,`- Listing timed out`);else{let e=t.value;v=e.length,b.push(``,`### Curated Knowledge`,e.length>0?`- ${e.length} entries`:'- Empty — use `knowledge({ action: "remember", ... })` to persist decisions')}}catch{b.push(``,`### Curated Knowledge`,`- Unable to read curated entries`)}let T=Id(h.totalRecords,_?.nodes??0,_?.edges??0,v);b.push(``),b.push(`## 📊 Context Pressure: ${T}/100`),T>=80?b.push(`⚠️ High pressure — consider pruning stale entries or compacting context.`):T>=50?b.push(`ℹ️ Moderate pressure — AI Kit memory is well-populated.`):b.push(`✅ Low pressure — plenty of headroom for more content.`);let E=0;if(h.lastIndexedAt){E=new Date(h.lastIndexedAt).getTime();let e=(Date.now()-E)/(1e3*60*60);b.push(``,`### Index Freshness`,e>24?u===`smart`?`- ⚠ Last indexed ${Math.floor(e)}h ago — smart indexing will refresh automatically`:`- ⚠ Last indexed ${Math.floor(e)}h ago — may be stale. Run \`reindex({})\``:`- ✅ Last indexed ${e<1?`less than 1h`:`${Math.floor(e)}h`} ago`)}let D=null,O=t;if(typeof O.getDiagnostics==`function`)try{D=O.getDiagnostics(),b.push(``,`### Storage`,`- **Backend**: ${l?.store?.backend??`unknown`}`,`- **Adapter**: ${D.adapterType}`,`- **Vector search**: ${D.vectorSearchEnabled?`✅ enabled`:`⚠ disabled (FTS-only fallback)`}`,D.dbPath?`- **DB**: ${D.dbPath}`:``,D.dbSizeBytes==null?``:`- **DB size**: ${(D.dbSizeBytes/1024/1024).toFixed(2)} MB`,`- **Embedding dim**: ${D.embeddingDim}`,`- **Vector dtype**: ${D.vectorDtype}`)}catch{}if(u===`smart`)if(b.push(``,`### Smart Indexing`),d){let e=d();e?b.push(`- **Mode**: Smart (trickle)`,`- **Status**: ${e.running?`✅ Running`:`⏸ Stopped`}`,`- **Queue**: ${e.queueSize} files pending`,`- **Changed files**: ${e.changedFilesSize} detected`,`- **Interval**: ${Math.round(e.intervalMs/1e3)}s per batch of ${e.batchSize}`):b.push(`- **Mode**: Smart (trickle)`,`- **Status**: scheduler state unavailable (init may have failed)`)}else b.push(`- **Mode**: Smart (trickle) — scheduler state unavailable`);{try{let e=y(process.cwd(),S.data,`stash`);if(o(e)){let t=f(e).mtimeMs;t>E&&(E=t)}}catch{}let e=[];if(s)try{let t=v>0?await s.list():[];for(let e of t){let t=new Date(e.updated||e.created).getTime();t>E&&(E=t)}e.push(...t.sort((e,t)=>new Date(t.updated).getTime()-new Date(e.updated).getTime()).slice(0,5))}catch{}let t=E>0?Date.now()-E:0;if(t>=144e5){let n=Math.floor(t/36e5);if(b.push(``,`### 🌅 Session Briefing`,`_${n}+ hours since last activity — here's what to pick up:_`,``),e.length>0){b.push(`**Recent decisions/notes:**`);for(let t of e)b.push(`- **${t.title}** (${t.category??`note`}) — ${(t.contentPreview??``).slice(0,80)}…`)}b.push(``,`**Suggested next steps:**`,'- `search({ query: "SESSION CHECKPOINT", origin: "curated" })` — find your last checkpoint',"- `restore({})` — resume from a saved checkpoint",'- `knowledge({ action: "list" })` — browse all stored knowledge')}}b.push(``,`### Runtime`,Md());let k=Ld(),A=Rd(),ee=r(),te=k!=null&&k!==ee,ne=A!=null&&A!==ee;if(te||ne){let e=i(),t=[];te&&t.push(`user scaffold v${k}`),ne&&t.push(`workspace scaffold v${A}`);let r=t.join(`, `);e.state===`success`?b.push(``,`### ✅ Upgrade Applied`,`- Server v${ee} — ${r} auto-upgraded successfully.`,`- _Restart the MCP server to use the updated version._`):e.state===`pending`?b.push(``,`### ⏳ Upgrade In Progress`,`- Server v${ee} ≠ ${r}`,`- Auto-upgrade is running in the background…`):e.state===`failed`?(n(),b.push(``,`### ⬆ Upgrade Available (auto-upgrade failed, retrying)`,`- Server v${ee} ≠ ${r}`,`- Error: ${e.error??`unknown`}`)):(n(),b.push(``,`### ⬆ Upgrade Available`,`- Server v${ee} ≠ ${r}`,`- Auto-upgrade triggered — check again shortly.`))}e.length>0&&b.push(``,`### ⚠ Warnings`,...e.map(e=>`- ${e}`));let re=Di();if(re.length>0){let e=re.sort((e,t)=>t.callCount-e.callCount);b.push(``,`### Tool Usage This Session`,``),b.push(`| Tool | Calls | Tokens In | Tokens Out | Errors | Avg Latency |`),b.push(`|------|-------|-----------|------------|--------|-------------|`);for(let t of e.slice(0,15)){let e=Math.round(t.totalInputChars/4),n=Math.round(t.totalOutputChars/4),r=Math.round(t.totalDurationMs/t.callCount);b.push(`| ${t.tool} | ${t.callCount} | ${e.toLocaleString()} | ${n.toLocaleString()} | ${t.errorCount} | ${r}ms |`)}}let j=yi();if(j.bufferSize>=10){let e=j.state===`healthy`?`🟢`:j.state===`degraded`?`🔴`:`🟡`;b.push(``,`### Auto-GC: ${e} ${j.state}`),b.push(`- p95 latency: ${j.p95}ms | buffer: ${j.bufferSize} samples`),j.gcCount>0&&b.push(`- GC cycles triggered: ${j.gcCount}`)}let M=b.join(`
|
|
2881
|
+
`),ie={totalRecords:h.totalRecords,totalFiles:h.totalFiles,lastIndexedAt:h.lastIndexedAt??null,onboarded:w,onboardDir:x,contentTypes:h.contentTypeBreakdown,wasmAvailable:!!Sn.get(),wasmDiagnostics:xn.get(),graphStats:_,curatedCount:v,serverVersion:ee,scaffoldVersion:k??null,workspaceScaffoldVersion:A??null,upgradeAvailable:te||ne,storeBackend:l?.store?.backend,storeDiagnostics:D??null,contextPressure:T};return{content:[{type:`text`,text:M+(u===`smart`?"\n\n---\n_Next: Use `search` to query indexed content or `graph({action:'find_nodes', name_pattern:'<top-level-module>'})` → then `graph({action:'neighbors', node_id})` for relationships. Smart indexing handles updates automatically._":"\n\n---\n_Next: Use `search` to query indexed content, `graph({action:'find_nodes', name_pattern:'<top-level-module>'})` → then `graph({action:'neighbors', node_id})` for relationships, or `reindex` to refresh the index._")}],structuredContent:ie}}catch(e){return Ad.error(`Status failed`,j(e)),W(`INTERNAL`,`Status check failed: ${e instanceof Error?e.message:String(e)}`)}})}const Vd=A(`tools`);function Hd(e){let t=V(`web_search`);e.registerTool(`web_search`,{title:t.title,description:`PREFERRED web search — fans out to multiple keyless providers (DuckDuckGo + Bing-HTML + Mojeek) by default, returning a deduplicated, consensus-ranked union within a 10s deadline. Optional providers (SearXNG, Google, Brave, Bing API) join the fan-out automatically when their env vars are set. Pass one query or multiple for parallel searching.`,inputSchema:{queries:N.array(N.string().max(2e3)).min(1).max(5).describe('Search queries (1–5). Single: `["react hooks"]`. Multiple searched in parallel.'),limit:N.number().min(1).max(20).default(5).describe(`Max results per query`),site:N.string().optional().describe(`Restrict to domain (e.g., "docs.aws.amazon.com")`),provider:N.enum([`multi`,`duckduckgo`,`bing-html`,`mojeek`,`searxng`,`google`,`brave`,`bing`]).optional().describe("Search provider. Defaults to env AIKIT_SEARCH_PROVIDER, then `multi` (fan-out). Single keyless: `duckduckgo`, `bing-html`, `mojeek`. Self-hosted: `searxng` (requires SEARXNG_URL). Keyed APIs: `google` (GOOGLE_API_KEY+GOOGLE_CSE_ID), `brave` (BRAVE_API_KEY), `bing` (BING_API_KEY). Missing keys auto-fall back to duckduckgo.")},annotations:t.annotations},async({queries:e,limit:t,site:n,provider:r})=>{let i=e,a=async e=>{let i=await Zt({query:e,limit:t,site:n,provider:r}),a=[`## Search: ${i.query} _(via ${i.provider})_`,``];if(i.results.length===0)a.push(`No results found.`);else for(let e of i.results)a.push(`### [${e.title}](${e.url})`,e.snippet,``);return a.join(`
|
|
2882
|
+
`)};if(i.length===1)try{return{content:[{type:`text`,text:`${await a(i[0])}\n---\n_Next: Use \`web_fetch\` to read any of these pages in full._`}]}}catch(e){return Vd.error(`Web search failed`,j(e)),W(`INTERNAL`,`Web search failed: ${e instanceof Error?e.message:String(e)}`)}let o=await Promise.allSettled(i.map(e=>a(e))),s=[],c=0;for(let e=0;e<o.length;e++){let t=o[e];if(t.status===`fulfilled`)s.push(t.value);else{c++;let n=t.reason instanceof Error?t.reason.message:String(t.reason);Vd.error(`Web search failed`,{query:i[e],error:n}),s.push(`## ❌ Search failed: ${i[e]}\n\n${n}`)}}let l=`_Searched ${o.length-c}/${o.length} queries successfully._`;s.push(``,`---`,l,"_Next: Use `web_fetch` to read any of these pages in full._");let u=s.join(`
|
|
489
2883
|
|
|
490
|
-
`);return c===o.length?W(`INTERNAL`,u):{content:[{type:`text`,text:u}]}})}function
|
|
491
|
-
`)}]}}catch(e){return
|
|
492
|
-
`)}]}})}function
|
|
493
|
-
`)}],structuredContent:i}}catch(e){return
|
|
2884
|
+
`);return c===o.length?W(`INTERNAL`,u):{content:[{type:`text`,text:u}]}})}function Ud(e){let t=V(`http`);e.registerTool(`http`,{title:t.title,description:`Make HTTP requests (GET/POST/PUT/PATCH/DELETE/HEAD) for API testing. Returns status, headers, and formatted body with timing info.`,inputSchema:{url:N.string().url().describe(`Request URL (http/https only)`),method:N.enum([`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`HEAD`]).default(`GET`).describe(`HTTP method`),headers:N.record(N.string(),N.string()).optional().describe(`Request headers as key-value pairs`),body:N.string().optional().describe(`Request body (for POST/PUT/PATCH)`),timeout:N.number().min(1e3).max(6e4).default(15e3).describe(`Timeout in milliseconds`)},annotations:t.annotations},async({url:e,method:t,headers:n,body:r,timeout:i})=>{try{let a=await qe({url:e,method:t,headers:n,body:r,timeout:i}),o=[`## ${t} ${e}`,``,`**Status:** ${a.status} ${a.statusText}`,`**Time:** ${a.durationMs}ms`,`**Size:** ${a.sizeBytes} bytes`,`**Content-Type:** ${a.contentType}`,``,`### Headers`,"```json",JSON.stringify(a.headers),"```",``,`### Body`,a.contentType.includes(`json`)?"```json":"```",a.body,"```"];return a.truncated&&o.push(``,`_Response truncated — total size: ${a.sizeBytes} bytes_`),{content:[{type:`text`,text:o.join(`
|
|
2885
|
+
`)}]}}catch(e){return Vd.error(`HTTP request failed`,j(e)),W(`INTERNAL`,`HTTP request failed: ${e instanceof Error?e.message:String(e)}`)}})}function Wd(e){let t=V(`regex_test`);e.registerTool(`regex_test`,{title:t.title,description:`Test a regex pattern against sample strings. Supports match, replace, and split modes.`,inputSchema:{pattern:N.string().max(500).describe(`Regex pattern (without delimiters)`),flags:N.string().max(10).regex(/^[gimsuy]*$/).default(``).describe(`Regex flags (g, i, m, s, etc.)`),test_strings:N.array(N.string().max(1e4)).max(50).describe(`Strings to test the pattern against`),mode:N.enum([`match`,`replace`,`split`]).default(`match`).describe(`Test mode`),replacement:N.string().optional().describe(`Replacement string (for replace mode)`)},annotations:t.annotations},async({pattern:e,flags:t,test_strings:n,mode:r,replacement:i})=>{let a=xt({pattern:e,flags:t,testStrings:n,mode:r,replacement:i});if(!a.valid)return W(`VALIDATION`,`Invalid regex: ${a.error}`);let o=[`## Regex: \`/${a.pattern}/${a.flags}\``,``,`Mode: ${r}`,``];for(let e of a.results){if(o.push(`**Input:** \`${e.input}\``),o.push(`**Matched:** ${e.matched}`),e.matches)for(let t of e.matches){let e=t.groups.length>0?` groups: [${t.groups.join(`, `)}]`:``;o.push(` - "${t.full}" at index ${t.index}${e}`)}e.replaced!==void 0&&o.push(`**Result:** \`${e.replaced}\``),e.split&&o.push(`**Split:** ${JSON.stringify(e.split)}`),o.push(``)}return{content:[{type:`text`,text:o.join(`
|
|
2886
|
+
`)}]}})}function Gd(e){let t=V(`encode`);e.registerTool(`encode`,{title:t.title,description:`Encode, decode, or hash text. Supports base64, URL encoding, SHA-256, MD5, JWT decode, hex.`,inputSchema:{operation:N.enum([`base64_encode`,`base64_decode`,`url_encode`,`url_decode`,`sha256`,`md5`,`jwt_decode`,`hex_encode`,`hex_decode`]).describe(`Operation to perform`),input:N.string().max(1e6).describe(`Input text`)},annotations:t.annotations},async({operation:e,input:t})=>{try{let n=je({operation:e,input:t});return{content:[{type:`text`,text:`## ${e}\n\n**Input:** \`${t.length>100?`${t.slice(0,100)}...`:t}\`\n**Output:**\n\`\`\`\n${n.output}\n\`\`\``}]}}catch(e){return Vd.error(`Encode failed`,j(e)),W(`INTERNAL`,`Encode failed: ${e instanceof Error?e.message:String(e)}`)}})}function Kd(e){let t=V(`measure`);e.registerTool(`measure`,{title:t.title,description:`Measure code complexity, line counts, and function counts for a file or directory. Returns per-file metrics sorted by complexity.`,outputSchema:ga,inputSchema:{path:N.string().describe(`File or directory path to measure`),extensions:N.array(N.string()).optional().describe(`File extensions to include (default: .ts,.tsx,.js,.jsx)`)},annotations:t.annotations},async({path:e,extensions:t})=>{try{let n=await rt({path:e,extensions:t}),r=[`## Code Metrics`,``,`**Files:** ${n.summary.totalFiles}`,`**Total lines:** ${n.summary.totalLines} (${n.summary.totalCodeLines} code)`,`**Functions:** ${n.summary.totalFunctions}`,`**Avg complexity:** ${n.summary.avgComplexity}`,`**Max complexity:** ${n.summary.maxComplexity.value} (${n.summary.maxComplexity.file})`,``,`### Top files by complexity`,``,`| File | Lines | Code | Complexity | Cognitive | Functions | Imports |`,`|------|-------|------|------------|-----------|-----------|---------|`];for(let e of n.files.slice(0,20)){let t=e.cognitiveComplexity===void 0?`—`:String(e.cognitiveComplexity);r.push(`| ${e.path} | ${e.lines.total} | ${e.lines.code} | ${e.complexity} | ${t} | ${e.functions} | ${e.imports} |`)}n.files.length>20&&r.push(``,`_...and ${n.files.length-20} more files_`);let i={summary:{totalFiles:n.summary.totalFiles,totalLines:n.summary.totalLines,totalCodeLines:n.summary.totalCodeLines,totalFunctions:n.summary.totalFunctions,avgComplexity:n.summary.avgComplexity,maxComplexity:{value:n.summary.maxComplexity.value,file:n.summary.maxComplexity.file}},files:n.files.map(e=>({path:e.path,lines:e.lines.total,code:e.lines.code,complexity:e.complexity,functions:e.functions}))};return{content:[{type:`text`,text:r.join(`
|
|
2887
|
+
`)}],structuredContent:i}}catch(e){return Vd.error(`Measure failed`,j(e)),W(`INTERNAL`,`Measure failed: ${e instanceof Error?e.message:String(e)}`)}})}function qd(e){let t=V(`changelog`);e.registerTool(`changelog`,{title:t.title,description:`Generate a changelog from git history between two refs. Groups by conventional commit type.`,inputSchema:{from:N.string().max(200).describe(`Start ref (tag, SHA, HEAD~N)`),to:N.string().max(200).default(`HEAD`).describe(`End ref (default: HEAD)`),format:N.enum([`grouped`,`chronological`,`per-scope`]).default(`grouped`).describe(`Output format`),include_breaking:N.boolean().default(!0).describe(`Highlight breaking changes`),cwd:N.string().optional().describe(`Repository root or working directory`)},annotations:t.annotations},async({from:e,to:t,format:n,include_breaking:r,cwd:i})=>{try{let a=fe({from:e,to:t,format:n,includeBreaking:r,cwd:i}),o=`${a.stats.total} commits (${Object.entries(a.stats.types).map(([e,t])=>`${t} ${e}`).join(`, `)})`;return{content:[{type:`text`,text:`${a.markdown}\n---\n_${o}_`}]}}catch(e){return Vd.error(`Changelog failed`,j(e)),W(`INTERNAL`,`Changelog failed: ${e instanceof Error?e.message:String(e)}`)}})}function Jd(e){let t=V(`schema_validate`);e.registerTool(`schema_validate`,{title:t.title,description:`Validate JSON data against a JSON Schema. Supports type, required, properties, items, enum, pattern, min/max.`,inputSchema:{data:N.string().max(5e5).describe(`JSON data to validate (as string)`),schema:N.string().max(5e5).describe(`JSON Schema to validate against (as string)`)},annotations:t.annotations},async({data:e,schema:t})=>{try{let n=jt({data:JSON.parse(e),schema:JSON.parse(t)});if(n.valid)return{content:[{type:`text`,text:`## Validation: PASSED
|
|
494
2888
|
|
|
495
2889
|
Data matches the schema.`}]};let r=[`## Validation: FAILED`,``,`**${n.errors.length} error(s):**`,``];for(let e of n.errors){let t=e.expected?` (expected: ${e.expected}, got: ${e.received})`:``;r.push(`- \`${e.path}\`: ${e.message}${t}`)}return{content:[{type:`text`,text:r.join(`
|
|
496
|
-
`)}]}}catch(e){return
|
|
497
|
-
`)}],structuredContent:a}})}function
|
|
498
|
-
`)}],structuredContent:o}}catch(e){return
|
|
2890
|
+
`)}]}}catch(e){return Vd.error(`Schema validation failed`,j(e)),W(`INTERNAL`,`Schema validation failed: ${e instanceof Error?e.message:String(e)}`)}})}function Yd(e){let t=V(`env`);e.registerTool(`env`,{title:t.title,description:`Get system and runtime environment info. Sensitive env vars are redacted by default.`,outputSchema:_a,inputSchema:{include_env:N.boolean().default(!1).describe(`Include environment variables`),filter_env:N.string().optional().describe(`Filter env vars by name substring`),show_sensitive:N.boolean().default(!1).describe(`Show sensitive values (keys, tokens, etc.) — redacted by default`)},annotations:t.annotations},async({include_env:e,filter_env:t,show_sensitive:n})=>{let r=Me({includeEnv:e,filterEnv:t,showSensitive:n}),i=[`## Environment`,``,`**Platform:** ${r.system.platform} ${r.system.arch}`,`**OS:** ${r.system.type} ${r.system.release}`,`**Host:** ${r.system.hostname}`,`**CPUs:** ${r.system.cpus}`,`**Memory:** ${r.system.memoryFreeGb}GB free / ${r.system.memoryTotalGb}GB total`,``,`**Node:** ${r.runtime.node}`,`**V8:** ${r.runtime.v8}`,`**CWD:** ${r.cwd}`];if(r.env){i.push(``,`### Environment Variables`,``);for(let[e,t]of Object.entries(r.env))i.push(`- \`${e}\`: ${t}`)}let a={platform:r.system.platform,arch:r.system.arch,nodeVersion:r.runtime.node,cwd:r.cwd,cpus:r.system.cpus,memoryFreeGb:r.system.memoryFreeGb,memoryTotalGb:r.system.memoryTotalGb};return{content:[{type:`text`,text:i.join(`
|
|
2891
|
+
`)}],structuredContent:a}})}function Xd(e){let t=V(`time`);e.registerTool(`time`,{title:t.title,description:`Parse dates, convert timezones, calculate durations, add time. Supports ISO 8601, unix timestamps, and human-readable formats.`,outputSchema:va,inputSchema:{operation:N.enum([`now`,`parse`,`convert`,`diff`,`add`]).describe(`now: current time | parse: parse a date string | convert: timezone conversion | diff: duration between two dates | add: add duration to date`),input:N.string().optional().describe(`Date input (ISO, unix timestamp, or parseable string). For diff: two comma-separated dates`),timezone:N.string().optional().describe(`Target timezone (e.g., "America/New_York", "Asia/Tokyo")`),duration:N.string().optional().describe(`Duration to add (e.g., "2h30m", "1d", "30s") — for add operation`)},annotations:t.annotations},async({operation:e,input:t,timezone:n,duration:r})=>{try{let i=Wt({operation:e,input:t,timezone:n,duration:r}),a=[`**${i.output}**`,``,`ISO: ${i.iso}`,`Unix: ${i.unix}`];i.details&&a.push(``,"```json",JSON.stringify(i.details),"```");let o={iso:i.iso,unix:i.unix,timezone:n??Intl.DateTimeFormat().resolvedOptions().timeZone,formatted:i.output};return{content:[{type:`text`,text:a.join(`
|
|
2892
|
+
`)}],structuredContent:o}}catch(e){return Vd.error(`Time failed`,j(e)),W(`INTERNAL`,`Time failed: ${e instanceof Error?e.message:String(e)}`)}})}function Zd(e){try{let t=_(e,`.flows`);if(!o(t))return null;let n=l(t,{withFileTypes:!0});for(let e of n){if(!e.isDirectory())continue;let n=_(t,e.name,`meta.json`);if(o(n)&&JSON.parse(c(n,`utf-8`)).status===`active`)return e.name}}catch{return null}return null}function Qd(e,t,n,r,i,a,o,s,c){let l=new fi,u=n.sources[0]?.path??process.cwd(),d={activeSlug:null};d.activeSlug=Zd(u);let f=new si(_(u,`.aikit-state`,`flow-context`),()=>d.activeSlug),p=new Qr;p.register(Yr),p.register(Cr),p.register(Kr),p.register(br),p.register(gr),p.register(_r),p.register(Zr),p.register(Gr(()=>d.activeSlug?{active:!0,slug:d.activeSlug}:null));let m=new ri(p,t.curated,{},f);l.use(ti(m),{order:5,name:`auto-knowledge`}),l.use(ki(),{order:10,name:`replay`}),l.use(Wi(),{order:1,name:`structured-content-guard`}),l.use(oi(ai(n.tokenBudget)),{order:90,name:`compression`}),Qi(e,l,n.toolPrefix??``);let h=c??fa(n,[...z,...Li],B,zi(n)),g=e=>h.has(e),v=[...h].filter(e=>Ri.has(e)?Ii.includes(e)?!!t.bridge:e===`er_update_policy`?!!t.policyStore:e===`er_evolve_review`?!!t.evolutionCollector:!1:!0);g(`search`)&&Dd(e,t.embedder,t.store,t.graphStore,t.bridge,t.evolutionCollector,a);let y={store:t.store,graphStore:t.graphStore,embedder:t.embedder};g(`lookup`)&&al(e,t.store,y);let b={onboardComplete:t.onboardComplete,onboardTimestamp:t.onboardTimestamp};g(`status`)&&Bd(e,t.store,t.graphStore,t.curated,b,n,o,s),g(`config`)&&Qo(e,n),g(`reindex`)&&ud(e,t.indexer,n,t.curated,t.store,i,o),g(`knowledge`)&&rl(e,t.curated,t.policyStore,t.evolutionCollector,i,f),g(`analyze`)&&bo(e,t.store,t.embedder,y),g(`blast_radius`)&&xo(e,t.store,t.embedder,t.graphStore,y),g(`produce_knowledge`)&&cd(e,n),g(`onboard`)&&bl(e,t.store,t.embedder,n,b,y),g(`graph`)&&pc(e,t.graphStore),g(`audit`)&&Eo(e,t.store,t.embedder,n.tokenBudget),g(`compact`)&&cs(e,t.embedder,t.fileCache,u,n.allRoots,y),g(`scope_map`)&&ls(e,t.embedder,t.store,y),g(`find`)&&us(e,t.embedder,t.store,u,y),g(`parse_output`)&&ws(e),g(`workset`)&&Sl(e),g(`check`)&&xs(e,y,n.tokenBudget),g(`symbol`)&&ds(e,t.embedder,t.store,t.graphStore),g(`eval`)&&Ss(e),g(`test_run`)&&Cs(e,y),g(`stash`)&&Cl(e),g(`git_context`)&&sl(e),g(`diff_parse`)&&cl(e),g(`rename`)&&ll(e),g(`codemod`)&&ul(e),g(`restore`)&&md(e),g(`file_summary`)&&fs(e,t.fileCache,u,n.allRoots,y),g(`checkpoint`)&&wl(e),g(`data_transform`)&&dl(e),g(`trace`)&&ps(e,t.embedder,t.store,t.graphStore),g(`process`)&&hc(e),g(`watch`)&&gc(e),g(`dead_symbols`)&&ms(e,t.embedder,t.store,u,n.allRoots,y),g(`delegate`)&&Ts(e,a),g(`health`)&&_c(e),g(`lane`)&&Tl(e),g(`queue`)&&El(e),g(`web_fetch`)&&vc(e),g(`guide`)&&yc(e,o),ml.some(e=>g(e))&&hl(e,n,v),g(`evidence_map`)&&ac(e),g(`digest`)&&oc(e,t.embedder),g(`forge_classify`)&&sc(e),g(`stratum_card`)&&cc(e,t.embedder,t.fileCache),g(`forge_ground`)&&lc(e,t.embedder,t.store),g(`present`)&&od(e,r),g(`browser`)&&fn(e,n),g(`web_search`)&&Hd(e),g(`http`)&&Ud(e),g(`regex_test`)&&Wd(e),g(`encode`)&&Gd(e),g(`measure`)&&Kd(e),g(`changelog`)&&qd(e),g(`schema_validate`)&&Jd(e),g(`env`)&&Yd(e),g(`time`)&&Xd(e),g(`flow`)&&rc(e,n,e=>{d.activeSlug=e}),t.bridge&&Ii.some(e=>g(e))&&(Ao(e,t.bridge,t.evolutionCollector),jo(e,t.bridge),Mo(e,t.bridge)),t.policyStore&&g(`er_update_policy`)&&Ml(e,t.policyStore),t.evolutionCollector&&g(`er_evolve_review`)&&ys(e,t.evolutionCollector),Fi(e,t.store,t.curated),g(`replay`)&&fd(e),g(`session_digest`)&&kd(e,a)}function $d(e,t,n){let r=e=>!n||n.has(e);r(`check`)&&xs(e,void 0,t.tokenBudget),r(`eval`)&&Ss(e),r(`test_run`)&&Cs(e),r(`parse_output`)&&ws(e),r(`delegate`)&&Ts(e),r(`git_context`)&&sl(e),r(`diff_parse`)&&cl(e),r(`rename`)&&ll(e),r(`codemod`)&&ul(e),r(`data_transform`)&&dl(e),r(`workset`)&&Sl(e),r(`stash`)&&Cl(e),r(`checkpoint`)&&wl(e),r(`restore`)&&md(e),r(`lane`)&&Tl(e),r(`queue`)&&El(e),r(`session_digest`)&&kd(e),r(`health`)&&_c(e),r(`process`)&&hc(e),r(`watch`)&&gc(e),r(`web_fetch`)&&vc(e),r(`guide`)&&yc(e),ml.some(e=>r(e))&&hl(e,t,[...n??new Set(z)]),r(`evidence_map`)&&ac(e),r(`forge_classify`)&&sc(e),r(`present`)&&od(e),r(`browser`)&&fn(e,t),r(`produce_knowledge`)&&cd(e),r(`replay`)&&fd(e),r(`status`)&&zd(e),r(`flow`)&&rc(e,t),r(`web_search`)&&Hd(e),r(`http`)&&Ud(e),r(`regex_test`)&&Wd(e),r(`encode`)&&Gd(e),r(`measure`)&&Kd(e),r(`changelog`)&&qd(e),r(`schema_validate`)&&Jd(e),r(`env`)&&Yd(e),r(`time`)&&Xd(e)}const ef=A(`resource-notifier`);var tf=class{mcpServer;constructor(e){this.mcpServer=e}async notifyStatusChanged(){await this.sendUpdate(`aikit://status`)}async notifyFileTreeChanged(){await this.sendUpdate(`aikit://file-tree`)}async notifyCuratedIndexChanged(){await this.sendUpdate(`aikit://curated`)}async notifyCuratedEntryChanged(e){await this.sendUpdate(`aikit://curated/${e}`)}async notifyResourceListChanged(){try{await this.mcpServer.server.sendResourceListChanged()}catch(e){ef.debug(`sendResourceListChanged failed`,{error:String(e)})}}async notifyAfterReindex(){await Promise.allSettled([this.notifyStatusChanged(),this.notifyFileTreeChanged()])}async notifyAfterCuratedWrite(e){let t=[this.notifyStatusChanged(),this.notifyCuratedIndexChanged()];e&&t.push(this.notifyCuratedEntryChanged(e)),await Promise.allSettled(t)}async sendUpdate(e){try{await this.mcpServer.server.sendResourceUpdated({uri:e})}catch(t){ef.debug(`sendResourceUpdated failed`,{uri:e,error:String(t)})}}};const Z=A(`server`);async function nf(n){Z.info(`Initializing AI Kit components`);let r=n.store.backend,i=n.store.path,a=null;if(r===`sqlite-vec`){let e=_(i,`aikit.db`);o(i)||s(i,{recursive:!0}),a=await Pn(e),Z.info(`SQLite adapter ready`,{type:a.type,vectorCapable:a.vectorCapable,dbPath:e}),a.vectorCapable||Z.warn(`┌──────────────────────────────────────────────────────────────────┐
|
|
499
2893
|
│ ⚠ SQLite vector extension unavailable — DEGRADED MODE │
|
|
500
2894
|
│ Vector search is disabled. Hybrid search returns FTS only. │
|
|
501
2895
|
│ To enable: install/rebuild better-sqlite3 (native module). │
|
|
502
|
-
└──────────────────────────────────────────────────────────────────┘`);let t=_(i,`lance`);o(t)&&Z.info(`Old LanceDB data found at ${t} — ignored. Safe to delete after verifying sqlite-vec works.`)}let[c,l,u,d]=await Promise.all([(async()=>{if(n.embedding.childProcess!==!1){let e=new Xn({model:n.embedding.model,dimensions:n.embedding.dimensions,interOpNumThreads:n.embedding.interOpNumThreads,intraOpNumThreads:n.embedding.intraOpNumThreads,idleTimeoutMs:n.embedding.idleTimeoutMs});return await e.initialize(),Z.info(`Embedder loaded (child process)`,{modelId:e.modelId,dimensions:e.dimensions}),e}let{OnnxEmbedder:e}=await import(`../../embeddings/dist/index.js`),t=new e({model:n.embedding.model,dimensions:n.embedding.dimensions,interOpNumThreads:n.embedding.interOpNumThreads,intraOpNumThreads:n.embedding.intraOpNumThreads});return await t.initialize(),Z.info(`Embedder loaded (in-process)`,{modelId:t.modelId,dimensions:t.dimensions}),t})(),(async()=>{let e=await Fn({backend:r,path:i,adapter:a??void 0,embeddingDim:n.embedding.dimensions});return await e.initialize(),Z.info(`Store initialized`,{backend:r}),e})(),(async()=>{let e=a?new Nn({adapter:a}):new Nn({path:i});return await e.initialize(),Z.info(`Graph store initialized`,{shared:!!a}),e})(),(async()=>{let e=await Cn();if(e){let e=xn.get();Z.info(`WASM tree-sitter enabled`,{grammars:e.grammarCount,dir:e.wasmDir})}else{let e=xn.get();Z.warn(`WASM tree-sitter not available; analyzers will use regex fallback`,{reason:e.reason,os:e.os,arch:e.arch,healAttempted:e.healAttempted,healSuccess:e.healSuccess,healError:e.healError,pathsChecked:e.pathsChecked.map(e=>`${e.path} (${e.exists?`found`:`missing`})`)})}return e})()]),p=new Qn(c,l),m=new Zn(n.store.path);m.load(),p.setHashCache(m);let h=n.curated.path,g=new e(h);await g.initialize();let v=new t(h,l,c,g);p.setGraphStore(u);let b=ko(n.er),x=b?new Dn(n.curated.path):void 0;x&&Z.info(`Policy store initialized`,{ruleCount:x.getRules().length});let C=b?new En:void 0,w=y(n.sources[0]?.path??process.cwd(),S.aiContext),T=o(w),E=n.onboardDir?o(n.onboardDir):!1,D=T||E,O,k=T?w:n.onboardDir;if(D&&k)try{O=f(k).mtime.toISOString()}catch{}return Z.info(`Onboard state detected`,{onboardComplete:D,onboardTimestamp:O,aiKbExists:T,onboardDirExists:E}),{embedder:c,store:l,indexer:p,curated:v,graphStore:u,fileCache:new se,bridge:b,policyStore:x,evolutionCollector:C,onboardComplete:D,onboardTimestamp:O}}function
|
|
503
|
-
`),`utf8`),Q.info(`Auto-heal: created ESM shim for @huggingface/transformers (.mjs wrapper → .cjs)`,{path:t}))}catch(e){Q.warn(`Auto-heal: failed to create ESM shim`,{error:e instanceof Error?e.message:String(e),path:t})}}}if(n.includes(`embedding`)||n.includes(`onnx`)||n.includes(`protobuf`)||n.includes(`model`)){let t=e.embedding?.model??w.model,n=_(Rn(),`.cache`,`huggingface`,`transformers-js`,t);try{await r(n,{recursive:!0,force:!0}),Q.info(`Auto-heal: cleared embedding model cache`,{path:n})}catch{}}if(n.includes(`lance`)||n.includes(`database`)||n.includes(`store`)){let t=_(e.store.path,`lance`);try{await r(t,{recursive:!0,force:!0}),Q.info(`Auto-heal: cleared LanceDB store`,{path:t})}catch{}}if(n.includes(`sqlite`)||n.includes(`database disk image`)||n.includes(`graph`)){let t=_(e.store.path,`graph.db`);try{await r(t,{force:!0}),Q.info(`Auto-heal: cleared graph database`,{path:t})}catch{}let n=_(e.store.path,`aikit.db`);try{await r(n,{force:!0}),await r(`${n}-wal`,{force:!0}).catch(()=>{}),await r(`${n}-shm`,{force:!0}).catch(()=>{}),Q.info(`Auto-heal: cleared corrupted aikit database`,{path:n})}catch{}}if(n.includes(`cannot find module`)&&!n.includes(`huggingface`)&&!n.includes(`.cache`)){let{fileURLToPath:e}=await import(`node:url`),t=e(import.meta.url);if(t.includes(`_npx`)||t.includes(`npm-cache`)){let e=
|
|
2896
|
+
└──────────────────────────────────────────────────────────────────┘`);let t=_(i,`lance`);o(t)&&Z.info(`Old LanceDB data found at ${t} — ignored. Safe to delete after verifying sqlite-vec works.`)}let[c,l,u,d]=await Promise.all([(async()=>{if(n.embedding.childProcess!==!1){let e=new Xn({model:n.embedding.model,dimensions:n.embedding.dimensions,interOpNumThreads:n.embedding.interOpNumThreads,intraOpNumThreads:n.embedding.intraOpNumThreads,idleTimeoutMs:n.embedding.idleTimeoutMs});return await e.initialize(),Z.info(`Embedder loaded (child process)`,{modelId:e.modelId,dimensions:e.dimensions}),e}let{OnnxEmbedder:e}=await import(`../../embeddings/dist/index.js`),t=new e({model:n.embedding.model,dimensions:n.embedding.dimensions,interOpNumThreads:n.embedding.interOpNumThreads,intraOpNumThreads:n.embedding.intraOpNumThreads});return await t.initialize(),Z.info(`Embedder loaded (in-process)`,{modelId:t.modelId,dimensions:t.dimensions}),t})(),(async()=>{let e=await Fn({backend:r,path:i,adapter:a??void 0,embeddingDim:n.embedding.dimensions});return await e.initialize(),Z.info(`Store initialized`,{backend:r}),e})(),(async()=>{let e=a?new Nn({adapter:a}):new Nn({path:i});return await e.initialize(),Z.info(`Graph store initialized`,{shared:!!a}),e})(),(async()=>{let e=await Cn();if(e){let e=xn.get();Z.info(`WASM tree-sitter enabled`,{grammars:e.grammarCount,dir:e.wasmDir})}else{let e=xn.get();Z.warn(`WASM tree-sitter not available; analyzers will use regex fallback`,{reason:e.reason,os:e.os,arch:e.arch,healAttempted:e.healAttempted,healSuccess:e.healSuccess,healError:e.healError,pathsChecked:e.pathsChecked.map(e=>`${e.path} (${e.exists?`found`:`missing`})`)})}return e})()]),p=new Qn(c,l),m=new Zn(n.store.path);m.load(),p.setHashCache(m);let h=n.curated.path,g=new e(h);await g.initialize();let v=new t(h,l,c,g);p.setGraphStore(u);let b=ko(n.er),x=b?new Dn(n.curated.path):void 0;x&&Z.info(`Policy store initialized`,{ruleCount:x.getRules().length});let C=b?new En:void 0,w=y(n.sources[0]?.path??process.cwd(),S.aiContext),T=o(w),E=n.onboardDir?o(n.onboardDir):!1,D=T||E,O,k=T?w:n.onboardDir;if(D&&k)try{O=f(k).mtime.toISOString()}catch{}return Z.info(`Onboard state detected`,{onboardComplete:D,onboardTimestamp:O,aiKbExists:T,onboardDirExists:E}),{embedder:c,store:l,indexer:p,curated:v,graphStore:u,fileCache:new se,bridge:b,policyStore:x,evolutionCollector:C,onboardComplete:D,onboardTimestamp:O}}function rf(e,t,n){if(e.serverInstructions)return e.serverInstructions;let r=new Set;for(let e of t){let t=n[e];if(t?.category)for(let e of t.category)r.add(e)}let i=[`This server provides ${t.size} tools across ${r.size} categories: ${[...r].sort().join(`, `)}.`];return e.readOnly&&i.push(`Server is in read-only mode. Mutating operations are disabled.`),e.features?.length&&i.push(`Active feature groups: ${e.features.join(`, `)}.`),i.join(` `)}const af=A(`background-task`);var of=class{queue=[];running=null;get isRunning(){return this.running!==null}get currentTask(){return this.running}get pendingCount(){return this.queue.length}schedule(e){return new Promise((t,n)=>{this.queue.push({...e,resolve:t,reject:n}),this.running||this.processQueue()})}async processQueue(){for(;this.queue.length>0;){let e=this.queue.shift();if(!e)break;this.running=e.name,af.info(`Background task started`,{task:e.name,pending:this.queue.length});let t=Date.now();try{await e.fn();let n=Date.now()-t;af.info(`Background task completed`,{task:e.name,durationMs:n}),e.resolve()}catch(n){let r=Date.now()-t;af.error(`Background task failed`,{task:e.name,durationMs:r,err:n}),e.reject(n instanceof Error?n:Error(String(n)))}}this.running=null}};const sf=A(`idle-timer`);var cf=class{timer=null;cleanupFns=[];idleMs;disposed=!1;sessionActive=!1;_busy=!1;constructor(e){this.idleMs=e?.idleMs??3e5}setBusy(e){this._busy=e,e?this.cancel():this.touch()}onIdle(e){this.cleanupFns.push(e)}markSessionActive(){this.sessionActive=!0}touch(){this.disposed||this._busy||(this.cancel(),this.timer=setTimeout(()=>{this.runCleanup()},this.idleMs),this.timer.unref&&this.timer.unref())}cancel(){this.timer&&=(clearTimeout(this.timer),null)}dispose(){this.cancel(),this.cleanupFns.length=0,this.disposed=!0}async runCleanup(){if(!this.sessionActive){sf.info(`Idle timeout reached with no active session — skipping cleanup (waiting for first tool call)`);return}if(this._busy){sf.info(`Skipping idle cleanup — background work in progress`);return}sf.info(`Idle for ${this.idleMs/1e3}s — running cleanup`);let e=await Promise.allSettled(this.cleanupFns.map(e=>e()));for(let t of e)t.status===`rejected`&&sf.warn(`Idle cleanup callback failed`,{error:String(t.reason)})}};const lf=A(`memory-monitor`);var uf=class{timer=null;warningBytes;criticalBytes;intervalMs;pressureFns=[];memoryPressureFns=[];lastLevel=`normal`;constructor(e){this.warningBytes=e?.warningBytes??4294967296,this.criticalBytes=e?.criticalBytes??8589934592,this.intervalMs=e?.intervalMs??6e4}onPressure(e){this.pressureFns.push(e)}registerMemoryPressureCallback(e){this.memoryPressureFns.push(e)}start(){this.timer||(this.timer=setInterval(()=>this.check(),this.intervalMs),this.timer.unref&&this.timer.unref(),lf.info(`Memory monitor started`,{warningMB:Math.round(this.warningBytes/1024/1024),criticalMB:Math.round(this.criticalBytes/1024/1024),intervalSec:Math.round(this.intervalMs/1e3)}))}stop(){this.timer&&=(clearInterval(this.timer),null)}getRssBytes(){return process.memoryUsage.rss()}check(){let e=this.getRssBytes(),t=`normal`;if(e>=this.criticalBytes?t=`critical`:e>=this.warningBytes&&(t=`warning`),t!==this.lastLevel||t===`critical`){let n=Math.round(e/1024/1024);t===`critical`?lf.warn(`Memory CRITICAL: ${n}MB RSS — consider restarting the server`):t===`warning`?lf.warn(`Memory WARNING: ${n}MB RSS`):this.lastLevel!==`normal`&&lf.info(`Memory returned to normal: ${n}MB RSS`),this.lastLevel=t}if(t!==`normal`)for(let n of this.pressureFns)try{n(t,e)}catch{}if(t===`critical`)for(let e of this.memoryPressureFns)try{let t=e();t&&typeof t.catch==`function`&&t.catch(()=>{})}catch{}return t===`critical`&&typeof globalThis.gc==`function`&&globalThis.gc(),t}};const df=A(`tool-timeout`),ff=new Set([`onboard`,`reindex`,`produce_knowledge`,`analyze`,`codemod`,`audit`]);var pf=class extends Error{toolName;timeoutMs;constructor(e,t){super(`Tool "${e}" timed out after ${t}ms`),this.toolName=e,this.timeoutMs=t,this.name=`ToolTimeoutError`}};function mf(e){return ff.has(e)?6e5:12e4}function hf(e,t,n){return new Promise((r,i)=>{let a=!1,o=setTimeout(()=>{if(!a){a=!0;let e=new pf(n,t);df.warn(e.message),i(e)}},t);o.unref&&o.unref(),e().then(e=>{a||(a=!0,clearTimeout(o),r(e))},e=>{a||(a=!0,clearTimeout(o),i(e))})})}const Q=A(`server`);function gf(e){let t=e.toLowerCase();return[`protobuf`,`invalid model`,`invalid onnx`,`unexpected end`,`unexpected token`,`failed to load`,`failed to initialize embedding`,`checksum`,`corrupt`,`malformed`,`could not load`,`onnx`,`database disk image is malformed`,`file is not a database`,`lance`,`cannot find module`,`module not found`].some(e=>t.includes(e))}function _f(e){let t=e.indexOf(`node_modules`);return t>0?e.substring(0,t-1):null}async function vf(e,t){let n=t.toLowerCase(),r;try{({rm:r}=await import(`node:fs/promises`))}catch{return}if(n.includes(`transformers.node.mjs`)&&n.includes(`cannot find module`)){let e=t.match(/Cannot find module '([^']+transformers\.node\.mjs)'/);if(e){let t=e[1],n=t.replace(/\.mjs$/,`.cjs`);try{let{existsSync:e,writeFileSync:r}=await import(`node:fs`);e(n)&&!e(t)&&(r(t,[`// Auto-generated ESM shim — published package missing this file`,`import { createRequire } from 'node:module';`,`const require = createRequire(import.meta.url);`,`const mod = require('./transformers.node.cjs');`,`export default mod;`,``].join(`
|
|
2897
|
+
`),`utf8`),Q.info(`Auto-heal: created ESM shim for @huggingface/transformers (.mjs wrapper → .cjs)`,{path:t}))}catch(e){Q.warn(`Auto-heal: failed to create ESM shim`,{error:e instanceof Error?e.message:String(e),path:t})}}}if(n.includes(`embedding`)||n.includes(`onnx`)||n.includes(`protobuf`)||n.includes(`model`)){let t=e.embedding?.model??w.model,n=_(Rn(),`.cache`,`huggingface`,`transformers-js`,t);try{await r(n,{recursive:!0,force:!0}),Q.info(`Auto-heal: cleared embedding model cache`,{path:n})}catch{}}if(n.includes(`lance`)||n.includes(`database`)||n.includes(`store`)){let t=_(e.store.path,`lance`);try{await r(t,{recursive:!0,force:!0}),Q.info(`Auto-heal: cleared LanceDB store`,{path:t})}catch{}}if(n.includes(`sqlite`)||n.includes(`database disk image`)||n.includes(`graph`)){let t=_(e.store.path,`graph.db`);try{await r(t,{force:!0}),Q.info(`Auto-heal: cleared graph database`,{path:t})}catch{}let n=_(e.store.path,`aikit.db`);try{await r(n,{force:!0}),await r(`${n}-wal`,{force:!0}).catch(()=>{}),await r(`${n}-shm`,{force:!0}).catch(()=>{}),Q.info(`Auto-heal: cleared corrupted aikit database`,{path:n})}catch{}}if(n.includes(`cannot find module`)&&!n.includes(`huggingface`)&&!n.includes(`.cache`)){let{fileURLToPath:e}=await import(`node:url`),t=e(import.meta.url);if(t.includes(`_npx`)||t.includes(`npm-cache`)){let e=_f(t);if(e)try{let{execSync:t}=await import(`node:child_process`);t(`npm install --prefer-offline --no-audit --no-fund`,{cwd:e,stdio:`ignore`,timeout:6e4}),Q.info(`Auto-heal: re-ran npm install to restore missing module`,{path:e})}catch{Q.warn(`Auto-heal: npm install failed during module restoration`,{hint:`Run: npm cache clean --force && npx -y @vpxa/aikit serve`})}}}}function yf(e,t){let n=fa(e,z,B,zi(e)),i=rf(e,n,B),a=new on({name:e.serverName??`aikit`,version:r()},{capabilities:{logging:{},completions:{},prompts:{}},instructions:i}),o=`initializing`,s=``,c=!1,l=null,u=null,d=null;function f(e){if(!e||typeof e!=`object`)return[];let t=e,n=[];for(let e of[`path`,`file`,`source_path`,`sourcePath`,`filePath`]){let r=t[e];typeof r==`string`&&r&&n.push(r)}for(let e of[`changed_files`,`paths`,`files`]){let r=t[e];if(Array.isArray(r))for(let e of r){if(typeof e==`string`){n.push(e);continue}e&&typeof e==`object`&&typeof e.path==`string`&&n.push(e.path)}}if(Array.isArray(t.sources))for(let e of t.sources)e&&typeof e==`object`&&typeof e.path==`string`&&n.push(e.path);return n}let p=()=>o===`failed`?[`❌ AI Kit initialization failed — this tool is unavailable.`,``,s?`Error: ${s}`:``,``,`**${Hi.size} tools are still available** and fully functional:`,`check, eval, test_run, git_context, health, measure, web_fetch, web_search,`,`flow, regex_test, encode,`,`stash, checkpoint, lane, process, time, env, and more.`,``,`To fix embedding errors, try deleting the cached model:`,` rm -rf ~/.cache/huggingface/transformers-js/mixedbread-ai/`,`Then restart the server to re-download a fresh copy.`,``,`Try restarting the MCP server to retry initialization.`].filter(Boolean).join(`
|
|
504
2898
|
`):[`AI Kit is still initializing (loading embeddings model & store).`,``,`**${Hi.size} tools are already available** while initialization completes — including:`,`check, eval, test_run, git_context, health, measure, web_fetch, web_search,`,`flow, regex_test, encode,`,`stash, checkpoint, lane, process, time, env, and more.`,``,`This tool requires the AI Kit index. Please retry in a few seconds,`,`or use one of the available tools above in the meantime.`].join(`
|
|
505
|
-
`);tr(a),Qi(a,new fi,e.toolPrefix??``);let m=a.sendToolListChanged.bind(a);a.sendToolListChanged=()=>{};let h=[];for(let e of z){if(!n.has(e))continue;let t=V(e),r=a.registerTool(e,{title:t.title,description:`${t.title} — initializing, available shortly`,inputSchema:{},annotations:t.annotations},async()=>({content:[{type:`text`,text:p()}]}));Hi.has(e)?r.remove():h.push(r)}
|
|
2899
|
+
`);tr(a),Qi(a,new fi,e.toolPrefix??``);let m=a.sendToolListChanged.bind(a);a.sendToolListChanged=()=>{};let h=[];for(let e of z){if(!n.has(e))continue;let t=V(e),r=a.registerTool(e,{title:t.title,description:`${t.title} — initializing, available shortly`,inputSchema:{},annotations:t.annotations},async()=>({content:[{type:`text`,text:p()}]}));Hi.has(e)?r.remove():h.push(r)}$d(a,e,n),a.sendToolListChanged=m;let g=a.registerResource(`aikit-status`,`aikit://status`,{description:`AI Kit status (initializing...)`,mimeType:`text/plain`},async()=>({contents:[{uri:`aikit://status`,text:`AI Kit is initializing...`,mimeType:`text/plain`}]})),_=a.registerPrompt(`_init`,{description:`Initializing AI Kit…`,argsSchema:{_dummy:dn(N.string(),()=>[])}},async()=>({messages:[]})),v,y,b=new Promise((e,t)=>{v=e,y=t}),x,S=new Promise(e=>{x=e}),C=()=>x?.(),w=(async()=>{await S;let n=[];try{let{createRequire:e}=await import(`node:module`),{readFileSync:t}=await import(`node:fs`),{fileURLToPath:r}=await import(`node:url`),{resolve:i,dirname:a}=await import(`node:path`),o=e(import.meta.url),s=i(a(r(import.meta.url)),`..`,`package.json`),c=JSON.parse(t(s,`utf8`)),l=Object.keys(c.dependencies??{}),u=[`@mixmark-io/domino`],d=[...l,...u.filter(e=>!l.includes(e))];for(let e of d)try{o.resolve(e)}catch{n.push(e)}if(n.length>0){Q.warn(`${n.length} dependencies not resolvable — attempting auto-repair`,{missing:n});let e=r(import.meta.url);if(e.includes(`_npx`)||e.includes(`npm-cache`))try{let{execSync:t}=await import(`node:child_process`),r=_f(e);if(r){Q.info(`Auto-heal: running npm install in npx cache to restore missing deps`,{path:r,missing:n}),t(`npm install --prefer-offline --no-audit --no-fund`,{cwd:r,stdio:`ignore`,timeout:6e4});let e=[];for(let t of n)try{o.resolve(t)}catch{e.push(t)}e.length===0?(Q.info(`Auto-heal: all missing dependencies restored successfully`),n=[]):(Q.warn(`Auto-heal: some deps still missing after npm install`,{stillMissing:e,hint:`Run: npm cache clean --force && npx -y @vpxa/aikit serve`}),n=e)}}catch(e){Q.warn(`Auto-heal: npm install failed — server may operate in degraded mode`,{error:e instanceof Error?e.message:String(e),hint:`Run: npm cache clean --force && npx -y @vpxa/aikit serve`})}else Q.warn(`Missing dependencies detected in non-npx environment`,{missing:n,hint:`Run: npm install (or pnpm install) to restore missing packages`})}}catch{}let r;try{r=await nf(e)}catch(t){let n=t instanceof Error?t.message:String(t);if(gf(n)){Q.warn(`AI Kit initialization failed with recoverable error — attempting auto-heal retry`,{error:n}),await vf(e,n);try{r=await nf(e),Q.info(`AI Kit auto-heal successful — initialization recovered after retry`)}catch(e){o=`failed`,s=e instanceof Error?e.message:String(e),Q.error(`AI Kit initialization failed after auto-heal attempt — server continuing with zero-dep tools only`,{error:s,originalError:n}),y?.(e instanceof Error?e:Error(s));return}}else{o=`failed`,s=n,Q.error(`AI Kit initialization failed — server continuing with zero-dep tools only`,{error:s}),y?.(t instanceof Error?t:Error(s));return}}let i=a.sendToolListChanged.bind(a);a.sendToolListChanged=()=>{};let p=a.sendPromptListChanged.bind(a);a.sendPromptListChanged=()=>{};let m=a.sendResourceListChanged.bind(a);a.sendResourceListChanged=()=>{};for(let e of h)e.remove();g.remove(),_.remove();let b=a._registeredTools??{};for(let e of Hi)b[e]?.remove();let x=new tf(a),C=As(a);Qd(a,r,e,$n(a),x,C,t,t===`smart`?(()=>{let e=d;return e?.getState?e.getState():null}):null),fr(a,{curated:r.curated,store:r.store,graphStore:r.graphStore},t),a.sendToolListChanged=i,a.sendPromptListChanged=p,a.sendResourceListChanged=m,Promise.resolve(a.sendToolListChanged()).catch(()=>{}),Promise.resolve(a.sendPromptListChanged()).catch(()=>{}),Promise.resolve(a.sendResourceListChanged()).catch(()=>{});let w=a._registeredTools??{};for(let[e,t]of Object.entries(w)){if(Bi.has(e))continue;let n=t.handler;t.handler=async(...t)=>{if(!r.indexer.isIndexing)return n(...t);let i=c?`re-indexing`:`running initial index`,a=new Promise(t=>setTimeout(()=>t({content:[{type:`text`,text:`⏳ AI Kit is ${i}. The tool "${e}" timed out waiting for index data (${Vi/1e3}s).\n\nThe existing index may be temporarily locked. Please retry shortly — indexing will complete automatically.`}]}),Vi));return Promise.race([n(...t),a])}}for(let[e,t]of Object.entries(w)){let n=t.handler,r=mf(e);t.handler=async(...t)=>{try{return await hf(()=>n(...t),r,e)}catch(t){if(t instanceof pf)return{content:[{type:`text`,text:`⏳ Tool "${e}" timed out after ${r/1e3}s. This may indicate a long-running operation. Please retry or break the task into smaller steps.`}]};throw t}}}let T=Object.keys(w).length;T<z.length&&Q.warn(`ALL_TOOL_NAMES count mismatch`,{expectedToolCount:z.length,registeredToolCount:T}),Q.info(`MCP server configured`,{toolCount:z.length,resourceCount:4});let D=new uf;D.onPressure((e,t)=>{e===`warning`&&ar(),e===`critical`&&(Q.warn(`Memory pressure critical — consider restarting`,{rssMB:Math.round(t/1024/1024)}),ar())}),D.registerMemoryPressureCallback(()=>r.embedder.shutdown?.()),D.start();let O=new cf;u=O,O.onIdle(async()=>{if(E.isRunning||r.indexer.isIndexing){Q.info(`Idle cleanup deferred — background tasks still running`),O.touch();return}Q.info(`Idle cleanup: releasing cached memory (connections stay open)`);try{r.store.releaseMemory?.(),r.graphStore.releaseMemory?.()}catch{}}),O.touch();let k=!1;for(let e of Object.values(w)){let t=e.handler;e.handler=async(...e)=>{if(k||(k=!0,O.markSessionActive()),O.touch(),d){let t=f(e[0]);t.length>0&&d.prioritize(...t)}return t(...e)}}process.stdin.on(`end`,()=>(Q.info(`stdin closed — MCP client disconnected. Shutting down.`),process.exit(0))),process.stdin.on(`error`,()=>(Q.info(`stdin error — MCP client disconnected. Shutting down.`),process.exit(0))),l=r,v?.(r)})(),T=async()=>{let t;try{t=await b}catch{Q.warn(`Skipping initial index — AI Kit initialization failed`);return}u?.setBusy(!0);try{let n=e.sources.map(e=>e.path).join(`, `);Q.info(`Running initial index`,{sourcePaths:n});let r=await t.indexer.index(e,e=>{e.phase===`crawling`||e.phase===`done`||(e.phase===`chunking`&&e.currentFile&&Q.debug(`Indexing file`,{current:e.filesProcessed+1,total:e.filesTotal,file:e.currentFile}),e.phase===`cleanup`&&Q.debug(`Index cleanup`,{staleEntries:e.filesTotal-e.filesProcessed}))});c=!0,Q.info(`Initial index complete`,{filesProcessed:r.filesProcessed,filesSkipped:r.filesSkipped,chunksCreated:r.chunksCreated,durationMs:r.durationMs});try{await t.store.createFtsIndex()}catch(e){Q.warn(`FTS index creation failed`,j(e))}try{let e=await t.curated.reindexAll();Q.info(`Curated re-index complete`,{indexed:e.indexed})}catch(e){Q.error(`Curated re-index failed`,j(e))}}catch(e){Q.error(`Initial index failed; will retry on aikit_reindex`,j(e))}finally{u?.setBusy(!1)}},E=new of,D=()=>E.schedule({name:`initial-index`,fn:T}),O=process.ppid,k=setInterval(()=>{try{process.kill(O,0)}catch{Q.info(`Parent process died; shutting down`,{parentPid:O}),clearInterval(k),d?.stop&&d.stop(),import(`../../tools/dist/index.js`).then(({processStopAll:e})=>e()).catch(()=>{}),b.then(async e=>{await Promise.all([e.embedder.shutdown?.().catch(()=>{})??Promise.resolve(),e.graphStore.close().catch(()=>{}),e.store.close().catch(()=>{})])}).catch(()=>{}).finally(()=>process.exit(0))}},5e3);return k.unref(),{server:a,startInit:C,ready:w,runInitialIndex:D,get aikit(){return l},scheduler:E,setSmartScheduler(e){d=e}}}const $=A(`server`);function bf(e,t){let n=fa(t,[...z,...Li],B,zi(t)),i=rf(t,n,B),a=new on({name:t.serverName??`aikit`,version:r()},{capabilities:{logging:{},completions:{},prompts:{}},instructions:i});return tr(a),Qd(a,e,t,$n(a),new tf(a),As(a),void 0,null,n),fr(a,{curated:e.curated,store:e.store,graphStore:e.graphStore},t.indexMode),a}async function xf(e){let t=await nf(e),n=bf(t,e);$.info(`MCP server configured`,{toolCount:z.length,resourceCount:2});let r=async()=>{try{let n=e.sources.map(e=>e.path).join(`, `);$.info(`Running initial index`,{sourcePaths:n});let r=await t.indexer.index(e,e=>{e.phase===`crawling`||e.phase===`done`||(e.phase===`chunking`&&e.currentFile&&$.debug(`Indexing file`,{current:e.filesProcessed+1,total:e.filesTotal,file:e.currentFile}),e.phase===`cleanup`&&$.debug(`Index cleanup`,{staleEntries:e.filesTotal-e.filesProcessed}))});$.info(`Initial index complete`,{filesProcessed:r.filesProcessed,filesSkipped:r.filesSkipped,chunksCreated:r.chunksCreated,durationMs:r.durationMs});try{await t.store.createFtsIndex()}catch(e){$.warn(`FTS index creation failed`,j(e))}try{let e=await t.curated.reindexAll();$.info(`Curated re-index complete`,{indexed:e.indexed})}catch(e){$.error(`Curated re-index failed`,j(e))}}catch(e){$.error(`Initial index failed; will retry on aikit_reindex`,j(e))}},i=async()=>{$.info(`Shutting down`),await Promise.all([t.embedder.shutdown?.().catch(()=>{})??Promise.resolve(),t.graphStore.close().catch(()=>{}),t.store.close().catch(()=>{})]),process.exit(0)};process.on(`SIGINT`,i),process.on(`SIGTERM`,i);let a=process.ppid,o=setInterval(()=>{try{process.kill(a,0)}catch{$.info(`Parent process died; shutting down`,{parentPid:a}),clearInterval(o),i()}},5e3);return o.unref(),{server:n,runInitialIndex:r,shutdown:i}}export{z as ALL_TOOL_NAMES,yf as createLazyServer,bf as createMcpServer,xf as createServer,nf as initializeAikit,Qd as registerMcpTools};
|