@vpxa/aikit 0.1.40 → 0.1.42

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.
@@ -14,5 +14,6 @@ declare function isTypedBlock(value: unknown): value is TypedBlock;
14
14
  * Handles alternative field names: text, code, headers+rows → value.
15
15
  */
16
16
  declare function normalizeBlock(block: TypedBlock): TypedBlock;
17
+ declare function normalizeContentForApp(content: unknown): unknown;
17
18
  //#endregion
18
- export { TypedBlock, formatValue, isTypedBlock, normalizeBlock, sanitizeId };
19
+ export { TypedBlock, formatValue, isTypedBlock, normalizeBlock, normalizeContentForApp, sanitizeId };
@@ -1 +1,4 @@
1
- function e(t){return t==null?`null`:Array.isArray(t)?`[${t.map(e).join(`, `)}]`:String(t)}function t(e){return e.replace(/[^a-zA-Z0-9_]/g,`_`)}function n(e){return typeof e!=`object`||!e||!(`type`in e)?!1:`value`in e||`text`in e||`headers`in e||`code`in e||`items`in e}function r(e){if(`value`in e)return e;let t=e;return typeof t.text==`string`?{...e,value:t.text}:typeof t.code==`string`?{...e,value:t.code}:Array.isArray(t.headers)&&Array.isArray(t.rows)?{...e,value:{headers:t.headers,rows:t.rows}}:Array.isArray(t.items)?{...e,value:{items:t.items}}:e}export{e as formatValue,n as isTypedBlock,r as normalizeBlock,t as sanitizeId};
1
+ function e(t){return t==null?`null`:Array.isArray(t)?`[${t.map(e).join(`, `)}]`:String(t)}function t(e){return e.replace(/[^a-zA-Z0-9_]/g,`_`)}function n(e){if(typeof e!=`object`||!e||!(`type`in e))return!1;let t=e;return`value`in t||typeof t.text==`string`||Array.isArray(t.headers)&&Array.isArray(t.rows)||typeof t.code==`string`||Array.isArray(t.items)||!!t.entries&&typeof t.entries==`object`&&!Array.isArray(t.entries)||!!t.columns&&typeof t.columns==`object`||`chartType`in t||Array.isArray(t.data)}function r(e){if(Array.isArray(e)){let t=e.filter(e=>typeof e==`object`&&!!e),n=t.map(e=>String(e.title??``)),r=Math.max(0,...t.map(e=>Array.isArray(e.items)?e.items.length:0));return{headers:n,rows:Array.from({length:r},(e,n)=>t.map(e=>Array.isArray(e.items)?e.items[n]??``:``))}}if(!e||typeof e!=`object`)return null;let t=e,n=Object.keys(t),r=Math.max(0,...n.map(e=>Array.isArray(t[e])?t[e].length:0));return{headers:n,rows:Array.from({length:r},(e,r)=>n.map(e=>Array.isArray(t[e])?t[e][r]??``:``))}}function i(e){let t=e,n=e;if(`value`in n||(typeof t.text==`string`?n={...n,value:t.text}:typeof t.code==`string`?n={...n,value:t.code}:Array.isArray(t.headers)&&Array.isArray(t.rows)?n={...n,value:{headers:t.headers,rows:t.rows}}:Array.isArray(t.items)?n={...n,value:{items:t.items}}:(`chartType`in t||Array.isArray(t.data))&&(n={...n,value:{chartType:t.chartType,data:t.data,xKey:t.xKey??`label`,yKeys:t.yKeys??[`value`]}})),n.type===`chart`){let e=n.value;if(e&&typeof e==`object`&&!Array.isArray(e)){let t=e,r=t.data&&typeof t.data==`object`&&!Array.isArray(t.data)?t.data:null,i=Array.isArray(t.labels)?t.labels:Array.isArray(r?.labels)?r.labels:null,a=Array.isArray(t.datasets)?t.datasets:Array.isArray(r?.datasets)?r.datasets:null,o=typeof t.type==`string`?t.type:typeof t.chartType==`string`?t.chartType:null;if(o&&i&&a&&!Array.isArray(t.data)){let e=a.map((e,t)=>String(e.label??`series${t+1}`)),t=i.map((e,t)=>{let n={label:String(e)};for(let[e,r]of a.entries()){let i=r,a=String(i.label??`series${e+1}`);n[a]=i.data?.[t]??0}return n});n={...n,value:{chartType:String(o),data:t,xKey:`label`,yKeys:e}}}}}let i=n;if(n.type===`text`||n.type===`paragraph`)return{...n,type:`markdown`};if(n.type===`heading`)return{...n,type:`markdown`,value:`## ${String(i.value??``)}`};if(n.type===`kv`){let e=t.entries&&typeof t.entries==`object`&&!Array.isArray(t.entries)?t.entries:i.value&&typeof i.value==`object`&&!Array.isArray(i.value)?i.value:null;if(e)return{...n,type:`table`,value:{headers:[`Key`,`Value`],rows:Object.entries(e).map(([e,t])=>[e,t])}}}if(n.type===`comparison`){let e=r(t.columns??(i.value&&typeof i.value==`object`?i.value.columns:void 0));if(e)return{...n,type:`table`,value:e};let a=i.value;if(a&&typeof a==`object`&&!Array.isArray(a)){let e=a;if(Array.isArray(e.headers)&&Array.isArray(e.rows))return{...n,type:`table`}}}if(n.type===`timeline`){let e=i.value;if(Array.isArray(e)){let t=e.map(e=>{if(e&&typeof e==`object`){let t=e;return[String(t.status??t.state??``),String(t.time??t.date??t.timestamp??``),String(t.label??t.description??t.title??t.text??``)]}return[``,``,String(e)]});return{...n,type:`table`,value:{headers:[`Status`,`Time`,`Description`],rows:t}}}}return n}function a(e){let t=e.value;if(e.type===`separator`)return{type:`markdown`,title:e.title,value:`---`};if(e.type===`checklist`){if(!t||typeof t!=`object`||!Array.isArray(t.items))return e;let n=t.items.map(e=>{if(!e||typeof e!=`object`)return null;let t=e;if(typeof t.label!=`string`)return null;let n=t.checked===!0?`x`:` `,r=typeof t.note==`string`&&t.note.length>0?` — ${t.note}`:``;return`- [${n}] ${t.label}${r}`}).filter(e=>typeof e==`string`);return n.length>0?{type:`markdown`,title:e.title,value:n.join(`
2
+ `)}:e}if(e.type===`status-board`){if(!t||typeof t!=`object`||!Array.isArray(t.items))return e;let n={success:`✅`,warning:`⚠️`,error:`❌`,info:`ℹ️`,pending:`⏳`},r=t.items.map(e=>{if(!e||typeof e!=`object`)return null;let t=e;return typeof t.label!=`string`||typeof t.status!=`string`?null:[n[t.status.toLowerCase()]??`●`,t.label,typeof t.detail==`string`?t.detail:``]}).filter(e=>Array.isArray(e));return r.length>0?{type:`table`,title:e.title,value:{headers:[`Status`,`Service`,`Detail`],rows:r}}:e}if(e.type===`prompt`){if(!t||typeof t!=`object`)return e;let n=t;if(typeof n.question!=`string`)return e;let r=[`**${n.question}**`];return typeof n.context==`string`&&n.context.length>0&&r.push(n.context),typeof n.placeholder==`string`&&n.placeholder.length>0&&r.push(`*${n.placeholder}*`),{type:`markdown`,title:e.title,value:r.join(`
3
+
4
+ `)}}if(e.type===`progress`){if(!t||typeof t!=`object`||!Array.isArray(t.items))return e;let n=t.items.map(e=>{if(!e||typeof e!=`object`)return null;let t=e;if(typeof t.label!=`string`||typeof t.value!=`number`)return null;let n=typeof t.max==`number`?t.max:100,r=Number.isFinite(n)&&n>0?n:100,i=Math.round(t.value/r*100);return[t.label,`${i}%`,`${t.value} / ${r}`]}).filter(e=>Array.isArray(e));return n.length>0?{type:`table`,title:e.title,value:{headers:[`Item`,`Progress`,`Value`],rows:n}}:e}if(e.type===`timeline`){if(!t||typeof t!=`object`||!Array.isArray(t.items))return e;let n={done:`✅`,active:`🔵`,pending:`⏳`,error:`❌`},r=t.items.map(e=>{if(!e||typeof e!=`object`)return null;let t=e;return typeof t.title==`string`?[n[typeof t.status==`string`?t.status.toLowerCase():``]??`●`,typeof t.phase==`string`&&t.phase.length>0?t.phase:t.title,typeof t.description==`string`?t.description:``]:null}).filter(e=>Array.isArray(e));return r.length>0?{type:`table`,title:e.title,value:{headers:[`Status`,`Phase`,`Description`],rows:r}}:e}return e}function o(e){return Array.isArray(e)?e.map(e=>n(e)?a(i(e)):e):e}export{e as formatValue,n as isTypedBlock,i as normalizeBlock,o as normalizeContentForApp,t as sanitizeId};
@@ -1,5 +1,5 @@
1
- import{escHtml as e}from"../present-utils.js";import{renderChecklistHtml as t,renderComparisonHtml as n,renderProgressHtml as r,renderPromptHtml as i,renderStatusBoardHtml as a,renderTimelineHtml as o}from"../present-blocks.js";import{renderChartAsHtml as s}from"../present-charts.js";import{formatValue as c,isTypedBlock as l,normalizeBlock as u,sanitizeId as d}from"./helpers.js";import{marked as f}from"marked";f.setOptions({async:!1,gfm:!0,breaks:!0}),f.use({renderer:{html(t){return e(t.text)}}});function p(e){return e.replace(/<table\b/g,`<div class="table-wrap"><table`).replace(/<\/table>/g,`</table></div>`)}function m(t,n){let r=[];if(t&&r.push(`<h1>${e(t)}</h1>`),typeof n==`string`)r.push(`<div class="md-content">${p(f.parse(n))}</div>`);else if(Array.isArray(n))if(n.length===0)r.push(`<p><em>empty</em></p>`);else if(l(n[0]))for(let e of n)r.push(h(u(e)));else typeof n[0]==`object`&&n[0]!==null?r.push(g(n)):r.push(`<ul>${n.map(t=>`<li>${e(String(t))}</li>`).join(``)}</ul>`);else if(typeof n==`object`&&n){let t=n;if(Array.isArray(t.blocks)&&t.blocks.length>0)for(let n of t.blocks)l(n)?r.push(h(u(n))):r.push(`<p>${e(String(n))}</p>`);else Array.isArray(t.metrics)?r.push(_(t.metrics)):Array.isArray(t.nodes)&&Array.isArray(t.edges)?(r.push(`<pre class="mermaid">${e(b(t))}</pre>`),r.push(`<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"><\/script>`),r.push(`<script>mermaid.run();<\/script>`)):r.push(y(t))}else r.push(`<p>${e(String(n))}</p>`);return r.join(`
2
- `)}function h(c){let l=[];switch(c.title&&l.push(`<h2>${e(c.title)}</h2>`),c.type){case`markdown`:l.push(`<div class="md-content">${p(f.parse(String(c.value??``)))}</div>`);break;case`code`:l.push(`<pre><code>${e(String(c.value??``))}</code></pre>`);break;case`mermaid`:l.push(`<pre class="mermaid">${e(String(c.value??``))}</pre>`);break;case`table`:if(Array.isArray(c.value)){let e=c.value;if(e.length>0&&Array.isArray(e[0])){let t=e,n=t[0].map(String),r=t.slice(1).map(e=>Object.fromEntries(n.map((t,n)=>[t,e[n]])));l.push(g(r))}else l.push(g(e))}else if(c.value&&typeof c.value==`object`&&`headers`in c.value&&`rows`in c.value){let{headers:e,rows:t}=c.value,n=t.map(t=>Object.fromEntries(e.map((e,n)=>[e,t[n]])));l.push(g(n))}break;case`metrics`:{let e;Array.isArray(c.value)?e=c.value:c.value&&typeof c.value==`object`&&(e=Object.entries(c.value).map(([e,t])=>({label:e,value:String(t)}))),e&&l.push(_(e));break}case`cards`:Array.isArray(c.value)&&l.push(v(c.value));break;case`tree`:c.value&&typeof c.value==`object`&&l.push(y(c.value));break;case`graph`:c.value&&typeof c.value==`object`&&l.push(`<pre class="mermaid">${e(b(c.value))}</pre>`);break;case`chart`:{let e=c.value;if(e&&!e.chartType&&e.type&&Array.isArray(e.labels)&&Array.isArray(e.datasets)){let t=e.labels,n=e.datasets,r=n.map((e,t)=>e.label??`series${t+1}`),i=t.map((e,t)=>{let i={_label:e};return n.forEach((e,n)=>{i[r[n]]=e.data[t]??0}),i}),a={type:`chart`,title:c.title,value:{chartType:String(e.type),data:i,xKey:`_label`,yKeys:r}};l.push(s(a))}else l.push(s(c));break}case`timeline`:{let e=c.value;Array.isArray(e)&&(e={items:e.map(e=>({title:String(e.event??e.title??``),phase:e.date==null?e.phase==null?void 0:String(e.phase):String(e.date),description:e.description==null?void 0:String(e.description),status:e.status??`done`}))}),e&&l.push(o(e));break}case`checklist`:c.value&&l.push(t(c.value));break;case`comparison`:c.value&&l.push(n(c.value));break;case`status-board`:c.value&&l.push(a(c.value));break;case`prompt`:c.value&&l.push(i(c.value));break;case`progress`:c.value&&l.push(r(c.value));break;case`text`:l.push(`<div class="md-content">${f.parse(String(c.value??``))}</div>`);break;case`heading`:{let t=Math.min(Math.max(Number(c.level)||2,1),6);l.push(`<h${t}>${e(String(c.value??``))}</h${t}>`);break}case`paragraph`:l.push(`<p>${e(String(c.value??``))}</p>`);break;case`separator`:l.push(`<hr class="separator">`);break;default:l.push(`<pre>${e(JSON.stringify(c.value,null,2))}</pre>`)}return l.join(`
1
+ import{escHtml as e}from"../present-utils.js";import{renderChecklistHtml as t,renderComparisonHtml as n,renderProgressHtml as r,renderPromptHtml as i,renderStatusBoardHtml as a,renderTimelineHtml as o}from"../present-blocks.js";import{renderChartAsHtml as s}from"../present-charts.js";import{formatValue as c,isTypedBlock as l,normalizeBlock as u,sanitizeId as d}from"./helpers.js";import{marked as f}from"marked";f.setOptions({async:!1,gfm:!0,breaks:!0}),f.use({renderer:{html(t){return e(t.text)}}});function p(e){return e.replace(/<table\b/g,`<div class="table-wrap"><table`).replace(/<\/table>/g,`</table></div>`)}function m(t,n){let r=[];if(t&&r.push(`<h1>${e(t)}</h1>`),typeof n==`string`)r.push(`<div class="md-content">${p(f.parse(n))}</div>`);else if(Array.isArray(n))if(n.length===0)r.push(`<p><em>empty</em></p>`);else if(l(n[0]))for(let e of n)r.push(h(u(e)));else typeof n[0]==`object`&&n[0]!==null?r.push(g(n)):r.push(`<ul>${n.map(t=>`<li>${e(String(t))}</li>`).join(``)}</ul>`);else if(typeof n==`object`&&n){let t=n;if(Array.isArray(t.blocks)&&t.blocks.length>0)for(let n of t.blocks)l(n)?r.push(h(u(n))):r.push(`<p>${e(String(n))}</p>`);else Array.isArray(t.metrics)?r.push(_(t.metrics)):Array.isArray(t.nodes)&&Array.isArray(t.edges)?(r.push(`<pre class="mermaid">${e(x(t))}</pre>`),r.push(`<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"><\/script>`),r.push(`<script>mermaid.run();<\/script>`)):r.push(y(t))}else r.push(`<p>${e(String(n))}</p>`);return r.join(`
2
+ `)}function h(c){let l=[];switch(c.title&&l.push(`<h2>${e(c.title)}</h2>`),c.type){case`markdown`:l.push(`<div class="md-content">${p(f.parse(String(c.value??``)))}</div>`);break;case`code`:l.push(`<pre><code>${e(String(c.value??``))}</code></pre>`);break;case`mermaid`:l.push(`<pre class="mermaid">${e(String(c.value??``))}</pre>`);break;case`table`:if(Array.isArray(c.value)){let e=c.value;if(e.length>0&&Array.isArray(e[0])){let t=e,n=t[0].map(String),r=t.slice(1).map(e=>Object.fromEntries(n.map((t,n)=>[t,e[n]])));l.push(g(r))}else l.push(g(e))}else if(c.value&&typeof c.value==`object`&&`headers`in c.value&&`rows`in c.value){let{headers:e,rows:t}=c.value,n=t.map(t=>Object.fromEntries(e.map((e,n)=>[e,t[n]])));l.push(g(n))}break;case`metrics`:{let e;Array.isArray(c.value)?e=c.value:c.value&&typeof c.value==`object`&&(e=Object.entries(c.value).map(([e,t])=>({label:e,value:String(t)}))),e&&l.push(_(e));break}case`cards`:Array.isArray(c.value)&&l.push(v(c.value));break;case`tree`:c.value&&typeof c.value==`object`&&l.push(y(c.value));break;case`graph`:c.value&&typeof c.value==`object`&&l.push(`<pre class="mermaid">${e(x(c.value))}</pre>`);break;case`chart`:l.push(s(c));break;case`timeline`:{let e=c.value;Array.isArray(e)&&(e={items:e.map(e=>({title:String(e.event??e.title??``),phase:e.date==null?e.phase==null?void 0:String(e.phase):String(e.date),description:e.description==null?void 0:String(e.description),status:e.status??`done`}))}),e&&l.push(o(e));break}case`checklist`:c.value&&l.push(t(c.value));break;case`comparison`:c.value&&l.push(n(c.value));break;case`status-board`:c.value&&l.push(a(c.value));break;case`prompt`:c.value&&l.push(i(c.value));break;case`progress`:c.value&&l.push(r(c.value));break;case`text`:l.push(`<div class="md-content">${f.parse(String(c.value??``))}</div>`);break;case`heading`:{let t=Math.min(Math.max(Number(c.level)||2,1),6);l.push(`<h${t}>${e(String(c.value??``))}</h${t}>`);break}case`paragraph`:l.push(`<p>${e(String(c.value??``))}</p>`);break;case`separator`:l.push(`<hr class="separator">`);break;case`actions`:{let t=(Array.isArray(c.value)?c.value:[]).map(t=>{if(t.type===`select`){let n=Array.isArray(t.options)?t.options.map(t=>`<option value="${e(String(t.value??t.label??``))}">${e(String(t.label??t.value??``))}</option>`).join(``):``;return`<select class="action-select"><option value="">${e(String(t.label??`Select...`))}</option>${n}</select>`}return`<button class="action-btn action-${String(t.variant??`secondary`)}">${e(String(t.label??``))}</button>`}).join(``);l.push(`<div class="action-bar">${t}</div>`);break}default:l.push(`<pre>${e(JSON.stringify(c.value,null,2))}</pre>`)}return l.join(`
3
3
  `)}function g(t){if(t.length===0)return`<p><em>empty table</em></p>`;let n=Object.keys(t[0]);return`<div class="table-wrap"><table><thead><tr>${n.map(t=>`<th>${e(t)}</th>`).join(``)}</tr></thead><tbody>${t.map(t=>`<tr>${n.map(n=>`<td>${e(String(t[n]??``))}</td>`).join(``)}</tr>`).join(`
4
- `)}</tbody></table></div>`}function _(t){return`<div class="metric-grid">${t.map(t=>`<div class="metric"><div class="metric-value">${e(String(t.value))}</div><div class="metric-label">${e(t.label)}</div></div>`).join(``)}</div>`}function v(t){return`<div class="card-grid">${t.map(t=>{let n=[`<div class="card">`];return t.title&&n.push(`<div class="card-title">${e(String(t.title))}</div>`),(t.body||t.description)&&n.push(`<div class="card-body">${e(String(t.body??t.description))}</div>`),(t.badge||t.status)&&n.push(`<span class="badge">${e(String(t.badge??t.status))}</span>`),n.push(`</div>`),n.join(``)}).join(``)}</div>`}function y(t){let n=[];for(let[r,i]of Object.entries(t))typeof i==`object`&&i&&!Array.isArray(i)?n.push(`<div class="tree-node"><span class="tree-key">${e(r)}:</span><div class="tree-children">${y(i)}</div></div>`):n.push(`<div class="tree-node"><span class="tree-key">${e(r)}:</span> ${e(c(i))}</div>`);return n.join(``)}function b(e){let t=[`graph LR`];for(let n of e.nodes){let e=d(String(n.id??n.name??``)),r=String(n.label??n.name??e);t.push(` ${e}["${r}"]`)}for(let n of e.edges){let e=d(String(n.source??n.from??``)),r=d(String(n.target??n.to??``)),i=n.label?`|${String(n.label)}|`:``;t.push(` ${e} -->${i} ${r}`)}return t.join(`
5
- `)}export{g as arrayToHtmlTable,v as cardsToHtml,m as contentToHtml,b as graphToMermaidRaw,_ as metricsToHtml,y as objectToHtmlTree,h as renderBlockAsHtml};
4
+ `)}</tbody></table></div>`}function _(t){return`<div class="metric-grid">${t.map(t=>`<div class="metric"><div class="metric-value">${e(String(t.value))}</div><div class="metric-label">${e(t.label)}</div></div>`).join(``)}</div>`}function v(t){return`<div class="card-grid">${t.map(t=>{let n=[`<div class="card">`];return t.title&&n.push(`<div class="card-title">${e(String(t.title))}</div>`),(t.body||t.description)&&n.push(`<div class="card-body">${e(String(t.body??t.description))}</div>`),(t.badge||t.status)&&n.push(`<span class="badge">${e(String(t.badge??t.status))}</span>`),n.push(`</div>`),n.join(``)}).join(``)}</div>`}function y(t){if(`name`in t&&`children`in t&&Array.isArray(t.children))return b(t);if(`name`in t&&!(`children`in t))return`<div class="tree-node"><span class="tree-key">${e(String(t.name))}</span></div>`;let n=[];for(let[r,i]of Object.entries(t))typeof i==`object`&&i&&!Array.isArray(i)?n.push(`<div class="tree-node"><span class="tree-key">${e(r)}:</span><div class="tree-children">${y(i)}</div></div>`):n.push(`<div class="tree-node"><span class="tree-key">${e(r)}:</span> ${e(c(i))}</div>`);return n.join(``)}function b(t){let n=e(String(t.name));return!t.children||t.children.length===0?`<div class="tree-node"><span class="tree-key">${n}</span></div>`:`<div class="tree-node"><span class="tree-key">${n}</span><div class="tree-children">${t.children.map(t=>typeof t==`object`&&t?b(t):`<div class="tree-node"><span class="tree-key">${e(String(t))}</span></div>`).join(``)}</div></div>`}function x(e){let t=[`graph LR`];for(let n of e.nodes){let e=d(String(n.id??n.name??``)),r=String(n.label??n.name??e);t.push(` ${e}["${r}"]`)}for(let n of e.edges){let e=d(String(n.source??n.from??``)),r=d(String(n.target??n.to??``)),i=n.label?`|${String(n.label)}|`:``;t.push(` ${e} -->${i} ${r}`)}return t.join(`
5
+ `)}export{g as arrayToHtmlTable,v as cardsToHtml,m as contentToHtml,x as graphToMermaidRaw,_ as metricsToHtml,y as objectToHtmlTree,h as renderBlockAsHtml};
@@ -1,9 +1,24 @@
1
1
  import { TypedBlock } from "./helpers.js";
2
2
 
3
3
  //#region packages/server/src/tools/present/markdown.d.ts
4
- declare function buildMarkdown(title: string | undefined, content: unknown): string;
5
- declare function renderBlockAsMarkdown(block: TypedBlock): string;
4
+ interface MarkdownOptions {
5
+ compactTables?: boolean;
6
+ }
7
+ declare function buildMarkdown(title: string | undefined, content: unknown, options?: MarkdownOptions): string;
8
+ declare function renderBlockAsMarkdown(block: TypedBlock, options?: MarkdownOptions): string;
6
9
  declare function arrayToMarkdownTable(rows: Record<string, unknown>[]): string;
10
+ /**
11
+ * Render an array-of-objects as a numbered list (safe for VS Code chat markdown).
12
+ * Uses the first column value as the bold label, remaining columns as inline details.
13
+ */
14
+ declare function arrayToListTable(rows: Record<string, unknown>[], title?: string): string;
15
+ /**
16
+ * Render a comparison block as grouped bullet lists (safe for VS Code chat markdown).
17
+ */
18
+ declare function comparisonToList(columns: Array<{
19
+ title: string;
20
+ items?: string[];
21
+ }>, title?: string): string;
7
22
  declare function metricsToMarkdown(metrics: Array<{
8
23
  label: string;
9
24
  value: string | number;
@@ -14,4 +29,4 @@ declare function graphToMermaid(data: {
14
29
  }): string;
15
30
  declare function objectToMarkdownTree(obj: Record<string, unknown>, indent?: number): string;
16
31
  //#endregion
17
- export { arrayToMarkdownTable, buildMarkdown, graphToMermaid, metricsToMarkdown, objectToMarkdownTree, renderBlockAsMarkdown };
32
+ export { MarkdownOptions, arrayToListTable, arrayToMarkdownTable, buildMarkdown, comparisonToList, graphToMermaid, metricsToMarkdown, objectToMarkdownTree, renderBlockAsMarkdown };
@@ -1,8 +1,11 @@
1
- import{formatValue as e,isTypedBlock as t,normalizeBlock as n,sanitizeId as r}from"./helpers.js";function i(e,r){let i=[];if(e&&i.push(`# ${e}\n`),typeof r==`string`)i.push(r);else if(Array.isArray(r))if(r.length===0)i.push(`*(empty)*`);else if(t(r[0]))for(let e of r)i.push(a(n(e)));else if(typeof r[0]==`object`&&r[0]!==null)i.push(o(r));else for(let e of r)i.push(`- ${String(e)}`);else if(typeof r==`object`&&r){let e=r;if(Array.isArray(e.blocks)&&e.blocks.length>0)for(let r of e.blocks)t(r)?i.push(a(n(r))):i.push(String(r));else Array.isArray(e.nodes)&&Array.isArray(e.edges)?i.push(c(e)):Array.isArray(e.metrics)?i.push(s(e.metrics)):i.push(l(e))}else i.push(String(r));return i.join(`
2
- `)}function a(e){let t=[];switch(e.title&&t.push(`## ${e.title}\n`),e.type){case`markdown`:t.push(String(e.value??``));break;case`code`:t.push(`\`\`\`${e.language??``}\n${String(e.value??``)}\n\`\`\``);break;case`mermaid`:t.push(`\`\`\`mermaid\n${String(e.value??``)}\n\`\`\``);break;case`table`:Array.isArray(e.value)&&t.push(o(e.value));break;case`metrics`:Array.isArray(e.value)&&t.push(s(e.value));break;case`graph`:e.value&&typeof e.value==`object`&&t.push(c(e.value));break;case`cards`:if(Array.isArray(e.value))for(let n of e.value)t.push(`### ${n.title??`Card`}`),(n.body||n.description)&&t.push(String(n.body??n.description)),(n.badge||n.status)&&t.push(`> **${n.badge??n.status}**`),t.push(``);break;case`tree`:e.value&&typeof e.value==`object`&&t.push(l(e.value));break;case`chart`:{let n=e.value;n?.data&&Array.isArray(n.data)&&t.push(`*${String(n.chartType??`chart`)} chart — ${n.data.length} data points*`);break}case`timeline`:{let n=e.value;if(n?.items)for(let e of n.items){let n=e.status===`done`?`✅`:e.status===`active`?`🔄`:e.status===`error`?`❌`:`⬜`;t.push(`${n} **${e.title}**${e.description?` — ${e.description}`:``}`)}break}case`checklist`:{let n=e.value;if(n?.items)for(let e of n.items)t.push(`- [${e.checked?`x`:` `}] ${e.label}${e.note?` — ${e.note}`:``}`);break}case`comparison`:{let n=e.value;if(n?.columns&&n.columns.length>0){let e=Math.max(...n.columns.map(e=>e.items?.length??0)),r=n.columns.map(e=>e.title);t.push(`| ${r.join(` | `)} |`),t.push(`| ${r.map(()=>`---`).join(` | `)} |`);for(let r=0;r<e;r++)t.push(`| ${n.columns.map(e=>e.items?.[r]??``).join(` | `)} |`)}break}case`status-board`:{let n=e.value;if(n?.items)for(let e of n.items){let n=e.status===`success`?`🟢`:e.status===`warning`?`🟡`:e.status===`error`?`🔴`:e.status===`info`?`🔵`:`⚪`;t.push(`${n} **${e.label}**${e.detail?` ${e.detail}`:``}`)}break}case`prompt`:{let n=e.value;n?.question&&(t.push(`> **${n.question}**`),n.context&&t.push(`> ${n.context}`));break}case`progress`:{let n=e.value;if(n?.items)for(let e of n.items){let n=e.max??100,r=n>0?Math.round(e.value/n*100):0,i=Math.round(r/5),a=`█`.repeat(i)+`░`.repeat(20-i);t.push(`${e.label}: ${a} ${r}%`)}break}case`text`:t.push(String(e.value??``));break;case`heading`:{let n=Math.min(Math.max(Number(e.level)||2,1),6);t.push(`${`#`.repeat(n)} ${String(e.value??``)}\n`);break}case`paragraph`:t.push(String(e.value??``));break;case`separator`:t.push(`---
3
- `);break;default:t.push(JSON.stringify(e.value,null,2))}return t.push(``),t.join(`
1
+ import{formatValue as e,isTypedBlock as t,normalizeBlock as n,sanitizeId as r}from"./helpers.js";function i(e,r,i){let c=[];if(e&&c.push(`# ${e}\n`),typeof r==`string`)c.push(r);else if(Array.isArray(r))if(r.length===0)c.push(`*(empty)*`);else if(t(r[0]))for(let e of r)c.push(a(n(e),i));else if(typeof r[0]==`object`&&r[0]!==null)i?.compactTables?c.push(s(r)):c.push(o(r));else for(let e of r)c.push(`- ${String(e)}`);else if(typeof r==`object`&&r){let e=r;if(Array.isArray(e.blocks)&&e.blocks.length>0)for(let r of e.blocks)t(r)?c.push(a(n(r),i)):c.push(String(r));else Array.isArray(e.nodes)&&Array.isArray(e.edges)?c.push(u(e)):Array.isArray(e.metrics)?c.push(l(e.metrics)):c.push(d(e))}else c.push(String(r));return c.join(`
2
+ `)}function a(e,t){let n=[];switch(e.title&&n.push(`## ${e.title}\n`),e.type){case`markdown`:n.push(String(e.value??``));break;case`code`:n.push(`\`\`\`${e.language??``}\n${String(e.value??``)}\n\`\`\``);break;case`mermaid`:n.push(`\`\`\`mermaid\n${String(e.value??``)}\n\`\`\``);break;case`table`:{let r=e.value;if(Array.isArray(r)&&r.length>0)t?.compactTables?n.push(s(r,e.title)):n.push(o(r));else if(r&&typeof r==`object`&&!Array.isArray(r)&&`headers`in r&&`rows`in r){let{headers:i,rows:a}=r;if(t?.compactTables){let t=[];e.title&&t.push(`**${e.title}**\n`);for(let e=0;e<a.length;e++){let n=i.map((t,n)=>`**${t}**: ${String(a[e]?.[n]??``)}`).join(` · `);t.push(`${e+1}. ${n}`)}n.push(t.join(`
3
+ `))}else{n.push(`| ${i.join(` | `)} |`),n.push(`| ${i.map(()=>`---`).join(` | `)} |`);for(let e of a)n.push(`| ${e.map(e=>String(e??``)).join(` | `)} |`)}}else n.push(`*(empty table)*`);break}case`metrics`:Array.isArray(e.value)&&n.push(l(e.value));break;case`graph`:e.value&&typeof e.value==`object`&&n.push(u(e.value));break;case`cards`:if(Array.isArray(e.value))for(let t of e.value)n.push(`### ${t.title??`Card`}`),(t.body||t.description)&&n.push(String(t.body??t.description)),(t.badge||t.status)&&n.push(`> **${t.badge??t.status}**`),n.push(``);break;case`tree`:e.value&&typeof e.value==`object`&&n.push(d(e.value));break;case`chart`:{let t=e.value;t?.data&&Array.isArray(t.data)&&n.push(`*${String(t.chartType??`chart`)} chart — ${t.data.length} data points*`);break}case`timeline`:{let t=e.value;if(t?.items)for(let e of t.items){let t=e.status===`done`?`✅`:e.status===`active`?`🔄`:e.status===`error`?`❌`:`⬜`;n.push(`${t} **${e.title}**${e.description?` — ${e.description}`:``}`)}break}case`checklist`:{let t=e.value;if(t?.items)for(let e of t.items)n.push(`- [${e.checked?`x`:` `}] ${e.label}${e.note?` — ${e.note}`:``}`);break}case`comparison`:{let r=e.value;if(r?.columns&&r.columns.length>0)if(t?.compactTables)n.push(c(r.columns,e.title));else{let e=Math.max(...r.columns.map(e=>e.items?.length??0)),t=r.columns.map(e=>e.title);n.push(`| ${t.join(` | `)} |`),n.push(`| ${t.map(()=>`---`).join(` | `)} |`);for(let t=0;t<e;t++)n.push(`| ${r.columns.map(e=>e.items?.[t]??``).join(` | `)} |`)}break}case`status-board`:{let t=e.value;if(t?.items)for(let e of t.items){let t=e.status===`success`?`🟢`:e.status===`warning`?`🟡`:e.status===`error`?`🔴`:e.status===`info`?`🔵`:`⚪`;n.push(`${t} **${e.label}**${e.detail?` — ${e.detail}`:``}`)}break}case`prompt`:{let t=e.value;t?.question&&(n.push(`> **${t.question}**`),t.context&&n.push(`> ${t.context}`));break}case`progress`:{let t=e.value;if(t?.items)for(let e of t.items){let t=e.max??100,r=t>0?Math.round(e.value/t*100):0,i=Math.round(r/5),a=`█`.repeat(i)+`░`.repeat(20-i);n.push(`${e.label}: ${a} ${r}%`)}break}case`text`:n.push(String(e.value??``));break;case`heading`:{let t=Math.min(Math.max(Number(e.level)||2,1),6);n.push(`${`#`.repeat(t)} ${String(e.value??``)}\n`);break}case`paragraph`:n.push(String(e.value??``));break;case`separator`:n.push(`---
4
+ `);break;default:n.push(JSON.stringify(e.value,null,2))}return n.push(``),n.join(`
4
5
  `)}function o(e){if(e.length===0)return`*(empty table)*`;let t=Object.keys(e[0]),n=[];n.push(`| ${t.join(` | `)} |`),n.push(`| ${t.map(()=>`---`).join(` | `)} |`);for(let r of e)n.push(`| ${t.map(e=>String(r[e]??``)).join(` | `)} |`);return n.join(`
5
- `)}function s(e){return e.map(e=>`- **${e.label}**: ${e.value}`).join(`
6
- `)}function c(e){let t=["```mermaid",`graph LR`];for(let n of e.nodes){let e=r(String(n.id??n.name??``)),i=String(n.label??n.name??e);t.push(` ${e}["${i}"]`)}for(let n of e.edges){let e=r(String(n.source??n.from??``)),i=r(String(n.target??n.to??``)),a=n.label?`|${String(n.label)}|`:``;t.push(` ${e} -->${a} ${i}`)}return t.push("```"),t.join(`
7
- `)}function l(t,n=0){let r=` `.repeat(n),i=[];for(let[a,o]of Object.entries(t))typeof o==`object`&&o&&!Array.isArray(o)?(i.push(`${r}- **${a}**:`),i.push(l(o,n+1))):i.push(`${r}- **${a}**: ${e(o)}`);return i.join(`
8
- `)}export{o as arrayToMarkdownTable,i as buildMarkdown,c as graphToMermaid,s as metricsToMarkdown,l as objectToMarkdownTree,a as renderBlockAsMarkdown};
6
+ `)}function s(e,t){if(e.length===0)return`*(empty table)*`;let n=Object.keys(e[0]),r=[];t&&r.push(`**${t}**\n`);for(let t=0;t<e.length;t++){let i=e[t],a=String(i[n[0]]??``);if(n.length===1)r.push(`${t+1}. **${a}**`);else{let e=n.slice(1).map(e=>`${e}: ${String(i[e]??``)}`).join(` · `);r.push(`${t+1}. **${a}** — ${e}`)}}return r.join(`
7
+ `)}function c(e,t){let n=[];t&&n.push(`**${t}**\n`);for(let t of e){if(n.push(`**${t.title}**`),t.items&&t.items.length>0)for(let e of t.items)n.push(`- ${e}`);else n.push(`- *(none)*`);n.push(``)}return n.join(`
8
+ `)}function l(e){return e.map(e=>`- **${e.label}**: ${e.value}`).join(`
9
+ `)}function u(e){let t=["```mermaid",`graph LR`];for(let n of e.nodes){let e=r(String(n.id??n.name??``)),i=String(n.label??n.name??e);t.push(` ${e}["${i}"]`)}for(let n of e.edges){let e=r(String(n.source??n.from??``)),i=r(String(n.target??n.to??``)),a=n.label?`|${String(n.label)}|`:``;t.push(` ${e} -->${a} ${i}`)}return t.push("```"),t.join(`
10
+ `)}function d(t,n=0){let r=` `.repeat(n),i=[];for(let[a,o]of Object.entries(t))typeof o==`object`&&o&&!Array.isArray(o)?(i.push(`${r}- **${a}**:`),i.push(d(o,n+1))):i.push(`${r}- **${a}**: ${e(o)}`);return i.join(`
11
+ `)}export{s as arrayToListTable,o as arrayToMarkdownTable,i as buildMarkdown,c as comparisonToList,u as graphToMermaid,l as metricsToMarkdown,d as objectToMarkdownTree,a as renderBlockAsMarkdown};
@@ -1,6 +1,26 @@
1
- import{getToolMeta as e}from"../../tool-metadata.js";import{buildBrowserHtml as t}from"./browser.js";import{buildMarkdown as n}from"./markdown.js";import{readFileSync as r}from"node:fs";import{dirname as i,join as a}from"node:path";import{fileURLToPath as o}from"node:url";import{z as s}from"zod";import{RESOURCE_MIME_TYPE as c,registerAppResource as l,registerAppTool as u}from"@modelcontextprotocol/ext-apps/server";import{exec as d}from"node:child_process";import{createServer as f}from"node:http";import{createUIResource as p}from"@mcp-ui/server";const m=import.meta.dirname??i(o(import.meta.url)),h=`ui://aikit/present.html`,g=s.object({type:s.enum([`button`,`select`]).describe(`Action type`),id:s.string().describe(`Unique action identifier`),label:s.string().describe(`Display label`),variant:s.enum([`primary`,`danger`,`default`]).optional().describe(`Button style variant`),options:s.array(s.union([s.string(),s.object({label:s.string(),value:s.string()})])).optional().describe(`Select options (for type=select)`)}),_={format:s.enum([`html`,`browser`]).default(`html`).describe(`Output format.
1
+ import{getToolMeta as e}from"../../tool-metadata.js";import{normalizeContentForApp as t}from"./helpers.js";import{buildBrowserHtml as n}from"./browser.js";import{buildMarkdown as r}from"./markdown.js";import{readFileSync as i}from"node:fs";import{dirname as a,join as o}from"node:path";import{fileURLToPath as s}from"node:url";import{z as c}from"zod";import{RESOURCE_MIME_TYPE as l,registerAppResource as u,registerAppTool as d}from"@modelcontextprotocol/ext-apps/server";import{exec as f}from"node:child_process";import{createServer as p}from"node:http";import{createUIResource as m}from"@mcp-ui/server";const h=import.meta.dirname??a(s(import.meta.url)),g=`ui://aikit/present.html`,_=c.object({type:c.enum([`button`,`select`]).describe(`Action type`),id:c.string().describe(`Unique action identifier`),label:c.string().describe(`Display label`),variant:c.enum([`primary`,`danger`,`default`]).optional().describe(`Button style variant`),options:c.array(c.union([c.string(),c.object({label:c.string(),value:c.string()})])).optional().describe(`Select options (for type=select)`)}),v={format:c.enum([`html`,`browser`]).default(`html`).describe(`Output format.
2
2
  - "html" (default): Rich markdown in chat + embedded UIResource for MCP-UI hosts. Actions shown as numbered choices.
3
- - "browser": Rich markdown in chat + opens beautiful themed dashboard in the browser. When actions are provided, the browser shows interactive buttons and the tool blocks until user clicks, returning their selection.`),title:s.string().optional().describe(`Optional heading`),content:s.any().describe(`Content to present. Accepts: markdown string, array of objects (→ table), { nodes, edges } (→ mermaid graph), typed blocks [{ type, value }], or any JSON (→ tree).`),actions:s.array(g).optional().describe(`Interactive actions (buttons/selects). In html mode, shown as numbered choices. In browser mode, rendered as clickable buttons and the tool blocks until user clicks.`),template:s.enum([`auto`,`list-sort`,`data-table`,`picker`,`flame-graph`,`form`,`timeline`,`kanban`,`tree`,`diff-view`,`dashboard`]).optional().describe(`UI template for interactive display in MCP Apps hosts.
3
+ - "browser": Rich markdown in chat + opens beautiful themed dashboard in the browser. When actions are provided, the browser shows interactive buttons and the tool blocks until user clicks, returning their selection.`),title:c.string().optional().describe(`Optional heading`),content:c.any().describe(`Content to present. Accepts these shapes:
4
+ • markdown string — rendered as rich text
5
+ • array of objects — auto-rendered as table
6
+ • { nodes, edges } — rendered as mermaid graph
7
+ • typed blocks array [{ type, title?, value }] — the primary format:
8
+ - type:"markdown" → value: string (markdown text)
9
+ - type:"code" → value: string, language?: string
10
+ - type:"mermaid" → value: string (mermaid syntax)
11
+ - type:"table" → value: Record[] | {headers:string[], rows:any[][]}
12
+ - type:"metrics" → value: [{label,value,trend?,status?}]
13
+ - type:"chart" → value: {chartType:"line"|"area"|"bar"|"horizontal-bar"|"pie"|"donut"|"sparkline"|"heatmap", data:Record[], xKey:string, yKeys:string[]}
14
+ - type:"cards" → value: [{title,body?,badge?,status?}]
15
+ - type:"tree" → value: {name,children?:[...]} | object
16
+ - type:"graph" → value: {nodes:[{id,label}], edges:[{from,to}]}
17
+ - type:"timeline" → value: [{title,description?,timestamp?,status?}]
18
+ - type:"checklist" → value: [{label,checked:boolean}]
19
+ - type:"status-board" → value: [{category,items:[{label,status}]}]
20
+ - type:"comparison" → value: [{title,items:string[]}] (columns)
21
+ - type:"progress" → value: {label,value:number,max?:number}
22
+ • any JSON — rendered as collapsible tree
23
+ IMPORTANT: For charts, use the ChartValue format above. Do NOT use Chart.js format ({labels, datasets}) — it will be auto-converted but the native format is preferred.`),actions:c.array(_).optional().describe(`Interactive actions (buttons/selects). In html mode, shown as numbered choices. In browser mode, rendered as clickable buttons and the tool blocks until user clicks.`),template:c.enum([`auto`,`list-sort`,`data-table`,`picker`,`flame-graph`,`form`,`timeline`,`kanban`,`tree`,`diff-view`,`dashboard`]).optional().describe(`UI template for interactive display in MCP Apps hosts.
4
24
  - auto (default): detect from content shape
5
25
  - list-sort: drag-drop reorderable list — content: { items: [{id, label}] }
6
26
  - data-table: filterable sortable table — content: { columns: [{key, label}], rows: [{...}], stats?: [{label, value}] }
@@ -11,9 +31,9 @@ import{getToolMeta as e}from"../../tool-metadata.js";import{buildBrowserHtml as
11
31
  - kanban: drag-drop board — content: { columns: [{id, label, color?}], cards: [{id, title, column, tags?[], priority?}] }
12
32
  - tree: collapsible tree view — content: { root: {label, children?:[...]} | [...] }
13
33
  - diff-view: side-by-side diff — content: { files: [{path, status, additions, deletions, hunks:[{header, changes:[{type, content}]}]}] }
14
- - dashboard: metric cards grid — content: { metrics: [{label, value, trend?, status?, type?}] }`)};let v,y=!1,b=null;process.on(`exit`,()=>{if(b){try{b.close()}catch{}b=null}});function x(){return a(m,`..`,`..`,`..`,`..`,`present`,`dist`,`index.html`)}function S(){if(!v)try{v=r(x(),`utf-8`)}catch{v=``}return v}function C(t,n){let r=e(`present`);y||=(l(t,`AI Kit Present App`,h,{description:`Rich interactive content viewer for AI Kit tools`},async()=>({contents:[{uri:h,mimeType:c,text:S()}]})),!0),u(t,`present`,{title:r.title,description:`Present content to the user in the best format. Two modes:
34
+ - dashboard: metric cards grid — content: { metrics: [{label, value, trend?, status?, type?}] }`)};let y,b=!1,x=null;process.on(`exit`,()=>{if(x){try{x.close()}catch{}x=null}});function S(){return o(h,`..`,`..`,`..`,`..`,`present`,`dist`,`index.html`)}function C(){if(!y)try{y=i(S(),`utf-8`)}catch{y=``}return y}function w(t,n){let r=e(`present`),i=C(),a=`Present content to the user in the best format. Two modes:
15
35
  - "html" (default): Rich markdown in chat + embedded UIResource. Use for display-only content (tables, charts, reports, status boards) where no user interaction is needed.
16
36
  - "browser": Serves a themed dashboard on a local URL. Use ONLY when you need user interaction back (confirmations, selections, form input). The tool blocks until user clicks an action button, then returns their selection.
17
37
  FORMAT RULE: If no user interaction is needed → use "html". If you need user input back → use "browser".
18
- BROWSER WORKFLOW: After calling present with format "browser", you MUST extract the URL from the response and call openBrowserPage({ url }) to open it in VS Code Simple Browser. A system browser fallback also opens automatically, but always call openBrowserPage yourself.`,annotations:r.annotations,inputSchema:_,_meta:{ui:{resourceUri:h}}},async({format:e,title:t,content:r,actions:i,template:a})=>(e??`html`)===`browser`||Array.isArray(i)&&i.length>0?await w(t,r,i,n,a):T(t,r,i,a))}async function w(e,r,i,a,o){let s=n(e,r),c=t(e,r,i,o),l=p({uri:`ui://aikit/present-browser.html`,content:{type:`rawHtml`,htmlString:c},encoding:`text`,adapters:{mcpApps:{enabled:!0}}}),u,m,h=Array.isArray(i)?i:[],g=``,_;try{b&&=(b.close(),null),h.length>0&&(u=new Promise(e=>{m=e}));let e=!1;g=await new Promise((t,n)=>{let r=f((t,n)=>{if(e||(e=!0,_&&clearTimeout(_)),t.method===`POST`&&t.url===`/callback`){let e=``;t.on(`data`,t=>{e+=t.toString()}),t.on(`end`,()=>{n.writeHead(200,{"Content-Type":`application/json`,"Access-Control-Allow-Origin":`*`}),n.end(`{"ok":true}`);try{let t=JSON.parse(e);m?.(t)}catch{}});return}if(t.method===`OPTIONS`){n.writeHead(204,{"Access-Control-Allow-Origin":`*`,"Access-Control-Allow-Methods":`POST`,"Access-Control-Allow-Headers":`Content-Type`}),n.end();return}n.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),n.end(c)});r.listen(0,`127.0.0.1`,()=>{let i=r.address();if(typeof i==`object`&&i){b=r;let n=`http://127.0.0.1:${i.port}`;_=setTimeout(()=>{if(!e)try{d(process.platform===`win32`?`start "" "${n}"`:process.platform===`darwin`?`open "${n}"`:`xdg-open "${n}"`)}catch{}},8e3),_.unref(),t(n)}else n(Error(`Failed to start present server`))}),setTimeout(()=>{r.close(),b===r&&(b=null)},300*1e3).unref()})}catch{}let v=g?`${s}\n\n---\n🌐 **Dashboard opened in browser:** ${g}\n\n**IMPORTANT:** You MUST now open this URL using the \`openBrowserPage\` tool (Simple Browser). If that tool is unavailable, open the URL in the system browser as a fallback. If neither works, ask the user to open the URL manually.`:s;if(h.length>0&&a?.available&&u)try{let e=await Promise.race([u,new Promise((e,t)=>setTimeout(()=>t(Error(`timeout`)),300*1e3))]);return{content:[{type:`text`,text:`${v}\n\n✅ **Selected:** ${e.actionId} = \`${e.value}\``},l]}}catch{return{content:[{type:`text`,text:`${v}\n\n⚠️ *No selection received (timed out).*`},l]}}return{content:[{type:`text`,text:v},l]}}function T(e,r,i,a){let o=Array.isArray(i)?i:[],s=n(e,r);if(o.length>0){let e=[``];for(let t=0;t<o.length;t++){let n=o[t],r=typeof n.label==`string`?n.label:`Action ${t+1}`;if(n.type===`select`&&Array.isArray(n.options)){let i=n.options.map(e=>typeof e==`string`?e:e.label).join(`, `);e.push(`${t+1}. **${r}** choose: ${i}`)}else e.push(`${t+1}. **${r}**`)}s+=`\n${e.join(`
19
- `)}`}let c=p({uri:`ui://aikit/present-static.html`,content:{type:`rawHtml`,htmlString:t(e,r,i,a)},encoding:`text`,adapters:{mcpApps:{enabled:!0}}});return{content:[{type:`text`,text:s},c]}}export{w as formatAsBrowser,T as formatAsHtml,S as getPresentHtml,C as registerPresentTool,x as resolvePresentHtml};
38
+ CONTENT GUIDE: Pass typed blocks [{ type, title?, value }] for structured content. See the \`content\` parameter description for all supported block types and their value shapes. For charts: use {chartType, data:Record[], xKey, yKeys} — NOT Chart.js format.
39
+ BROWSER WORKFLOW: After calling present with format "browser", you MUST extract the URL from the response and call openBrowserPage({ url }) tool - PlayWright MCP to open it in VS Code Simple Browser. A system browser fallback also opens automatically, but always call openBrowserPage tool - PlayWright MCP yourself.`,o=async({format:e,title:t,content:r,actions:i,template:a})=>(e??`html`)===`browser`||Array.isArray(i)&&i.length>0?await T(t,r,i,n,a):E(t,r,i,a);if(i){b||=(u(t,`AI Kit Present App`,g,{description:`Rich interactive content viewer for AI Kit tools`},async()=>({contents:[{uri:g,mimeType:l,text:i}]})),!0),d(t,`present`,{title:r.title,description:a,annotations:r.annotations,inputSchema:v,_meta:{ui:{resourceUri:g}}},o);return}t.tool(`present`,a,v,r.annotations,o)}async function T(e,t,i,a,o){let s=r(e,t,{compactTables:!0}),c=n(e,t,i,o),l=m({uri:`ui://aikit/present-browser.html`,content:{type:`rawHtml`,htmlString:c},encoding:`text`,adapters:{mcpApps:{enabled:!0}}}),u,d,h=Array.isArray(i)?i:[],g=``,_;try{x&&=(x.close(),null),h.length>0&&(u=new Promise(e=>{d=e}));let e=!1;g=await new Promise((t,n)=>{let r=p((t,n)=>{if(e||(e=!0,_&&clearTimeout(_)),t.method===`POST`&&t.url===`/callback`){let e=``;t.on(`data`,t=>{e+=t.toString()}),t.on(`end`,()=>{n.writeHead(200,{"Content-Type":`application/json`,"Access-Control-Allow-Origin":`*`}),n.end(`{"ok":true}`);try{let t=JSON.parse(e);d?.(t)}catch{}});return}if(t.method===`OPTIONS`){n.writeHead(204,{"Access-Control-Allow-Origin":`*`,"Access-Control-Allow-Methods":`POST`,"Access-Control-Allow-Headers":`Content-Type`}),n.end();return}n.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),n.end(c)});r.listen(0,`127.0.0.1`,()=>{let i=r.address();if(typeof i==`object`&&i){x=r;let n=`http://127.0.0.1:${i.port}`;_=setTimeout(()=>{if(!e)try{f(process.platform===`win32`?`start "" "${n}"`:process.platform===`darwin`?`open "${n}"`:`xdg-open "${n}"`)}catch{}},8e3),_.unref(),t(n)}else n(Error(`Failed to start present server`))}),setTimeout(()=>{r.close(),x===r&&(x=null)},300*1e3).unref()})}catch{}let v=g?`${s}\n\n---\n🌐 **Dashboard opened in browser:** ${g}\n\n**IMPORTANT:** You MUST now open this URL using the \`openBrowserPage\` tool - PlayWright MCP. If that tool is unavailable, open the URL in the system browser as a fallback. If neither works, ask the user to open the URL manually.`:s;if(h.length>0&&a?.available&&u)try{let e=await Promise.race([u,new Promise((e,t)=>setTimeout(()=>t(Error(`timeout`)),300*1e3))]);return{content:[{type:`text`,text:`${v}\n\n✅ **Selected:** ${e.actionId} = \`${e.value}\``},l]}}catch{return{content:[{type:`text`,text:`${v}\n\n⚠️ *No selection received (timed out).*`},l]}}return{content:[{type:`text`,text:v},l]}}function E(e,r,i,a){let o=Array.isArray(i)?i:[],s=e?`📊 **${e}**`:`📊 **Dashboard**`,c=m({uri:`ui://aikit/present-static.html`,content:{type:`rawHtml`,htmlString:n(e,r,i,a)},encoding:`text`,adapters:{mcpApps:{enabled:!0}}});return{content:[{type:`text`,text:s},c],structuredContent:{title:e,content:t(r),actions:o}}}export{T as formatAsBrowser,E as formatAsHtml,C as getPresentHtml,w as registerPresentTool,S as resolvePresentHtml};
@@ -9,7 +9,7 @@ import{escHtml as e}from"./present-utils.js";const t=e;function n(e){let n=e;ret
9
9
  <div class="checklist-label">${t(e.label)}</div>
10
10
  ${i}
11
11
  </div>
12
- </div>`}).join(``)}</div>`}function i(e){let n=e;if(!n?.columns||!Array.isArray(n.columns)||n.columns.length===0)return``;let r=n.columns.length,i=Math.max(...n.columns.map(e=>e.items?.length??0));return`<div class="comparison-grid" style="grid-template-columns:repeat(${r},1fr)">${n.columns.map(e=>{let n=`<div class="comparison-header">${t(e.title)}</div>`,r=[];for(let n=0;n<i;n++){let i=e.items?.[n]??``;r.push(`<div class="comparison-item">${t(i)}</div>`)}return`<div class="comparison-col">${n}${r.join(``)}</div>`}).join(``)}</div>`}function a(e){let n=e;return!n?.items||!Array.isArray(n.items)?``:`<div class="status-board">${n.items.map(e=>{let n=e.detail?`<span class="status-detail">${t(e.detail)}</span>`:``;return`<div class="status-row">
12
+ </div>`}).join(``)}</div>`}function i(e){let n=e;if(n?.left&&n?.right)return i({columns:[{title:n.left.label??`Left`,items:n.left.items??[]},{title:n.right.label??`Right`,items:n.right.items??[]}]});if(!n?.columns||!Array.isArray(n.columns)||n.columns.length===0)return``;let r=n.columns.length,a=Math.max(...n.columns.map(e=>e.items?.length??0));return`<div class="comparison-grid" style="grid-template-columns:repeat(${r},1fr)">${n.columns.map(e=>{let n=`<div class="comparison-header">${t(e.title)}</div>`,r=[];for(let n=0;n<a;n++){let i=e.items?.[n]??``;r.push(`<div class="comparison-item">${t(i)}</div>`)}return`<div class="comparison-col">${n}${r.join(``)}</div>`}).join(``)}</div>`}function a(e){let n=e;return!n?.items||!Array.isArray(n.items)?``:`<div class="status-board">${n.items.map(e=>{let n=e.detail?`<span class="status-detail">${t(e.detail)}</span>`:``;return`<div class="status-row">
13
13
  <div class="status-indicator ${e.status??`pending`}"></div>
14
14
  <span class="status-label">${t(e.label)}</span>
15
15
  ${n}
@@ -34,17 +34,17 @@ When using `format: "browser"`, the tool starts a local HTTP server and returns
34
34
  ### Steps:
35
35
  1. Call `present` with `format: "browser"` and `actions` — it returns text containing `🌐 **Dashboard opened in browser:** http://127.0.0.1:{port}`
36
36
  2. **Extract the URL from the response**
37
- 3. **Call `openBrowserPage({ url: "http://127.0.0.1:{port}" })` to open it in VS Code's Simple Browser**
37
+ 3. **Call `openBrowserPage({ url: "http://127.0.0.1:{port}" })` - PlayWright MCP to open it in VS Code's Simple Browser**
38
38
 
39
39
  ```
40
40
  // Step 1: Call present
41
41
  result = present({ format: "browser", title: "...", content: [...], actions: [...] })
42
42
 
43
43
  // Step 2: MUST open in VS Code Simple Browser
44
- openBrowserPage({ url: "http://127.0.0.1:{port}" })
44
+ openBrowserPage({ url: "http://127.0.0.1:{port}" }) // PlayWright MCP API to open URL in VS Code's Simple Browser
45
45
  ```
46
46
 
47
- > **Fallback**: The server also auto-opens the system browser as a safety net. But you should ALWAYS call `openBrowserPage` yourself so the user sees it inside VS Code.
47
+ > **Fallback**: The server also auto-opens the system browser as a safety net. But you should ALWAYS call `openBrowserPage` - PlayWright MCP yourself so the user sees it inside VS Code.
48
48
 
49
49
  **Note:** The HTTP server auto-closes after 5 minutes. Open the page promptly after receiving the URL.
50
50
 
@@ -87,6 +87,8 @@ Content is an array of **typed blocks**. Each block has `{ type, title?, value }
87
87
 
88
88
  **Chart types:** `line`, `area`, `bar`, `horizontal-bar`, `pie`, `donut`, `sparkline`, `heatmap`
89
89
 
90
+ > **⚠️ Chart Format**: Always use the ChartValue format shown above (`chartType`, `data`, `xKey`, `yKeys`). Do **NOT** use Chart.js format (`labels`, `datasets`) — while it will be auto-converted, the native ChartValue format is required for reliable rendering.
91
+
90
92
  ### New Block Types
91
93
 
92
94
  | Type | Value Shape | Renders As |