@krishanjinbo/vue-markdown-stream 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -14
- package/dist/components/MarkdownRenderer.d.ts +67 -7
- package/dist/components/blocks/ActionPills.vue.d.ts +33 -0
- package/dist/components/blocks/ConfirmBlock.vue.d.ts +35 -0
- package/dist/components/blocks/DataTableBlock.vue.d.ts +33 -0
- package/dist/components/blocks/FormBlock.vue.d.ts +37 -0
- package/dist/components/blocks/ProgressBlock.vue.d.ts +30 -0
- package/dist/components/blocks/SelectBlock.vue.d.ts +37 -0
- package/dist/components/blocks/index.d.ts +13 -0
- package/dist/composables/useAgentEvents.d.ts +27 -0
- package/dist/composables/useMarkdownParser.d.ts +6 -2
- package/dist/core/autoCloseContainers.d.ts +8 -0
- package/dist/core/eventBus.d.ts +73 -0
- package/dist/core/propValidator.d.ts +38 -0
- package/dist/index.d.ts +15 -4
- package/dist/types/index.d.ts +16 -0
- package/dist/types/protocol.d.ts +9 -0
- package/dist/vue-markdown-stream.cjs.js +58 -17
- package/dist/vue-markdown-stream.cjs.js.map +1 -1
- package/dist/vue-markdown-stream.es.js +1060 -141
- package/dist/vue-markdown-stream.es.js.map +1 -1
- package/package.json +75 -75
- package/dist/utils/autoClose.d.ts +0 -14
- package/dist/utils/htmlToVnodes.d.ts +0 -7
|
@@ -1,21 +1,32 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
2
|
-
`),
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("vue"),H=require("markdown-it"),j=require("markdown-it-container"),K=/^:::\s*(\w+)/;function T(t){const r=t.split(`
|
|
2
|
+
`),n=[];for(const a of r){const o=a.trim();o===":::"?n.length>0&&n.pop():K.test(o)&&n.push(o)}if(n.length>0){const a=n.map(()=>":::").join(`
|
|
3
|
+
`);return t+`
|
|
4
|
+
`+a}return t}function _(t){const r={},n=/(\w[\w-]*)=(?:"([^"]*)"|(\S+))/g;let a;for(;(a=n.exec(t))!==null;)r[a[1]]=a[2]??a[3]??"";const o=/(?:^|\s)(\w[\w-]*)(?=\s|$)/g,l=t.replace(/(\w[\w-]*)=(?:"([^"]*)"|(\S+))/g,"");for(;(a=o.exec(l))!==null;)r[a[1]]="true";return r}function S(t){return Object.entries(t).map(([r,n])=>{const a=r.replace(/([A-Z])/g,"-$1").toLowerCase(),o=n.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">");return`data-${a}="${o}"`}).join(" ")}const J=[{name:"alert",validate:t=>/^alert/.test(t.trim()),render:(t,r)=>{const n=t[r];return n.nesting===1?`<vue-block data-component="AlertBlock" data-type="${n.info.trim().replace("alert","").trim()||"info"}">
|
|
5
5
|
`:`</vue-block>
|
|
6
|
-
`}}
|
|
6
|
+
`}},{name:"card",validate:t=>/^card/.test(t.trim()),render:(t,r)=>{const n=t[r];return n.nesting===1?`<vue-block data-component="DataCard" data-title="${n.info.trim().replace("card","").trim().replace(/"/g,""")}">
|
|
7
7
|
`:`</vue-block>
|
|
8
|
-
`}}
|
|
8
|
+
`}},{name:"confirm",validate:t=>/^\s*confirm/.test(t.trim()),render:(t,r)=>{const n=t[r];if(n.nesting===1){const a=n.info.trim(),o=_(a.replace(/^confirm\s*/,""));return`<vue-block data-component="ConfirmBlock" ${S(o)}>
|
|
9
|
+
`}return`</vue-block>
|
|
10
|
+
`}},{name:"select",validate:t=>/^\s*select/.test(t.trim()),render:(t,r)=>{const n=t[r];if(n.nesting===1){const a=n.info.trim(),o=_(a.replace(/^select\s*/,""));return`<vue-block data-component="SelectBlock" ${S(o)}>
|
|
11
|
+
`}return`</vue-block>
|
|
12
|
+
`}},{name:"form",validate:t=>/^\s*form/.test(t.trim()),render:(t,r)=>{const n=t[r];if(n.nesting===1){const a=n.info.trim(),o=_(a.replace(/^form\s*/,""));return`<vue-block data-component="FormBlock" ${S(o)}>
|
|
13
|
+
`}return`</vue-block>
|
|
14
|
+
`}},{name:"progress",validate:t=>/^\s*progress/.test(t.trim()),render:(t,r)=>{const n=t[r];if(n.nesting===1){const a=n.info.trim(),o=_(a.replace(/^progress\s*/,""));return`<vue-block data-component="ProgressBlock" ${S(o)}>
|
|
15
|
+
`}return`</vue-block>
|
|
16
|
+
`}},{name:"datatable",validate:t=>/^\s*datatable/.test(t.trim()),render:(t,r)=>{const n=t[r];if(n.nesting===1){const a=n.info.trim(),o=_(a.replace(/^datatable\s*/,""));return`<vue-block data-component="DataTableBlock" ${S(o)}>
|
|
17
|
+
`}return`</vue-block>
|
|
18
|
+
`}},{name:"actions",validate:t=>/^\s*actions/.test(t.trim()),render:(t,r)=>t[r].nesting===1?`<vue-block data-component="ActionPills">
|
|
19
|
+
`:`</vue-block>
|
|
20
|
+
`}];function w(){const t=new H({html:!0,linkify:!0,typographer:!0});for(const n of J)t.use(j,n.name,{validate:n.validate,render:n.render});function r(n){const a=T(n);return t.render(a)}return{parse:r,md:t}}const W=(()=>{const{parse:t}=w();return t})();function G(t){return W(t)}function M(){const t=new Set,r=e.ref([]),n=e.ref(null);function a(d){const c={...d,timestamp:Date.now()};r.value.push({...c,consumed:!1}),n.value=c,t.forEach(m=>{try{m(c)}catch(f){console.error("[AgentEventBus] Handler error:",f)}})}function o(d){return t.add(d),()=>t.delete(d)}function l(){r.value=[],n.value=null}function i(){t.clear(),l()}return{emit:a,on:o,clearHistory:l,destroy:i,history:e.readonly(r),lastEvent:e.readonly(n)}}const N=["onclick","onload","onerror","onmouseover","onfocus","onblur","onsubmit","onchange","oninput","onkeydown","onkeyup","onkeypress","href","src","action","formaction","srcdoc","innerHTML","outerHTML","dangerouslySetInnerHTML","style","is"],$={ConfirmBlock:{allowed:{action:{type:"string",required:!0,maxLength:100},level:{type:"enum",enum:["info","warning","danger"],default:"warning"},confirmText:{type:"string",default:"确认",maxLength:20},cancelText:{type:"string",default:"取消",maxLength:20}}},SelectBlock:{allowed:{mode:{type:"enum",enum:["single","multiple"],default:"single"},columns:{type:"number",default:2,min:1,max:4},maxSelect:{type:"number",default:1,min:1,max:20}}},FormBlock:{allowed:{id:{type:"string",required:!0,maxLength:50},submitText:{type:"string",default:"提交",maxLength:20},layout:{type:"enum",enum:["vertical","horizontal"],default:"vertical"}}},ProgressBlock:{allowed:{value:{type:"number",required:!0,min:0,max:100},max:{type:"number",default:100,min:1},label:{type:"string",maxLength:100},status:{type:"enum",enum:["active","success","error"],default:"active"}}},DataTableBlock:{allowed:{sortable:{type:"boolean",default:!1},filterable:{type:"boolean",default:!1},pageSize:{type:"number",default:10,min:5,max:100}}},ActionPills:{allowed:{layout:{type:"enum",enum:["inline","wrap"],default:"inline"}}},AlertBlock:{allowed:{type:{type:"enum",enum:["info","success","warning","error"],default:"info"}}},DataCard:{allowed:{title:{type:"string",maxLength:100}}}};function A(t,r,n=$){const a=n[t],o={};if(!a){for(const[d,c]of Object.entries(r))N.includes(d.toLowerCase())||(o[d]=c);return o}const l=new Set([...N,...a.blocked||[]]),i={};for(const[d,c]of Object.entries(r))l.has(d.toLowerCase())||(i[d]=c);for(const[d,c]of Object.entries(a.allowed)){const m=i[d];if(m===void 0||m===""){c.required&&console.warn(`[PropValidator] Missing required prop "${d}" for ${t}`),c.default!==void 0&&(o[d]=c.default);continue}switch(c.type){case"string":{let f=String(m);c.maxLength&&f.length>c.maxLength&&(f=f.slice(0,c.maxLength)),o[d]=f;break}case"number":{const f=Number(m);if(isNaN(f))o[d]=c.default??0;else{let k=f;c.min!==void 0&&(k=Math.max(k,c.min)),c.max!==void 0&&(k=Math.min(k,c.max)),o[d]=k}break}case"boolean":o[d]=m==="true"||m==="1"||m==="yes";break;case"enum":c.enum?.includes(m)?o[d]=m:o[d]=c.default??c.enum?.[0];break}}return o}function D(t){try{return JSON.parse(JSON.stringify(t))}catch{return{}}}const X={class:"alert-header"},Y={class:"alert-icon"},Z={class:"alert-label"},Q={class:"alert-body"},ee=e.defineComponent({__name:"AlertBlock",props:{type:{default:"info"}},setup(t){const r=t,n={info:"ℹ️",success:"✅",warning:"⚠️",error:"❌"},a={info:"提示",success:"成功",warning:"注意",error:"错误"};return(o,l)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["alert-block",`alert-${r.type}`])},[e.createElementVNode("div",X,[e.createElementVNode("span",Y,e.toDisplayString(n[r.type]),1),e.createElementVNode("span",Z,e.toDisplayString(a[r.type]),1)]),e.createElementVNode("div",Q,[e.renderSlot(o.$slots,"default",{},void 0,!0)])],2))}}),L=(t,r)=>{const n=t.__vccOpts||t;for(const[a,o]of r)n[a]=o;return n},F=L(ee,[["__scopeId","data-v-ebc7ae3e"]]),te={class:"data-card"},ne={key:0,class:"card-header"},oe={class:"card-title"},re={class:"card-body"},le=e.defineComponent({__name:"DataCard",props:{title:{}},setup(t){return(r,n)=>(e.openBlock(),e.createElementBlock("div",te,[t.title?(e.openBlock(),e.createElementBlock("div",ne,[n[0]||(n[0]=e.createElementVNode("span",{class:"card-icon"},"📋",-1)),e.createElementVNode("span",oe,e.toDisplayString(t.title),1)])):e.createCommentVNode("",!0),e.createElementVNode("div",re,[e.renderSlot(r.$slots,"default",{},void 0,!0)])]))}}),z=L(le,[["__scopeId","data-v-ece81c58"]]),ae={class:"confirm-content"},se={key:0,style:{display:"flex",gap:"12px","margin-top":"12px"}},ce={key:1,style:{"margin-top":"12px","font-size":"14px",color:"#666"}},P=e.defineComponent({__name:"ConfirmBlock",props:{action:{default:""},level:{default:"warning"},confirmText:{default:"确认"},cancelText:{default:"取消"},blockId:{},eventBus:{}},emits:["agent-action"],setup(t,{emit:r}){const n=t,a=r,o=e.ref(!1),l=e.ref(null);function i(){o.value||(o.value=!0,l.value="confirmed",a("agent-action","confirm",{action:n.action,confirmed:!0}))}function d(){o.value||(o.value=!0,l.value="cancelled",a("agent-action","cancel",{action:n.action,confirmed:!1}))}const c={info:{border:"#4472C4",bg:"#E8F0FE"},warning:{border:"#E6A23C",bg:"#FDF6EC"},danger:{border:"#F56C6C",bg:"#FEF0F0"}};return(m,f)=>(e.openBlock(),e.createElementBlock("div",{class:"confirm-block",style:e.normalizeStyle({borderLeft:`4px solid ${c[t.level].border}`,background:c[t.level].bg,padding:"16px",borderRadius:"8px",margin:"12px 0"})},[e.createElementVNode("div",ae,[e.renderSlot(m.$slots,"default")]),o.value?(e.openBlock(),e.createElementBlock("div",ce,e.toDisplayString(l.value==="confirmed"?"✓ 已确认":"✗ 已取消"),1)):(e.openBlock(),e.createElementBlock("div",se,[e.createElementVNode("button",{class:"confirm-btn confirm-btn--primary",style:e.normalizeStyle({background:c[t.level].border,color:"#fff",border:"none",padding:"8px 20px",borderRadius:"6px",cursor:"pointer",fontSize:"14px"}),onClick:i},e.toDisplayString(t.confirmText),5),e.createElementVNode("button",{class:"confirm-btn confirm-btn--secondary",style:{background:"transparent",color:"#666",border:"1px solid #ddd",padding:"8px 20px",borderRadius:"6px",cursor:"pointer",fontSize:"14px"},onClick:d},e.toDisplayString(t.cancelText),1)]))],4))}}),ie={class:"select-block",style:{margin:"12px 0"}},de=["onClick"],ue={style:{"font-weight":"500","font-size":"14px",color:"#333"}},me={key:0,style:{"font-size":"12px",color:"#999","margin-top":"4px"}},pe={key:1,style:{"margin-top":"12px","font-size":"14px",color:"#52c41a"}},O=e.defineComponent({__name:"SelectBlock",props:{mode:{default:"single"},columns:{default:2},maxSelect:{default:1},blockId:{},eventBus:{}},emits:["agent-action"],setup(t,{emit:r}){const n=t,a=r,o=e.ref([]),l=e.ref(new Set),i=e.ref(!1),d=e.ref(null);let c="";function m(){if(!d.value)return;const u=d.value.innerHTML;if(u===c)return;c=u;const v=d.value.querySelectorAll("li");o.value=Array.from(v).map(s=>{const p=s.textContent?.trim()||"",g=p.split("|").map(y=>y.trim());return{title:g[0]||p,subtitle:g[1]||""}})}e.onMounted(m),e.onUpdated(m);function f(u){if(!i.value)if(n.mode==="single")l.value=new Set([u]);else{const v=new Set(l.value);v.has(u)?v.delete(u):v.size<n.maxSelect&&v.add(u),l.value=v}}function k(){i.value||l.value.size===0||(i.value=!0,a("agent-action","select",{selectedIndices:Array.from(l.value),selectedOptions:Array.from(l.value).map(u=>o.value[u])}))}return(u,v)=>(e.openBlock(),e.createElementBlock("div",ie,[e.createElementVNode("div",{ref_key:"slotContainer",ref:d,style:{display:"none"}},[e.renderSlot(u.$slots,"default")],512),e.createElementVNode("div",{style:e.normalizeStyle({display:"grid",gridTemplateColumns:`repeat(${t.columns}, 1fr)`,gap:"10px"})},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(o.value,(s,p)=>(e.openBlock(),e.createElementBlock("div",{key:p,style:e.normalizeStyle({padding:"12px 16px",border:l.value.has(p)?"2px solid #5B4FC4":"1px solid #e8e8e8",borderRadius:"8px",cursor:i.value?"default":"pointer",background:l.value.has(p)?"#F5F3FF":"#fff",opacity:i.value&&!l.value.has(p)?.5:1,transition:"all 0.15s"}),onClick:g=>f(p)},[e.createElementVNode("div",ue,e.toDisplayString(s.title),1),s.subtitle?(e.openBlock(),e.createElementBlock("div",me,e.toDisplayString(s.subtitle),1)):e.createCommentVNode("",!0)],12,de))),128))],4),!i.value&&l.value.size>0?(e.openBlock(),e.createElementBlock("button",{key:0,style:{"margin-top":"12px",background:"#5B4FC4",color:"#fff",border:"none",padding:"10px 24px","border-radius":"8px",cursor:"pointer","font-size":"14px"},onClick:k}," 确认选择 ")):e.createCommentVNode("",!0),i.value?(e.openBlock(),e.createElementBlock("div",pe," ✓ 已选择 ")):e.createCommentVNode("",!0)]))}}),fe={class:"form-block",style:{margin:"12px 0",border:"1px solid #e8e8e8","border-radius":"10px",padding:"16px"}},ve={style:{"font-size":"13px",color:"#666","font-weight":"500"}},ke=["onUpdate:modelValue","placeholder"],ge=["onUpdate:modelValue","placeholder"],ye=["onUpdate:modelValue"],be=["value"],xe={key:3,style:{display:"flex","align-items":"center",gap:"8px",cursor:"pointer"}},he=["onUpdate:modelValue"],Be={style:{"font-size":"14px"}},_e={key:2,style:{color:"#52c41a","font-size":"14px"}},I=e.defineComponent({__name:"FormBlock",props:{id:{default:""},submitText:{default:"提交"},layout:{default:"vertical"},blockId:{},eventBus:{}},emits:["agent-action"],setup(t,{emit:r}){const n=t,a=r,o=e.ref([]),l=e.ref({}),i=e.ref(!1);function d(u){const v=u.querySelectorAll("li"),s=[];v.forEach(p=>{const y=(p.textContent?.trim()||"").match(/^(\w+):\s*(text|textarea|select|checkbox)\s*(.*)$/);if(!y)return;const h=y[1],B=y[2],b=y[3]||"",x={name:h,type:B,placeholder:""};if(B==="select"){const C=b.match(/\[([^\]]+)\]/);C&&(x.options=C[1].split(",").map(E=>E.trim().replace(/^["']|["']$/g,"")))}else B==="checkbox"?(x.placeholder=b.replace(/^["']|["']$/g,"").trim(),l.value[h]=!1):(x.placeholder=b.replace(/^["']|["']$/g,"").trim(),l.value[h]="");s.push(x)}),o.value=s}const c=e.ref(null);let m="";function f(){if(!c.value)return;const u=c.value.innerHTML;u!==m&&(m=u,d(c.value))}e.onMounted(f),e.onUpdated(f);function k(){i.value||(i.value=!0,a("agent-action","submit",{formId:n.id,values:{...l.value}}))}return(u,v)=>(e.openBlock(),e.createElementBlock("div",fe,[e.createElementVNode("div",{ref_key:"slotContainer",ref:c,style:{display:"none"}},[e.renderSlot(u.$slots,"default")],512),i.value?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("div",{key:0,style:e.normalizeStyle({display:"flex",flexDirection:t.layout==="vertical"?"column":"row",gap:"12px",flexWrap:"wrap"})},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(o.value,s=>(e.openBlock(),e.createElementBlock("div",{key:s.name,style:{display:"flex","flex-direction":"column",gap:"4px","min-width":"200px",flex:"1"}},[e.createElementVNode("label",ve,e.toDisplayString(s.name),1),s.type==="text"?e.withDirectives((e.openBlock(),e.createElementBlock("input",{key:0,"onUpdate:modelValue":p=>l.value[s.name]=p,placeholder:s.placeholder,style:{padding:"8px 12px",border:"1px solid #ddd","border-radius":"6px","font-size":"14px"}},null,8,ke)),[[e.vModelText,l.value[s.name]]]):s.type==="textarea"?e.withDirectives((e.openBlock(),e.createElementBlock("textarea",{key:1,"onUpdate:modelValue":p=>l.value[s.name]=p,placeholder:s.placeholder,style:{padding:"8px 12px",border:"1px solid #ddd","border-radius":"6px","font-size":"14px","min-height":"80px",resize:"vertical"}},null,8,ge)),[[e.vModelText,l.value[s.name]]]):s.type==="select"?e.withDirectives((e.openBlock(),e.createElementBlock("select",{key:2,"onUpdate:modelValue":p=>l.value[s.name]=p,style:{padding:"8px 12px",border:"1px solid #ddd","border-radius":"6px","font-size":"14px"}},[v[0]||(v[0]=e.createElementVNode("option",{value:"",disabled:""},"请选择",-1)),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(s.options,p=>(e.openBlock(),e.createElementBlock("option",{key:p,value:p},e.toDisplayString(p),9,be))),128))],8,ye)),[[e.vModelSelect,l.value[s.name]]]):s.type==="checkbox"?(e.openBlock(),e.createElementBlock("label",xe,[e.withDirectives(e.createElementVNode("input",{type:"checkbox","onUpdate:modelValue":p=>l.value[s.name]=p},null,8,he),[[e.vModelCheckbox,l.value[s.name]]]),e.createElementVNode("span",Be,e.toDisplayString(s.placeholder),1)])):e.createCommentVNode("",!0)]))),128))],4)),i.value?(e.openBlock(),e.createElementBlock("div",_e,"✓ 已提交")):(e.openBlock(),e.createElementBlock("button",{key:1,style:{"margin-top":"14px",background:"#5B4FC4",color:"#fff",border:"none",padding:"10px 24px","border-radius":"8px",cursor:"pointer","font-size":"14px"},onClick:k},e.toDisplayString(t.submitText),1))]))}}),Se={class:"progress-block",style:{margin:"12px 0"}},Ce={style:{display:"flex","justify-content":"space-between","align-items":"center","margin-bottom":"6px"}},Ee={style:{"font-size":"14px",color:"#333"}},we={style:{"font-size":"13px",color:"#999","font-variant-numeric":"tabular-nums"}},$e={style:{height:"8px",background:"#f0f0f0","border-radius":"4px",overflow:"hidden"}},R=e.defineComponent({__name:"ProgressBlock",props:{value:{default:0},max:{default:100},label:{},status:{default:"active"},blockId:{},eventBus:{}},setup(t){const r=t,n=e.computed(()=>Math.min(100,Math.max(0,r.value/r.max*100))),a=e.computed(()=>({active:"#5B4FC4",success:"#52c41a",error:"#F56C6C"})[r.status]);return(o,l)=>(e.openBlock(),e.createElementBlock("div",Se,[e.createElementVNode("div",Ce,[e.createElementVNode("div",Ee,[e.renderSlot(o.$slots,"default")]),e.createElementVNode("span",we,e.toDisplayString(n.value.toFixed(0))+"% ",1)]),e.createElementVNode("div",$e,[e.createElementVNode("div",{style:e.normalizeStyle({width:n.value+"%",height:"100%",background:a.value,borderRadius:"4px",transition:"width 0.3s ease"})},null,4)])]))}}),Ne={class:"datatable-block",style:{margin:"12px 0",border:"1px solid #e8e8e8","border-radius":"10px",overflow:"hidden"}},Ve={key:0,style:{padding:"10px 14px","border-bottom":"1px solid #f0f0f0"}},Te={key:1,style:{width:"100%","border-collapse":"collapse","font-size":"14px"}},Me=["onClick"],Ae={key:0,style:{"margin-left":"4px"}},U=e.defineComponent({__name:"DataTableBlock",props:{sortable:{type:Boolean,default:!1},filterable:{type:Boolean,default:!1},pageSize:{default:10},blockId:{},eventBus:{}},setup(t){const r=t,n=e.ref([]),a=e.ref([]),o=e.ref(null),l=e.ref("asc"),i=e.ref(""),d=e.ref(null);let c="";function m(){if(!d.value)return;const u=d.value.innerHTML;if(u===c)return;c=u;const v=d.value.querySelector("table");if(!v)return;const s=v.querySelectorAll("th");n.value=Array.from(s).map(g=>g.textContent?.trim()||"");const p=v.querySelectorAll("tbody tr");a.value=Array.from(p).map(g=>Array.from(g.querySelectorAll("td")).map(y=>y.textContent?.trim()||""))}e.onMounted(m),e.onUpdated(m);const f=e.computed(()=>{let u=[...a.value];if(r.filterable&&i.value){const v=i.value.toLowerCase();u=u.filter(s=>s.some(p=>p.toLowerCase().includes(v)))}if(r.sortable&&o.value!==null){const v=o.value,s=l.value==="asc"?1:-1;u.sort((p,g)=>(p[v]??"").localeCompare(g[v]??"")*s)}return u});function k(u){r.sortable&&(o.value===u?l.value=l.value==="asc"?"desc":"asc":(o.value=u,l.value="asc"))}return(u,v)=>(e.openBlock(),e.createElementBlock("div",Ne,[e.createElementVNode("div",{ref_key:"slotContainer",ref:d,style:{display:"none"}},[e.renderSlot(u.$slots,"default")],512),t.filterable?(e.openBlock(),e.createElementBlock("div",Ve,[e.withDirectives(e.createElementVNode("input",{"onUpdate:modelValue":v[0]||(v[0]=s=>i.value=s),placeholder:"搜索...",style:{width:"100%",padding:"6px 10px",border:"1px solid #ddd","border-radius":"6px","font-size":"13px","box-sizing":"border-box"}},null,512),[[e.vModelText,i.value]])])):e.createCommentVNode("",!0),n.value.length?(e.openBlock(),e.createElementBlock("table",Te,[e.createElementVNode("thead",null,[e.createElementVNode("tr",null,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(n.value,(s,p)=>(e.openBlock(),e.createElementBlock("th",{key:p,style:e.normalizeStyle({padding:"10px 14px",textAlign:"left",background:"#FAFAFA",borderBottom:"1px solid #f0f0f0",cursor:t.sortable?"pointer":"default",userSelect:"none",fontSize:"13px",fontWeight:600,color:"#555"}),onClick:g=>k(p)},[e.createTextVNode(e.toDisplayString(s)+" ",1),t.sortable&&o.value===p?(e.openBlock(),e.createElementBlock("span",Ae,e.toDisplayString(l.value==="asc"?"↑":"↓"),1)):e.createCommentVNode("",!0)],12,Me))),128))])]),e.createElementVNode("tbody",null,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(f.value,(s,p)=>(e.openBlock(),e.createElementBlock("tr",{key:p},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(s,(g,y)=>(e.openBlock(),e.createElementBlock("td",{key:y,style:{padding:"10px 14px",borderBottom:"1px solid #f5f5f5",color:"#333"}},e.toDisplayString(g),1))),128))]))),128))])])):e.createCommentVNode("",!0)]))}}),De={class:"action-pills",style:{margin:"12px 0"}},Le=["disabled","onClick"],q=e.defineComponent({__name:"ActionPills",props:{layout:{default:"inline"},blockId:{},eventBus:{}},emits:["agent-action"],setup(t,{emit:r}){const n=r,a=e.ref([]),o=e.ref(null),l=e.ref(null);let i="";function d(){if(!l.value)return;const m=l.value.innerHTML;if(m===i)return;i=m;const f=l.value.querySelectorAll("li");a.value=Array.from(f).map(k=>k.textContent?.trim()||"")}e.onMounted(d),e.onUpdated(d);function c(m,f){o.value===null&&(o.value=f,n("agent-action","pill_click",{text:m,index:f}))}return(m,f)=>(e.openBlock(),e.createElementBlock("div",De,[e.createElementVNode("div",{ref_key:"slotContainer",ref:l,style:{display:"none"}},[e.renderSlot(m.$slots,"default")],512),e.createElementVNode("div",{style:e.normalizeStyle({display:"flex",flexWrap:t.layout==="wrap"?"wrap":"nowrap",gap:"8px",overflowX:"auto"})},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(a.value,(k,u)=>(e.openBlock(),e.createElementBlock("button",{key:u,disabled:o.value!==null,style:e.normalizeStyle({display:"flex",alignItems:"center",gap:"6px",padding:"8px 16px",background:o.value===u?"#EDE9FE":"#F5F5F5",border:o.value===u?"1px solid #5B4FC4":"1px solid #E8E8E8",borderRadius:"20px",cursor:o.value!==null?"default":"pointer",fontSize:"13px",color:o.value===u?"#5B4FC4":"#555",whiteSpace:"nowrap",opacity:o.value!==null&&o.value!==u?.5:1,transition:"all 0.2s"}),onClick:v=>c(k,u)},e.toDisplayString(k),13,Le))),128))],4)]))}}),Fe={AlertBlock:F,DataCard:z,ConfirmBlock:P,SelectBlock:O,FormBlock:I,ProgressBlock:R,DataTableBlock:U,ActionPills:q},ze=Symbol("agentEventBus"),Pe=e.defineComponent({name:"MarkdownRenderer",props:{content:{type:String,required:!0},components:{type:Object,default:()=>({})},propsSchemas:{type:Object,default:()=>({})},enableValidation:{type:Boolean,default:!0}},emits:{"agent:action":t=>!0},setup(t,{emit:r}){const n={...Fe,...t.components},a={...$,...t.propsSchemas},o=M();o.on(m=>{r("agent:action",m)}),e.provide(ze,o);const{parse:l}=w(),i=e.ref([]);let d=0;function c(m){if(m.nodeType===Node.TEXT_NODE)return m.textContent||null;if(m.nodeType!==Node.ELEMENT_NODE)return null;const f=m,k=f.tagName.toLowerCase();if(k==="vue-block"){const s=f.getAttribute("data-component")||"",p=n[s];if(!p){console.warn(`[MarkdownRenderer] Unknown component: ${s}, fallback to HTML`);const b=Array.from(f.childNodes).map(c).filter(Boolean);return e.h("div",{class:"markdown-block-fallback"},b)}const g={};for(const b of Array.from(f.attributes))if(b.name.startsWith("data-")&&b.name!=="data-component"){const x=b.name.replace("data-","").replace(/-([a-z])/g,(C,E)=>E.toUpperCase());g[x]=b.value}const y=t.enableValidation?A(s,g,a):g,h=g.id||`block-${++d}`,B=Array.from(f.childNodes).map(c).filter(Boolean);return e.h(p,{...y,blockId:h,eventBus:o,"onAgent-action":(b,x)=>{o.emit({blockId:h,event:b,componentType:s,data:D(x)})}},{default:()=>B})}const u=Array.from(f.childNodes).map(c).filter(Boolean),v={};for(const s of Array.from(f.attributes))v[s.name]=s.value;return e.h(k,v,u)}return e.watch(()=>t.content,m=>{d=0;const f=l(m),v=new DOMParser().parseFromString(`<div>${f}</div>`,"text/html").body.firstChild;i.value=Array.from(v.childNodes).map(c).filter(Boolean)},{immediate:!0}),e.onBeforeUnmount(()=>{o.destroy()}),()=>e.h("div",{class:"markdown-body"},i.value)}}),V=`# Agent UI Protocol 演示
|
|
9
21
|
|
|
10
|
-
|
|
22
|
+
这是一个演示 **Agent UI Protocol v2** 流式输出 + 交互组件的示例。
|
|
11
23
|
|
|
12
|
-
##
|
|
24
|
+
## 基础 Markdown 渲染
|
|
13
25
|
|
|
14
26
|
- ✅ 流式打字机渲染
|
|
15
27
|
- ✅ 普通 Markdown 语法(标题、粗体、代码等)
|
|
16
28
|
- ✅ \`:::alert\` 块 → AlertBlock 组件
|
|
17
29
|
- ✅ \`:::card\` 块 → DataCard 组件
|
|
18
|
-
- ✅ 流式未闭合时自动补全容器
|
|
19
30
|
|
|
20
31
|
## 代码示例
|
|
21
32
|
|
|
@@ -32,6 +43,38 @@ md.use(container, 'alert', {
|
|
|
32
43
|
**渲染原理**:markdown-it-container 的 \`render\` 回调输出自定义 \`<vue-block>\` 占位元素,再由 \`DOMParser\` 递归转为 \`h()\` VNode 树。
|
|
33
44
|
:::
|
|
34
45
|
|
|
46
|
+
## v2 新增:交互组件
|
|
47
|
+
|
|
48
|
+
### 确认操作
|
|
49
|
+
|
|
50
|
+
::: confirm action="delete_user" level=danger
|
|
51
|
+
确认要删除用户 **张三** 吗?此操作不可撤销。
|
|
52
|
+
:::
|
|
53
|
+
|
|
54
|
+
### 进度展示
|
|
55
|
+
|
|
56
|
+
::: progress value=73 max=100 status=active
|
|
57
|
+
正在导入数据... 已处理 73/100 条
|
|
58
|
+
:::
|
|
59
|
+
|
|
60
|
+
### 数据表格
|
|
61
|
+
|
|
62
|
+
::: datatable sortable filterable
|
|
63
|
+
| 姓名 | 部门 | 状态 |
|
|
64
|
+
|------|------|------|
|
|
65
|
+
| 张三 | 研发 | 在线 |
|
|
66
|
+
| 李四 | 产品 | 离线 |
|
|
67
|
+
| 王五 | 设计 | 忙碌 |
|
|
68
|
+
:::
|
|
69
|
+
|
|
70
|
+
### 快捷操作
|
|
71
|
+
|
|
72
|
+
::: actions
|
|
73
|
+
- 查看处理进度
|
|
74
|
+
- 生成报告
|
|
75
|
+
- 导出数据
|
|
76
|
+
:::
|
|
77
|
+
|
|
35
78
|
## 数据展示
|
|
36
79
|
|
|
37
80
|
::: card 技术栈对比
|
|
@@ -48,24 +91,22 @@ md.use(container, 'alert', {
|
|
|
48
91
|
:::
|
|
49
92
|
|
|
50
93
|
::: alert success
|
|
51
|
-
✨
|
|
94
|
+
✨ Agent UI Protocol v2 已就绪!支持 Confirm / Select / Form / Progress / DataTable / Actions 等交互组件。
|
|
52
95
|
:::
|
|
53
96
|
|
|
54
97
|
::: card 实现步骤
|
|
55
98
|
|
|
56
99
|
1. **预处理**:\`autoCloseContainers()\` 补全未闭合 \`:::\`
|
|
57
100
|
2. **解析**:\`markdown-it\` + \`markdown-it-container\` 输出含 \`<vue-block>\` 的 HTML
|
|
58
|
-
3.
|
|
59
|
-
4.
|
|
60
|
-
|
|
61
|
-
|
|
101
|
+
3. **校验**:\`PropValidator\` 对 Props 进行白名单校验 + 清洗
|
|
102
|
+
4. **转换**:\`DOMParser\` 遍历 DOM,将 \`<vue-block>\` 替换为 \`h(Vue组件)\`
|
|
103
|
+
5. **事件**:\`AgentEventBus\` 将组件交互事件传递给宿主应用
|
|
104
|
+
6. **渲染**:\`MarkdownRenderer\` 组件通过 render 函数返回 VNode 树
|
|
62
105
|
|
|
63
|
-
::: alert error
|
|
64
|
-
注意:此方案仅适用于**客户端渲染**场景,SSR 环境下需将 \`DOMParser\` 替换为 \`parse5\`。
|
|
65
106
|
:::
|
|
66
107
|
|
|
67
108
|
---
|
|
68
109
|
|
|
69
110
|
🎉 流式输出完成!
|
|
70
|
-
`;function
|
|
111
|
+
`;function Oe(){const t=e.ref(""),r=e.ref(!1);let n=null,a=0;function o(){r.value||(r.value=!0,a=t.value.length,n=setInterval(()=>{if(a>=V.length){l();return}const d=Math.floor(Math.random()*3)+1;t.value+=V.slice(a,a+d),a+=d},30))}function l(){n!==null&&(clearInterval(n),n=null),r.value=!1}function i(){l(),t.value="",a=0}return{text:t,isStreaming:r,startStream:o,stopStream:l,resetStream:i}}function Ie(){const t=e.ref(null),r=e.ref([]),n=e.ref(!1);function a(l){return async i=>{t.value=i,r.value.push(i),n.value=!0;try{await l(i)}finally{n.value=!1}}}function o(l){const i=[`[用户操作] ${l.componentType}`];switch(l.event){case"confirm":i.push(`确认了操作: ${l.data.action||""}`);break;case"cancel":i.push(`取消了操作: ${l.data.action||""}`);break;case"submit":i.push(`提交了表单: ${JSON.stringify(l.data.values||{})}`);break;case"select":i.push(`选择了: ${JSON.stringify(l.data.selectedIndices||[])}`);break;case"pill_click":i.push(`点击了快捷操作: "${l.data.text||""}"`);break;default:i.push(`${l.event}: ${JSON.stringify(l.data)}`)}return i.join(" — ")}return{lastAction:t,actionHistory:r,isProcessing:n,createHandler:a,serializeToMessage:o}}const Re="2.0.0",Ue=["alert","card","confirm","select","form","progress","datatable","actions"];exports.ActionPills=q;exports.AlertBlock=F;exports.ConfirmBlock=P;exports.DataCard=z;exports.DataTableBlock=U;exports.FormBlock=I;exports.MarkdownRenderer=Pe;exports.PROTOCOL_VERSION=Re;exports.ProgressBlock=R;exports.SUPPORTED_BLOCKS=Ue;exports.SelectBlock=O;exports.autoCloseContainers=T;exports.createAgentEventBus=M;exports.defaultSchemas=$;exports.renderMarkdown=G;exports.sanitizePayload=D;exports.useAgentEvents=Ie;exports.useMarkdownParser=w;exports.useStreamingText=Oe;exports.validateProps=A;
|
|
71
112
|
//# sourceMappingURL=vue-markdown-stream.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue-markdown-stream.cjs.js","sources":["../src/utils/autoClose.ts","../src/composables/useMarkdownParser.ts","../src/utils/htmlToVnodes.ts","../src/components/blocks/AlertBlock.vue","../src/components/blocks/DataCard.vue","../src/components/MarkdownRenderer.ts","../src/composables/useStreamingText.ts"],"sourcesContent":["/**\r\n * 流式输出场景下自动补全未闭合的 ::: 容器块\r\n * markdown-it-container 需要完整的开闭标记才能正确解析\r\n *\r\n * 例如流式输出到一半:\r\n * ::: alert warning\r\n * 这是一条\r\n *\r\n * 会被补全为:\r\n * ::: alert warning\r\n * 这是一条\r\n * :::\r\n */\r\nexport function autoCloseContainers(raw: string): string {\r\n const lines = raw.split('\\n')\r\n const stack: string[] = []\r\n\r\n for (const line of lines) {\r\n const trimmed = line.trim()\r\n\r\n // 匹配开标记 ::: xxx(后面有内容)\r\n if (/^:::\\s+\\S/.test(trimmed)) {\r\n stack.push(trimmed)\r\n continue\r\n }\r\n\r\n // 匹配闭标记 ::: (仅三冒号)\r\n if (trimmed === ':::') {\r\n stack.pop()\r\n }\r\n }\r\n\r\n // 补全所有未闭合的块\r\n if (stack.length === 0) return raw\r\n\r\n return raw + '\\n' + stack.map(() => ':::').join('\\n')\r\n}\r\n","import MarkdownIt from 'markdown-it'\r\nimport container from 'markdown-it-container'\r\nimport { autoCloseContainers } from '../utils/autoClose'\r\n\r\nconst md = new MarkdownIt({\r\n html: true,\r\n linkify: true,\r\n typographer: true,\r\n})\r\n\r\n// ::: alert [type]\r\n// type: info | success | warning | error\r\nmd.use(container, 'alert', {\r\n validate(params: string) {\r\n return /^alert(\\s+(info|success|warning|error))?/.test(params.trim())\r\n },\r\n render(tokens: any[], idx: number) {\r\n const token = tokens[idx]\r\n if (token.nesting === 1) {\r\n const match = token.info.trim().match(/^alert\\s*(\\S*)/)\r\n const type = match?.[1] || 'info'\r\n return `<vue-block data-component=\"AlertBlock\" data-type=\"${type}\">\\n`\r\n }\r\n return '</vue-block>\\n'\r\n },\r\n})\r\n\r\n// ::: card [title]\r\nmd.use(container, 'card', {\r\n validate(params: string) {\r\n return /^card/.test(params.trim())\r\n },\r\n render(tokens: any[], idx: number) {\r\n const token = tokens[idx]\r\n if (token.nesting === 1) {\r\n const match = token.info.trim().match(/^card\\s*(.*)/)\r\n const title = match?.[1]?.trim() || ''\r\n const safeTitle = title.replace(/\"/g, '"')\r\n return `<vue-block data-component=\"DataCard\" data-title=\"${safeTitle}\">\\n`\r\n }\r\n return '</vue-block>\\n'\r\n },\r\n})\r\n\r\n/**\r\n * 将 markdown 字符串渲染为 HTML\r\n * 流式场景下会自动补全未闭合的容器块\r\n */\r\nexport function renderMarkdown(raw: string): string {\r\n const completed = autoCloseContainers(raw)\r\n return md.render(completed)\r\n}\r\n","import { h, type Component, type VNode } from 'vue'\r\n\r\nexport type ComponentMap = Record<string, Component>\r\n\r\n/**\r\n * 递归将 DOM 节点转换为 VNode\r\n * 遇到 <vue-block data-component=\"Xxx\"> 时挂载对应 Vue 组件\r\n */\r\nfunction domNodeToVNode(node: Node, componentMap: ComponentMap): VNode | string | null {\r\n // 文本节点\r\n if (node.nodeType === Node.TEXT_NODE) {\r\n return node.textContent || ''\r\n }\r\n\r\n // 注释节点忽略\r\n if (node.nodeType === Node.COMMENT_NODE) {\r\n return null\r\n }\r\n\r\n const el = node as Element\r\n const tag = el.tagName?.toLowerCase()\r\n\r\n // Vue 组件占位块\r\n if (tag === 'vue-block') {\r\n const compName = el.getAttribute('data-component')\r\n if (!compName || !componentMap[compName]) {\r\n // 找不到组件时降级渲染内部 HTML\r\n return h('div', { innerHTML: el.innerHTML })\r\n }\r\n\r\n const Comp = componentMap[compName]\r\n\r\n // 收集 data-* 作为 props(排除 data-component)\r\n const props: Record<string, string> = {}\r\n for (const attr of Array.from(el.attributes)) {\r\n if (attr.name !== 'data-component' && attr.name.startsWith('data-')) {\r\n const key = attr.name.slice(5) // 去掉 'data-'\r\n props[key] = attr.value\r\n }\r\n }\r\n\r\n // 递归转换子节点作为 default slot\r\n const children = domNodesToVNodes(el.childNodes, componentMap)\r\n return h(Comp, props, { default: () => children })\r\n }\r\n\r\n // 普通 HTML 元素:递归处理子节点\r\n const children = domNodesToVNodes(el.childNodes, componentMap)\r\n\r\n // 收集标准属性\r\n const attrs: Record<string, string> = {}\r\n for (const attr of Array.from(el.attributes)) {\r\n attrs[attr.name] = attr.value\r\n }\r\n\r\n return h(tag, attrs, children)\r\n}\r\n\r\nfunction domNodesToVNodes(nodes: NodeList, componentMap: ComponentMap): (VNode | string)[] {\r\n const result: (VNode | string)[] = []\r\n for (const node of Array.from(nodes)) {\r\n const vnode = domNodeToVNode(node, componentMap)\r\n if (vnode !== null) {\r\n result.push(vnode)\r\n }\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * 将 markdown-it 输出的 HTML 字符串转换为 VNode 数组\r\n * 其中 <vue-block> 自定义标签会被替换为真实 Vue 组件\r\n */\r\nexport function htmlToVnodes(html: string, componentMap: ComponentMap): (VNode | string)[] {\r\n if (!html) return []\r\n\r\n const parser = new DOMParser()\r\n // 包裹在 div 中确保能解析 fragment\r\n const doc = parser.parseFromString(`<div id=\"__root\">${html}</div>`, 'text/html')\r\n const root = doc.getElementById('__root')\r\n if (!root) return []\r\n\r\n return domNodesToVNodes(root.childNodes, componentMap)\r\n}\r\n","<script setup lang=\"ts\">\r\ntype AlertType = 'info' | 'success' | 'warning' | 'error'\r\n\r\nconst props = withDefaults(\r\n defineProps<{ type?: AlertType }>(),\r\n { type: 'info' }\r\n)\r\n\r\nconst iconMap: Record<AlertType, string> = {\r\n info: 'ℹ️',\r\n success: '✅',\r\n warning: '⚠️',\r\n error: '❌',\r\n}\r\n\r\nconst labelMap: Record<AlertType, string> = {\r\n info: '提示',\r\n success: '成功',\r\n warning: '注意',\r\n error: '错误',\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"alert-block\" :class=\"`alert-${props.type}`\">\r\n <div class=\"alert-header\">\r\n <span class=\"alert-icon\">{{ iconMap[props.type] }}</span>\r\n <span class=\"alert-label\">{{ labelMap[props.type] }}</span>\r\n </div>\r\n <div class=\"alert-body\">\r\n <slot />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style scoped>\r\n.alert-block {\r\n border-left: 4px solid;\r\n border-radius: 6px;\r\n padding: 12px 16px;\r\n margin: 16px 0;\r\n background: var(--alert-bg);\r\n border-color: var(--alert-border);\r\n}\r\n\r\n.alert-info { --alert-bg: #eff6ff; --alert-border: #3b82f6; --alert-label-color: #1d4ed8; }\r\n.alert-success { --alert-bg: #f0fdf4; --alert-border: #22c55e; --alert-label-color: #15803d; }\r\n.alert-warning { --alert-bg: #fffbeb; --alert-border: #f59e0b; --alert-label-color: #b45309; }\r\n.alert-error { --alert-bg: #fef2f2; --alert-border: #ef4444; --alert-label-color: #b91c1c; }\r\n\r\n.alert-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n margin-bottom: 8px;\r\n font-weight: 600;\r\n font-size: 0.9em;\r\n color: var(--alert-label-color);\r\n}\r\n\r\n.alert-icon { font-size: 1em; }\r\n\r\n.alert-body {\r\n font-size: 0.92em;\r\n line-height: 1.7;\r\n color: #374151;\r\n}\r\n\r\n.alert-body :deep(p) {\r\n margin: 4px 0;\r\n}\r\n\r\n.alert-body :deep(code) {\r\n background: rgba(0, 0, 0, 0.07);\r\n padding: 1px 5px;\r\n border-radius: 3px;\r\n font-size: 0.88em;\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\ndefineProps<{\r\n title?: string\r\n}>()\r\n</script>\r\n\r\n<template>\r\n <div class=\"data-card\">\r\n <div v-if=\"title\" class=\"card-header\">\r\n <span class=\"card-icon\">📋</span>\r\n <span class=\"card-title\">{{ title }}</span>\r\n </div>\r\n <div class=\"card-body\">\r\n <slot />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style scoped>\r\n.data-card {\r\n border: 1px solid #e5e7eb;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n margin: 16px 0;\r\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\r\n background: #fff;\r\n}\r\n\r\n.card-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 10px 16px;\r\n background: #f8fafc;\r\n border-bottom: 1px solid #e5e7eb;\r\n font-weight: 600;\r\n font-size: 0.9em;\r\n color: #374151;\r\n}\r\n\r\n.card-icon { font-size: 1em; }\r\n\r\n.card-body {\r\n padding: 14px 16px;\r\n font-size: 0.93em;\r\n line-height: 1.7;\r\n color: #374151;\r\n}\r\n\r\n.card-body :deep(p) {\r\n margin: 4px 0;\r\n}\r\n\r\n.card-body :deep(ul),\r\n.card-body :deep(ol) {\r\n padding-left: 20px;\r\n margin: 6px 0;\r\n}\r\n\r\n.card-body :deep(table) {\r\n width: 100%;\r\n border-collapse: collapse;\r\n font-size: 0.9em;\r\n}\r\n\r\n.card-body :deep(th),\r\n.card-body :deep(td) {\r\n border: 1px solid #e5e7eb;\r\n padding: 6px 10px;\r\n text-align: left;\r\n}\r\n\r\n.card-body :deep(th) {\r\n background: #f3f4f6;\r\n font-weight: 600;\r\n}\r\n\r\n.card-body :deep(code) {\r\n background: rgba(0, 0, 0, 0.07);\r\n padding: 1px 5px;\r\n border-radius: 3px;\r\n font-size: 0.88em;\r\n}\r\n</style>\r\n","import { defineComponent, h, type PropType, type VNode } from 'vue'\r\nimport { renderMarkdown } from '../composables/useMarkdownParser'\r\nimport { htmlToVnodes, type ComponentMap } from '../utils/htmlToVnodes'\r\nimport AlertBlock from './blocks/AlertBlock.vue'\r\nimport DataCard from './blocks/DataCard.vue'\r\n\r\nconst componentMap: ComponentMap = {\r\n AlertBlock,\r\n DataCard,\r\n}\r\n\r\nexport default defineComponent({\r\n name: 'MarkdownRenderer',\r\n props: {\r\n content: {\r\n type: String as PropType<string>,\r\n default: '',\r\n },\r\n },\r\n setup(props) {\r\n return () => {\r\n const html = renderMarkdown(props.content)\r\n const vnodes = htmlToVnodes(html, componentMap)\r\n return h('div', { class: 'markdown-body' }, vnodes as VNode[])\r\n }\r\n },\r\n})\r\n","import { ref } from 'vue'\r\n\r\nconst MOCK_TEXT = `# 流式 Markdown 渲染演示\r\n\r\n这是一个演示**流式输出 Markdown 内容**并将特定块渲染为 Vue 组件的示例。\r\n\r\n## 功能特性\r\n\r\n- ✅ 流式打字机渲染\r\n- ✅ 普通 Markdown 语法(标题、粗体、代码等)\r\n- ✅ \\`:::alert\\` 块 → AlertBlock 组件\r\n- ✅ \\`:::card\\` 块 → DataCard 组件\r\n- ✅ 流式未闭合时自动补全容器\r\n\r\n## 代码示例\r\n\r\n\\`\\`\\`typescript\r\nconst md = new MarkdownIt()\r\nmd.use(container, 'alert', {\r\n render(tokens, idx) {\r\n return '<vue-block data-component=\"AlertBlock\">'\r\n }\r\n})\r\n\\`\\`\\`\r\n\r\n::: alert info\r\n**渲染原理**:markdown-it-container 的 \\`render\\` 回调输出自定义 \\`<vue-block>\\` 占位元素,再由 \\`DOMParser\\` 递归转为 \\`h()\\` VNode 树。\r\n:::\r\n\r\n## 数据展示\r\n\r\n::: card 技术栈对比\r\n\r\n| 方案 | 性能 | 体积 |\r\n|------|------|------|\r\n| h() + DOMParser | ✅ 优秀 | ✅ 轻量 |\r\n| compile() | ❌ 每次重编译 | ❌ +14KB |\r\n\r\n:::\r\n\r\n::: alert warning\r\n未闭合的容器块在流式输出过程中会由 \\`autoCloseContainers()\\` 自动补全,确保 markdown-it 始终解析到**合法输入**。\r\n:::\r\n\r\n::: alert success\r\n✨ 所有功能均已实现!当前页面正是流式渲染的实时效果,块组件完整挂载了 Vue 响应式系统。\r\n:::\r\n\r\n::: card 实现步骤\r\n\r\n1. **预处理**:\\`autoCloseContainers()\\` 补全未闭合 \\`:::\\`\r\n2. **解析**:\\`markdown-it\\` + \\`markdown-it-container\\` 输出含 \\`<vue-block>\\` 的 HTML\r\n3. **转换**:\\`DOMParser\\` 遍历 DOM,将 \\`<vue-block>\\` 节点替换为 \\`h(Vue组件)\\`\r\n4. **渲染**:\\`MarkdownRenderer\\` 组件通过 render 函数返回 VNode 树\r\n\r\n:::\r\n\r\n::: alert error\r\n注意:此方案仅适用于**客户端渲染**场景,SSR 环境下需将 \\`DOMParser\\` 替换为 \\`parse5\\`。\r\n:::\r\n\r\n---\r\n\r\n🎉 流式输出完成!\r\n`\r\n\r\n/**\r\n * Mock 流式文本输出\r\n * 模拟 AI 逐字输出场景\r\n */\r\nexport function useStreamingText() {\r\n const text = ref('')\r\n const isStreaming = ref(false)\r\n let timer: ReturnType<typeof setInterval> | null = null\r\n let position = 0\r\n\r\n function startStream() {\r\n if (isStreaming.value) return\r\n\r\n isStreaming.value = true\r\n position = text.value.length // 支持续播\r\n\r\n timer = setInterval(() => {\r\n if (position >= MOCK_TEXT.length) {\r\n stopStream()\r\n return\r\n }\r\n // 每次追加 1~3 个字符,模拟真实流式速度\r\n const chunkSize = Math.floor(Math.random() * 3) + 1\r\n text.value += MOCK_TEXT.slice(position, position + chunkSize)\r\n position += chunkSize\r\n }, 30)\r\n }\r\n\r\n function stopStream() {\r\n if (timer !== null) {\r\n clearInterval(timer)\r\n timer = null\r\n }\r\n isStreaming.value = false\r\n }\r\n\r\n function resetStream() {\r\n stopStream()\r\n text.value = ''\r\n position = 0\r\n }\r\n\r\n return {\r\n text,\r\n isStreaming,\r\n startStream,\r\n stopStream,\r\n resetStream,\r\n }\r\n}\r\n"],"names":["autoCloseContainers","raw","lines","stack","line","trimmed","md","MarkdownIt","container","params","tokens","idx","token","renderMarkdown","completed","domNodeToVNode","node","componentMap","el","tag","compName","h","Comp","props","attr","key","children","domNodesToVNodes","attrs","nodes","result","vnode","htmlToVnodes","html","root","__props","iconMap","labelMap","_createElementBlock","_normalizeClass","_createElementVNode","_hoisted_1","_hoisted_2","_toDisplayString","_hoisted_3","_hoisted_4","_renderSlot","_ctx","_openBlock","_cache","AlertBlock","DataCard","MarkdownRenderer","defineComponent","vnodes","MOCK_TEXT","useStreamingText","text","ref","isStreaming","timer","position","startStream","stopStream","chunkSize","resetStream"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vue-markdown-stream.cjs.js","sources":["../src/core/autoCloseContainers.ts","../src/composables/useMarkdownParser.ts","../src/core/eventBus.ts","../src/core/propValidator.ts","../src/components/blocks/AlertBlock.vue","../src/components/blocks/DataCard.vue","../src/components/blocks/ConfirmBlock.vue","../src/components/blocks/SelectBlock.vue","../src/components/blocks/FormBlock.vue","../src/components/blocks/ProgressBlock.vue","../src/components/blocks/DataTableBlock.vue","../src/components/blocks/ActionPills.vue","../src/components/blocks/index.ts","../src/components/MarkdownRenderer.ts","../src/composables/useStreamingText.ts","../src/composables/useAgentEvents.ts","../src/types/protocol.ts"],"sourcesContent":["const CONTAINER_PATTERN = /^:::\\s*(\\w+)/\n\n/**\n * 自动补全未闭合的 ::: 容器块\n *\n * 增强点(v2):\n * - 支持嵌套容器块的正确补全\n * - 支持 ::: 后带属性参数的容器\n */\nexport function autoCloseContainers(content: string): string {\n const lines = content.split('\\n')\n const openStack: string[] = [] // 记录打开的容器类型\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n if (trimmed === ':::') {\n // 关闭最近一个打开的容器\n if (openStack.length > 0) {\n openStack.pop()\n }\n } else if (CONTAINER_PATTERN.test(trimmed)) {\n // 打开一个新容器\n openStack.push(trimmed)\n }\n }\n\n // 补全所有未关闭的容器(从内到外)\n if (openStack.length > 0) {\n const closings = openStack.map(() => ':::').join('\\n')\n return content + '\\n' + closings\n }\n\n return content\n}\n","import MarkdownIt from 'markdown-it'\r\nimport container from 'markdown-it-container'\r\nimport { autoCloseContainers } from '../core/autoCloseContainers'\r\n\r\n/**\r\n * 解析块属性字符串\r\n * 输入: 'action=\"delete\" level=warning id=my-confirm'\r\n * 输出: { action: 'delete', level: 'warning', id: 'my-confirm' }\r\n */\r\nfunction parseBlockAttributes(attrStr: string): Record<string, string> {\r\n const attrs: Record<string, string> = {}\r\n // 匹配 key=value 或 key=\"value with spaces\"\r\n const regex = /(\\w[\\w-]*)=(?:\"([^\"]*)\"|(\\S+))/g\r\n let match\r\n while ((match = regex.exec(attrStr)) !== null) {\r\n attrs[match[1]!] = match[2] ?? match[3] ?? ''\r\n }\r\n // 匹配独立的布尔属性(无 = 号)\r\n const boolRegex = /(?:^|\\s)(\\w[\\w-]*)(?=\\s|$)/g\r\n const cleaned = attrStr.replace(/(\\w[\\w-]*)=(?:\"([^\"]*)\"|(\\S+))/g, '')\r\n while ((match = boolRegex.exec(cleaned)) !== null) {\r\n attrs[match[1]!] = 'true'\r\n }\r\n return attrs\r\n}\r\n\r\n/**\r\n * 将属性对象转为 data-* HTML 属性字符串\r\n */\r\nfunction attrsToDataString(attrs: Record<string, string>): string {\r\n return Object.entries(attrs)\r\n .map(([key, value]) => {\r\n // camelCase → kebab-case for data attributes\r\n const kebab = key.replace(/([A-Z])/g, '-$1').toLowerCase()\r\n // HTML 属性值转义\r\n const escaped = value\r\n .replace(/&/g, '&')\r\n .replace(/\"/g, '"')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>')\r\n return `data-${kebab}=\"${escaped}\"`\r\n })\r\n .join(' ')\r\n}\r\n\r\n/**\r\n * 支持的容器块类型及其解析规则\r\n */\r\nconst BLOCK_CONFIGS = [\r\n // --- 已有 ---\r\n {\r\n name: 'alert',\r\n validate: (params: string) => /^alert/.test(params.trim()),\r\n render: (tokens: any[], idx: number) => {\r\n const token = tokens[idx]\r\n if (token.nesting === 1) {\r\n const type = token.info.trim().replace('alert', '').trim() || 'info'\r\n return `<vue-block data-component=\"AlertBlock\" data-type=\"${type}\">\\n`\r\n }\r\n return '</vue-block>\\n'\r\n },\r\n },\r\n {\r\n name: 'card',\r\n validate: (params: string) => /^card/.test(params.trim()),\r\n render: (tokens: any[], idx: number) => {\r\n const token = tokens[idx]\r\n if (token.nesting === 1) {\r\n const title = token.info.trim().replace('card', '').trim()\r\n const safeTitle = title.replace(/\"/g, '"')\r\n return `<vue-block data-component=\"DataCard\" data-title=\"${safeTitle}\">\\n`\r\n }\r\n return '</vue-block>\\n'\r\n },\r\n },\r\n\r\n // --- v2 新增 ---\r\n {\r\n name: 'confirm',\r\n validate: (params: string) => /^\\s*confirm/.test(params.trim()),\r\n render: (tokens: any[], idx: number) => {\r\n const token = tokens[idx]\r\n if (token.nesting === 1) {\r\n const info = token.info.trim()\r\n const attrs = parseBlockAttributes(info.replace(/^confirm\\s*/, ''))\r\n return `<vue-block data-component=\"ConfirmBlock\" ${attrsToDataString(attrs)}>\\n`\r\n }\r\n return '</vue-block>\\n'\r\n },\r\n },\r\n {\r\n name: 'select',\r\n validate: (params: string) => /^\\s*select/.test(params.trim()),\r\n render: (tokens: any[], idx: number) => {\r\n const token = tokens[idx]\r\n if (token.nesting === 1) {\r\n const info = token.info.trim()\r\n const attrs = parseBlockAttributes(info.replace(/^select\\s*/, ''))\r\n return `<vue-block data-component=\"SelectBlock\" ${attrsToDataString(attrs)}>\\n`\r\n }\r\n return '</vue-block>\\n'\r\n },\r\n },\r\n {\r\n name: 'form',\r\n validate: (params: string) => /^\\s*form/.test(params.trim()),\r\n render: (tokens: any[], idx: number) => {\r\n const token = tokens[idx]\r\n if (token.nesting === 1) {\r\n const info = token.info.trim()\r\n const attrs = parseBlockAttributes(info.replace(/^form\\s*/, ''))\r\n return `<vue-block data-component=\"FormBlock\" ${attrsToDataString(attrs)}>\\n`\r\n }\r\n return '</vue-block>\\n'\r\n },\r\n },\r\n {\r\n name: 'progress',\r\n validate: (params: string) => /^\\s*progress/.test(params.trim()),\r\n render: (tokens: any[], idx: number) => {\r\n const token = tokens[idx]\r\n if (token.nesting === 1) {\r\n const info = token.info.trim()\r\n const attrs = parseBlockAttributes(info.replace(/^progress\\s*/, ''))\r\n return `<vue-block data-component=\"ProgressBlock\" ${attrsToDataString(attrs)}>\\n`\r\n }\r\n return '</vue-block>\\n'\r\n },\r\n },\r\n {\r\n name: 'datatable',\r\n validate: (params: string) => /^\\s*datatable/.test(params.trim()),\r\n render: (tokens: any[], idx: number) => {\r\n const token = tokens[idx]\r\n if (token.nesting === 1) {\r\n const info = token.info.trim()\r\n const attrs = parseBlockAttributes(info.replace(/^datatable\\s*/, ''))\r\n return `<vue-block data-component=\"DataTableBlock\" ${attrsToDataString(attrs)}>\\n`\r\n }\r\n return '</vue-block>\\n'\r\n },\r\n },\r\n {\r\n name: 'actions',\r\n validate: (params: string) => /^\\s*actions/.test(params.trim()),\r\n render: (tokens: any[], idx: number) => {\r\n const token = tokens[idx]\r\n if (token.nesting === 1) {\r\n return `<vue-block data-component=\"ActionPills\">\\n`\r\n }\r\n return '</vue-block>\\n'\r\n },\r\n },\r\n]\r\n\r\n/**\r\n * 创建 Markdown 解析器(增强版)\r\n */\r\nexport function useMarkdownParser() {\r\n const md = new MarkdownIt({ html: true, linkify: true, typographer: true })\r\n\r\n // 注册所有容器块\r\n for (const config of BLOCK_CONFIGS) {\r\n md.use(container, config.name, {\r\n validate: config.validate,\r\n render: config.render,\r\n })\r\n }\r\n\r\n function parse(content: string): string {\r\n const safeContent = autoCloseContainers(content)\r\n return md.render(safeContent)\r\n }\r\n\r\n return { parse, md }\r\n}\r\n\r\n/**\r\n * 向后兼容 v1 的 renderMarkdown 函数\r\n * @deprecated 请使用 useMarkdownParser().parse() 代替\r\n */\r\nconst _parser = /* @__PURE__ */ (() => {\r\n const { parse } = useMarkdownParser()\r\n return parse\r\n})()\r\n\r\nexport function renderMarkdown(raw: string): string {\r\n return _parser(raw)\r\n}\r\n","import { ref, readonly, type Ref } from 'vue'\n\n/**\n * Agent 事件载荷\n */\nexport interface AgentActionPayload {\n /** 触发事件的组件块 ID(自动生成或 data-id 指定) */\n blockId: string\n /** 事件类型:confirm / cancel / submit / select / click 等 */\n event: string\n /** 组件类型:ConfirmBlock / SelectBlock / FormBlock 等 */\n componentType: string\n /** 事件携带的数据 */\n data: Record<string, unknown>\n /** 事件时间戳 */\n timestamp: number\n}\n\n/**\n * Agent 事件历史记录\n */\nexport interface AgentEventRecord extends AgentActionPayload {\n /** 是否已被宿主应用消费 */\n consumed: boolean\n}\n\nexport type AgentActionHandler = (payload: AgentActionPayload) => void | Promise<void>\n\n/**\n * 创建 Agent 事件总线实例\n *\n * 设计为工厂函数而非单例,支持多个 MarkdownRenderer 实例\n * 各自拥有独立的事件空间\n */\nexport function createAgentEventBus() {\n const handlers: Set<AgentActionHandler> = new Set()\n const history: Ref<AgentEventRecord[]> = ref([])\n const lastEvent: Ref<AgentActionPayload | null> = ref(null)\n\n /**\n * 组件内部调用:触发 Agent 事件\n */\n function emit(payload: Omit<AgentActionPayload, 'timestamp'>) {\n const fullPayload: AgentActionPayload = {\n ...payload,\n timestamp: Date.now(),\n }\n\n // 记录历史\n history.value.push({ ...fullPayload, consumed: false })\n lastEvent.value = fullPayload\n\n // 通知所有监听器\n handlers.forEach((handler) => {\n try {\n handler(fullPayload)\n } catch (err) {\n console.error('[AgentEventBus] Handler error:', err)\n }\n })\n }\n\n /**\n * 宿主应用调用:监听 Agent 事件\n */\n function on(handler: AgentActionHandler) {\n handlers.add(handler)\n return () => handlers.delete(handler) // 返回取消函数\n }\n\n /**\n * 清空事件历史\n */\n function clearHistory() {\n history.value = []\n lastEvent.value = null\n }\n\n /**\n * 销毁事件总线\n */\n function destroy() {\n handlers.clear()\n clearHistory()\n }\n\n return {\n emit,\n on,\n clearHistory,\n destroy,\n history: readonly(history),\n lastEvent: readonly(lastEvent),\n }\n}\n\nexport type AgentEventBus = ReturnType<typeof createAgentEventBus>\n","/**\n * 单个 prop 的校验规则\n */\nexport interface PropRule {\n type: 'string' | 'number' | 'boolean' | 'enum'\n required?: boolean\n default?: unknown\n enum?: string[] // type 为 'enum' 时的允许值列表\n maxLength?: number // type 为 'string' 时的最大长度\n min?: number // type 为 'number' 时的最小值\n max?: number // type 为 'number' 时的最大值\n}\n\n/**\n * 组件的 Props Schema 定义\n */\nexport interface ComponentPropsSchema {\n /** 允许的 props 白名单 */\n allowed: Record<string, PropRule>\n /** 全局黑名单(会覆盖 allowed 中同名属性) */\n blocked?: string[]\n}\n\n/**\n * 全局默认黑名单 — 这些属性绝不允许从 LLM 输出传入组件\n */\nconst GLOBAL_BLOCKED_PROPS = [\n 'onclick', 'onload', 'onerror', 'onmouseover', 'onfocus', 'onblur',\n 'onsubmit', 'onchange', 'oninput', 'onkeydown', 'onkeyup', 'onkeypress',\n 'href', 'src', 'action', 'formaction', 'srcdoc',\n 'innerHTML', 'outerHTML', 'dangerouslySetInnerHTML',\n 'style', // 防止通过 style 注入\n 'is', // 防止动态组件注入\n]\n\n/**\n * 默认的内置组件 Schema 注册表\n */\nexport const defaultSchemas: Record<string, ComponentPropsSchema> = {\n ConfirmBlock: {\n allowed: {\n action: { type: 'string', required: true, maxLength: 100 },\n level: { type: 'enum', enum: ['info', 'warning', 'danger'], default: 'warning' },\n confirmText: { type: 'string', default: '确认', maxLength: 20 },\n cancelText: { type: 'string', default: '取消', maxLength: 20 },\n },\n },\n SelectBlock: {\n allowed: {\n mode: { type: 'enum', enum: ['single', 'multiple'], default: 'single' },\n columns: { type: 'number', default: 2, min: 1, max: 4 },\n maxSelect: { type: 'number', default: 1, min: 1, max: 20 },\n },\n },\n FormBlock: {\n allowed: {\n id: { type: 'string', required: true, maxLength: 50 },\n submitText: { type: 'string', default: '提交', maxLength: 20 },\n layout: { type: 'enum', enum: ['vertical', 'horizontal'], default: 'vertical' },\n },\n },\n ProgressBlock: {\n allowed: {\n value: { type: 'number', required: true, min: 0, max: 100 },\n max: { type: 'number', default: 100, min: 1 },\n label: { type: 'string', maxLength: 100 },\n status: { type: 'enum', enum: ['active', 'success', 'error'], default: 'active' },\n },\n },\n DataTableBlock: {\n allowed: {\n sortable: { type: 'boolean', default: false },\n filterable: { type: 'boolean', default: false },\n pageSize: { type: 'number', default: 10, min: 5, max: 100 },\n },\n },\n ActionPills: {\n allowed: {\n layout: { type: 'enum', enum: ['inline', 'wrap'], default: 'inline' },\n },\n },\n // AlertBlock 和 DataCard 保持兼容,不加校验限制\n AlertBlock: { allowed: { type: { type: 'enum', enum: ['info', 'success', 'warning', 'error'], default: 'info' } } },\n DataCard: { allowed: { title: { type: 'string', maxLength: 100 } } },\n}\n\n/**\n * 校验并清洗从 data-* 属性提取的 props\n *\n * @param componentName - 组件名称\n * @param rawProps - 从 data-* 提取的原始 props(全部为 string 类型)\n * @param schemas - Schema 注册表(可自定义扩展)\n * @returns 校验通过的 props 对象\n */\nexport function validateProps(\n componentName: string,\n rawProps: Record<string, string>,\n schemas: Record<string, ComponentPropsSchema> = defaultSchemas,\n): Record<string, unknown> {\n const schema = schemas[componentName]\n const result: Record<string, unknown> = {}\n\n // 无 schema 的组件:只做全局黑名单过滤,props 原样透传(向后兼容)\n if (!schema) {\n for (const [key, value] of Object.entries(rawProps)) {\n if (!GLOBAL_BLOCKED_PROPS.includes(key.toLowerCase())) {\n result[key] = value\n }\n }\n return result\n }\n\n const blocked = new Set([\n ...GLOBAL_BLOCKED_PROPS,\n ...(schema.blocked || []),\n ])\n\n // 1. 过滤黑名单\n const filtered: Record<string, string> = {}\n for (const [key, value] of Object.entries(rawProps)) {\n if (!blocked.has(key.toLowerCase())) {\n filtered[key] = value\n }\n }\n\n // 2. 按 schema 校验 + 类型转换\n for (const [propName, rule] of Object.entries(schema.allowed)) {\n const rawValue = filtered[propName]\n\n if (rawValue === undefined || rawValue === '') {\n if (rule.required) {\n console.warn(`[PropValidator] Missing required prop \"${propName}\" for ${componentName}`)\n }\n if (rule.default !== undefined) {\n result[propName] = rule.default\n }\n continue\n }\n\n // 类型转换 + 校验\n switch (rule.type) {\n case 'string': {\n let strVal = String(rawValue)\n if (rule.maxLength && strVal.length > rule.maxLength) {\n strVal = strVal.slice(0, rule.maxLength)\n }\n result[propName] = strVal\n break\n }\n\n case 'number': {\n const numVal = Number(rawValue)\n if (isNaN(numVal)) {\n result[propName] = rule.default ?? 0\n } else {\n let clamped = numVal\n if (rule.min !== undefined) clamped = Math.max(clamped, rule.min)\n if (rule.max !== undefined) clamped = Math.min(clamped, rule.max)\n result[propName] = clamped\n }\n break\n }\n\n case 'boolean':\n result[propName] = rawValue === 'true' || rawValue === '1' || rawValue === 'yes'\n break\n\n case 'enum':\n if (rule.enum?.includes(rawValue)) {\n result[propName] = rawValue\n } else {\n result[propName] = rule.default ?? rule.enum?.[0]\n }\n break\n }\n }\n\n return result\n}\n\n/**\n * 清洗事件 payload —— 防止原型链污染和函数注入\n */\nexport function sanitizePayload(payload: unknown): Record<string, unknown> {\n try {\n // JSON 序列化/反序列化:递归移除函数、Symbol、undefined、循环引用\n return JSON.parse(JSON.stringify(payload))\n } catch {\n return {}\n }\n}\n","<script setup lang=\"ts\">\r\ntype AlertType = 'info' | 'success' | 'warning' | 'error'\r\n\r\nconst props = withDefaults(\r\n defineProps<{ type?: AlertType }>(),\r\n { type: 'info' }\r\n)\r\n\r\nconst iconMap: Record<AlertType, string> = {\r\n info: 'ℹ️',\r\n success: '✅',\r\n warning: '⚠️',\r\n error: '❌',\r\n}\r\n\r\nconst labelMap: Record<AlertType, string> = {\r\n info: '提示',\r\n success: '成功',\r\n warning: '注意',\r\n error: '错误',\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"alert-block\" :class=\"`alert-${props.type}`\">\r\n <div class=\"alert-header\">\r\n <span class=\"alert-icon\">{{ iconMap[props.type] }}</span>\r\n <span class=\"alert-label\">{{ labelMap[props.type] }}</span>\r\n </div>\r\n <div class=\"alert-body\">\r\n <slot />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style scoped>\r\n.alert-block {\r\n border-left: 4px solid;\r\n border-radius: 6px;\r\n padding: 12px 16px;\r\n margin: 16px 0;\r\n background: var(--alert-bg);\r\n border-color: var(--alert-border);\r\n}\r\n\r\n.alert-info { --alert-bg: #eff6ff; --alert-border: #3b82f6; --alert-label-color: #1d4ed8; }\r\n.alert-success { --alert-bg: #f0fdf4; --alert-border: #22c55e; --alert-label-color: #15803d; }\r\n.alert-warning { --alert-bg: #fffbeb; --alert-border: #f59e0b; --alert-label-color: #b45309; }\r\n.alert-error { --alert-bg: #fef2f2; --alert-border: #ef4444; --alert-label-color: #b91c1c; }\r\n\r\n.alert-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n margin-bottom: 8px;\r\n font-weight: 600;\r\n font-size: 0.9em;\r\n color: var(--alert-label-color);\r\n}\r\n\r\n.alert-icon { font-size: 1em; }\r\n\r\n.alert-body {\r\n font-size: 0.92em;\r\n line-height: 1.7;\r\n color: #374151;\r\n}\r\n\r\n.alert-body :deep(p) {\r\n margin: 4px 0;\r\n}\r\n\r\n.alert-body :deep(code) {\r\n background: rgba(0, 0, 0, 0.07);\r\n padding: 1px 5px;\r\n border-radius: 3px;\r\n font-size: 0.88em;\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\r\ndefineProps<{\r\n title?: string\r\n}>()\r\n</script>\r\n\r\n<template>\r\n <div class=\"data-card\">\r\n <div v-if=\"title\" class=\"card-header\">\r\n <span class=\"card-icon\">📋</span>\r\n <span class=\"card-title\">{{ title }}</span>\r\n </div>\r\n <div class=\"card-body\">\r\n <slot />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<style scoped>\r\n.data-card {\r\n border: 1px solid #e5e7eb;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n margin: 16px 0;\r\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\r\n background: #fff;\r\n}\r\n\r\n.card-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 10px 16px;\r\n background: #f8fafc;\r\n border-bottom: 1px solid #e5e7eb;\r\n font-weight: 600;\r\n font-size: 0.9em;\r\n color: #374151;\r\n}\r\n\r\n.card-icon { font-size: 1em; }\r\n\r\n.card-body {\r\n padding: 14px 16px;\r\n font-size: 0.93em;\r\n line-height: 1.7;\r\n color: #374151;\r\n}\r\n\r\n.card-body :deep(p) {\r\n margin: 4px 0;\r\n}\r\n\r\n.card-body :deep(ul),\r\n.card-body :deep(ol) {\r\n padding-left: 20px;\r\n margin: 6px 0;\r\n}\r\n\r\n.card-body :deep(table) {\r\n width: 100%;\r\n border-collapse: collapse;\r\n font-size: 0.9em;\r\n}\r\n\r\n.card-body :deep(th),\r\n.card-body :deep(td) {\r\n border: 1px solid #e5e7eb;\r\n padding: 6px 10px;\r\n text-align: left;\r\n}\r\n\r\n.card-body :deep(th) {\r\n background: #f3f4f6;\r\n font-weight: 600;\r\n}\r\n\r\n.card-body :deep(code) {\r\n background: rgba(0, 0, 0, 0.07);\r\n padding: 1px 5px;\r\n border-radius: 3px;\r\n font-size: 0.88em;\r\n}\r\n</style>\r\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport type { AgentEventBus } from '../../core/eventBus'\n\nconst props = withDefaults(defineProps<{\n action?: string\n level?: 'info' | 'warning' | 'danger'\n confirmText?: string\n cancelText?: string\n blockId?: string\n eventBus?: AgentEventBus\n}>(), {\n action: '',\n level: 'warning',\n confirmText: '确认',\n cancelText: '取消',\n})\n\nconst emit = defineEmits<{\n 'agent-action': [event: string, data: Record<string, unknown>]\n}>()\n\nconst submitted = ref(false)\nconst result = ref<'confirmed' | 'cancelled' | null>(null)\n\nfunction handleConfirm() {\n if (submitted.value) return\n submitted.value = true\n result.value = 'confirmed'\n emit('agent-action', 'confirm', {\n action: props.action,\n confirmed: true,\n })\n}\n\nfunction handleCancel() {\n if (submitted.value) return\n submitted.value = true\n result.value = 'cancelled'\n emit('agent-action', 'cancel', {\n action: props.action,\n confirmed: false,\n })\n}\n\nconst levelColors = {\n info: { border: '#4472C4', bg: '#E8F0FE' },\n warning: { border: '#E6A23C', bg: '#FDF6EC' },\n danger: { border: '#F56C6C', bg: '#FEF0F0' },\n}\n</script>\n\n<template>\n <div\n class=\"confirm-block\"\n :style=\"{\n borderLeft: `4px solid ${levelColors[level].border}`,\n background: levelColors[level].bg,\n padding: '16px',\n borderRadius: '8px',\n margin: '12px 0',\n }\"\n >\n <div class=\"confirm-content\">\n <slot />\n </div>\n <div\n v-if=\"!submitted\"\n style=\"display: flex; gap: 12px; margin-top: 12px;\"\n >\n <button\n class=\"confirm-btn confirm-btn--primary\"\n :style=\"{\n background: levelColors[level].border,\n color: '#fff',\n border: 'none',\n padding: '8px 20px',\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '14px',\n }\"\n @click=\"handleConfirm\"\n >\n {{ confirmText }}\n </button>\n <button\n class=\"confirm-btn confirm-btn--secondary\"\n :style=\"{\n background: 'transparent',\n color: '#666',\n border: '1px solid #ddd',\n padding: '8px 20px',\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '14px',\n }\"\n @click=\"handleCancel\"\n >\n {{ cancelText }}\n </button>\n </div>\n <div\n v-else\n style=\"margin-top: 12px; font-size: 14px; color: #666;\"\n >\n {{ result === 'confirmed' ? '\\u2713 已确认' : '\\u2717 已取消' }}\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, onMounted, onUpdated } from 'vue'\nimport type { AgentEventBus } from '../../core/eventBus'\n\nconst props = withDefaults(defineProps<{\n mode?: 'single' | 'multiple'\n columns?: number\n maxSelect?: number\n blockId?: string\n eventBus?: AgentEventBus\n}>(), {\n mode: 'single',\n columns: 2,\n maxSelect: 1,\n})\n\nconst emit = defineEmits<{\n 'agent-action': [event: string, data: Record<string, unknown>]\n}>()\n\ninterface SelectOption {\n title: string\n subtitle: string\n}\n\nconst options = ref<SelectOption[]>([])\nconst selected = ref<Set<number>>(new Set())\nconst confirmed = ref(false)\nconst slotContainer = ref<HTMLElement | null>(null)\n\nlet lastSlotHTML = ''\n\nfunction parseSlot() {\n if (!slotContainer.value) return\n const html = slotContainer.value.innerHTML\n if (html === lastSlotHTML) return\n lastSlotHTML = html\n\n const items = slotContainer.value.querySelectorAll('li')\n options.value = Array.from(items).map((li) => {\n const text = li.textContent?.trim() || ''\n const parts = text.split('|').map((s) => s.trim())\n return {\n title: parts[0] || text,\n subtitle: parts[1] || '',\n }\n })\n}\n\nonMounted(parseSlot)\nonUpdated(parseSlot)\n\nfunction toggleSelect(index: number) {\n if (confirmed.value) return\n if (props.mode === 'single') {\n selected.value = new Set([index])\n } else {\n const s = new Set(selected.value)\n if (s.has(index)) {\n s.delete(index)\n } else if (s.size < props.maxSelect) {\n s.add(index)\n }\n selected.value = s\n }\n}\n\nfunction handleConfirm() {\n if (confirmed.value || selected.value.size === 0) return\n confirmed.value = true\n emit('agent-action', 'select', {\n selectedIndices: Array.from(selected.value),\n selectedOptions: Array.from(selected.value).map((i) => options.value[i]),\n })\n}\n</script>\n\n<template>\n <div class=\"select-block\" style=\"margin: 12px 0;\">\n <!-- 隐藏的 slot 用于解析选项 -->\n <div ref=\"slotContainer\" style=\"display: none;\">\n <slot />\n </div>\n\n <!-- 渲染的可选项 -->\n <div\n :style=\"{\n display: 'grid',\n gridTemplateColumns: `repeat(${columns}, 1fr)`,\n gap: '10px',\n }\"\n >\n <div\n v-for=\"(opt, i) in options\"\n :key=\"i\"\n :style=\"{\n padding: '12px 16px',\n border: selected.has(i) ? '2px solid #5B4FC4' : '1px solid #e8e8e8',\n borderRadius: '8px',\n cursor: confirmed ? 'default' : 'pointer',\n background: selected.has(i) ? '#F5F3FF' : '#fff',\n opacity: confirmed && !selected.has(i) ? 0.5 : 1,\n transition: 'all 0.15s',\n }\"\n @click=\"toggleSelect(i)\"\n >\n <div style=\"font-weight: 500; font-size: 14px; color: #333;\">{{ opt.title }}</div>\n <div v-if=\"opt.subtitle\" style=\"font-size: 12px; color: #999; margin-top: 4px;\">{{ opt.subtitle }}</div>\n </div>\n </div>\n\n <button\n v-if=\"!confirmed && selected.size > 0\"\n style=\"\n margin-top: 12px;\n background: #5B4FC4;\n color: #fff;\n border: none;\n padding: 10px 24px;\n border-radius: 8px;\n cursor: pointer;\n font-size: 14px;\n \"\n @click=\"handleConfirm\"\n >\n 确认选择\n </button>\n <div\n v-if=\"confirmed\"\n style=\"margin-top: 12px; font-size: 14px; color: #52c41a;\"\n >\n ✓ 已选择\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, onMounted, onUpdated } from 'vue'\nimport type { AgentEventBus } from '../../core/eventBus'\n\nconst props = withDefaults(defineProps<{\n id?: string\n submitText?: string\n layout?: 'vertical' | 'horizontal'\n blockId?: string\n eventBus?: AgentEventBus\n}>(), {\n id: '',\n submitText: '提交',\n layout: 'vertical',\n})\n\nconst emit = defineEmits<{\n 'agent-action': [event: string, data: Record<string, unknown>]\n}>()\n\ninterface FormField {\n name: string\n type: 'text' | 'textarea' | 'select' | 'checkbox'\n placeholder: string\n options?: string[]\n}\n\nconst fields = ref<FormField[]>([])\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst formData = ref<Record<string, any>>({})\nconst submitted = ref(false)\n\n/**\n * 从 slot 内容解析表单字段定义\n * 格式: \"- fieldName: type \"placeholder\"\" 或 \"- fieldName: select [\"opt1\", \"opt2\"]\"\n */\nfunction parseFieldsFromSlot(slotEl: HTMLElement) {\n const items = slotEl.querySelectorAll('li')\n const parsed: FormField[] = []\n\n items.forEach((li) => {\n const text = li.textContent?.trim() || ''\n // 匹配: name: type \"placeholder\" 或 name: select [\"opt1\", \"opt2\"]\n const match = text.match(/^(\\w+):\\s*(text|textarea|select|checkbox)\\s*(.*)$/)\n if (!match) return\n\n const name = match[1]!\n const type = match[2]! as FormField['type']\n const rest = match[3] || ''\n const field: FormField = { name, type, placeholder: '' }\n\n if (type === 'select') {\n // 解析 [\"opt1\", \"opt2\"]\n const optMatch = rest.match(/\\[([^\\]]+)\\]/)\n if (optMatch) {\n field.options = optMatch[1]!.split(',').map((s) => s.trim().replace(/^[\"']|[\"']$/g, ''))\n }\n } else if (type === 'checkbox') {\n field.placeholder = rest.replace(/^[\"']|[\"']$/g, '').trim()\n formData.value[name!] = false\n } else {\n field.placeholder = rest.replace(/^[\"']|[\"']$/g, '').trim()\n formData.value[name!] = ''\n }\n\n parsed.push(field)\n })\n\n fields.value = parsed\n}\n\nconst slotContainer = ref<HTMLElement | null>(null)\n\nlet lastSlotHTML = ''\n\nfunction tryParseSlot() {\n if (!slotContainer.value) return\n const html = slotContainer.value.innerHTML\n if (html === lastSlotHTML) return\n lastSlotHTML = html\n parseFieldsFromSlot(slotContainer.value)\n}\n\nonMounted(tryParseSlot)\nonUpdated(tryParseSlot)\n\nfunction handleSubmit() {\n if (submitted.value) return\n submitted.value = true\n emit('agent-action', 'submit', {\n formId: props.id,\n values: { ...formData.value },\n })\n}\n</script>\n\n<template>\n <div class=\"form-block\" style=\"margin: 12px 0; border: 1px solid #e8e8e8; border-radius: 10px; padding: 16px;\">\n <!-- 隐藏的 slot 用于解析字段定义 -->\n <div ref=\"slotContainer\" style=\"display: none;\">\n <slot />\n </div>\n\n <!-- 渲染的表单 -->\n <div v-if=\"!submitted\" :style=\"{ display: 'flex', flexDirection: layout === 'vertical' ? 'column' : 'row', gap: '12px', flexWrap: 'wrap' }\">\n <div\n v-for=\"field in fields\"\n :key=\"field.name\"\n style=\"display: flex; flex-direction: column; gap: 4px; min-width: 200px; flex: 1;\"\n >\n <label style=\"font-size: 13px; color: #666; font-weight: 500;\">{{ field.name }}</label>\n <input\n v-if=\"field.type === 'text'\"\n v-model=\"formData[field.name]\"\n :placeholder=\"field.placeholder\"\n style=\"padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px;\"\n />\n <textarea\n v-else-if=\"field.type === 'textarea'\"\n v-model=\"formData[field.name]\"\n :placeholder=\"field.placeholder\"\n style=\"padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; min-height: 80px; resize: vertical;\"\n />\n <select\n v-else-if=\"field.type === 'select'\"\n v-model=\"formData[field.name]\"\n style=\"padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px;\"\n >\n <option value=\"\" disabled>请选择</option>\n <option v-for=\"opt in field.options\" :key=\"opt\" :value=\"opt\">{{ opt }}</option>\n </select>\n <label v-else-if=\"field.type === 'checkbox'\" style=\"display: flex; align-items: center; gap: 8px; cursor: pointer;\">\n <input type=\"checkbox\" v-model=\"formData[field.name]\" />\n <span style=\"font-size: 14px;\">{{ field.placeholder }}</span>\n </label>\n </div>\n </div>\n\n <button\n v-if=\"!submitted\"\n style=\"margin-top: 14px; background: #5B4FC4; color: #fff; border: none; padding: 10px 24px; border-radius: 8px; cursor: pointer; font-size: 14px;\"\n @click=\"handleSubmit\"\n >\n {{ submitText }}\n </button>\n\n <div v-else style=\"color: #52c41a; font-size: 14px;\">✓ 已提交</div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { AgentEventBus } from '../../core/eventBus'\n\nconst props = withDefaults(defineProps<{\n value?: number\n max?: number\n label?: string\n status?: 'active' | 'success' | 'error'\n blockId?: string\n eventBus?: AgentEventBus\n}>(), {\n value: 0,\n max: 100,\n status: 'active',\n})\n\nconst percentage = computed(() =>\n Math.min(100, Math.max(0, (props.value / props.max) * 100))\n)\n\nconst barColor = computed(() => ({\n active: '#5B4FC4',\n success: '#52c41a',\n error: '#F56C6C',\n}[props.status]))\n</script>\n\n<template>\n <div class=\"progress-block\" style=\"margin: 12px 0;\">\n <div style=\"display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px;\">\n <div style=\"font-size: 14px; color: #333;\">\n <slot />\n </div>\n <span style=\"font-size: 13px; color: #999; font-variant-numeric: tabular-nums;\">\n {{ percentage.toFixed(0) }}%\n </span>\n </div>\n <div style=\"height: 8px; background: #f0f0f0; border-radius: 4px; overflow: hidden;\">\n <div\n :style=\"{\n width: percentage + '%',\n height: '100%',\n background: barColor,\n borderRadius: '4px',\n transition: 'width 0.3s ease',\n }\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, onMounted, onUpdated, computed } from 'vue'\nimport type { AgentEventBus } from '../../core/eventBus'\n\nconst props = withDefaults(defineProps<{\n sortable?: boolean\n filterable?: boolean\n pageSize?: number\n blockId?: string\n eventBus?: AgentEventBus\n}>(), {\n sortable: false,\n filterable: false,\n pageSize: 10,\n})\n\nconst headers = ref<string[]>([])\nconst rows = ref<string[][]>([])\nconst sortColumn = ref<number | null>(null)\nconst sortDir = ref<'asc' | 'desc'>('asc')\nconst filterText = ref('')\nconst slotContainer = ref<HTMLElement | null>(null)\n\nlet lastSlotHTML = ''\n\nfunction parseSlot() {\n if (!slotContainer.value) return\n const html = slotContainer.value.innerHTML\n if (html === lastSlotHTML) return\n lastSlotHTML = html\n\n const table = slotContainer.value.querySelector('table')\n if (!table) return\n const ths = table.querySelectorAll('th')\n headers.value = Array.from(ths).map((th) => th.textContent?.trim() || '')\n const trs = table.querySelectorAll('tbody tr')\n rows.value = Array.from(trs).map((tr) =>\n Array.from(tr.querySelectorAll('td')).map((td) => td.textContent?.trim() || '')\n )\n}\n\nonMounted(parseSlot)\nonUpdated(parseSlot)\n\nconst filteredRows = computed(() => {\n let result = [...rows.value]\n if (props.filterable && filterText.value) {\n const q = filterText.value.toLowerCase()\n result = result.filter((row) => row.some((cell) => cell.toLowerCase().includes(q)))\n }\n if (props.sortable && sortColumn.value !== null) {\n const col = sortColumn.value\n const dir = sortDir.value === 'asc' ? 1 : -1\n result.sort((a, b) => (a[col] ?? '').localeCompare(b[col] ?? '') * dir)\n }\n return result\n})\n\nfunction toggleSort(index: number) {\n if (!props.sortable) return\n if (sortColumn.value === index) {\n sortDir.value = sortDir.value === 'asc' ? 'desc' : 'asc'\n } else {\n sortColumn.value = index\n sortDir.value = 'asc'\n }\n}\n</script>\n\n<template>\n <div class=\"datatable-block\" style=\"margin: 12px 0; border: 1px solid #e8e8e8; border-radius: 10px; overflow: hidden;\">\n <div ref=\"slotContainer\" style=\"display: none;\">\n <slot />\n </div>\n <div v-if=\"filterable\" style=\"padding: 10px 14px; border-bottom: 1px solid #f0f0f0;\">\n <input\n v-model=\"filterText\"\n placeholder=\"搜索...\"\n style=\"width: 100%; padding: 6px 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 13px; box-sizing: border-box;\"\n />\n </div>\n <table v-if=\"headers.length\" style=\"width: 100%; border-collapse: collapse; font-size: 14px;\">\n <thead>\n <tr>\n <th\n v-for=\"(header, i) in headers\"\n :key=\"i\"\n :style=\"{\n padding: '10px 14px',\n textAlign: 'left',\n background: '#FAFAFA',\n borderBottom: '1px solid #f0f0f0',\n cursor: sortable ? 'pointer' : 'default',\n userSelect: 'none',\n fontSize: '13px',\n fontWeight: 600,\n color: '#555',\n }\"\n @click=\"toggleSort(i)\"\n >\n {{ header }}\n <span v-if=\"sortable && sortColumn === i\" style=\"margin-left: 4px;\">\n {{ sortDir === 'asc' ? '↑' : '↓' }}\n </span>\n </th>\n </tr>\n </thead>\n <tbody>\n <tr v-for=\"(row, ri) in filteredRows\" :key=\"ri\">\n <td\n v-for=\"(cell, ci) in row\"\n :key=\"ci\"\n :style=\"{\n padding: '10px 14px',\n borderBottom: '1px solid #f5f5f5',\n color: '#333',\n }\"\n >\n {{ cell }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, onMounted, onUpdated } from 'vue'\nimport type { AgentEventBus } from '../../core/eventBus'\n\nwithDefaults(defineProps<{\n layout?: 'inline' | 'wrap'\n blockId?: string\n eventBus?: AgentEventBus\n}>(), {\n layout: 'inline',\n})\n\nconst emit = defineEmits<{\n 'agent-action': [event: string, data: Record<string, unknown>]\n}>()\n\nconst pills = ref<string[]>([])\nconst clickedIndex = ref<number | null>(null)\nconst slotContainer = ref<HTMLElement | null>(null)\n\nlet lastSlotHTML = ''\n\nfunction parseSlot() {\n if (!slotContainer.value) return\n const html = slotContainer.value.innerHTML\n if (html === lastSlotHTML) return\n lastSlotHTML = html\n\n const items = slotContainer.value.querySelectorAll('li')\n pills.value = Array.from(items).map((li) => li.textContent?.trim() || '')\n}\n\nonMounted(parseSlot)\nonUpdated(parseSlot)\n\nfunction handleClick(text: string, index: number) {\n if (clickedIndex.value !== null) return\n clickedIndex.value = index\n emit('agent-action', 'pill_click', {\n text,\n index,\n })\n}\n</script>\n\n<template>\n <div class=\"action-pills\" style=\"margin: 12px 0;\">\n <div ref=\"slotContainer\" style=\"display: none;\">\n <slot />\n </div>\n <div :style=\"{ display: 'flex', flexWrap: layout === 'wrap' ? 'wrap' : 'nowrap', gap: '8px', overflowX: 'auto' }\">\n <button\n v-for=\"(pill, i) in pills\"\n :key=\"i\"\n :disabled=\"clickedIndex !== null\"\n :style=\"{\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n padding: '8px 16px',\n background: clickedIndex === i ? '#EDE9FE' : '#F5F5F5',\n border: clickedIndex === i ? '1px solid #5B4FC4' : '1px solid #E8E8E8',\n borderRadius: '20px',\n cursor: clickedIndex !== null ? 'default' : 'pointer',\n fontSize: '13px',\n color: clickedIndex === i ? '#5B4FC4' : '#555',\n whiteSpace: 'nowrap',\n opacity: clickedIndex !== null && clickedIndex !== i ? 0.5 : 1,\n transition: 'all 0.2s',\n }\"\n @click=\"handleClick(pill, i)\"\n >\n {{ pill }}\n </button>\n </div>\n </div>\n</template>\n","export { default as AlertBlock } from './AlertBlock.vue'\nexport { default as DataCard } from './DataCard.vue'\nexport { default as ConfirmBlock } from './ConfirmBlock.vue'\nexport { default as SelectBlock } from './SelectBlock.vue'\nexport { default as FormBlock } from './FormBlock.vue'\nexport { default as ProgressBlock } from './ProgressBlock.vue'\nexport { default as DataTableBlock } from './DataTableBlock.vue'\nexport { default as ActionPills } from './ActionPills.vue'\n\nimport type { ComponentMap } from '../../types'\nimport AlertBlock from './AlertBlock.vue'\nimport DataCard from './DataCard.vue'\nimport ConfirmBlock from './ConfirmBlock.vue'\nimport SelectBlock from './SelectBlock.vue'\nimport FormBlock from './FormBlock.vue'\nimport ProgressBlock from './ProgressBlock.vue'\nimport DataTableBlock from './DataTableBlock.vue'\nimport ActionPills from './ActionPills.vue'\n\n/**\n * 默认的内置组件注册表\n */\nexport const builtinComponentMap: ComponentMap = {\n AlertBlock,\n DataCard,\n ConfirmBlock,\n SelectBlock,\n FormBlock,\n ProgressBlock,\n DataTableBlock,\n ActionPills,\n}\n","import {\r\n defineComponent, h, watch, ref, provide, onBeforeUnmount,\r\n type PropType, type VNode\r\n} from 'vue'\r\nimport { useMarkdownParser } from '../composables/useMarkdownParser'\r\nimport { createAgentEventBus, type AgentEventBus, type AgentActionPayload } from '../core/eventBus'\r\nimport { validateProps, sanitizePayload, type ComponentPropsSchema, defaultSchemas } from '../core/propValidator'\r\nimport { builtinComponentMap } from './blocks'\r\nimport type { ComponentMap } from '../types'\r\n\r\n// Provide/Inject key\r\nexport const AGENT_EVENT_BUS_KEY = Symbol('agentEventBus')\r\n\r\nexport default defineComponent({\r\n name: 'MarkdownRenderer',\r\n\r\n props: {\r\n /** Markdown 内容(支持流式追加) */\r\n content: {\r\n type: String,\r\n required: true,\r\n },\r\n /**\r\n * 自定义组件注册表(与内置组件合并,用户组件优先)\r\n */\r\n components: {\r\n type: Object as PropType<ComponentMap>,\r\n default: () => ({}),\r\n },\r\n /**\r\n * [v2 新增] 自定义 Props Schema(与默认 Schema 合并)\r\n */\r\n propsSchemas: {\r\n type: Object as PropType<Record<string, ComponentPropsSchema>>,\r\n default: () => ({}),\r\n },\r\n /**\r\n * [v2 新增] 是否启用 Props 安全校验(默认启用)\r\n */\r\n enableValidation: {\r\n type: Boolean,\r\n default: true,\r\n },\r\n },\r\n\r\n emits: {\r\n /**\r\n * [v2 新增] Agent 操作事件\r\n *\r\n * 当任何内置或自定义块组件触发用户交互时,\r\n * 通过此事件通知宿主应用,宿主应用负责将其转发给 Agent\r\n */\r\n 'agent:action': (_payload: AgentActionPayload) => true,\r\n },\r\n\r\n setup(props, { emit }) {\r\n // 合并组件注册表\r\n const mergedComponentMap: ComponentMap = {\r\n ...builtinComponentMap,\r\n ...props.components,\r\n }\r\n\r\n // 合并 Props Schema\r\n const mergedSchemas: Record<string, ComponentPropsSchema> = {\r\n ...defaultSchemas,\r\n ...props.propsSchemas,\r\n }\r\n\r\n // 创建事件总线\r\n const eventBus: AgentEventBus = createAgentEventBus()\r\n\r\n // 事件总线 → emit 到宿主\r\n eventBus.on((payload) => {\r\n emit('agent:action', payload)\r\n })\r\n\r\n // 通过 provide 注入给所有子组件\r\n provide(AGENT_EVENT_BUS_KEY, eventBus)\r\n\r\n // Markdown 解析器\r\n const { parse } = useMarkdownParser()\r\n const vnodes = ref<VNode[]>([])\r\n\r\n // 自增的块 ID(用于没有指定 data-id 的组件)\r\n let blockIdCounter = 0\r\n\r\n /**\r\n * 核心:将 DOM 节点递归转换为 VNode\r\n * 增强点:vue-block 节点注入 eventBus + Props 校验 + 事件绑定\r\n */\r\n function domNodeToVNode(node: Node): VNode | string | null {\r\n // 文本节点\r\n if (node.nodeType === Node.TEXT_NODE) {\r\n return node.textContent || null\r\n }\r\n\r\n // 非元素节点忽略\r\n if (node.nodeType !== Node.ELEMENT_NODE) return null\r\n const el = node as HTMLElement\r\n const tagName = el.tagName.toLowerCase()\r\n\r\n // ========== vue-block 组件节点 ==========\r\n if (tagName === 'vue-block') {\r\n const componentName = el.getAttribute('data-component') || ''\r\n const Component = mergedComponentMap[componentName]\r\n\r\n if (!Component) {\r\n // 未注册的组件 → 优雅降级为普通 HTML 渲染\r\n console.warn(`[MarkdownRenderer] Unknown component: ${componentName}, fallback to HTML`)\r\n const children = Array.from(el.childNodes).map(domNodeToVNode).filter(Boolean)\r\n return h('div', { class: 'markdown-block-fallback' }, children as VNode[])\r\n }\r\n\r\n // 提取 data-* 属性为 props\r\n const rawProps: Record<string, string> = {}\r\n for (const attr of Array.from(el.attributes)) {\r\n if (attr.name.startsWith('data-') && attr.name !== 'data-component') {\r\n const propName = attr.name\r\n .replace('data-', '')\r\n .replace(/-([a-z])/g, (_, c) => c.toUpperCase()) // kebab → camelCase\r\n rawProps[propName] = attr.value\r\n }\r\n }\r\n\r\n // Props 校验 + 清洗\r\n const validatedProps = props.enableValidation\r\n ? validateProps(componentName, rawProps, mergedSchemas)\r\n : rawProps\r\n\r\n // 生成块 ID\r\n const blockId = rawProps.id || `block-${++blockIdCounter}`\r\n\r\n // 子内容作为 default slot\r\n const children = Array.from(el.childNodes).map(domNodeToVNode).filter(Boolean)\r\n\r\n // 构建 VNode,注入 eventBus 引用和 blockId\r\n return h(Component, {\r\n ...validatedProps,\r\n blockId,\r\n eventBus,\r\n // 监听组件的 agent-action 事件(组件内部 emit)\r\n 'onAgent-action': (event: string, data: Record<string, unknown>) => {\r\n eventBus.emit({\r\n blockId,\r\n event,\r\n componentType: componentName,\r\n data: sanitizePayload(data) as Record<string, unknown>,\r\n })\r\n },\r\n }, {\r\n default: () => children,\r\n })\r\n }\r\n\r\n // ========== 普通 HTML 元素 ==========\r\n const children = Array.from(el.childNodes).map(domNodeToVNode).filter(Boolean)\r\n const attrs: Record<string, string> = {}\r\n for (const attr of Array.from(el.attributes)) {\r\n attrs[attr.name] = attr.value\r\n }\r\n return h(tagName, attrs, children as VNode[])\r\n }\r\n\r\n // 监听 content 变化 → 重新解析\r\n watch(\r\n () => props.content,\r\n (newContent) => {\r\n blockIdCounter = 0 // 每次内容更新重置计数器\r\n const html = parse(newContent)\r\n const parser = new DOMParser()\r\n const doc = parser.parseFromString(`<div>${html}</div>`, 'text/html')\r\n const root = doc.body.firstChild!\r\n\r\n vnodes.value = Array.from(root.childNodes)\r\n .map(domNodeToVNode)\r\n .filter(Boolean) as VNode[]\r\n },\r\n { immediate: true }\r\n )\r\n\r\n // 清理\r\n onBeforeUnmount(() => {\r\n eventBus.destroy()\r\n })\r\n\r\n return () => h('div', { class: 'markdown-body' }, vnodes.value)\r\n },\r\n})\r\n","import { ref } from 'vue'\r\n\r\nconst MOCK_TEXT = `# Agent UI Protocol 演示\r\n\r\n这是一个演示 **Agent UI Protocol v2** 流式输出 + 交互组件的示例。\r\n\r\n## 基础 Markdown 渲染\r\n\r\n- ✅ 流式打字机渲染\r\n- ✅ 普通 Markdown 语法(标题、粗体、代码等)\r\n- ✅ \\`:::alert\\` 块 → AlertBlock 组件\r\n- ✅ \\`:::card\\` 块 → DataCard 组件\r\n\r\n## 代码示例\r\n\r\n\\`\\`\\`typescript\r\nconst md = new MarkdownIt()\r\nmd.use(container, 'alert', {\r\n render(tokens, idx) {\r\n return '<vue-block data-component=\"AlertBlock\">'\r\n }\r\n})\r\n\\`\\`\\`\r\n\r\n::: alert info\r\n**渲染原理**:markdown-it-container 的 \\`render\\` 回调输出自定义 \\`<vue-block>\\` 占位元素,再由 \\`DOMParser\\` 递归转为 \\`h()\\` VNode 树。\r\n:::\r\n\r\n## v2 新增:交互组件\r\n\r\n### 确认操作\r\n\r\n::: confirm action=\"delete_user\" level=danger\r\n确认要删除用户 **张三** 吗?此操作不可撤销。\r\n:::\r\n\r\n### 进度展示\r\n\r\n::: progress value=73 max=100 status=active\r\n正在导入数据... 已处理 73/100 条\r\n:::\r\n\r\n### 数据表格\r\n\r\n::: datatable sortable filterable\r\n| 姓名 | 部门 | 状态 |\r\n|------|------|------|\r\n| 张三 | 研发 | 在线 |\r\n| 李四 | 产品 | 离线 |\r\n| 王五 | 设计 | 忙碌 |\r\n:::\r\n\r\n### 快捷操作\r\n\r\n::: actions\r\n- 查看处理进度\r\n- 生成报告\r\n- 导出数据\r\n:::\r\n\r\n## 数据展示\r\n\r\n::: card 技术栈对比\r\n\r\n| 方案 | 性能 | 体积 |\r\n|------|------|------|\r\n| h() + DOMParser | ✅ 优秀 | ✅ 轻量 |\r\n| compile() | ❌ 每次重编译 | ❌ +14KB |\r\n\r\n:::\r\n\r\n::: alert warning\r\n未闭合的容器块在流式输出过程中会由 \\`autoCloseContainers()\\` 自动补全,确保 markdown-it 始终解析到**合法输入**。\r\n:::\r\n\r\n::: alert success\r\n✨ Agent UI Protocol v2 已就绪!支持 Confirm / Select / Form / Progress / DataTable / Actions 等交互组件。\r\n:::\r\n\r\n::: card 实现步骤\r\n\r\n1. **预处理**:\\`autoCloseContainers()\\` 补全未闭合 \\`:::\\`\r\n2. **解析**:\\`markdown-it\\` + \\`markdown-it-container\\` 输出含 \\`<vue-block>\\` 的 HTML\r\n3. **校验**:\\`PropValidator\\` 对 Props 进行白名单校验 + 清洗\r\n4. **转换**:\\`DOMParser\\` 遍历 DOM,将 \\`<vue-block>\\` 替换为 \\`h(Vue组件)\\`\r\n5. **事件**:\\`AgentEventBus\\` 将组件交互事件传递给宿主应用\r\n6. **渲染**:\\`MarkdownRenderer\\` 组件通过 render 函数返回 VNode 树\r\n\r\n:::\r\n\r\n---\r\n\r\n🎉 流式输出完成!\r\n`\r\n\r\n/**\r\n * Mock 流式文本输出\r\n * 模拟 AI 逐字输出场景\r\n */\r\nexport function useStreamingText() {\r\n const text = ref('')\r\n const isStreaming = ref(false)\r\n let timer: ReturnType<typeof setInterval> | null = null\r\n let position = 0\r\n\r\n function startStream() {\r\n if (isStreaming.value) return\r\n\r\n isStreaming.value = true\r\n position = text.value.length // 支持续播\r\n\r\n timer = setInterval(() => {\r\n if (position >= MOCK_TEXT.length) {\r\n stopStream()\r\n return\r\n }\r\n // 每次追加 1~3 个字符,模拟真实流式速度\r\n const chunkSize = Math.floor(Math.random() * 3) + 1\r\n text.value += MOCK_TEXT.slice(position, position + chunkSize)\r\n position += chunkSize\r\n }, 30)\r\n }\r\n\r\n function stopStream() {\r\n if (timer !== null) {\r\n clearInterval(timer)\r\n timer = null\r\n }\r\n isStreaming.value = false\r\n }\r\n\r\n function resetStream() {\r\n stopStream()\r\n text.value = ''\r\n position = 0\r\n }\r\n\r\n return {\r\n text,\r\n isStreaming,\r\n startStream,\r\n stopStream,\r\n resetStream,\r\n }\r\n}\r\n","import { ref, type Ref } from 'vue'\nimport type { AgentActionPayload } from '../core/eventBus'\n\n/**\n * 便捷的 Agent 事件管理 Composable\n *\n * 使用方式:\n * ```vue\n * <script setup>\n * const { createHandler, serializeToMessage } = useAgentEvents()\n *\n * const handleAction = createHandler(async (payload) => {\n * await sendToAgent(payload)\n * })\n * </script>\n *\n * <template>\n * <MarkdownRenderer :content=\"text\" @agent:action=\"handleAction\" />\n * </template>\n * ```\n */\nexport function useAgentEvents() {\n const lastAction: Ref<AgentActionPayload | null> = ref(null)\n const actionHistory: Ref<AgentActionPayload[]> = ref([])\n const isProcessing = ref(false)\n\n /**\n * 处理 Agent 事件的通用 handler\n *\n * @param handler - 自定义处理逻辑(通常是发送给 Agent API)\n * @returns 绑定到 @agent:action 的事件处理函数\n */\n function createHandler(\n handler: (payload: AgentActionPayload) => void | Promise<void>,\n ) {\n return async (payload: AgentActionPayload) => {\n lastAction.value = payload\n actionHistory.value.push(payload)\n isProcessing.value = true\n\n try {\n await handler(payload)\n } finally {\n isProcessing.value = false\n }\n }\n }\n\n /**\n * 将 AgentActionPayload 序列化为可发送给 Agent 的文本消息\n */\n function serializeToMessage(payload: AgentActionPayload): string {\n const parts = [`[用户操作] ${payload.componentType}`]\n\n switch (payload.event) {\n case 'confirm':\n parts.push(`确认了操作: ${payload.data.action || ''}`)\n break\n case 'cancel':\n parts.push(`取消了操作: ${payload.data.action || ''}`)\n break\n case 'submit':\n parts.push(`提交了表单: ${JSON.stringify(payload.data.values || {})}`)\n break\n case 'select':\n parts.push(`选择了: ${JSON.stringify(payload.data.selectedIndices || [])}`)\n break\n case 'pill_click':\n parts.push(`点击了快捷操作: \"${payload.data.text || ''}\"`)\n break\n default:\n parts.push(`${payload.event}: ${JSON.stringify(payload.data)}`)\n }\n\n return parts.join(' — ')\n }\n\n return {\n lastAction,\n actionHistory,\n isProcessing,\n createHandler,\n serializeToMessage,\n }\n}\n","/**\n * Agent UI Protocol 版本\n */\nexport const PROTOCOL_VERSION = '2.0.0'\n\n/**\n * 支持的容器块类型\n */\nexport const SUPPORTED_BLOCKS = [\n 'alert', 'card', // v1 已有\n 'confirm', 'select', 'form', // v2 交互组件\n 'progress', 'datatable', // v2 展示组件\n 'actions', // v2 快捷操作\n] as const\n\nexport type SupportedBlock = typeof SUPPORTED_BLOCKS[number]\n"],"names":["CONTAINER_PATTERN","autoCloseContainers","content","lines","openStack","line","trimmed","closings","parseBlockAttributes","attrStr","attrs","regex","match","boolRegex","cleaned","attrsToDataString","key","value","kebab","escaped","BLOCK_CONFIGS","params","tokens","idx","token","info","useMarkdownParser","md","MarkdownIt","config","container","parse","safeContent","_parser","renderMarkdown","raw","createAgentEventBus","handlers","history","ref","lastEvent","emit","payload","fullPayload","handler","err","on","clearHistory","destroy","readonly","GLOBAL_BLOCKED_PROPS","defaultSchemas","validateProps","componentName","rawProps","schemas","schema","result","blocked","filtered","propName","rule","rawValue","strVal","numVal","clamped","sanitizePayload","props","__props","iconMap","labelMap","_createElementBlock","_normalizeClass","_createElementVNode","_hoisted_1","_hoisted_2","_toDisplayString","_hoisted_3","_hoisted_4","_renderSlot","_ctx","_openBlock","_cache","__emit","submitted","handleConfirm","handleCancel","levelColors","_normalizeStyle","options","selected","confirmed","slotContainer","lastSlotHTML","parseSlot","html","items","li","text","parts","s","onMounted","onUpdated","toggleSelect","index","i","_Fragment","_renderList","opt","$event","_hoisted_5","fields","formData","parseFieldsFromSlot","slotEl","parsed","name","type","rest","field","optMatch","tryParseSlot","handleSubmit","_hoisted_6","_hoisted_7","_hoisted_9","_hoisted_10","percentage","computed","barColor","headers","rows","sortColumn","sortDir","filterText","table","ths","th","trs","tr","td","filteredRows","q","row","cell","col","dir","a","b","toggleSort","header","_createTextVNode","ri","ci","pills","clickedIndex","handleClick","pill","builtinComponentMap","AlertBlock","DataCard","ConfirmBlock","SelectBlock","FormBlock","ProgressBlock","DataTableBlock","ActionPills","AGENT_EVENT_BUS_KEY","MarkdownRenderer","defineComponent","_payload","mergedComponentMap","mergedSchemas","eventBus","provide","vnodes","blockIdCounter","domNodeToVNode","node","el","tagName","Component","children","h","attr","_","c","validatedProps","blockId","event","data","watch","newContent","root","onBeforeUnmount","MOCK_TEXT","useStreamingText","isStreaming","timer","position","startStream","stopStream","chunkSize","resetStream","useAgentEvents","lastAction","actionHistory","isProcessing","createHandler","serializeToMessage","PROTOCOL_VERSION","SUPPORTED_BLOCKS"],"mappings":"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"}
|