agent-whiteboard 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +470 -0
- package/LICENSE +21 -0
- package/README.md +313 -0
- package/bin/cli.js +30 -0
- package/dist/client/apple-touch-icon.png +0 -0
- package/dist/client/assets/KaTeX_AMS-Regular-0cdd387c.woff2 +0 -0
- package/dist/client/assets/KaTeX_AMS-Regular-30da91e8.woff +0 -0
- package/dist/client/assets/KaTeX_AMS-Regular-68534840.ttf +0 -0
- package/dist/client/assets/KaTeX_Caligraphic-Bold-07d8e303.ttf +0 -0
- package/dist/client/assets/KaTeX_Caligraphic-Bold-1ae6bd74.woff +0 -0
- package/dist/client/assets/KaTeX_Caligraphic-Bold-de7701e4.woff2 +0 -0
- package/dist/client/assets/KaTeX_Caligraphic-Regular-3398dd02.woff +0 -0
- package/dist/client/assets/KaTeX_Caligraphic-Regular-5d53e70a.woff2 +0 -0
- package/dist/client/assets/KaTeX_Caligraphic-Regular-ed0b7437.ttf +0 -0
- package/dist/client/assets/KaTeX_Fraktur-Bold-74444efd.woff2 +0 -0
- package/dist/client/assets/KaTeX_Fraktur-Bold-9163df9c.ttf +0 -0
- package/dist/client/assets/KaTeX_Fraktur-Bold-9be7ceb8.woff +0 -0
- package/dist/client/assets/KaTeX_Fraktur-Regular-1e6f9579.ttf +0 -0
- package/dist/client/assets/KaTeX_Fraktur-Regular-51814d27.woff2 +0 -0
- package/dist/client/assets/KaTeX_Fraktur-Regular-5e28753b.woff +0 -0
- package/dist/client/assets/KaTeX_Main-Bold-0f60d1b8.woff2 +0 -0
- package/dist/client/assets/KaTeX_Main-Bold-138ac28d.ttf +0 -0
- package/dist/client/assets/KaTeX_Main-Bold-c76c5d69.woff +0 -0
- package/dist/client/assets/KaTeX_Main-BoldItalic-70ee1f64.ttf +0 -0
- package/dist/client/assets/KaTeX_Main-BoldItalic-99cd42a3.woff2 +0 -0
- package/dist/client/assets/KaTeX_Main-BoldItalic-a6f7ec0d.woff +0 -0
- package/dist/client/assets/KaTeX_Main-Italic-0d85ae7c.ttf +0 -0
- package/dist/client/assets/KaTeX_Main-Italic-97479ca6.woff2 +0 -0
- package/dist/client/assets/KaTeX_Main-Italic-f1d6ef86.woff +0 -0
- package/dist/client/assets/KaTeX_Main-Regular-c2342cd8.woff2 +0 -0
- package/dist/client/assets/KaTeX_Main-Regular-c6368d87.woff +0 -0
- package/dist/client/assets/KaTeX_Main-Regular-d0332f52.ttf +0 -0
- package/dist/client/assets/KaTeX_Math-BoldItalic-850c0af5.woff +0 -0
- package/dist/client/assets/KaTeX_Math-BoldItalic-dc47344d.woff2 +0 -0
- package/dist/client/assets/KaTeX_Math-BoldItalic-f9377ab0.ttf +0 -0
- package/dist/client/assets/KaTeX_Math-Italic-08ce98e5.ttf +0 -0
- package/dist/client/assets/KaTeX_Math-Italic-7af58c5e.woff2 +0 -0
- package/dist/client/assets/KaTeX_Math-Italic-8a8d2445.woff +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Bold-1ece03f7.ttf +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Bold-e99ae511.woff2 +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Bold-ece03cfd.woff +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Italic-00b26ac8.woff2 +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Italic-3931dd81.ttf +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Italic-91ee6750.woff +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Regular-11e4dc8a.woff +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Regular-68e8c73e.woff2 +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Regular-f36ea897.ttf +0 -0
- package/dist/client/assets/KaTeX_Script-Regular-036d4e95.woff2 +0 -0
- package/dist/client/assets/KaTeX_Script-Regular-1c67f068.ttf +0 -0
- package/dist/client/assets/KaTeX_Script-Regular-d96cdf2b.woff +0 -0
- package/dist/client/assets/KaTeX_Size1-Regular-6b47c401.woff2 +0 -0
- package/dist/client/assets/KaTeX_Size1-Regular-95b6d2f1.ttf +0 -0
- package/dist/client/assets/KaTeX_Size1-Regular-c943cc98.woff +0 -0
- package/dist/client/assets/KaTeX_Size2-Regular-2014c523.woff +0 -0
- package/dist/client/assets/KaTeX_Size2-Regular-a6b2099f.ttf +0 -0
- package/dist/client/assets/KaTeX_Size2-Regular-d04c5421.woff2 +0 -0
- package/dist/client/assets/KaTeX_Size3-Regular-500e04d5.ttf +0 -0
- package/dist/client/assets/KaTeX_Size3-Regular-6ab6b62e.woff +0 -0
- package/dist/client/assets/KaTeX_Size4-Regular-99f9c675.woff +0 -0
- package/dist/client/assets/KaTeX_Size4-Regular-a4af7d41.woff2 +0 -0
- package/dist/client/assets/KaTeX_Size4-Regular-c647367d.ttf +0 -0
- package/dist/client/assets/KaTeX_Typewriter-Regular-71d517d6.woff2 +0 -0
- package/dist/client/assets/KaTeX_Typewriter-Regular-e14fed02.woff +0 -0
- package/dist/client/assets/KaTeX_Typewriter-Regular-f01f3e87.ttf +0 -0
- package/dist/client/assets/arc-4691b008.js +1 -0
- package/dist/client/assets/architecture-7EHR7CIX-e08620e8.js +1 -0
- package/dist/client/assets/architectureDiagram-3BPJPVTR-9e2f2dbd.js +36 -0
- package/dist/client/assets/blockDiagram-GPEHLZMM-d9dd3f7a.js +132 -0
- package/dist/client/assets/bootstrap.min-21652813.js +6 -0
- package/dist/client/assets/c4Diagram-AAUBKEIU-4ed97d79.js +10 -0
- package/dist/client/assets/channel-9d07a35d.js +1 -0
- package/dist/client/assets/chunk-2J33WTMH-8049adc8.js +1 -0
- package/dist/client/assets/chunk-4BX2VUAB-fd7396be.js +1 -0
- package/dist/client/assets/chunk-55IACEB6-ae1ac93c.js +1 -0
- package/dist/client/assets/chunk-727SXJPM-7e3658d9.js +206 -0
- package/dist/client/assets/chunk-AQP2D5EJ-114f64b0.js +231 -0
- package/dist/client/assets/chunk-FMBD7UC4-f2647c5e.js +15 -0
- package/dist/client/assets/chunk-ND2GUHAM-0a2fcd8c.js +1 -0
- package/dist/client/assets/chunk-QZHKN3VN-d00b3f33.js +1 -0
- package/dist/client/assets/classDiagram-4FO5ZUOK-ebb5cc4a.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-ebb5cc4a.js +1 -0
- package/dist/client/assets/colors-98f238a0.js +1 -0
- package/dist/client/assets/cose-bilkent-S5V4N54A-1f808fd7.js +1 -0
- package/dist/client/assets/cytoscape.esm-1491e259.js +331 -0
- package/dist/client/assets/dagre-BM42HDAG-e8ec1c9d.js +4 -0
- package/dist/client/assets/defaultLocale-a8476f12.js +1 -0
- package/dist/client/assets/diagram-2AECGRRQ-83b385bf.js +43 -0
- package/dist/client/assets/diagram-5GNKFQAL-d9406c6a.js +10 -0
- package/dist/client/assets/diagram-KO2AKTUF-3db5b95a.js +3 -0
- package/dist/client/assets/diagram-LMA3HP47-fbd90397.js +24 -0
- package/dist/client/assets/diagram-OG6HWLK6-3c697534.js +24 -0
- package/dist/client/assets/embed-2b4adf34.js +201 -0
- package/dist/client/assets/erDiagram-TEJ5UH35-7f41448f.js +85 -0
- package/dist/client/assets/eventmodeling-FCH6USID-e6d9019e.js +1 -0
- package/dist/client/assets/flowDiagram-I6XJVG4X-38f05cc7.js +162 -0
- package/dist/client/assets/ganttDiagram-6RSMTGT7-39287f5a.js +292 -0
- package/dist/client/assets/gitGraph-WXDBUCRP-037e225a.js +1 -0
- package/dist/client/assets/gitGraphDiagram-PVQCEYII-3c2e3ef7.js +106 -0
- package/dist/client/assets/graph-f3dfaf53.js +1 -0
- package/dist/client/assets/index-4abf3da0.js +5 -0
- package/dist/client/assets/index-a1656aec.js +1 -0
- package/dist/client/assets/index-d674a6a4.css +1 -0
- package/dist/client/assets/info-J43DQDTF-51cd3390.js +1 -0
- package/dist/client/assets/infoDiagram-5YYISTIA-ef2826b7.js +2 -0
- package/dist/client/assets/init-a5b10ee5.js +1 -0
- package/dist/client/assets/ishikawaDiagram-YF4QCWOH-d0b8a2e0.js +70 -0
- package/dist/client/assets/journeyDiagram-JHISSGLW-586ffcf8.js +139 -0
- package/dist/client/assets/kanban-definition-UN3LZRKU-9e58e14f.js +89 -0
- package/dist/client/assets/katex-0cba7652.js +257 -0
- package/dist/client/assets/katex-4c9ea27f.js +257 -0
- package/dist/client/assets/layout-3a4eff80.js +1 -0
- package/dist/client/assets/linear-fed00341.js +1 -0
- package/dist/client/assets/mermaid-parser.core-7ee09942.js +173 -0
- package/dist/client/assets/mermaid.core-9fc8f6d2.js +306 -0
- package/dist/client/assets/mindmap-definition-RKZ34NQL-a40dd09f.js +96 -0
- package/dist/client/assets/ordinal-db88ae6f.js +1 -0
- package/dist/client/assets/packet-YPE3B663-f55b568a.js +1 -0
- package/dist/client/assets/pie-LRSECV5Y-88c1a5e6.js +1 -0
- package/dist/client/assets/pieDiagram-4H26LBE5-51aae5ea.js +30 -0
- package/dist/client/assets/quadrantDiagram-W4KKPZXB-bc4686f9.js +7 -0
- package/dist/client/assets/radar-GUYGQ44K-6ede145d.js +1 -0
- package/dist/client/assets/range-6bc2a9a0.js +1 -0
- package/dist/client/assets/requirementDiagram-4Y6WPE33-8466f5a1.js +84 -0
- package/dist/client/assets/sankeyDiagram-5OEKKPKP-fd54c1f6.js +40 -0
- package/dist/client/assets/sequenceDiagram-3UESZ5HK-baa63c66.js +162 -0
- package/dist/client/assets/stateDiagram-AJRCARHV-bb4f0d0b.js +1 -0
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-9479cd12.js +1 -0
- package/dist/client/assets/step-171ad54a.js +1 -0
- package/dist/client/assets/time-043b3464.js +1 -0
- package/dist/client/assets/timeline-definition-PNZ67QCA-f64b58bd.js +120 -0
- package/dist/client/assets/treeView-BLDUP644-73ba1a83.js +1 -0
- package/dist/client/assets/treemap-LRROVOQU-98e47821.js +1 -0
- package/dist/client/assets/vennDiagram-CIIHVFJN-a410b1f7.js +34 -0
- package/dist/client/assets/wardley-L42UT6IY-a27badb9.js +1 -0
- package/dist/client/assets/wardleyDiagram-YWT4CUSO-527ffe52.js +78 -0
- package/dist/client/assets/xychartDiagram-2RQKCTM6-42b5914b.js +7 -0
- package/dist/client/favicon-16x16.png +0 -0
- package/dist/client/favicon-32x32.png +0 -0
- package/dist/client/favicon.ico +0 -0
- package/dist/client/icon-192.png +0 -0
- package/dist/client/icon-512.png +0 -0
- package/dist/client/index.html +18 -0
- package/dist/server/app.js +429 -0
- package/dist/server/channel.js +42 -0
- package/dist/server/events.js +90 -0
- package/dist/server/export-html.js +461 -0
- package/dist/server/index.js +79 -0
- package/dist/server/interaction.js +135 -0
- package/dist/server/mcp.js +421 -0
- package/dist/server/paths.js +11 -0
- package/dist/server/persist.js +56 -0
- package/dist/server/presentation.js +5 -0
- package/dist/server/render-core.js +213 -0
- package/dist/server/session.js +103 -0
- package/dist/server/slideshow.js +83 -0
- package/dist/server/snapshot-reader.js +190 -0
- package/dist/server/snapshot-writer.js +117 -0
- package/dist/server/snapshot.js +47 -0
- package/dist/server/step-frames-builder.js +70 -0
- package/dist/server/validate.js +140 -0
- package/dist/server/viewport-cache.js +69 -0
- package/dist/server/ws.js +71 -0
- package/package.json +92 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import{_ as a,a6 as gi,l as Xt,I as xi,E as _t,D as Yt,C as di,i as fi,o as Nt,s as pi,g as mi,n as yi,b as bi,c as Ai,t as wi,H as Ci,k as Si}from"./mermaid.core-9fc8f6d2.js";import{i as _i}from"./init-a5b10ee5.js";import{o as ki}from"./ordinal-db88ae6f.js";import{r as Ri}from"./range-6bc2a9a0.js";import{l as Wt}from"./linear-fed00341.js";import{l as zt}from"./step-171ad54a.js";import"./index-4abf3da0.js";import"./defaultLocale-a8476f12.js";function bt(){var e=ki().unknown(void 0),t=e.domain,i=e.range,s=0,n=1,r,u,p=!1,f=0,T=0,P=.5;delete e.unknown;function _(){var y=t().length,E=n<s,v=E?n:s,L=E?s:n;r=(L-v)/Math.max(1,y-f+T*2),p&&(r=Math.floor(r)),v+=(L-v-r*(y-f))*P,u=r*(1-f),p&&(v=Math.round(v),u=Math.round(u));var I=Ri(y).map(function(m){return v+r*m});return i(E?I.reverse():I)}return e.domain=function(y){return arguments.length?(t(y),_()):t()},e.range=function(y){return arguments.length?([s,n]=y,s=+s,n=+n,_()):[s,n]},e.rangeRound=function(y){return[s,n]=y,s=+s,n=+n,p=!0,_()},e.bandwidth=function(){return u},e.step=function(){return r},e.round=function(y){return arguments.length?(p=!!y,_()):p},e.padding=function(y){return arguments.length?(f=Math.min(1,T=+y),_()):f},e.paddingInner=function(y){return arguments.length?(f=Math.min(1,y),_()):f},e.paddingOuter=function(y){return arguments.length?(T=+y,_()):T},e.align=function(y){return arguments.length?(P=Math.max(0,Math.min(1,y)),_()):P},e.copy=function(){return bt(t(),[s,n]).round(p).paddingInner(f).paddingOuter(T).align(P)},_i.apply(_(),arguments)}var At=function(){var e=a(function(F,h,c,g){for(c=c||{},g=F.length;g--;c[F[g]]=h);return c},"o"),t=[1,10,12,14,16,18,19,21,23],i=[2,6],s=[1,3],n=[1,5],r=[1,6],u=[1,7],p=[1,5,10,12,14,16,18,19,21,23,34,35,36],f=[1,25],T=[1,26],P=[1,28],_=[1,29],y=[1,30],E=[1,31],v=[1,32],L=[1,33],I=[1,34],m=[1,35],R=[1,36],l=[1,37],W=[1,43],O=[1,42],X=[1,47],Y=[1,50],S=[1,10,12,14,16,18,19,21,23,34,35,36],U=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36],b=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36,41,42,43,44,45,46,47,48,49,50],w=[1,64],V={trace:a(function(){},"trace"),yy:{},symbols_:{error:2,start:3,eol:4,XYCHART:5,chartConfig:6,document:7,CHART_ORIENTATION:8,statement:9,title:10,text:11,X_AXIS:12,parseXAxis:13,Y_AXIS:14,parseYAxis:15,LINE:16,plotData:17,BAR:18,acc_title:19,acc_title_value:20,acc_descr:21,acc_descr_value:22,acc_descr_multiline_value:23,SQUARE_BRACES_START:24,commaSeparatedNumbers:25,SQUARE_BRACES_END:26,NUMBER_WITH_DECIMAL:27,COMMA:28,xAxisData:29,bandData:30,ARROW_DELIMITER:31,commaSeparatedTexts:32,yAxisData:33,NEWLINE:34,SEMI:35,EOF:36,alphaNum:37,STR:38,MD_STR:39,alphaNumToken:40,AMP:41,NUM:42,ALPHA:43,PLUS:44,EQUALS:45,MULT:46,DOT:47,BRKT:48,MINUS:49,UNDERSCORE:50,$accept:0,$end:1},terminals_:{2:"error",5:"XYCHART",8:"CHART_ORIENTATION",10:"title",12:"X_AXIS",14:"Y_AXIS",16:"LINE",18:"BAR",19:"acc_title",20:"acc_title_value",21:"acc_descr",22:"acc_descr_value",23:"acc_descr_multiline_value",24:"SQUARE_BRACES_START",26:"SQUARE_BRACES_END",27:"NUMBER_WITH_DECIMAL",28:"COMMA",31:"ARROW_DELIMITER",34:"NEWLINE",35:"SEMI",36:"EOF",38:"STR",39:"MD_STR",41:"AMP",42:"NUM",43:"ALPHA",44:"PLUS",45:"EQUALS",46:"MULT",47:"DOT",48:"BRKT",49:"MINUS",50:"UNDERSCORE"},productions_:[0,[3,2],[3,3],[3,2],[3,1],[6,1],[7,0],[7,2],[9,2],[9,2],[9,2],[9,2],[9,2],[9,3],[9,2],[9,3],[9,2],[9,2],[9,1],[17,3],[25,3],[25,1],[13,1],[13,2],[13,1],[29,1],[29,3],[30,3],[32,3],[32,1],[15,1],[15,2],[15,1],[33,3],[4,1],[4,1],[4,1],[11,1],[11,1],[11,1],[37,1],[37,2],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1]],performAction:a(function(h,c,g,x,C,o,Q){var d=o.length-1;switch(C){case 5:x.setOrientation(o[d]);break;case 9:x.setDiagramTitle(o[d].text.trim());break;case 12:x.setLineData({text:"",type:"text"},o[d]);break;case 13:x.setLineData(o[d-1],o[d]);break;case 14:x.setBarData({text:"",type:"text"},o[d]);break;case 15:x.setBarData(o[d-1],o[d]);break;case 16:this.$=o[d].trim(),x.setAccTitle(this.$);break;case 17:case 18:this.$=o[d].trim(),x.setAccDescription(this.$);break;case 19:this.$=o[d-1];break;case 20:this.$=[Number(o[d-2]),...o[d]];break;case 21:this.$=[Number(o[d])];break;case 22:x.setXAxisTitle(o[d]);break;case 23:x.setXAxisTitle(o[d-1]);break;case 24:x.setXAxisTitle({type:"text",text:""});break;case 25:x.setXAxisBand(o[d]);break;case 26:x.setXAxisRangeData(Number(o[d-2]),Number(o[d]));break;case 27:this.$=o[d-1];break;case 28:this.$=[o[d-2],...o[d]];break;case 29:this.$=[o[d]];break;case 30:x.setYAxisTitle(o[d]);break;case 31:x.setYAxisTitle(o[d-1]);break;case 32:x.setYAxisTitle({type:"text",text:""});break;case 33:x.setYAxisRangeData(Number(o[d-2]),Number(o[d]));break;case 37:this.$={text:o[d],type:"text"};break;case 38:this.$={text:o[d],type:"text"};break;case 39:this.$={text:o[d],type:"markdown"};break;case 40:this.$=o[d];break;case 41:this.$=o[d-1]+""+o[d];break}},"anonymous"),table:[e(t,i,{3:1,4:2,7:4,5:s,34:n,35:r,36:u}),{1:[3]},e(t,i,{4:2,7:4,3:8,5:s,34:n,35:r,36:u}),e(t,i,{4:2,7:4,6:9,3:10,5:s,8:[1,11],34:n,35:r,36:u}),{1:[2,4],9:12,10:[1,13],12:[1,14],14:[1,15],16:[1,16],18:[1,17],19:[1,18],21:[1,19],23:[1,20]},e(p,[2,34]),e(p,[2,35]),e(p,[2,36]),{1:[2,1]},e(t,i,{4:2,7:4,3:21,5:s,34:n,35:r,36:u}),{1:[2,3]},e(p,[2,5]),e(t,[2,7],{4:22,34:n,35:r,36:u}),{11:23,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{11:39,13:38,24:W,27:O,29:40,30:41,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{11:45,15:44,27:X,33:46,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{11:49,17:48,24:Y,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{11:52,17:51,24:Y,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{20:[1,53]},{22:[1,54]},e(S,[2,18]),{1:[2,2]},e(S,[2,8]),e(S,[2,9]),e(U,[2,37],{40:55,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l}),e(U,[2,38]),e(U,[2,39]),e(b,[2,40]),e(b,[2,42]),e(b,[2,43]),e(b,[2,44]),e(b,[2,45]),e(b,[2,46]),e(b,[2,47]),e(b,[2,48]),e(b,[2,49]),e(b,[2,50]),e(b,[2,51]),e(S,[2,10]),e(S,[2,22],{30:41,29:56,24:W,27:O}),e(S,[2,24]),e(S,[2,25]),{31:[1,57]},{11:59,32:58,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},e(S,[2,11]),e(S,[2,30],{33:60,27:X}),e(S,[2,32]),{31:[1,61]},e(S,[2,12]),{17:62,24:Y},{25:63,27:w},e(S,[2,14]),{17:65,24:Y},e(S,[2,16]),e(S,[2,17]),e(b,[2,41]),e(S,[2,23]),{27:[1,66]},{26:[1,67]},{26:[2,29],28:[1,68]},e(S,[2,31]),{27:[1,69]},e(S,[2,13]),{26:[1,70]},{26:[2,21],28:[1,71]},e(S,[2,15]),e(S,[2,26]),e(S,[2,27]),{11:59,32:72,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},e(S,[2,33]),e(S,[2,19]),{25:73,27:w},{26:[2,28]},{26:[2,20]}],defaultActions:{8:[2,1],10:[2,3],21:[2,2],72:[2,28],73:[2,20]},parseError:a(function(h,c){if(c.recoverable)this.trace(h);else{var g=new Error(h);throw g.hash=c,g}},"parseError"),parse:a(function(h){var c=this,g=[0],x=[],C=[null],o=[],Q=this.table,d="",ct=0,It=0,hi=2,Mt=1,li=o.slice.call(arguments,1),D=Object.create(this.lexer),$={yy:{}};for(var ft in this.yy)Object.prototype.hasOwnProperty.call(this.yy,ft)&&($.yy[ft]=this.yy[ft]);D.setInput(h,$.yy),$.yy.lexer=D,$.yy.parser=this,typeof D.yylloc>"u"&&(D.yylloc={});var pt=D.yylloc;o.push(pt);var ci=D.options&&D.options.ranges;typeof $.yy.parseError=="function"?this.parseError=$.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function ui(B){g.length=g.length-2*B,C.length=C.length-B,o.length=o.length-B}a(ui,"popStack");function Vt(){var B;return B=x.pop()||D.lex()||Mt,typeof B!="number"&&(B instanceof Array&&(x=B,B=x.pop()),B=c.symbols_[B]||B),B}a(Vt,"lex");for(var M,q,z,mt,G={},ut,N,Bt,gt;;){if(q=g[g.length-1],this.defaultActions[q]?z=this.defaultActions[q]:((M===null||typeof M>"u")&&(M=Vt()),z=Q[q]&&Q[q][M]),typeof z>"u"||!z.length||!z[0]){var yt="";gt=[];for(ut in Q[q])this.terminals_[ut]&&ut>hi&>.push("'"+this.terminals_[ut]+"'");D.showPosition?yt="Parse error on line "+(ct+1)+`:
|
|
2
|
+
`+D.showPosition()+`
|
|
3
|
+
Expecting `+gt.join(", ")+", got '"+(this.terminals_[M]||M)+"'":yt="Parse error on line "+(ct+1)+": Unexpected "+(M==Mt?"end of input":"'"+(this.terminals_[M]||M)+"'"),this.parseError(yt,{text:D.match,token:this.terminals_[M]||M,line:D.yylineno,loc:pt,expected:gt})}if(z[0]instanceof Array&&z.length>1)throw new Error("Parse Error: multiple actions possible at state: "+q+", token: "+M);switch(z[0]){case 1:g.push(M),C.push(D.yytext),o.push(D.yylloc),g.push(z[1]),M=null,It=D.yyleng,d=D.yytext,ct=D.yylineno,pt=D.yylloc;break;case 2:if(N=this.productions_[z[1]][1],G.$=C[C.length-N],G._$={first_line:o[o.length-(N||1)].first_line,last_line:o[o.length-1].last_line,first_column:o[o.length-(N||1)].first_column,last_column:o[o.length-1].last_column},ci&&(G._$.range=[o[o.length-(N||1)].range[0],o[o.length-1].range[1]]),mt=this.performAction.apply(G,[d,It,ct,$.yy,z[1],C,o].concat(li)),typeof mt<"u")return mt;N&&(g=g.slice(0,-1*N*2),C=C.slice(0,-1*N),o=o.slice(0,-1*N)),g.push(this.productions_[z[1]][0]),C.push(G.$),o.push(G._$),Bt=Q[g[g.length-2]][g[g.length-1]],g.push(Bt);break;case 3:return!0}}return!0},"parse")},k=function(){var F={EOF:1,parseError:a(function(c,g){if(this.yy.parser)this.yy.parser.parseError(c,g);else throw new Error(c)},"parseError"),setInput:a(function(h,c){return this.yy=c||this.yy||{},this._input=h,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:a(function(){var h=this._input[0];this.yytext+=h,this.yyleng++,this.offset++,this.match+=h,this.matched+=h;var c=h.match(/(?:\r\n?|\n).*/g);return c?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),h},"input"),unput:a(function(h){var c=h.length,g=h.split(/(?:\r\n?|\n)/g);this._input=h+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-c),this.offset-=c;var x=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),g.length-1&&(this.yylineno-=g.length-1);var C=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:g?(g.length===x.length?this.yylloc.first_column:0)+x[x.length-g.length].length-g[0].length:this.yylloc.first_column-c},this.options.ranges&&(this.yylloc.range=[C[0],C[0]+this.yyleng-c]),this.yyleng=this.yytext.length,this},"unput"),more:a(function(){return this._more=!0,this},"more"),reject:a(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
|
|
4
|
+
`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:a(function(h){this.unput(this.match.slice(h))},"less"),pastInput:a(function(){var h=this.matched.substr(0,this.matched.length-this.match.length);return(h.length>20?"...":"")+h.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:a(function(){var h=this.match;return h.length<20&&(h+=this._input.substr(0,20-h.length)),(h.substr(0,20)+(h.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:a(function(){var h=this.pastInput(),c=new Array(h.length+1).join("-");return h+this.upcomingInput()+`
|
|
5
|
+
`+c+"^"},"showPosition"),test_match:a(function(h,c){var g,x,C;if(this.options.backtrack_lexer&&(C={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(C.yylloc.range=this.yylloc.range.slice(0))),x=h[0].match(/(?:\r\n?|\n).*/g),x&&(this.yylineno+=x.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:x?x[x.length-1].length-x[x.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+h[0].length},this.yytext+=h[0],this.match+=h[0],this.matches=h,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(h[0].length),this.matched+=h[0],g=this.performAction.call(this,this.yy,this,c,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),g)return g;if(this._backtrack){for(var o in C)this[o]=C[o];return!1}return!1},"test_match"),next:a(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var h,c,g,x;this._more||(this.yytext="",this.match="");for(var C=this._currentRules(),o=0;o<C.length;o++)if(g=this._input.match(this.rules[C[o]]),g&&(!c||g[0].length>c[0].length)){if(c=g,x=o,this.options.backtrack_lexer){if(h=this.test_match(g,C[o]),h!==!1)return h;if(this._backtrack){c=!1;continue}else return!1}else if(!this.options.flex)break}return c?(h=this.test_match(c,C[x]),h!==!1?h:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text.
|
|
6
|
+
`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:a(function(){var c=this.next();return c||this.lex()},"lex"),begin:a(function(c){this.conditionStack.push(c)},"begin"),popState:a(function(){var c=this.conditionStack.length-1;return c>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:a(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:a(function(c){return c=this.conditionStack.length-1-Math.abs(c||0),c>=0?this.conditionStack[c]:"INITIAL"},"topState"),pushState:a(function(c){this.begin(c)},"pushState"),stateStackSize:a(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:a(function(c,g,x,C){switch(x){case 0:break;case 1:break;case 2:return this.popState(),34;case 3:return this.popState(),34;case 4:return 34;case 5:break;case 6:return 10;case 7:return this.pushState("acc_title"),19;case 8:return this.popState(),"acc_title_value";case 9:return this.pushState("acc_descr"),21;case 10:return this.popState(),"acc_descr_value";case 11:this.pushState("acc_descr_multiline");break;case 12:this.popState();break;case 13:return"acc_descr_multiline_value";case 14:return 5;case 15:return 5;case 16:return 8;case 17:return this.pushState("axis_data"),"X_AXIS";case 18:return this.pushState("axis_data"),"Y_AXIS";case 19:return this.pushState("axis_band_data"),24;case 20:return 31;case 21:return this.pushState("data"),16;case 22:return this.pushState("data"),18;case 23:return this.pushState("data_inner"),24;case 24:return 27;case 25:return this.popState(),26;case 26:this.popState();break;case 27:this.pushState("string");break;case 28:this.popState();break;case 29:return"STR";case 30:return 24;case 31:return 26;case 32:return 43;case 33:return"COLON";case 34:return 44;case 35:return 28;case 36:return 45;case 37:return 46;case 38:return 48;case 39:return 50;case 40:return 47;case 41:return 41;case 42:return 49;case 43:return 42;case 44:break;case 45:return 35;case 46:return 36}},"anonymous"),rules:[/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:(\r?\n))/i,/^(?:(\r?\n))/i,/^(?:[\n\r]+)/i,/^(?:%%[^\n]*)/i,/^(?:title\b)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:\})/i,/^(?:[^\}]*)/i,/^(?:xychart-beta\b)/i,/^(?:xychart\b)/i,/^(?:(?:vertical|horizontal))/i,/^(?:x-axis\b)/i,/^(?:y-axis\b)/i,/^(?:\[)/i,/^(?:-->)/i,/^(?:line\b)/i,/^(?:bar\b)/i,/^(?:\[)/i,/^(?:[+-]?(?:\d+(?:\.\d+)?|\.\d+))/i,/^(?:\])/i,/^(?:(?:`\) \{ this\.pushState\(md_string\); \}\n<md_string>\(\?:\(\?!`"\)\.\)\+ \{ return MD_STR; \}\n<md_string>\(\?:`))/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:\[)/i,/^(?:\])/i,/^(?:[A-Za-z]+)/i,/^(?::)/i,/^(?:\+)/i,/^(?:,)/i,/^(?:=)/i,/^(?:\*)/i,/^(?:#)/i,/^(?:[\_])/i,/^(?:\.)/i,/^(?:&)/i,/^(?:-)/i,/^(?:[0-9]+)/i,/^(?:\s+)/i,/^(?:;)/i,/^(?:$)/i],conditions:{data_inner:{rules:[0,1,4,5,6,7,9,11,14,15,16,17,18,21,22,24,25,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0},data:{rules:[0,1,3,4,5,6,7,9,11,14,15,16,17,18,21,22,23,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0},axis_band_data:{rules:[0,1,4,5,6,7,9,11,14,15,16,17,18,21,22,25,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0},axis_data:{rules:[0,1,2,4,5,6,7,9,11,14,15,16,17,18,19,20,21,22,24,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0},acc_descr_multiline:{rules:[12,13],inclusive:!1},acc_descr:{rules:[10],inclusive:!1},acc_title:{rules:[8],inclusive:!1},title:{rules:[],inclusive:!1},md_string:{rules:[],inclusive:!1},string:{rules:[28,29],inclusive:!1},INITIAL:{rules:[0,1,4,5,6,7,9,11,14,15,16,17,18,21,22,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0}}};return F}();V.lexer=k;function H(){this.yy={}}return a(H,"Parser"),H.prototype=V,V.Parser=H,new H}();At.parser=At;var Ti=At;function wt(e){return e.type==="bar"}a(wt,"isBarPlot");function kt(e){return e.type==="band"}a(kt,"isBandAxisData");function j(e){return e.type==="linear"}a(j,"isLinearAxisData");var tt,Ht=(tt=class{constructor(t){this.parentGroup=t}getMaxDimension(t,i){if(!this.parentGroup)return{width:t.reduce((r,u)=>Math.max(u.length,r),0)*i,height:i};const s={width:0,height:0},n=this.parentGroup.append("g").attr("visibility","hidden").attr("font-size",i);for(const r of t){const u=gi(n,1,r),p=u?u.width:r.length*i,f=u?u.height:i;s.width=Math.max(s.width,p),s.height=Math.max(s.height,f)}return n.remove(),s}},a(tt,"TextDimensionCalculatorWithFont"),tt),Ot=.7,Ft=.2,it,Ut=(it=class{constructor(t,i,s,n){this.axisConfig=t,this.title=i,this.textDimensionCalculator=s,this.axisThemeConfig=n,this.boundingRect={x:0,y:0,width:0,height:0},this.axisPosition="left",this.showTitle=!1,this.showLabel=!1,this.showTick=!1,this.showAxisLine=!1,this.outerPadding=0,this.titleTextHeight=0,this.labelTextHeight=0,this.range=[0,10],this.boundingRect={x:0,y:0,width:0,height:0},this.axisPosition="left"}setRange(t){this.range=t,this.axisPosition==="left"||this.axisPosition==="right"?this.boundingRect.height=t[1]-t[0]:this.boundingRect.width=t[1]-t[0],this.recalculateScale()}getRange(){return[this.range[0]+this.outerPadding,this.range[1]-this.outerPadding]}setAxisPosition(t){this.axisPosition=t,this.setRange(this.range)}getTickDistance(){const t=this.getRange();return Math.abs(t[0]-t[1])/this.getTickValues().length}getAxisOuterPadding(){return this.outerPadding}getLabelDimension(){return this.textDimensionCalculator.getMaxDimension(this.getTickValues().map(t=>t.toString()),this.axisConfig.labelFontSize)}recalculateOuterPaddingToDrawBar(){Ot*this.getTickDistance()>this.outerPadding*2&&(this.outerPadding=Math.floor(Ot*this.getTickDistance()/2)),this.recalculateScale()}calculateSpaceIfDrawnHorizontally(t){let i=t.height;if(this.axisConfig.showAxisLine&&i>this.axisConfig.axisLineWidth&&(i-=this.axisConfig.axisLineWidth,this.showAxisLine=!0),this.axisConfig.showLabel){const s=this.getLabelDimension(),n=Ft*t.width;this.outerPadding=Math.min(s.width/2,n);const r=s.height+this.axisConfig.labelPadding*2;this.labelTextHeight=s.height,r<=i&&(i-=r,this.showLabel=!0)}if(this.axisConfig.showTick&&i>=this.axisConfig.tickLength&&(this.showTick=!0,i-=this.axisConfig.tickLength),this.axisConfig.showTitle&&this.title){const s=this.textDimensionCalculator.getMaxDimension([this.title],this.axisConfig.titleFontSize),n=s.height+this.axisConfig.titlePadding*2;this.titleTextHeight=s.height,n<=i&&(i-=n,this.showTitle=!0)}this.boundingRect.width=t.width,this.boundingRect.height=t.height-i}calculateSpaceIfDrawnVertical(t){let i=t.width;if(this.axisConfig.showAxisLine&&i>this.axisConfig.axisLineWidth&&(i-=this.axisConfig.axisLineWidth,this.showAxisLine=!0),this.axisConfig.showLabel){const s=this.getLabelDimension(),n=Ft*t.height;this.outerPadding=Math.min(s.height/2,n);const r=s.width+this.axisConfig.labelPadding*2;r<=i&&(i-=r,this.showLabel=!0)}if(this.axisConfig.showTick&&i>=this.axisConfig.tickLength&&(this.showTick=!0,i-=this.axisConfig.tickLength),this.axisConfig.showTitle&&this.title){const s=this.textDimensionCalculator.getMaxDimension([this.title],this.axisConfig.titleFontSize),n=s.height+this.axisConfig.titlePadding*2;this.titleTextHeight=s.height,n<=i&&(i-=n,this.showTitle=!0)}this.boundingRect.width=t.width-i,this.boundingRect.height=t.height}calculateSpace(t){return this.axisPosition==="left"||this.axisPosition==="right"?this.calculateSpaceIfDrawnVertical(t):this.calculateSpaceIfDrawnHorizontally(t),this.recalculateScale(),{width:this.boundingRect.width,height:this.boundingRect.height}}setBoundingBoxXY(t){this.boundingRect.x=t.x,this.boundingRect.y=t.y}getDrawableElementsForLeftAxis(){const t=[];if(this.showAxisLine){const i=this.boundingRect.x+this.boundingRect.width-this.axisConfig.axisLineWidth/2;t.push({type:"path",groupTexts:["left-axis","axisl-line"],data:[{path:`M ${i},${this.boundingRect.y} L ${i},${this.boundingRect.y+this.boundingRect.height} `,strokeFill:this.axisThemeConfig.axisLineColor,strokeWidth:this.axisConfig.axisLineWidth}]})}if(this.showLabel&&t.push({type:"text",groupTexts:["left-axis","label"],data:this.getTickValues().map(i=>({text:i.toString(),x:this.boundingRect.x+this.boundingRect.width-(this.showLabel?this.axisConfig.labelPadding:0)-(this.showTick?this.axisConfig.tickLength:0)-(this.showAxisLine?this.axisConfig.axisLineWidth:0),y:this.getScaleValue(i),fill:this.axisThemeConfig.labelColor,fontSize:this.axisConfig.labelFontSize,rotation:0,verticalPos:"middle",horizontalPos:"right"}))}),this.showTick){const i=this.boundingRect.x+this.boundingRect.width-(this.showAxisLine?this.axisConfig.axisLineWidth:0);t.push({type:"path",groupTexts:["left-axis","ticks"],data:this.getTickValues().map(s=>({path:`M ${i},${this.getScaleValue(s)} L ${i-this.axisConfig.tickLength},${this.getScaleValue(s)}`,strokeFill:this.axisThemeConfig.tickColor,strokeWidth:this.axisConfig.tickWidth}))})}return this.showTitle&&t.push({type:"text",groupTexts:["left-axis","title"],data:[{text:this.title,x:this.boundingRect.x+this.axisConfig.titlePadding,y:this.boundingRect.y+this.boundingRect.height/2,fill:this.axisThemeConfig.titleColor,fontSize:this.axisConfig.titleFontSize,rotation:270,verticalPos:"top",horizontalPos:"center"}]}),t}getDrawableElementsForBottomAxis(){const t=[];if(this.showAxisLine){const i=this.boundingRect.y+this.axisConfig.axisLineWidth/2;t.push({type:"path",groupTexts:["bottom-axis","axis-line"],data:[{path:`M ${this.boundingRect.x},${i} L ${this.boundingRect.x+this.boundingRect.width},${i}`,strokeFill:this.axisThemeConfig.axisLineColor,strokeWidth:this.axisConfig.axisLineWidth}]})}if(this.showLabel&&t.push({type:"text",groupTexts:["bottom-axis","label"],data:this.getTickValues().map(i=>({text:i.toString(),x:this.getScaleValue(i),y:this.boundingRect.y+this.axisConfig.labelPadding+(this.showTick?this.axisConfig.tickLength:0)+(this.showAxisLine?this.axisConfig.axisLineWidth:0),fill:this.axisThemeConfig.labelColor,fontSize:this.axisConfig.labelFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}))}),this.showTick){const i=this.boundingRect.y+(this.showAxisLine?this.axisConfig.axisLineWidth:0);t.push({type:"path",groupTexts:["bottom-axis","ticks"],data:this.getTickValues().map(s=>({path:`M ${this.getScaleValue(s)},${i} L ${this.getScaleValue(s)},${i+this.axisConfig.tickLength}`,strokeFill:this.axisThemeConfig.tickColor,strokeWidth:this.axisConfig.tickWidth}))})}return this.showTitle&&t.push({type:"text",groupTexts:["bottom-axis","title"],data:[{text:this.title,x:this.range[0]+(this.range[1]-this.range[0])/2,y:this.boundingRect.y+this.boundingRect.height-this.axisConfig.titlePadding-this.titleTextHeight,fill:this.axisThemeConfig.titleColor,fontSize:this.axisConfig.titleFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}]}),t}getDrawableElementsForTopAxis(){const t=[];if(this.showAxisLine){const i=this.boundingRect.y+this.boundingRect.height-this.axisConfig.axisLineWidth/2;t.push({type:"path",groupTexts:["top-axis","axis-line"],data:[{path:`M ${this.boundingRect.x},${i} L ${this.boundingRect.x+this.boundingRect.width},${i}`,strokeFill:this.axisThemeConfig.axisLineColor,strokeWidth:this.axisConfig.axisLineWidth}]})}if(this.showLabel&&t.push({type:"text",groupTexts:["top-axis","label"],data:this.getTickValues().map(i=>({text:i.toString(),x:this.getScaleValue(i),y:this.boundingRect.y+(this.showTitle?this.titleTextHeight+this.axisConfig.titlePadding*2:0)+this.axisConfig.labelPadding,fill:this.axisThemeConfig.labelColor,fontSize:this.axisConfig.labelFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}))}),this.showTick){const i=this.boundingRect.y;t.push({type:"path",groupTexts:["top-axis","ticks"],data:this.getTickValues().map(s=>({path:`M ${this.getScaleValue(s)},${i+this.boundingRect.height-(this.showAxisLine?this.axisConfig.axisLineWidth:0)} L ${this.getScaleValue(s)},${i+this.boundingRect.height-this.axisConfig.tickLength-(this.showAxisLine?this.axisConfig.axisLineWidth:0)}`,strokeFill:this.axisThemeConfig.tickColor,strokeWidth:this.axisConfig.tickWidth}))})}return this.showTitle&&t.push({type:"text",groupTexts:["top-axis","title"],data:[{text:this.title,x:this.boundingRect.x+this.boundingRect.width/2,y:this.boundingRect.y+this.axisConfig.titlePadding,fill:this.axisThemeConfig.titleColor,fontSize:this.axisConfig.titleFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}]}),t}getDrawableElements(){if(this.axisPosition==="left")return this.getDrawableElementsForLeftAxis();if(this.axisPosition==="right")throw Error("Drawing of right axis is not implemented");return this.axisPosition==="bottom"?this.getDrawableElementsForBottomAxis():this.axisPosition==="top"?this.getDrawableElementsForTopAxis():[]}},a(it,"BaseAxis"),it),et,Di=(et=class extends Ut{constructor(t,i,s,n,r){super(t,n,r,i),this.categories=s,this.scale=bt().domain(this.categories).range(this.getRange())}setRange(t){super.setRange(t)}recalculateScale(){this.scale=bt().domain(this.categories).range(this.getRange()).paddingInner(1).paddingOuter(0).align(.5),Xt.trace("BandAxis axis final categories, range: ",this.categories,this.getRange())}getTickValues(){return this.categories}getScaleValue(t){return this.scale(t)??this.getRange()[0]}},a(et,"BandAxis"),et),st,vi=(st=class extends Ut{constructor(t,i,s,n,r){super(t,n,r,i),this.domain=s,this.scale=Wt().domain(this.domain).range(this.getRange())}getTickValues(){return this.scale.ticks()}recalculateScale(){const t=[...this.domain];this.axisPosition==="left"&&t.reverse(),this.scale=Wt().domain(t).range(this.getRange())}getScaleValue(t){return this.scale(t)}},a(st,"LinearAxis"),st);function Ct(e,t,i,s){const n=new Ht(s);return kt(e)?new Di(t,i,e.categories,e.title,n):new vi(t,i,[e.min,e.max],e.title,n)}a(Ct,"getAxis");var nt,Pi=(nt=class{constructor(t,i,s,n){this.textDimensionCalculator=t,this.chartConfig=i,this.chartData=s,this.chartThemeConfig=n,this.boundingRect={x:0,y:0,width:0,height:0},this.showChartTitle=!1}setBoundingBoxXY(t){this.boundingRect.x=t.x,this.boundingRect.y=t.y}calculateSpace(t){const i=this.textDimensionCalculator.getMaxDimension([this.chartData.title],this.chartConfig.titleFontSize),s=Math.max(i.width,t.width),n=i.height+2*this.chartConfig.titlePadding;return i.width<=s&&i.height<=n&&this.chartConfig.showTitle&&this.chartData.title&&(this.boundingRect.width=s,this.boundingRect.height=n,this.showChartTitle=!0),{width:this.boundingRect.width,height:this.boundingRect.height}}getDrawableElements(){const t=[];return this.showChartTitle&&t.push({groupTexts:["chart-title"],type:"text",data:[{fontSize:this.chartConfig.titleFontSize,text:this.chartData.title,verticalPos:"middle",horizontalPos:"center",x:this.boundingRect.x+this.boundingRect.width/2,y:this.boundingRect.y+this.boundingRect.height/2,fill:this.chartThemeConfig.titleColor,rotation:0}]}),t}},a(nt,"ChartTitle"),nt);function $t(e,t,i,s){const n=new Ht(s);return new Pi(n,e,t,i)}a($t,"getChartTitleComponent");var at,Li=(at=class{constructor(t,i,s,n,r){this.plotData=t,this.xAxis=i,this.yAxis=s,this.orientation=n,this.plotIndex=r}getDrawableElement(){const t=this.plotData.data.map(s=>[this.xAxis.getScaleValue(s[0]),this.yAxis.getScaleValue(s[1])]);let i;return this.orientation==="horizontal"?i=zt().y(s=>s[0]).x(s=>s[1])(t):i=zt().x(s=>s[0]).y(s=>s[1])(t),i?[{groupTexts:["plot",`line-plot-${this.plotIndex}`],type:"path",data:[{path:i,strokeFill:this.plotData.strokeFill,strokeWidth:this.plotData.strokeWidth}]}]:[]}},a(at,"LinePlot"),at),ot,Ei=(ot=class{constructor(t,i,s,n,r,u){this.barData=t,this.boundingRect=i,this.xAxis=s,this.yAxis=n,this.orientation=r,this.plotIndex=u}getDrawableElement(){const t=this.barData.data.map(r=>[this.xAxis.getScaleValue(r[0]),this.yAxis.getScaleValue(r[1])]),i=.05,s=Math.min(this.xAxis.getAxisOuterPadding()*2,this.xAxis.getTickDistance())*(1-i),n=s/2;return this.orientation==="horizontal"?[{groupTexts:["plot",`bar-plot-${this.plotIndex}`],type:"rect",data:t.map(r=>({x:this.boundingRect.x,y:r[0]-n,height:s,width:r[1]-this.boundingRect.x,fill:this.barData.fill,strokeWidth:0,strokeFill:this.barData.fill}))}]:[{groupTexts:["plot",`bar-plot-${this.plotIndex}`],type:"rect",data:t.map(r=>({x:r[0]-n,y:r[1],width:s,height:this.boundingRect.y+this.boundingRect.height-r[1],fill:this.barData.fill,strokeWidth:0,strokeFill:this.barData.fill}))}]}},a(ot,"BarPlot"),ot),rt,Ii=(rt=class{constructor(t,i,s){this.chartConfig=t,this.chartData=i,this.chartThemeConfig=s,this.boundingRect={x:0,y:0,width:0,height:0}}setAxes(t,i){this.xAxis=t,this.yAxis=i}setBoundingBoxXY(t){this.boundingRect.x=t.x,this.boundingRect.y=t.y}calculateSpace(t){return this.boundingRect.width=t.width,this.boundingRect.height=t.height,{width:this.boundingRect.width,height:this.boundingRect.height}}getDrawableElements(){if(!(this.xAxis&&this.yAxis))throw Error("Axes must be passed to render Plots");const t=[];for(const[i,s]of this.chartData.plots.entries())switch(s.type){case"line":{const n=new Li(s,this.xAxis,this.yAxis,this.chartConfig.chartOrientation,i);t.push(...n.getDrawableElement())}break;case"bar":{const n=new Ei(s,this.boundingRect,this.xAxis,this.yAxis,this.chartConfig.chartOrientation,i);t.push(...n.getDrawableElement())}break}return t}},a(rt,"BasePlot"),rt);function qt(e,t,i){return new Ii(e,t,i)}a(qt,"getPlotComponent");var ht,Mi=(ht=class{constructor(t,i,s,n){this.chartConfig=t,this.chartData=i,this.componentStore={title:$t(t,i,s,n),plot:qt(t,i,s),xAxis:Ct(i.xAxis,t.xAxis,{titleColor:s.xAxisTitleColor,labelColor:s.xAxisLabelColor,tickColor:s.xAxisTickColor,axisLineColor:s.xAxisLineColor},n),yAxis:Ct(i.yAxis,t.yAxis,{titleColor:s.yAxisTitleColor,labelColor:s.yAxisLabelColor,tickColor:s.yAxisTickColor,axisLineColor:s.yAxisLineColor},n)}}calculateVerticalSpace(){let t=this.chartConfig.width,i=this.chartConfig.height,s=0,n=0,r=Math.floor(t*this.chartConfig.plotReservedSpacePercent/100),u=Math.floor(i*this.chartConfig.plotReservedSpacePercent/100),p=this.componentStore.plot.calculateSpace({width:r,height:u});t-=p.width,i-=p.height,p=this.componentStore.title.calculateSpace({width:this.chartConfig.width,height:i}),n=p.height,i-=p.height,this.componentStore.xAxis.setAxisPosition("bottom"),p=this.componentStore.xAxis.calculateSpace({width:t,height:i}),i-=p.height,this.componentStore.yAxis.setAxisPosition("left"),p=this.componentStore.yAxis.calculateSpace({width:t,height:i}),s=p.width,t-=p.width,t>0&&(r+=t,t=0),i>0&&(u+=i,i=0),this.componentStore.plot.calculateSpace({width:r,height:u}),this.componentStore.plot.setBoundingBoxXY({x:s,y:n}),this.componentStore.xAxis.setRange([s,s+r]),this.componentStore.xAxis.setBoundingBoxXY({x:s,y:n+u}),this.componentStore.yAxis.setRange([n,n+u]),this.componentStore.yAxis.setBoundingBoxXY({x:0,y:n}),this.chartData.plots.some(f=>wt(f))&&this.componentStore.xAxis.recalculateOuterPaddingToDrawBar()}calculateHorizontalSpace(){let t=this.chartConfig.width,i=this.chartConfig.height,s=0,n=0,r=0,u=Math.floor(t*this.chartConfig.plotReservedSpacePercent/100),p=Math.floor(i*this.chartConfig.plotReservedSpacePercent/100),f=this.componentStore.plot.calculateSpace({width:u,height:p});t-=f.width,i-=f.height,f=this.componentStore.title.calculateSpace({width:this.chartConfig.width,height:i}),s=f.height,i-=f.height,this.componentStore.xAxis.setAxisPosition("left"),f=this.componentStore.xAxis.calculateSpace({width:t,height:i}),t-=f.width,n=f.width,this.componentStore.yAxis.setAxisPosition("top"),f=this.componentStore.yAxis.calculateSpace({width:t,height:i}),i-=f.height,r=s+f.height,t>0&&(u+=t,t=0),i>0&&(p+=i,i=0),this.componentStore.plot.calculateSpace({width:u,height:p}),this.componentStore.plot.setBoundingBoxXY({x:n,y:r}),this.componentStore.yAxis.setRange([n,n+u]),this.componentStore.yAxis.setBoundingBoxXY({x:n,y:s}),this.componentStore.xAxis.setRange([r,r+p]),this.componentStore.xAxis.setBoundingBoxXY({x:0,y:r}),this.chartData.plots.some(T=>wt(T))&&this.componentStore.xAxis.recalculateOuterPaddingToDrawBar()}calculateSpace(){this.chartConfig.chartOrientation==="horizontal"?this.calculateHorizontalSpace():this.calculateVerticalSpace()}getDrawableElement(){this.calculateSpace();const t=[];this.componentStore.plot.setAxes(this.componentStore.xAxis,this.componentStore.yAxis);for(const i of Object.values(this.componentStore))t.push(...i.getDrawableElements());return t}},a(ht,"Orchestrator"),ht),lt,Vi=(lt=class{static build(t,i,s,n){return new Mi(t,i,s,n).getDrawableElement()}},a(lt,"XYChartBuilder"),lt),K=0,Gt,Z=Dt(),J=Tt(),A=vt(),St=J.plotColorPalette.split(",").map(e=>e.trim()),xt=!1,Rt=!1;function Tt(){const e=xi(),t=_t();return Yt(e.xyChart,t.themeVariables.xyChart)}a(Tt,"getChartDefaultThemeConfig");function Dt(){const e=_t();return Yt(di.xyChart,e.xyChart)}a(Dt,"getChartDefaultConfig");function vt(){return{yAxis:{type:"linear",title:"",min:1/0,max:-1/0},xAxis:{type:"band",title:"",categories:[]},title:"",plots:[]}}a(vt,"getChartDefaultData");function dt(e){const t=_t();return fi(e.trim(),t)}a(dt,"textSanitizer");function jt(e){Gt=e}a(jt,"setTmpSVGG");function Qt(e){e==="horizontal"?Z.chartOrientation="horizontal":Z.chartOrientation="vertical"}a(Qt,"setOrientation");function Kt(e){A.xAxis.title=dt(e.text)}a(Kt,"setXAxisTitle");function Pt(e,t){A.xAxis={type:"linear",title:A.xAxis.title,min:e,max:t},xt=!0}a(Pt,"setXAxisRangeData");function Zt(e){A.xAxis={type:"band",title:A.xAxis.title,categories:e.map(t=>dt(t.text))},xt=!0}a(Zt,"setXAxisBand");function Jt(e){A.yAxis.title=dt(e.text)}a(Jt,"setYAxisTitle");function ti(e,t){A.yAxis={type:"linear",title:A.yAxis.title,min:e,max:t},Rt=!0}a(ti,"setYAxisRangeData");function ii(e){const t=Math.min(...e),i=Math.max(...e),s=j(A.yAxis)?A.yAxis.min:1/0,n=j(A.yAxis)?A.yAxis.max:-1/0;A.yAxis={type:"linear",title:A.yAxis.title,min:Math.min(s,t),max:Math.max(n,i)}}a(ii,"setYAxisRangeFromPlotData");function Lt(e){let t=[];if(e.length===0)return t;if(!xt){const i=j(A.xAxis)?A.xAxis.min:1/0,s=j(A.xAxis)?A.xAxis.max:-1/0;Pt(Math.min(i,1),Math.max(s,e.length))}if(Rt||ii(e),kt(A.xAxis)&&(t=A.xAxis.categories.map((i,s)=>[i,e[s]])),j(A.xAxis)){const i=A.xAxis.min,s=A.xAxis.max,n=(s-i)/(e.length-1),r=[];for(let u=i;u<=s;u+=n)r.push(`${u}`);t=r.map((u,p)=>[u,e[p]])}return t}a(Lt,"transformDataWithoutCategory");function Et(e){return St[e===0?0:e%St.length]}a(Et,"getPlotColorFromPalette");function ei(e,t){const i=Lt(t);A.plots.push({type:"line",strokeFill:Et(K),strokeWidth:2,data:i}),K++}a(ei,"setLineData");function si(e,t){const i=Lt(t);A.plots.push({type:"bar",fill:Et(K),data:i}),K++}a(si,"setBarData");function ni(){if(A.plots.length===0)throw Error("No Plot to render, please provide a plot with some data");return A.title=Nt(),Vi.build(Z,A,J,Gt)}a(ni,"getDrawableElem");function ai(){return J}a(ai,"getChartThemeConfig");function oi(){return Z}a(oi,"getChartConfig");function ri(){return A}a(ri,"getXYChartData");var Bi=a(function(){wi(),K=0,Z=Dt(),A=vt(),J=Tt(),St=J.plotColorPalette.split(",").map(e=>e.trim()),xt=!1,Rt=!1},"clear"),Wi={getDrawableElem:ni,clear:Bi,setAccTitle:pi,getAccTitle:mi,setDiagramTitle:yi,getDiagramTitle:Nt,getAccDescription:bi,setAccDescription:Ai,setOrientation:Qt,setXAxisTitle:Kt,setXAxisRangeData:Pt,setXAxisBand:Zt,setYAxisTitle:Jt,setYAxisRangeData:ti,setLineData:ei,setBarData:si,setTmpSVGG:jt,getChartThemeConfig:ai,getChartConfig:oi,getXYChartData:ri},zi=a((e,t,i,s)=>{const n=s.db,r=n.getChartThemeConfig(),u=n.getChartConfig(),p=n.getXYChartData().plots[0].data.map(m=>m[1]);function f(m){return m==="top"?"text-before-edge":"middle"}a(f,"getDominantBaseLine");function T(m){return m==="left"?"start":m==="right"?"end":"middle"}a(T,"getTextAnchor");function P(m){return`translate(${m.x}, ${m.y}) rotate(${m.rotation||0})`}a(P,"getTextTransformation"),Xt.debug(`Rendering xychart chart
|
|
7
|
+
`+e);const _=Ci(t),y=_.append("g").attr("class","main"),E=y.append("rect").attr("width",u.width).attr("height",u.height).attr("class","background");Si(_,u.height,u.width,!0),_.attr("viewBox",`0 0 ${u.width} ${u.height}`),E.attr("fill",r.backgroundColor),n.setTmpSVGG(_.append("g").attr("class","mermaid-tmp-group"));const v=n.getDrawableElem(),L={};function I(m){let R=y,l="";for(const[W]of m.entries()){let O=y;W>0&&L[l]&&(O=L[l]),l+=m[W],R=L[l],R||(R=L[l]=O.append("g").attr("class",m[W]))}return R}a(I,"getGroup");for(const m of v){if(m.data.length===0)continue;const R=I(m.groupTexts);switch(m.type){case"rect":if(R.selectAll("rect").data(m.data).enter().append("rect").attr("x",l=>l.x).attr("y",l=>l.y).attr("width",l=>l.width).attr("height",l=>l.height).attr("fill",l=>l.fill).attr("stroke",l=>l.strokeFill).attr("stroke-width",l=>l.strokeWidth),u.showDataLabel){const l=u.showDataLabelOutsideBar;if(u.chartOrientation==="horizontal"){let W=function(w,V){const{data:k,label:H}=w;return V*H.length*O<=k.width-X};a(W,"fitsHorizontally");const O=.7,X=10,Y=m.data.map((w,V)=>({data:w,label:p[V].toString()})).filter(w=>w.data.width>0&&w.data.height>0),S=Y.map(w=>{const{data:V}=w;let k=V.height*.7;for(;!W(w,k)&&k>0;)k-=1;return k}),U=Math.floor(Math.min(...S)),b=a(w=>l?w.data.x+w.data.width+X:w.data.x+w.data.width-X,"determineLabelXPosition");R.selectAll("text").data(Y).enter().append("text").attr("x",b).attr("y",w=>w.data.y+w.data.height/2).attr("text-anchor",l?"start":"end").attr("dominant-baseline","middle").attr("fill",r.dataLabelColor).attr("font-size",`${U}px`).text(w=>w.label)}else{let W=function(b,w,V){const{data:k,label:H}=b,F=.7,h=w*H.length*F,c=k.x+k.width/2,g=c-h/2,x=c+h/2,C=g>=k.x&&x<=k.x+k.width,o=k.y+V+w<=k.y+k.height;return C&&o};a(W,"fitsInBar");const O=10,X=m.data.map((b,w)=>({data:b,label:p[w].toString()})).filter(b=>b.data.width>0&&b.data.height>0),Y=X.map(b=>{const{data:w,label:V}=b;let k=w.width/(V.length*.7);for(;!W(b,k,O)&&k>0;)k-=1;return k}),S=Math.floor(Math.min(...Y)),U=a(b=>l?b.data.y-O:b.data.y+O,"determineLabelYPosition");R.selectAll("text").data(X).enter().append("text").attr("x",b=>b.data.x+b.data.width/2).attr("y",U).attr("text-anchor","middle").attr("dominant-baseline",l?"auto":"hanging").attr("fill",r.dataLabelColor).attr("font-size",`${S}px`).text(b=>b.label)}}break;case"text":R.selectAll("text").data(m.data).enter().append("text").attr("x",0).attr("y",0).attr("fill",l=>l.fill).attr("font-size",l=>l.fontSize).attr("dominant-baseline",l=>f(l.verticalPos)).attr("text-anchor",l=>T(l.horizontalPos)).attr("transform",l=>P(l)).text(l=>l.text);break;case"path":R.selectAll("path").data(m.data).enter().append("path").attr("d",l=>l.path).attr("fill",l=>l.fill?l.fill:"none").attr("stroke",l=>l.strokeFill).attr("stroke-width",l=>l.strokeWidth);break}}},"draw"),Oi={draw:zi},Gi={parser:Ti,db:Wi,renderer:Oi};export{Gi as diagram};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Agent Whiteboard</title>
|
|
7
|
+
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
|
8
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
|
9
|
+
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
|
10
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-4abf3da0.js"></script>
|
|
12
|
+
<link rel="stylesheet" href="/assets/index-d674a6a4.css">
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div id="app"></div>
|
|
16
|
+
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
// Pure Hono application — no startup side effects.
|
|
2
|
+
// Exported so tests can import it without spinning up a real server.
|
|
3
|
+
import { Hono } from "hono";
|
|
4
|
+
import { serveStatic } from "@hono/node-server/serve-static";
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import { signalClick, signalDone, waitForClick, waitForDone } from "./interaction.js";
|
|
7
|
+
import { clearCanvas, exportCanvas, getLastWorkspace, setLastWorkspace } from "./session.js";
|
|
8
|
+
import { broadcast } from "./ws.js";
|
|
9
|
+
import { FRAME_TYPES, hasMermaidKeyword, isValidSnapshotFilename, nodeActionsSchema, nodeToFrameSchema, validateFrame } from "./validate.js";
|
|
10
|
+
import { cancelSlideshow, startSlideshow } from "./slideshow.js";
|
|
11
|
+
import { findSnapshotById, findSnapshotByIdInWorkspace, isFrameArray, listAllSnapshots, listSnapshots, loadSnapshotContent } from "./snapshot-reader.js";
|
|
12
|
+
import { deleteSnapshotFiles, deleteWorkspace, validateWorkspaceForDelete } from "./snapshot-writer.js";
|
|
13
|
+
import { applyLoadedSnapshotResult, appendFrameAndBroadcast, commitRenderResult, commitStepFramesResult, initStepFramesResult, seekAndBroadcast, stepAndBroadcast, validateWorkspaceInput, } from "./render-core.js";
|
|
14
|
+
import { generateExportHtml } from "./export-html.js";
|
|
15
|
+
import { setViewport } from "./viewport-cache.js";
|
|
16
|
+
import { getSnapshotsRoot } from "./paths.js";
|
|
17
|
+
// Re-export for tests that reference MERMAID_KEYWORDS / isValidMermaid directly.
|
|
18
|
+
export { MERMAID_KEYWORDS } from "./validate.js";
|
|
19
|
+
export function isValidMermaid(payload) {
|
|
20
|
+
return hasMermaidKeyword(payload);
|
|
21
|
+
}
|
|
22
|
+
// Defense-in-depth only (see M3 in docs/02_assumptions-and-risks.md) — SVG/HTML
|
|
23
|
+
// payloads are already sanitized client-side via DOMPurify before render, and
|
|
24
|
+
// server-side for HTML exports. 'unsafe-inline' on script/style is required
|
|
25
|
+
// because the self-contained HTML export embeds the full mermaid.js bundle
|
|
26
|
+
// and generated CSS inline (F17 in docs/03) — a nonce/hash scheme would be
|
|
27
|
+
// more restrictive but isn't worth the complexity for a backstop control.
|
|
28
|
+
const CSP_HEADER = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; " +
|
|
29
|
+
"img-src 'self' data:; connect-src 'self' ws: wss:; object-src 'none'; base-uri 'none'; " +
|
|
30
|
+
"frame-ancestors 'none'";
|
|
31
|
+
export function createApp(options = {}) {
|
|
32
|
+
const app = new Hono();
|
|
33
|
+
app.use("*", async (c, next) => {
|
|
34
|
+
await next();
|
|
35
|
+
c.header("Content-Security-Policy", CSP_HEADER);
|
|
36
|
+
});
|
|
37
|
+
app.post("/render", async (c) => {
|
|
38
|
+
const body = await c.req.json();
|
|
39
|
+
const workspaceResult = validateWorkspaceInput(body.options?.workspace);
|
|
40
|
+
if (!workspaceResult.ok) {
|
|
41
|
+
return c.json({ ok: false, error: workspaceResult.error }, 400);
|
|
42
|
+
}
|
|
43
|
+
const { workspace } = workspaceResult;
|
|
44
|
+
if (typeof body.payload !== "string") {
|
|
45
|
+
return c.json({ ok: false, error: "payload must be a string" }, 400);
|
|
46
|
+
}
|
|
47
|
+
if (!FRAME_TYPES.includes(body.type)) {
|
|
48
|
+
return c.json({ ok: false, error: `type must be one of: ${FRAME_TYPES.join(", ")}` }, 400);
|
|
49
|
+
}
|
|
50
|
+
const type = body.type;
|
|
51
|
+
const { payload } = body;
|
|
52
|
+
const title = body.options?.title;
|
|
53
|
+
const validationError = await validateFrame({ type, payload });
|
|
54
|
+
if (validationError) {
|
|
55
|
+
return c.json({ ok: false, error: validationError });
|
|
56
|
+
}
|
|
57
|
+
const result = commitRenderResult(type, payload, workspace, title);
|
|
58
|
+
return c.json(result);
|
|
59
|
+
});
|
|
60
|
+
app.post("/step", async (c) => {
|
|
61
|
+
const body = await c.req.json();
|
|
62
|
+
if (body.direction !== "next" && body.direction !== "prev") {
|
|
63
|
+
return c.json({ ok: false, error: 'direction must be "next" or "prev"' }, 400);
|
|
64
|
+
}
|
|
65
|
+
return c.json(stepAndBroadcast(body.direction));
|
|
66
|
+
});
|
|
67
|
+
app.post("/seek", async (c) => {
|
|
68
|
+
const body = await c.req.json();
|
|
69
|
+
if (typeof body.frame !== "number" || !Number.isInteger(body.frame)) {
|
|
70
|
+
return c.json({ ok: false, error: "frame must be an integer" }, 400);
|
|
71
|
+
}
|
|
72
|
+
return c.json(seekAndBroadcast(body.frame));
|
|
73
|
+
});
|
|
74
|
+
app.post("/clear", (c) => {
|
|
75
|
+
cancelSlideshow({ persist: false }); // clear() must never produce a snapshot (F10)
|
|
76
|
+
clearCanvas();
|
|
77
|
+
broadcast({ action: "clear" });
|
|
78
|
+
return c.json({ ok: true });
|
|
79
|
+
});
|
|
80
|
+
// ── Slideshow (Phase 2 — Sprint 9) ───────────────────────────────────────────
|
|
81
|
+
app.post("/slideshow", async (c) => {
|
|
82
|
+
const body = await c.req.json();
|
|
83
|
+
if (!Array.isArray(body.slides) || body.slides.length === 0) {
|
|
84
|
+
return c.json({ ok: false, error: "slides must be a non-empty array" }, 400);
|
|
85
|
+
}
|
|
86
|
+
if (typeof body.delay_ms !== "number" || body.delay_ms <= 0) {
|
|
87
|
+
return c.json({ ok: false, error: "delay_ms must be a positive number" }, 400);
|
|
88
|
+
}
|
|
89
|
+
const workspaceResult = validateWorkspaceInput(body.workspace);
|
|
90
|
+
if (!workspaceResult.ok) {
|
|
91
|
+
return c.json({ ok: false, error: workspaceResult.error }, 400);
|
|
92
|
+
}
|
|
93
|
+
const { workspace } = workspaceResult;
|
|
94
|
+
const rawSlides = body.slides;
|
|
95
|
+
// Validate each slide — same rules as POST /render.
|
|
96
|
+
const validatedSlides = [];
|
|
97
|
+
for (let i = 0; i < rawSlides.length; i++) {
|
|
98
|
+
const s = rawSlides[i];
|
|
99
|
+
if (typeof s.type !== "string" || typeof s.payload !== "string") {
|
|
100
|
+
return c.json({
|
|
101
|
+
ok: false,
|
|
102
|
+
error: `slide[${i}]: "type" and "payload" must be strings`,
|
|
103
|
+
}, 400);
|
|
104
|
+
}
|
|
105
|
+
if (!FRAME_TYPES.includes(s.type)) {
|
|
106
|
+
return c.json({ ok: false, error: `slide[${i}]: type must be one of: ${FRAME_TYPES.join(", ")}` }, 400);
|
|
107
|
+
}
|
|
108
|
+
if (s.title !== undefined && typeof s.title !== "string") {
|
|
109
|
+
return c.json({ ok: false, error: `slide[${i}]: "title" must be a string` }, 400);
|
|
110
|
+
}
|
|
111
|
+
const err = await validateFrame({ type: s.type, payload: s.payload });
|
|
112
|
+
if (err) {
|
|
113
|
+
return c.json({ ok: false, error: `slide[${i}]: ${err}` });
|
|
114
|
+
}
|
|
115
|
+
validatedSlides.push({
|
|
116
|
+
type: s.type,
|
|
117
|
+
payload: s.payload,
|
|
118
|
+
...(s.title !== undefined ? { title: s.title } : {}),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
startSlideshow(validatedSlides, body.delay_ms, workspace);
|
|
122
|
+
return c.json({ ok: true });
|
|
123
|
+
});
|
|
124
|
+
app.post("/slideshow/stop", (c) => {
|
|
125
|
+
cancelSlideshow();
|
|
126
|
+
return c.json({ ok: true });
|
|
127
|
+
});
|
|
128
|
+
// ── Incremental step-frames builder (v0.8) ───────────────────────────────────
|
|
129
|
+
app.post("/step-frames/init", async (c) => {
|
|
130
|
+
const body = await c.req.json();
|
|
131
|
+
if (typeof body.frame_type !== "string" || !FRAME_TYPES.includes(body.frame_type)) {
|
|
132
|
+
return c.json({
|
|
133
|
+
ok: false,
|
|
134
|
+
error: `frame_type must be one of: ${FRAME_TYPES.join(", ")}`,
|
|
135
|
+
}, 400);
|
|
136
|
+
}
|
|
137
|
+
const workspaceResult = validateWorkspaceInput(body.workspace);
|
|
138
|
+
if (!workspaceResult.ok) {
|
|
139
|
+
return c.json({ ok: false, error: workspaceResult.error }, 400);
|
|
140
|
+
}
|
|
141
|
+
const { workspace } = workspaceResult;
|
|
142
|
+
const title = typeof body.title === "string" ? body.title : undefined;
|
|
143
|
+
const { id } = initStepFramesResult(body.frame_type, workspace, title);
|
|
144
|
+
return c.json({ ok: true, id });
|
|
145
|
+
});
|
|
146
|
+
app.post("/step-frames/:id/frame", async (c) => {
|
|
147
|
+
const id = c.req.param("id");
|
|
148
|
+
const body = await c.req.json();
|
|
149
|
+
if (typeof body.payload !== "string") {
|
|
150
|
+
return c.json({ ok: false, error: "payload must be a string" }, 400);
|
|
151
|
+
}
|
|
152
|
+
const label = typeof body.label === "string" ? body.label : undefined;
|
|
153
|
+
const type = typeof body.type === "string" ? body.type : undefined;
|
|
154
|
+
const result = await appendFrameAndBroadcast(id, body.payload, label, type);
|
|
155
|
+
if (!result.ok) {
|
|
156
|
+
return c.json(result, result.error.includes("not found or expired") ? 404 : 400);
|
|
157
|
+
}
|
|
158
|
+
return c.json({ ok: true, frame_count: result.frame_count });
|
|
159
|
+
});
|
|
160
|
+
app.post("/step-frames/:id/commit", async (c) => {
|
|
161
|
+
const id = c.req.param("id");
|
|
162
|
+
let nodeToFrame;
|
|
163
|
+
try {
|
|
164
|
+
const body = await c.req.json();
|
|
165
|
+
if (body.node_to_frame !== undefined) {
|
|
166
|
+
const parsed = nodeToFrameSchema.safeParse(body.node_to_frame);
|
|
167
|
+
if (!parsed.success) {
|
|
168
|
+
return c.json({ ok: false, error: "node_to_frame must be a map of node ID → frame index" }, 400);
|
|
169
|
+
}
|
|
170
|
+
nodeToFrame = parsed.data;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// No body or non-JSON body — commit with no node_to_frame.
|
|
175
|
+
}
|
|
176
|
+
const result = commitStepFramesResult(id, nodeToFrame);
|
|
177
|
+
if (!result.ok) {
|
|
178
|
+
return c.json(result, result.error.includes("not found or expired") ? 404 : 400);
|
|
179
|
+
}
|
|
180
|
+
return c.json(result);
|
|
181
|
+
});
|
|
182
|
+
// ── User events — bidirectionality (Sprint 10 experiment) ───────────────────
|
|
183
|
+
app.post("/user-done", async (c) => {
|
|
184
|
+
signalDone(); // wake any pending wait_done() MCP tool calls
|
|
185
|
+
// Also forward to channel relay if Claude Code was started with the channels flag.
|
|
186
|
+
const channelPort = Number(process.env.CHANNEL_PORT ?? 3001);
|
|
187
|
+
try {
|
|
188
|
+
await fetch(`http://127.0.0.1:${channelPort}/user-done`, { method: "POST" });
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
// Channel server not running — ignore.
|
|
192
|
+
}
|
|
193
|
+
return c.json({ ok: true });
|
|
194
|
+
});
|
|
195
|
+
app.post("/wait-done", async (c) => {
|
|
196
|
+
await waitForDone();
|
|
197
|
+
return c.json({ ok: true });
|
|
198
|
+
});
|
|
199
|
+
// ── Node / edge click events (Phase 2 — Sprint 12) ───────────────────────────
|
|
200
|
+
app.post("/node-click", async (c) => {
|
|
201
|
+
const body = await c.req.json();
|
|
202
|
+
if (body.type !== "node" && body.type !== "edge") {
|
|
203
|
+
return c.json({ ok: false, error: 'type must be "node" or "edge"' }, 400);
|
|
204
|
+
}
|
|
205
|
+
const event = {
|
|
206
|
+
type: body.type,
|
|
207
|
+
id: body.id ?? "",
|
|
208
|
+
label: body.label ?? "",
|
|
209
|
+
action: body.action ?? null,
|
|
210
|
+
};
|
|
211
|
+
signalClick(event); // no-op if no wait_click() is pending
|
|
212
|
+
return c.json({ ok: true });
|
|
213
|
+
});
|
|
214
|
+
app.post("/wait-click", async (c) => {
|
|
215
|
+
let nodeActions = {};
|
|
216
|
+
try {
|
|
217
|
+
const body = await c.req.json();
|
|
218
|
+
if (body.node_actions !== undefined) {
|
|
219
|
+
const parsed = nodeActionsSchema.safeParse(body.node_actions);
|
|
220
|
+
if (!parsed.success) {
|
|
221
|
+
return c.json({ ok: false, error: "node_actions must be a map of node ID → string[]" }, 400);
|
|
222
|
+
}
|
|
223
|
+
nodeActions = parsed.data;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
// No body or non-JSON body — treat as plain click (node_actions stays {}).
|
|
228
|
+
}
|
|
229
|
+
broadcast({ action: "set_node_actions", node_actions: nodeActions, enabled: true });
|
|
230
|
+
const event = await waitForClick();
|
|
231
|
+
broadcast({ action: "set_node_actions", enabled: false });
|
|
232
|
+
return c.json({ ok: true, ...event });
|
|
233
|
+
});
|
|
234
|
+
app.get("/export", (c) => {
|
|
235
|
+
const id = c.req.query("id");
|
|
236
|
+
if (id !== undefined && id !== "") {
|
|
237
|
+
const root = getSnapshotsRoot();
|
|
238
|
+
const payload = findSnapshotById(id, root);
|
|
239
|
+
if (payload === null) {
|
|
240
|
+
return c.json({ ok: false, error: "graph not found" }, 404);
|
|
241
|
+
}
|
|
242
|
+
return c.json({ ok: true, data: payload });
|
|
243
|
+
}
|
|
244
|
+
return c.json({ ok: true, data: exportCanvas() });
|
|
245
|
+
});
|
|
246
|
+
// ── History navigator (v0.4 — Sprint 17) ──────────────────────────────────────
|
|
247
|
+
app.get("/snapshots", (c) => {
|
|
248
|
+
// Workspace is mandatory, no lastWorkspace fallback — matches MCP's
|
|
249
|
+
// list_snapshots exactly (F3/NF20; same validateWorkspaceInput() both
|
|
250
|
+
// transports already use for render()/slideshow()).
|
|
251
|
+
const workspaceResult = validateWorkspaceInput(c.req.query("workspace"));
|
|
252
|
+
if (!workspaceResult.ok) {
|
|
253
|
+
return c.json({ ok: false, error: workspaceResult.error }, 400);
|
|
254
|
+
}
|
|
255
|
+
const { workspace } = workspaceResult;
|
|
256
|
+
const root = getSnapshotsRoot();
|
|
257
|
+
const snapshots = listSnapshots(workspace, root);
|
|
258
|
+
return c.json({ ok: true, snapshots });
|
|
259
|
+
});
|
|
260
|
+
// ── Sprint 18 — GET /snapshots/all (v0.5) ────────────────────────────────────
|
|
261
|
+
app.get("/snapshots/all", (c) => {
|
|
262
|
+
const workspace = getLastWorkspace();
|
|
263
|
+
const root = getSnapshotsRoot();
|
|
264
|
+
const workspaces = listAllSnapshots(root, workspace);
|
|
265
|
+
return c.json({ ok: true, workspaces });
|
|
266
|
+
});
|
|
267
|
+
app.post("/snapshots/load", async (c) => {
|
|
268
|
+
const body = await c.req.json();
|
|
269
|
+
const filename = body.filename;
|
|
270
|
+
if (typeof filename !== "string") {
|
|
271
|
+
return c.json({ ok: false, error: "filename must be a string" }, 400);
|
|
272
|
+
}
|
|
273
|
+
// Path safety: filename must end with _screen.json and contain no / or ..
|
|
274
|
+
if (!isValidSnapshotFilename(filename)) {
|
|
275
|
+
return c.json({ ok: false, error: "invalid filename: path traversal not allowed" });
|
|
276
|
+
}
|
|
277
|
+
const currentWorkspace = getLastWorkspace();
|
|
278
|
+
const root = getSnapshotsRoot();
|
|
279
|
+
// Optional workspace override (v0.5 cross-workspace load).
|
|
280
|
+
let workspace;
|
|
281
|
+
if (body.workspace !== undefined) {
|
|
282
|
+
const validated = validateWorkspaceInput(body.workspace);
|
|
283
|
+
if (!validated.ok) {
|
|
284
|
+
return c.json({ ok: false, error: "invalid workspace: path traversal not allowed" }, 400);
|
|
285
|
+
}
|
|
286
|
+
workspace = validated.workspace;
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
workspace = currentWorkspace;
|
|
290
|
+
}
|
|
291
|
+
const raw = loadSnapshotContent(workspace, root, filename);
|
|
292
|
+
if (raw === null) {
|
|
293
|
+
return c.json({ ok: false, error: `snapshot not found: ${filename}` });
|
|
294
|
+
}
|
|
295
|
+
let snapshot;
|
|
296
|
+
try {
|
|
297
|
+
snapshot = JSON.parse(raw);
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
return c.json({ ok: false, error: "snapshot file is malformed JSON" });
|
|
301
|
+
}
|
|
302
|
+
if (!isFrameArray(snapshot.frames)) {
|
|
303
|
+
return c.json({ ok: false, error: "snapshot file is missing required fields" });
|
|
304
|
+
}
|
|
305
|
+
const frames = snapshot.frames;
|
|
306
|
+
const title = typeof snapshot.title === "string" ? snapshot.title : undefined;
|
|
307
|
+
const nodeToFrame = snapshot.nodeToFrame !== null && typeof snapshot.nodeToFrame === "object"
|
|
308
|
+
? snapshot.nodeToFrame
|
|
309
|
+
: undefined;
|
|
310
|
+
// Pre-v0.11 snapshots may lack an id (J1, `02`) — not addressable by the
|
|
311
|
+
// viewport cache; the browser falls back to treating it as unseen (auto-fit).
|
|
312
|
+
const snapshotId = typeof snapshot.id === "string" ? snapshot.id : undefined;
|
|
313
|
+
const rawPayload = typeof snapshot.rawPayload === "string" ? snapshot.rawPayload : undefined;
|
|
314
|
+
const result = await applyLoadedSnapshotResult(frames, workspace, title, nodeToFrame, snapshotId, rawPayload);
|
|
315
|
+
if (!result.ok) {
|
|
316
|
+
return c.json({ ok: false, error: result.error });
|
|
317
|
+
}
|
|
318
|
+
return c.json({ ok: true });
|
|
319
|
+
});
|
|
320
|
+
// ── Mermaid viewport persistence (v0.19, F19/C3) ────────────────────────────
|
|
321
|
+
app.post("/viewport", async (c) => {
|
|
322
|
+
const body = await c.req.json();
|
|
323
|
+
if (typeof body.id !== "string" || body.id.length === 0) {
|
|
324
|
+
return c.json({ ok: false, error: "id must be a non-empty string" }, 400);
|
|
325
|
+
}
|
|
326
|
+
// frame (v0.26.1, bug B19/FR21): the cache key is now id:frameIndex, so each
|
|
327
|
+
// frame of a sequence persists its own manual viewport independently.
|
|
328
|
+
if (typeof body.frame !== "number" || !Number.isInteger(body.frame) || body.frame < 0) {
|
|
329
|
+
return c.json({ ok: false, error: "frame must be a non-negative integer" }, 400);
|
|
330
|
+
}
|
|
331
|
+
const { scale, positionX, positionY } = body;
|
|
332
|
+
if (typeof scale !== "number" || !Number.isFinite(scale)) {
|
|
333
|
+
return c.json({ ok: false, error: "scale must be a finite number" }, 400);
|
|
334
|
+
}
|
|
335
|
+
if (typeof positionX !== "number" || !Number.isFinite(positionX)) {
|
|
336
|
+
return c.json({ ok: false, error: "positionX must be a finite number" }, 400);
|
|
337
|
+
}
|
|
338
|
+
if (typeof positionY !== "number" || !Number.isFinite(positionY)) {
|
|
339
|
+
return c.json({ ok: false, error: "positionY must be a finite number" }, 400);
|
|
340
|
+
}
|
|
341
|
+
const viewport = { scale, positionX, positionY };
|
|
342
|
+
setViewport(body.id, body.frame, viewport);
|
|
343
|
+
return c.json({ ok: true });
|
|
344
|
+
});
|
|
345
|
+
// ── Snapshot delete endpoints (v0.12) ─────────────────────────────────────
|
|
346
|
+
app.post("/snapshots/delete-files", async (c) => {
|
|
347
|
+
const body = await c.req.json();
|
|
348
|
+
const root = getSnapshotsRoot();
|
|
349
|
+
const validated = validateWorkspaceForDelete(body.workspace, root);
|
|
350
|
+
if (!validated.ok) {
|
|
351
|
+
return c.json({ ok: false, error: validated.error }, validated.status);
|
|
352
|
+
}
|
|
353
|
+
const { workspace } = validated;
|
|
354
|
+
if (!Array.isArray(body.filenames) || body.filenames.length === 0) {
|
|
355
|
+
return c.json({ ok: false, error: "filenames must be a non-empty array" }, 400);
|
|
356
|
+
}
|
|
357
|
+
const filenames = body.filenames;
|
|
358
|
+
for (const f of filenames) {
|
|
359
|
+
if (typeof f !== "string") {
|
|
360
|
+
return c.json({ ok: false, error: "each filename must be a string" }, 400);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const result = deleteSnapshotFiles(workspace, root, filenames);
|
|
364
|
+
if (!result.ok) {
|
|
365
|
+
return c.json({ ok: false, error: result.error }, 400);
|
|
366
|
+
}
|
|
367
|
+
return c.json({ ok: true, deleted: result.deleted });
|
|
368
|
+
});
|
|
369
|
+
app.post("/snapshots/delete-workspace", async (c) => {
|
|
370
|
+
const body = await c.req.json();
|
|
371
|
+
const root = getSnapshotsRoot();
|
|
372
|
+
const validated = validateWorkspaceForDelete(body.workspace, root);
|
|
373
|
+
if (!validated.ok) {
|
|
374
|
+
return c.json({ ok: false, error: validated.error }, validated.status);
|
|
375
|
+
}
|
|
376
|
+
const { workspace } = validated;
|
|
377
|
+
deleteWorkspace(workspace, root);
|
|
378
|
+
if (getLastWorkspace() === workspace) {
|
|
379
|
+
setLastWorkspace("");
|
|
380
|
+
}
|
|
381
|
+
return c.json({ ok: true });
|
|
382
|
+
});
|
|
383
|
+
// ── HTML Export (v0.13) ────────────────────────────────────────────────────
|
|
384
|
+
app.post("/export-html", async (c) => {
|
|
385
|
+
const body = await c.req.json();
|
|
386
|
+
if (!Array.isArray(body.items) || body.items.length === 0) {
|
|
387
|
+
return c.json({ ok: false, error: "items must be a non-empty array" }, 400);
|
|
388
|
+
}
|
|
389
|
+
// Default cdn (v0.32, F23): offline (fully embedded, today's pre-v0.32
|
|
390
|
+
// behavior) is opt-in via mode: "offline"; any other value falls back to cdn.
|
|
391
|
+
const mode = body.mode === "offline" ? "offline" : "cdn";
|
|
392
|
+
const root = getSnapshotsRoot();
|
|
393
|
+
const validItems = [];
|
|
394
|
+
for (const item of body.items) {
|
|
395
|
+
if (typeof item !== "object" || item === null)
|
|
396
|
+
continue;
|
|
397
|
+
const { workspace, id } = item;
|
|
398
|
+
if (typeof workspace !== "string")
|
|
399
|
+
continue;
|
|
400
|
+
const validated = validateWorkspaceInput(workspace);
|
|
401
|
+
if (!validated.ok)
|
|
402
|
+
continue;
|
|
403
|
+
if (typeof id !== "string")
|
|
404
|
+
continue;
|
|
405
|
+
const record = findSnapshotByIdInWorkspace(validated.workspace, id, root);
|
|
406
|
+
if (record === null)
|
|
407
|
+
continue;
|
|
408
|
+
validItems.push({ workspace: validated.workspace, id, record });
|
|
409
|
+
}
|
|
410
|
+
if (validItems.length === 0) {
|
|
411
|
+
return c.json({ ok: false, error: "no valid items to export" }, 400);
|
|
412
|
+
}
|
|
413
|
+
const { html, downloadFilename } = await generateExportHtml(validItems, mode);
|
|
414
|
+
return new Response(html, {
|
|
415
|
+
status: 200,
|
|
416
|
+
headers: {
|
|
417
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
418
|
+
"Content-Disposition": `attachment; filename="${downloadFilename}"`,
|
|
419
|
+
},
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
// ── Static client (v1.0 — NF33) ──────────────────────────────────────────
|
|
423
|
+
// Mounted last so it never shadows an API route above; only serves GET
|
|
424
|
+
// requests that didn't match anything else (assets, and "/" -> index.html).
|
|
425
|
+
if (options.staticRoot !== undefined && existsSync(options.staticRoot)) {
|
|
426
|
+
app.get("*", serveStatic({ root: options.staticRoot }));
|
|
427
|
+
}
|
|
428
|
+
return app;
|
|
429
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Stdio MCP channel server — bridges browser user events to Claude Code context.
|
|
2
|
+
//
|
|
3
|
+
// Claude Code spawns this as a subprocess. It also listens on CHANNEL_PORT (default 3001)
|
|
4
|
+
// so the main HTTP server can forward browser events to it via POST /user-done.
|
|
5
|
+
//
|
|
6
|
+
// Start via: claude --dangerously-load-development-channels server:agent-whiteboard-channel
|
|
7
|
+
// (requires the entry in .mcp.json below to be present)
|
|
8
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
9
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import { createServer } from 'node:http';
|
|
11
|
+
const CHANNEL_PORT = Number(process.env.CHANNEL_PORT ?? 3001);
|
|
12
|
+
const mcp = new Server({ name: 'agent-whiteboard-channel', version: '0.1.0' }, {
|
|
13
|
+
capabilities: {
|
|
14
|
+
// Declares this server as a Claude Code channel.
|
|
15
|
+
// assertNotificationCapability() has no case for this method and passes silently.
|
|
16
|
+
experimental: { 'claude/channel': {} },
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
await mcp.connect(new StdioServerTransport());
|
|
20
|
+
// Tiny HTTP relay: main server POSTs here when the browser fires a user event.
|
|
21
|
+
const relay = createServer((req, res) => {
|
|
22
|
+
if (req.method === 'POST' && req.url === '/user-done') {
|
|
23
|
+
// notifications/claude/channel is a Claude-proprietary extension not in the MCP schema.
|
|
24
|
+
;
|
|
25
|
+
mcp.notification({
|
|
26
|
+
method: 'notifications/claude/channel',
|
|
27
|
+
params: {
|
|
28
|
+
content: 'User has finished exploring the whiteboard and is ready for you to continue.',
|
|
29
|
+
meta: { event: 'user_done' },
|
|
30
|
+
},
|
|
31
|
+
}).catch(() => { });
|
|
32
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
33
|
+
res.end(JSON.stringify({ ok: true }));
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
res.writeHead(404);
|
|
37
|
+
res.end();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
relay.listen(CHANNEL_PORT, '127.0.0.1', () => {
|
|
41
|
+
process.stderr.write(`[agent-whiteboard-channel] relay on 127.0.0.1:${CHANNEL_PORT}\n`);
|
|
42
|
+
});
|