agent-whiteboard 1.0.4 → 1.2.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 +76 -0
- package/README.md +61 -5
- package/dist/client/assets/abnfDiagram-VCTEODGH-Dj8sd_5g.js +1 -0
- package/dist/client/assets/{arc-4691b008.js → arc-Aq92EeUK.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-B1QyVL0F.js +36 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-CmU2SSFe.js +129 -0
- package/dist/client/assets/{bootstrap.min-21652813.js → bootstrap.min-UtnB-TxT.js} +1 -2
- package/dist/client/assets/c4Diagram-7LVT6UL2-DWmhp77c.js +38 -0
- package/dist/client/assets/channel-C9ni1Via.js +1 -0
- package/dist/client/assets/{chunk-QZHKN3VN-0f554985.js → chunk-2Q5K7J3B-CQLo8Sbv.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-23d48b24.js → chunk-5VM5RSS4-fJTWmTTt.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-1104c326.js → chunk-F27PBJKO-DqzEf5gi.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-CMamLTz3.js +231 -0
- package/dist/client/assets/{chunk-4BX2VUAB-3c93511d.js → chunk-JWPE2WC7-tyYPyD43.js} +1 -1
- package/dist/client/assets/chunk-POPQ4Y6H-XdY4rO_V.js +1 -0
- package/dist/client/assets/chunk-SVP7TREG-COvqzqaO.js +88 -0
- package/dist/client/assets/chunk-TICWLB2K-BsJSFuPC.js +206 -0
- package/dist/client/assets/{chunk-55IACEB6-0243aefd.js → chunk-XXDRQBXY-HfVrYBJb.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-DktNEiGC.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-DktNEiGC.js +1 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-UXGdwSjS.js +1 -0
- package/dist/client/assets/cynefin-OW5HDTMX-PWlPYnpn.js +178 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CYE5H1wR.js +62 -0
- package/dist/client/assets/cytoscape.esm-C5JX3QLV.js +331 -0
- package/dist/client/assets/dagre-GXQ25YYZ-DaknBOKq.js +4 -0
- package/dist/client/assets/diagram-S7CK7UJ4-Dz7rZHxF.js +30 -0
- package/dist/client/assets/diagram-UQ7AKVKN-DzCi6tiS.js +41 -0
- package/dist/client/assets/{diagram-KO2AKTUF-81f42eeb.js → diagram-VSXAHHWV-TlUzhXgv.js} +3 -3
- package/dist/client/assets/diagram-VX7I27RA-Ds7jdzK7.js +24 -0
- package/dist/client/assets/diagram-Z3DM3KII-C--JD7cf.js +24 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-Dw5OM-dL.js +1 -0
- package/dist/client/assets/embed-DcNeILcO.js +211 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-DXalBHjA.js +99 -0
- package/dist/client/assets/flowDiagram-HODETNUW-DcI3UqyE.js +168 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-DyrkIo6c.js +292 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-fJKfGRcg.js +106 -0
- package/dist/client/assets/index-CIhkXT_z.js +19 -0
- package/dist/client/assets/index-DEiH2AE7.css +1 -0
- package/dist/client/assets/infoDiagram-27XIBGKW-Cijiqswb.js +2 -0
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-50b43d03.js → ishikawaDiagram-5VMMS53U-C1WqWU2l.js} +2 -2
- package/dist/client/assets/{journeyDiagram-JHISSGLW-dce41bdf.js → journeyDiagram-3NMN7TZE-7j05mbLR.js} +4 -4
- package/dist/client/assets/{kanban-definition-UN3LZRKU-92401a86.js → kanban-definition-UXKFOSKX-D5mGDfs_.js} +1 -1
- package/dist/client/assets/{katex-4c9ea27f.js → katex-BTI-AXqd.js} +3 -3
- package/dist/client/assets/{katex-0cba7652.js → katex-C5jXJg4s.js} +3 -3
- package/dist/client/assets/layout-KEin06-H.js +1 -0
- package/dist/client/assets/linear-D1EL1Azg.js +1 -0
- package/dist/client/assets/mermaid.core-CLvSDmrA.js +310 -0
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-66fe4544.js → mindmap-definition-YA3MSWOX-BsCqKd_d.js} +12 -12
- package/dist/client/assets/{ordinal-db88ae6f.js → ordinal-BeghXfj9.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-Du5lDU3W.js +1 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-hjv5Ao6B.js +39 -0
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-f64a7ed7.js → quadrantDiagram-AXDQQJYC-DNon8LWK.js} +4 -4
- package/dist/client/assets/railroadDiagram-O6MQD6OU-yNZz2zxo.js +1 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-CZiiEUHW.js +84 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-D-u8ZQUV.js +40 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BxjTf3q9.js +162 -0
- package/dist/client/assets/sizeCapture-INFHLROL-Dt4Wg8C5.js +1 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-B8BM6dln.js +1 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-MJauX84T.js +1 -0
- package/dist/client/assets/step-BEQzeS8V.js +1 -0
- package/dist/client/assets/swimlanes-42K2YHIH-eGWrsWAA.js +1 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-BYFu8tyL.js +8 -0
- package/dist/client/assets/{time-043b3464.js → time-BaoZouO5.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-b0c81243.js → timeline-definition-24CTP7MA-CAM-3Qj9.js} +10 -10
- package/dist/client/assets/vennDiagram-4TSXK5OY-CuFEwwD0.js +34 -0
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-8d4d7626.js → wardleyDiagram-VM6X3IG4-DI5yCVh0.js} +3 -3
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-BI0Bn-OQ.js +7 -0
- package/dist/client/index.html +2 -3
- package/dist/server/app.js +28 -396
- package/dist/server/channel.js +5 -3
- package/dist/server/export-html.js +30 -60
- package/dist/server/export-zip.js +83 -0
- package/dist/server/import-zip.js +196 -0
- package/dist/server/index.js +2 -1
- package/dist/server/mcp.js +28 -13
- package/dist/server/port.js +18 -0
- package/dist/server/quiz-submission.js +116 -0
- package/dist/server/quiz.js +110 -0
- package/dist/server/render-core.js +8 -7
- package/dist/server/routes/export.js +95 -0
- package/dist/server/routes/import.js +24 -0
- package/dist/server/routes/quiz.js +12 -0
- package/dist/server/routes/render.js +184 -0
- package/dist/server/routes/slideshow.js +52 -0
- package/dist/server/routes/snapshots.js +119 -0
- package/dist/server/snapshot-reader.js +58 -20
- package/dist/server/snapshot-writer.js +18 -16
- package/dist/server/validate.js +14 -0
- package/dist/server/viewport-cache.js +97 -25
- package/package.json +13 -7
- package/dist/client/assets/architecture-7EHR7CIX-1221aa61.js +0 -1
- package/dist/client/assets/architectureDiagram-3BPJPVTR-1eef4d3c.js +0 -36
- package/dist/client/assets/blockDiagram-GPEHLZMM-313fb06c.js +0 -132
- package/dist/client/assets/c4Diagram-AAUBKEIU-92b023ad.js +0 -10
- package/dist/client/assets/channel-5ee4a8a7.js +0 -1
- package/dist/client/assets/chunk-2J33WTMH-fb021fdc.js +0 -1
- package/dist/client/assets/chunk-727SXJPM-8e0e0228.js +0 -206
- package/dist/client/assets/chunk-AQP2D5EJ-2ccc13c2.js +0 -231
- package/dist/client/assets/classDiagram-4FO5ZUOK-42ffecb6.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-42ffecb6.js +0 -1
- package/dist/client/assets/cose-bilkent-S5V4N54A-45ec2e52.js +0 -1
- package/dist/client/assets/cytoscape.esm-1491e259.js +0 -331
- package/dist/client/assets/dagre-BM42HDAG-ed748885.js +0 -4
- package/dist/client/assets/diagram-2AECGRRQ-ccc0ddec.js +0 -43
- package/dist/client/assets/diagram-5GNKFQAL-342efdea.js +0 -10
- package/dist/client/assets/diagram-LMA3HP47-39e66739.js +0 -24
- package/dist/client/assets/diagram-OG6HWLK6-b3e61903.js +0 -24
- package/dist/client/assets/embed-2b4adf34.js +0 -201
- package/dist/client/assets/erDiagram-TEJ5UH35-281f5cc7.js +0 -85
- package/dist/client/assets/eventmodeling-FCH6USID-6508120e.js +0 -1
- package/dist/client/assets/flowDiagram-I6XJVG4X-c3a4269e.js +0 -162
- package/dist/client/assets/ganttDiagram-6RSMTGT7-013b07ec.js +0 -292
- package/dist/client/assets/gitGraph-WXDBUCRP-02827359.js +0 -1
- package/dist/client/assets/gitGraphDiagram-PVQCEYII-de184acb.js +0 -106
- package/dist/client/assets/graph-f3dfaf53.js +0 -1
- package/dist/client/assets/index-d674a6a4.css +0 -1
- package/dist/client/assets/index-ffc5af4b.js +0 -5
- package/dist/client/assets/info-J43DQDTF-81e32294.js +0 -1
- package/dist/client/assets/infoDiagram-5YYISTIA-196101ee.js +0 -2
- package/dist/client/assets/layout-3a4eff80.js +0 -1
- package/dist/client/assets/linear-fed00341.js +0 -1
- package/dist/client/assets/mermaid-parser.core-52f9cd92.js +0 -173
- package/dist/client/assets/mermaid.core-7dfc221a.js +0 -306
- package/dist/client/assets/packet-YPE3B663-278a272a.js +0 -1
- package/dist/client/assets/pie-LRSECV5Y-2e55d9ae.js +0 -1
- package/dist/client/assets/pieDiagram-4H26LBE5-65b1bcbd.js +0 -30
- package/dist/client/assets/radar-GUYGQ44K-608e459f.js +0 -1
- package/dist/client/assets/requirementDiagram-4Y6WPE33-90ebf889.js +0 -84
- package/dist/client/assets/sankeyDiagram-5OEKKPKP-2d1a83b3.js +0 -40
- package/dist/client/assets/sequenceDiagram-3UESZ5HK-aece3633.js +0 -162
- package/dist/client/assets/stateDiagram-AJRCARHV-9bd5b5f4.js +0 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-7c16c251.js +0 -1
- package/dist/client/assets/step-171ad54a.js +0 -1
- package/dist/client/assets/treeView-BLDUP644-e54b9ddb.js +0 -1
- package/dist/client/assets/treemap-LRROVOQU-6a81775b.js +0 -1
- package/dist/client/assets/vennDiagram-CIIHVFJN-8eaf0464.js +0 -34
- package/dist/client/assets/wardley-L42UT6IY-7abd921f.js +0 -1
- package/dist/client/assets/xychartDiagram-2RQKCTM6-450f333c.js +0 -7
- /package/dist/client/assets/{KaTeX_AMS-Regular-0cdd387c.woff2 → KaTeX_AMS-Regular-BQhdFMY1.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_AMS-Regular-30da91e8.woff → KaTeX_AMS-Regular-DMm9YOAa.woff} +0 -0
- /package/dist/client/assets/{KaTeX_AMS-Regular-68534840.ttf → KaTeX_AMS-Regular-DRggAlZN.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Caligraphic-Bold-07d8e303.ttf → KaTeX_Caligraphic-Bold-ATXxdsX0.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Caligraphic-Bold-1ae6bd74.woff → KaTeX_Caligraphic-Bold-BEiXGLvX.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Caligraphic-Bold-de7701e4.woff2 → KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Caligraphic-Regular-3398dd02.woff → KaTeX_Caligraphic-Regular-CTRA-rTL.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Caligraphic-Regular-5d53e70a.woff2 → KaTeX_Caligraphic-Regular-Di6jR-x-.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Caligraphic-Regular-ed0b7437.ttf → KaTeX_Caligraphic-Regular-wX97UBjC.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Fraktur-Bold-9163df9c.ttf → KaTeX_Fraktur-Bold-BdnERNNW.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Fraktur-Bold-9be7ceb8.woff → KaTeX_Fraktur-Bold-BsDP51OF.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Fraktur-Bold-74444efd.woff2 → KaTeX_Fraktur-Bold-CL6g_b3V.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Fraktur-Regular-1e6f9579.ttf → KaTeX_Fraktur-Regular-CB_wures.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Fraktur-Regular-51814d27.woff2 → KaTeX_Fraktur-Regular-CTYiF6lA.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Fraktur-Regular-5e28753b.woff → KaTeX_Fraktur-Regular-Dxdc4cR9.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Main-Bold-0f60d1b8.woff2 → KaTeX_Main-Bold-Cx986IdX.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Main-Bold-c76c5d69.woff → KaTeX_Main-Bold-Jm3AIy58.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Main-Bold-138ac28d.ttf → KaTeX_Main-Bold-waoOVXN0.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Main-BoldItalic-99cd42a3.woff2 → KaTeX_Main-BoldItalic-DxDJ3AOS.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Main-BoldItalic-70ee1f64.ttf → KaTeX_Main-BoldItalic-DzxPMmG6.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Main-BoldItalic-a6f7ec0d.woff → KaTeX_Main-BoldItalic-SpSLRI95.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Main-Italic-0d85ae7c.ttf → KaTeX_Main-Italic-3WenGoN9.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Main-Italic-f1d6ef86.woff → KaTeX_Main-Italic-BMLOBm91.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Main-Italic-97479ca6.woff2 → KaTeX_Main-Italic-NWA7e6Wa.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Main-Regular-c2342cd8.woff2 → KaTeX_Main-Regular-B22Nviop.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Main-Regular-c6368d87.woff → KaTeX_Main-Regular-Dr94JaBh.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Main-Regular-d0332f52.ttf → KaTeX_Main-Regular-ypZvNtVU.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Math-BoldItalic-f9377ab0.ttf → KaTeX_Math-BoldItalic-B3XSjfu4.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Math-BoldItalic-dc47344d.woff2 → KaTeX_Math-BoldItalic-CZnvNsCZ.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Math-BoldItalic-850c0af5.woff → KaTeX_Math-BoldItalic-iY-2wyZ7.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Math-Italic-8a8d2445.woff → KaTeX_Math-Italic-DA0__PXp.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Math-Italic-08ce98e5.ttf → KaTeX_Math-Italic-flOr_0UB.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Math-Italic-7af58c5e.woff2 → KaTeX_Math-Italic-t53AETM-.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_SansSerif-Bold-1ece03f7.ttf → KaTeX_SansSerif-Bold-CFMepnvq.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_SansSerif-Bold-e99ae511.woff2 → KaTeX_SansSerif-Bold-D1sUS0GD.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_SansSerif-Bold-ece03cfd.woff → KaTeX_SansSerif-Bold-DbIhKOiC.woff} +0 -0
- /package/dist/client/assets/{KaTeX_SansSerif-Italic-00b26ac8.woff2 → KaTeX_SansSerif-Italic-C3H0VqGB.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_SansSerif-Italic-91ee6750.woff → KaTeX_SansSerif-Italic-DN2j7dab.woff} +0 -0
- /package/dist/client/assets/{KaTeX_SansSerif-Italic-3931dd81.ttf → KaTeX_SansSerif-Italic-YYjJ1zSn.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_SansSerif-Regular-f36ea897.ttf → KaTeX_SansSerif-Regular-BNo7hRIc.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_SansSerif-Regular-11e4dc8a.woff → KaTeX_SansSerif-Regular-CS6fqUqJ.woff} +0 -0
- /package/dist/client/assets/{KaTeX_SansSerif-Regular-68e8c73e.woff2 → KaTeX_SansSerif-Regular-DDBCnlJ7.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Script-Regular-1c67f068.ttf → KaTeX_Script-Regular-C5JkGWo-.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Script-Regular-036d4e95.woff2 → KaTeX_Script-Regular-D3wIWfF6.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Script-Regular-d96cdf2b.woff → KaTeX_Script-Regular-D5yQViql.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Size1-Regular-c943cc98.woff → KaTeX_Size1-Regular-C195tn64.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Size1-Regular-95b6d2f1.ttf → KaTeX_Size1-Regular-Dbsnue_I.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Size1-Regular-6b47c401.woff2 → KaTeX_Size1-Regular-mCD8mA8B.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Size2-Regular-a6b2099f.ttf → KaTeX_Size2-Regular-B7gKUWhC.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Size2-Regular-d04c5421.woff2 → KaTeX_Size2-Regular-Dy4dx90m.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Size2-Regular-2014c523.woff → KaTeX_Size2-Regular-oD1tc_U0.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Size3-Regular-6ab6b62e.woff → KaTeX_Size3-Regular-CTq5MqoE.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Size3-Regular-500e04d5.ttf → KaTeX_Size3-Regular-DgpXs0kz.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Size4-Regular-99f9c675.woff → KaTeX_Size4-Regular-BF-4gkZK.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Size4-Regular-c647367d.ttf → KaTeX_Size4-Regular-DWFBv043.ttf} +0 -0
- /package/dist/client/assets/{KaTeX_Size4-Regular-a4af7d41.woff2 → KaTeX_Size4-Regular-Dl5lxZxV.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Typewriter-Regular-e14fed02.woff → KaTeX_Typewriter-Regular-C0xS9mPB.woff} +0 -0
- /package/dist/client/assets/{KaTeX_Typewriter-Regular-71d517d6.woff2 → KaTeX_Typewriter-Regular-CO6r4hn1.woff2} +0 -0
- /package/dist/client/assets/{KaTeX_Typewriter-Regular-f01f3e87.ttf → KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf} +0 -0
- /package/dist/client/assets/{colors-98f238a0.js → colors-Cc3OSVma.js} +0 -0
- /package/dist/client/assets/{defaultLocale-a8476f12.js → defaultLocale-CzDG__Ur.js} +0 -0
- /package/dist/client/assets/{index-a1656aec.js → index-Bi7T_FA4.js} +0 -0
- /package/dist/client/assets/{init-a5b10ee5.js → init-Dmth1JHB.js} +0 -0
- /package/dist/client/assets/{range-6bc2a9a0.js → range-OtVwhkKS.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,79 @@
|
|
|
1
|
+
## 1.2.0 — 2026-11-06
|
|
2
|
+
|
|
3
|
+
**Milestone v1.8 — Quiz Support (Sprints 89–91) complete.** Adds interactive quizzes for reviewing the user's knowledge (FR33 in `01`, F44–F50/NF57 in `03`).
|
|
4
|
+
|
|
5
|
+
- **`quiz` render type (F44, F45):** `render({ type: "quiz", ... })` takes a JSON payload of single- and multiple-choice questions, validated by the new `server/quiz.ts` (unique ids, option counts, weight sums). Mode is derived from the answer key: every question keyed → inline, none → deferred, a mix is rejected. Valid in `render()` only, not in slideshows or step-frames.
|
|
6
|
+
- **Scoring (F47):** per question `all_or_nothing` (exact correct set) or `partial` (multiple-choice only; weighted correct options summing to 1, wrong picks earn nothing), with optional `points`.
|
|
7
|
+
- **Quiz renderer + inline evaluation (F44, F46):** new `Quiz.svelte` with radio/checkbox controls and an in-quiz Submit, disabled until every question is answered. Inline quizzes show per-question results and the total score after Submit.
|
|
8
|
+
- **Deferred evaluation (F48, F49):** Submit posts to the new `POST /quiz/submit`; the new `wait_quiz` MCP tool / `POST /wait-quiz` long-poll returns the questions and answers (plus the client-computed score for inline quizzes). 10-minute timeout, submit-before-wait retention, and superseding like `wait_click`.
|
|
9
|
+
- **History & export (F50):** quizzes are snapshotted as questions only and reload fresh; HTML export replaces a quiz with an explanatory note and never leaks its questions or answer key.
|
|
10
|
+
- **Dev/tooling:** `/quiz` added to the Vite dev proxy; README documents the quiz format, `wait_quiz` and the new endpoints; showcase Section 15 (`-q`) demonstrates inline and deferred quizzes.
|
|
11
|
+
- Full suite: 745 unit tests passing; `tsc`/`svelte-check`/`eslint`/`npm run build` clean.
|
|
12
|
+
|
|
13
|
+
## 1.1.1 — 2026-11-06
|
|
14
|
+
|
|
15
|
+
**Milestone v1.7 — Unified Import/Export Button (Sprints 87–88) complete.** Pure UI consolidation; no server, API, MCP or snapshot-format changes.
|
|
16
|
+
|
|
17
|
+
- **Showcase page (F43):** `mockup/import-export-button-options.html` presented icon/tooltip/menu-label candidates; chosen: opposing-arrows icon, "Import / export" tooltip, "Export…" / "Import…" labels.
|
|
18
|
+
- **Single toolbar button (F41):** the separate Export and Import buttons are replaced by one `ImportExportMenu.svelte` button opening a popover with "Export…" (existing export modal) and "Import…" (existing `ImportModal`). Delete and drag-and-drop `.zip` import are unchanged.
|
|
19
|
+
- **ARIA menu behaviour (F42):** `aria-haspopup`/`aria-expanded`, `role="menuitem"`, arrow/Escape handling, focus return to the button, outside-click and Tab dismissal.
|
|
20
|
+
- **Tests (NF56):** new `ImportExportMenu` unit tests plus updated `App` tests.
|
|
21
|
+
|
|
22
|
+
## 1.1.0 — 2026-09-07
|
|
23
|
+
|
|
24
|
+
**Milestone v1.6 — Workspace Export/Import for Sharing (Sprints 85–86) complete.** Adds a full workspace sharing round-trip: export a workspace as a portable `.zip` and import it into another whiteboard instance (FR31 in `01`, F34–F40/NF55 in `03`). The first milestone to ship real new user-facing functionality since 1.0 — correctly a minor bump per the versioning rule in `05`.
|
|
25
|
+
|
|
26
|
+
- **Delete confirm wording (F34):** the whole-workspace delete confirm button now reads `Click again to delete "<workspace-name>"` instead of the generic "Click again to confirm".
|
|
27
|
+
- **Zip export (F35, F36):** the export modal gains an HTML/Zip format toggle (step 2), defaulting to HTML. Zip mode hits a new `POST /export-zip` (`server/export-zip.ts`, the `archiver` package) which copies the selected snapshots' raw JSON files byte-for-byte into a `.zip` plus a generated `manifest.json` (workspace, timestamp, app version, included filenames) — no rendering pass, no viewport-cache data.
|
|
28
|
+
- **Import entry point (F37):** a new `ImportModal.svelte` + toolbar button, plus a page-level drag-and-drop target (`App.svelte`'s `<main>`), reads a dropped/selected zip's `manifest.json` locally via `jszip` (promoted to a real dependency) to resolve the workspace name before any upload.
|
|
29
|
+
- **Collision prompt (F38):** if the resolved workspace name already exists, the client shows Merge / Import as new (editable, auto-incremented `"<name> (n)"`) / Cancel, checked against the already-fetched `GET /snapshots/all` list rather than a dedicated new endpoint.
|
|
30
|
+
- **Server import pipeline (F39, NF55):** a new `server/import-zip.ts` + `POST /import` (multipart, `extract-zip`) implements create/merge modes and the id/timestamp dedup rule (no match → add; same id+timestamp → skip; same id, newer timestamp → overwrite older, guaranteeing at most one file per id). Hardening: a 50MB route-specific body-size cap (`server/app.ts` excludes `/import` from the global 10MB JSON cap so the two limits don't stack), extraction to an isolated temp directory first (`extract-zip`'s own zip-slip protection, confirmed against a hand-crafted `../../` fixture), and a configurable per-entry/total uncompressed-size and entry-count guard as a zip-bomb defense.
|
|
31
|
+
- **Post-import focus + summary (F40):** on success, the client loads the newest added/updated snapshot via the existing `POST /snapshots/load` (switching the active workspace as a side effect) and shows `{added, updated, skipped}` counts instead of a bare success message.
|
|
32
|
+
- **Dev-proxy fix:** `client/vite.config.ts`'s proxy map was missing an `/import` entry, so `npm run dev` never reached the real endpoint — found via manual testing (both drag-drop and the file picker failed identically, pointing at the transport layer). Fixed with a regression test.
|
|
33
|
+
- Full suite: 662 unit tests passing (up from 586), 38 e2e tests passing, `tsc --noEmit`/`svelte-check`/`eslint`/`npm run build` clean.
|
|
34
|
+
|
|
35
|
+
## 1.0.8 — 2026-09-07
|
|
36
|
+
|
|
37
|
+
**Milestone v1.5 — Design Debt: Server Hygiene & Tooling (Sprint 84) complete.** Promotes the remaining LOW-severity findings from the Design Debt Log (`docs/06_nodejs_review.md`) into shipped work: an implicit-global dependency, an untyped MCP cast, unvalidated port env vars, a monolithic route file, and a stale build-tooling major (NF50–NF54 in `03`).
|
|
38
|
+
|
|
39
|
+
- **Explicit `crypto` import (NF50):** `server/snapshot-writer.ts` now imports `randomUUID` explicitly from `node:crypto` instead of relying on the bare global, matching `server/export-html.ts`'s existing pattern.
|
|
40
|
+
- **Typed channel notification (NF51):** `server/channel.ts` defines a minimal `NotifiableServer` interface for the one MCP `notification()` method it needs instead of casting to `any`; a rejected notification is now logged instead of silently swallowed.
|
|
41
|
+
- **Port validation (NF52):** a new `server/port.ts` `parsePort()` helper validates `PORT` (`server/index.ts`) and `CHANNEL_PORT` (`server/channel.ts`, `server/app.ts`), failing fast with a clear error instead of a low-level bind/fetch error. `0` (OS-assigned ephemeral port) is deliberately accepted, matching `Number.isInteger(port) && port >= 0 && port <= 65535` — the existing test suite already relies on `PORT=0` for ephemeral test binding.
|
|
42
|
+
- **`app.ts` route split (NF53):** route registrations move out of `createApp()` into `server/routes/{render,slideshow,snapshots,export}.ts`, each exporting a `(app: Hono) => void` registration function — no behavior change, verified against the full REST/MCP suite.
|
|
43
|
+
- **`vite`/`vitest` upgrade (NF54):** `vite` 4→5, `@sveltejs/vite-plugin-svelte` 2→3, `vitest` 0.34→3.2 — the latest versions still compatible with this project's Svelte 4 (the vite/vitest literal-latest majors require a Svelte 5 migration via `vite-plugin-svelte` 4+/5+, out of scope here). Found and fixed a real latent XSS risk uncovered along the way: `dompurify` 3.4.8+ silently fails to sanitize against a `happy-dom` `Window` (upstream incompatibility, capricorn86/happy-dom#1810), which would have broken sanitization in both the client test suite and the production `/export-html` endpoint once `dompurify`'s floating version moved past 3.4.7. Replaced `happy-dom` with `jsdom` everywhere (client test environment + `server/export-html.ts`'s DOMPurify window); `renderHtmlPayload()` also gained explicit `ADD_TAGS: ["style"]` + `FORCE_BODY: true`, since the B20 embedded-`<style>`-scoping feature turned out to depend on the same happy-dom leniency rather than an explicit DOMPurify allowlist.
|
|
44
|
+
- Full suite: 586 unit tests passing (up from 573), `tsc --noEmit`/`svelte-check`/`eslint`/`npm run build` clean.
|
|
45
|
+
|
|
46
|
+
## 1.0.7 — 2026-09-07
|
|
47
|
+
|
|
48
|
+
**Milestone v1.4 — Design Debt: Server Hardening & Performance (Sprint 83) complete.** Promotes 2 findings from the Design Debt Log (`docs/06_nodejs_review.md`) into shipped work: an unbounded request body and event-loop-blocking synchronous file I/O on the snapshot/viewport read and delete paths (NF48–NF49 in `03`).
|
|
49
|
+
|
|
50
|
+
- **Global request body size limit (NF48):** `createApp()` installs `hono/body-limit` (10MB cap) ahead of every route handler — an oversized request body now gets a fast 413 instead of running unbounded input through synchronous mermaid/katex/vega-lite rendering.
|
|
51
|
+
- **Async snapshot read/delete I/O (NF49):** `server/snapshot-reader.ts`'s `listSnapshots`/`listAllSnapshots`/`loadSnapshotContent`/`findSnapshotById`/`findSnapshotByIdInWorkspace`/`readSnapshotIdSafe` and `server/snapshot-writer.ts`'s `validateWorkspaceForDelete`/`deleteSnapshotFiles`/`deleteWorkspace` now use `fs/promises` instead of blocking the event loop for the duration of a directory scan or delete. `saveSnapshot()` is deliberately left synchronous — a single non-loop write, out of scope for this milestone's "loop-heavy read/delete paths" acceptance criteria.
|
|
52
|
+
- **Viewport-cache rewrite (NF49):** `server/viewport-cache.ts` now keeps an in-memory cache (loaded once per snapshots root) with disk writes coalesced on a short debounce instead of a full synchronous read-modify-write per zoom/pan event. `render-core.ts`'s `stepAndBroadcast`/`seekAndBroadcast` (the only callers of `getViewport`) become async accordingly.
|
|
53
|
+
- Full suite: 573 unit tests passing (up from 569), `tsc --noEmit`/`svelte-check`/`eslint` clean.
|
|
54
|
+
|
|
55
|
+
## 1.0.6 — 2026-09-07
|
|
56
|
+
|
|
57
|
+
**Milestone v1.3 — Design Debt: Data Integrity & Export Isolation (Sprint 82) complete.** Promotes 3 findings from the Design Debt Log (`docs/06_nodejs_review.md` + a post-v1.0 dev-mode config finding) into shipped work: a data-validation gap, a concurrency-safety gap, and a dev-mode config hardcoding issue (NF45–NF47 in `03`).
|
|
58
|
+
|
|
59
|
+
- **`nodeToFrame` validation (NF45):** `server/app.ts`'s snapshot-load route and `server/snapshot-reader.ts`'s `findSnapshotByIdInWorkspace` now validate `nodeToFrame` read back from disk with `nodeToFrameSchema.safeParse()` instead of an unchecked type assertion — a hand-corrupted value is dropped, matching existing malformed-`frames` handling, instead of propagating bad data to the browser.
|
|
60
|
+
- **Export pipeline isolation (NF46):** `server/export-html.ts` no longer patches Node's global `document`/`window` for happy-dom-backed rendering, nor serializes concurrent calls behind a promise queue (the `exportQueue` workaround for bug B14). Investigation found none of the renderers actually need the global: `DOMPurify` already receives its per-call `Window` instance explicitly, and KaTeX/Vega-Lite render without any DOM at all. Removing the unnecessary global mutation eliminates the race outright rather than just serializing around it.
|
|
61
|
+
- **Dev-mode port/proxy config (NF47):** `client/vite.config.ts` now reads `CLIENT_PORT` (default `5173`) and `PORT` (default `3000`) from the environment for the dev server's own port and every proxy target, instead of hardcoding both — overriding the server's `PORT` for `npm run dev` no longer silently breaks the client's API/WS proxying.
|
|
62
|
+
- Full suite: 569 unit tests passing (up from 565), `tsc --noEmit`/`svelte-check`/`eslint` clean.
|
|
63
|
+
|
|
64
|
+
## 1.0.5 — 2026-09-03
|
|
65
|
+
|
|
66
|
+
**Milestone v1.2 — Design Debt: Client Hardening (Sprint 81) complete.** Promotes 9 findings from the frontend design-debt log (`docs/06_frontend_review.md`) into shipped work: reconnect resilience, dark-mode theming gaps, a sanitization gap, type-safety cleanup, and test-coverage gaps (F32–F33, NF38–NF44 in `03`).
|
|
67
|
+
|
|
68
|
+
- **`client/src/ws.ts` (F32):** `connectWebSocket()` now retries the `/stream` connection with bounded exponential backoff (500ms–10s, 10 attempts) after a close, resetting on successful reopen. `App.svelte`'s disconnect banner distinguishes a retry-in-progress state from the exhausted-retry-budget fallback ("restart `npm run dev`").
|
|
69
|
+
- **Theme tokens (F33):** `Mermaid.svelte`, `Katex.svelte`, `VegaLite.svelte` (error/hint styling) and `NodeActionPopup.svelte` swap hardcoded hex colors for existing `--board-*` tokens, so these app-chrome UI states follow the dark-mode toggle. The F26 rendered-content isolation guard test is narrowed to allowlist exactly these known app-chrome selectors — the actual agent-rendered payload styling stays untouched.
|
|
70
|
+
- **Mermaid sanitization (NF38):** `Mermaid.svelte` now runs `mermaid.render()`'s SVG output through the same `DOMPurify.sanitize()` pass `Html.svelte` already applies. A regression surfaced during manual testing — mermaid's default `htmlLabels` mode wraps label text in `<foreignObject>`, which DOMPurify's `svg`/`svgFilters` profile strips wholesale (a deliberate DOMPurify safety exclusion, not a bug) — fixed by setting `htmlLabels: false` so labels render as plain, sanitize-safe SVG `<text>`/`<tspan>` instead.
|
|
71
|
+
- **Type safety (NF39, NF40):** `snapshotActions.ts`'s three `res.json()` calls are now typed against a shared `ApiResult` interface. `registry.ts`'s `RendererContext.presentation` is non-nullable (mirroring canvasStore's presentation/placeholder mutual-exclusivity invariant); the step-frames-placeholder entry gets its own `PlaceholderContext`, and `App.svelte`'s single call site performs the null checks explicitly — removing all five `presentation!`/`placeholder!` assertions.
|
|
72
|
+
- **Tests (NF41, NF42):** a `scopeCss` parity test guards the client/server hand-duplicated implementations against drift. `trapFocus.test.ts`, `download.test.ts`, and a client `scopeCss.test.ts` add coverage that was previously missing.
|
|
73
|
+
- **`Icon.svelte` extraction (NF43):** `App.svelte`'s inline `<svg>` icon blocks (theme toggle, history, delete, export, done-checkmark) are replaced with a shared `Icon.svelte` + `icons.ts` shape-data module, rendered as real SVG elements (not `{@html}`, which the lint config forbids).
|
|
74
|
+
- **Mermaid id fix (NF44):** `mermaid.render(id, src)`'s id is now derived from the existing `renderToken` counter instead of `Date.now()`.
|
|
75
|
+
- Full suite: 563 unit tests passing (up from 529), `tsc --noEmit`/`svelte-check`/`eslint` clean.
|
|
76
|
+
|
|
1
77
|
## 1.0.4 — 2026-08-04
|
|
2
78
|
|
|
3
79
|
Republish of 1.0.3 with a rebuilt dist/ — the previous publish shipped a stale pre-fix build that didn't include the CSP unsafe-eval fix.
|
package/README.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
A domain-agnostic interactive whiteboard for AI teacher agents. An AI agent renders diagrams, math, charts, and HTML to a local browser tab via MCP tools — and can pause to wait for the user to signal they're ready to continue.
|
|
4
4
|
|
|
5
|
+
Agent Whiteboard started as a tool for AI tutoring agents.
|
|
6
|
+
|
|
7
|
+
The original goal was simple: instead of explaining everything through long text responses, let an AI agent teach visually. Diagrams, equations, charts, and animations are often a much more effective way to explain an idea than paragraphs of text, especially when the explanation can unfold step by step and pause whenever the learner needs time to think.
|
|
8
|
+
|
|
9
|
+
As the project evolved, it became clear that the same interaction model is useful far beyond education. Whether an agent is explaining a software architecture, walking through a network protocol, debugging a problem, presenting data, or documenting a system, the ability to communicate visually is just as valuable. Agent Whiteboard is therefore intentionally domain-agnostic.
|
|
10
|
+
|
|
11
|
+
Through a set of MCP tools, an AI agent can render Mermaid diagrams, SVG, HTML, LaTeX, Vega-Lite charts, and interactive quizzes, build incremental multi-step explanations, pause until the user is ready to continue, react to clicks on diagrams, and keep a persistent history of snapshots that can later be reloaded or exported.
|
|
12
|
+
|
|
13
|
+
Unlike collaborative whiteboard applications, Agent Whiteboard is not a canvas that humans edit directly. The whiteboard is controlled by the AI agent, making it a lightweight visual communication surface between the agent and its user. Everything runs locally and integrates naturally with Claude Code through MCP.
|
|
14
|
+
|
|
5
15
|
<img src="docs/screenshots/step-frames.gif" width="700" alt="Step-through sequence advancing through a TCP three-way handshake">
|
|
6
16
|
|
|
7
17
|
The browser also has a History panel for browsing, reloading, deleting, and exporting past snapshots:
|
|
@@ -13,13 +23,13 @@ The browser also has a History panel for browsing, reloading, deleting, and expo
|
|
|
13
23
|
```
|
|
14
24
|
[Claude Code agent]
|
|
15
25
|
│
|
|
16
|
-
└──
|
|
26
|
+
└── 15 MCP tools — render, step-frames builder, slideshow, wait_click/wait_done/wait_quiz, snapshot export (see below)
|
|
17
27
|
│
|
|
18
28
|
▼
|
|
19
29
|
[Node.js server :3000] — MCP over SSE + REST fallback + WebSocket
|
|
20
30
|
│
|
|
21
31
|
▼
|
|
22
|
-
[Browser tab :5173] — Svelte SPA, auto-opens on start, with a History panel for browsing/deleting/exporting past snapshots
|
|
32
|
+
[Browser tab :5173] — Svelte SPA, auto-opens on start, with a History panel for browsing/deleting/exporting/importing past snapshots
|
|
23
33
|
```
|
|
24
34
|
|
|
25
35
|
The server exposes MCP tools over SSE at `http://localhost:3000/mcp`. Claude Code connects automatically when `.mcp.json` is present in the project root.
|
|
@@ -29,7 +39,6 @@ Every `render()`/commit call is persisted as a snapshot on disk (`~/.agent-white
|
|
|
29
39
|
## Requirements
|
|
30
40
|
|
|
31
41
|
- Node.js ≥ 18
|
|
32
|
-
- Claude Code (CLI or IDE extension)
|
|
33
42
|
|
|
34
43
|
## Quickstart
|
|
35
44
|
|
|
@@ -91,6 +100,7 @@ Push content to the whiteboard canvas. Replaces whatever is currently on screen.
|
|
|
91
100
|
| `"html"` | HTML/CSS fragment. Sanitized via DOMPurify — inline styles only; `<script>` and `<style>` tags are stripped. |
|
|
92
101
|
| `"katex"` | LaTeX string, rendered in display mode. |
|
|
93
102
|
| `"vega-lite"` | Vega-Lite JSON spec (must be valid JSON string). |
|
|
103
|
+
| `"quiz"` | JSON quiz — see [Quizzes](#quizzes). Valid in `render()` only (not in `slideshow()` or step-frames). |
|
|
94
104
|
|
|
95
105
|
`render()` is single-frame only. For a step-through sequence (multiple frames navigable via `step()`/`seek()`), use [`init_step_frames`/`append_frame`/`commit_step_frames`](#init_step_framesframe_type-workspace-title) instead — see [Step-frames sequences](#step-frames-sequences).
|
|
96
106
|
|
|
@@ -152,6 +162,32 @@ Block until the user clicks the **Done** button in the browser tab, then return.
|
|
|
152
162
|
|
|
153
163
|
<img src="docs/screenshots/done-button.png" width="700" alt="Done button visible in the controls panel while wait_done() is armed">
|
|
154
164
|
|
|
165
|
+
### `wait_quiz()`
|
|
166
|
+
|
|
167
|
+
Block until the user clicks **Submit** on the quiz currently on screen (rendered with `render({ type: "quiz", ... })`), then return the questions and the user's answers. A submission made before you call `wait_quiz()` is kept and returned immediately, as long as that quiz is still on screen. Times out after 10 minutes; a second `wait_quiz()` supersedes a pending one.
|
|
168
|
+
|
|
169
|
+
**Returns:** `{ "ok": true, "type": "submitted", "questions": {...}, "answers": [{ "questionId": "q1", "selected": ["a"] }] }` — plus `"score": { "earned", "possible" }` for an inline quiz. Also `{ "ok": true, "type": "timeout" | "superseded" }`, or `{ "ok": false, "error": "..." }` if no quiz is on the canvas.
|
|
170
|
+
|
|
171
|
+
### Quizzes
|
|
172
|
+
|
|
173
|
+
A quiz is a JSON payload of single- or multiple-choice questions:
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{ "title": "Check-in",
|
|
177
|
+
"questions": [
|
|
178
|
+
{ "id": "q1", "text": "Which verb is idempotent?", "kind": "single",
|
|
179
|
+
"options": [{ "id": "a", "text": "PUT", "correct": true }, { "id": "b", "text": "POST" }] },
|
|
180
|
+
{ "id": "q2", "text": "Pick the primes", "kind": "multiple", "scoring": "partial", "points": 2,
|
|
181
|
+
"options": [{ "id": "a", "text": "2", "correct": true, "weight": 0.5 }, { "id": "b", "text": "3", "correct": true, "weight": 0.5 }, { "id": "c", "text": "4" }] }
|
|
182
|
+
] }
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
- **Inline evaluation** — mark `correct: true` on the right option(s) of *every* question. The browser scores the quiz when the user clicks Submit and shows per-question results plus the total (the answer key is visible to anyone inspecting the page, so use deferred mode for anything you don't want revealed).
|
|
186
|
+
- **Deferred evaluation** — mark none. Submit sends the answers to the agent, which receives them via `wait_quiz()` and evaluates them itself.
|
|
187
|
+
- A payload where only some questions have a key is rejected.
|
|
188
|
+
- **Scoring** (per question): `"all_or_nothing"` (default) earns `points` (default 1) only for the exact correct set. `"partial"` (multiple-choice only) gives every correct option a `weight`; the weights must sum to 1, the score is the sum of the weights of the correct options selected, and wrong picks earn nothing.
|
|
189
|
+
- Quizzes are saved to history (questions only — answers and scores are never persisted) and reload as a fresh quiz. They are not included in HTML exports.
|
|
190
|
+
|
|
155
191
|
### `wait_click([node_actions])`
|
|
156
192
|
|
|
157
193
|
Arm the browser for a single node or edge click on the current Mermaid diagram. Nodes are highlighted with a blue outline and pointer cursor. Applies reliably to `graph`/`flowchart` diagrams; other types are best-effort.
|
|
@@ -238,6 +274,14 @@ init_step_frames(frame_type="mermaid", workspace="my-lesson", title="TCP handsha
|
|
|
238
274
|
|
|
239
275
|
`wait_done()` isn't coupled to step-frames — it just blocks on the Done button — so this pattern works with any number of frames and needs no dedicated tool support.
|
|
240
276
|
|
|
277
|
+
Knowledge check — render a quiz and wait for the user to submit it (see [Quizzes](#quizzes)):
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
render(type="quiz", payload='{"questions":[{"id":"q1","text":"What does SYN start?","kind":"single","options":[{"id":"a","text":"A handshake"},{"id":"b","text":"A teardown"}]}]}', options={ workspace: "my-lesson" })
|
|
281
|
+
→ wait_quiz() # no answer key in the payload → deferred: the answers come back to the agent
|
|
282
|
+
→ evaluate result.answers, explain the right answers, and score them
|
|
283
|
+
```
|
|
284
|
+
|
|
241
285
|
## REST fallback
|
|
242
286
|
|
|
243
287
|
All tools have HTTP equivalents for scripting or testing without an MCP client:
|
|
@@ -253,6 +297,8 @@ All tools have HTTP equivalents for scripting or testing without an MCP client:
|
|
|
253
297
|
| `POST /seek` | `{ "frame": N }` |
|
|
254
298
|
| `POST /user-done` | — (simulates the Done button click) |
|
|
255
299
|
| `POST /wait-done` | — (long-polls until Done is signalled) |
|
|
300
|
+
| `POST /quiz/submit` | `{ "answers": [{ "questionId": "...", "selected": ["optionId", ...] }], "score": { "earned": N, "possible": N } (optional, inline quizzes only) }` (sent by browser) — 400 if no quiz is on screen or the answers don't match it |
|
|
301
|
+
| `POST /wait-quiz` | — (long-polls until the quiz is submitted; REST twin of `wait_quiz`) |
|
|
256
302
|
| `POST /node-click` | `{ "type": "node"\|"edge", "id": "...", "label": "..." }` (sent by browser) |
|
|
257
303
|
| `POST /wait-click` | `{ "node_actions": { "<id>": ["action1", ...] } }` (optional; long-polls until a node/edge click is signalled) |
|
|
258
304
|
| `POST /step-frames/init` | `{ "frame_type": "...", "workspace": "...", "title": "..." }` — starts an incremental builder, returns `{ "ok": true, "id": "<uuid>" }` |
|
|
@@ -264,12 +310,16 @@ All tools have HTTP equivalents for scripting or testing without an MCP client:
|
|
|
264
310
|
| `POST /snapshots/delete-files` | `{ "workspace": "...", "filenames": ["..._screen.json", ...] }` — deletes specific snapshot files |
|
|
265
311
|
| `POST /snapshots/delete-workspace` | `{ "workspace": "..." }` — deletes an entire workspace's snapshot folder |
|
|
266
312
|
| `POST /export-html` | `{ "items": [{ "workspace": "...", "id": "..." }, ...], "mode": "cdn" \| "offline" }` (`mode` optional, defaults to `"cdn"`) — returns the assembled HTML file as a download, not JSON |
|
|
313
|
+
| `POST /export-zip` | `{ "items": [{ "workspace": "...", "id": "..." }, ...] }` — returns a portable `.zip` (the selected snapshots' raw JSON plus a `manifest.json`) as a download, for sharing a workspace |
|
|
314
|
+
| `POST /import` | multipart form: `file` (a `.zip` from `/export-zip`), `mode` (`"create"` \| `"merge"`, default `"create"`), `targetWorkspace` — imports snapshots, deduplicating by snapshot `id`/`timestamp`; 50MB cap |
|
|
267
315
|
|
|
268
316
|
All endpoints return the same JSON shapes as the MCP tools, except `POST /export-html`, which streams the HTML file directly (`Content-Disposition: attachment`). `mode: "cdn"` (default) links Mermaid/Bootstrap/KaTeX from a CDN; `mode: "offline"` embeds them for offline reading, at the cost of a much larger file (the Mermaid bundle alone is ~3.3MB).
|
|
269
317
|
|
|
270
318
|
## History & snapshots
|
|
271
319
|
|
|
272
|
-
Every `render()` / `commit_step_frames()` call writes a JSON snapshot to `~/.agent-whiteboard/<workspace>/` (override with the `WHITEBOARD_SNAPSHOTS_DIR` environment variable). The browser's right-side controls panel has a history toggle that opens a panel for browsing and loading past snapshots grouped by workspace, plus delete
|
|
320
|
+
Every `render()` / `commit_step_frames()` call writes a JSON snapshot to `~/.agent-whiteboard/<workspace>/` (override with the `WHITEBOARD_SNAPSHOTS_DIR` environment variable). The browser's right-side controls panel has a history toggle that opens a panel for browsing and loading past snapshots grouped by workspace, plus a delete button that opens a 2-step modal (`client/src/DeleteExportModal.svelte`) for deleting an entire workspace or a chosen subset of snapshots. Destructive actions require a second confirming click.
|
|
321
|
+
|
|
322
|
+
A single **Import / export** button (opposing-arrows icon) opens a small keyboard-accessible menu with two entries: **Export…** opens the same modal for exporting a workspace or subset of snapshots as self-contained HTML (the `export_html()` tool / `POST /export-html` endpoint described above) or as a portable `.zip` (`POST /export-zip`), and **Import…** opens a dialog for loading such a `.zip` into this or another whiteboard instance (`POST /import`, merging or creating a new workspace). Dropping a `.zip` anywhere on the page also starts an import.
|
|
273
323
|
|
|
274
324
|
| Step 1 — choose a workspace | Step 2 — delete requires a second confirming click |
|
|
275
325
|
|----------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
|
|
@@ -281,11 +331,12 @@ Every `render()` / `commit_step_frames()` call writes a JSON snapshot to `~/.age
|
|
|
281
331
|
|
|
282
332
|
## Manual showcase
|
|
283
333
|
|
|
284
|
-
Exercises every renderer and interactive feature (requires `npm run dev` running):
|
|
334
|
+
Exercises every renderer and interactive feature (requires `npm run dev` running). Each use case is preceded by a short intro card (title + description) — handy for recording a walkthrough video, one clip per use case:
|
|
285
335
|
|
|
286
336
|
```bash
|
|
287
337
|
node tests/human_driven/showcase.js # Sections 1–8: renderer slideshow + seek
|
|
288
338
|
node tests/human_driven/showcase.js -x # Section 12: export-by-id (v0.11)
|
|
339
|
+
node tests/human_driven/showcase.js -q # Section 15: quiz, inline + deferred (v1.8)
|
|
289
340
|
node tests/human_driven/showcase.js -a # All sections
|
|
290
341
|
```
|
|
291
342
|
|
|
@@ -297,9 +348,14 @@ Section flags (combinable):
|
|
|
297
348
|
-u, --popup Section 10: node_actions popup menu
|
|
298
349
|
-e, --edge Section 11: edge click demo
|
|
299
350
|
-x, --exportid Section 12: export by graph ID (v0.11)
|
|
351
|
+
-c, --incremental Section 13: incremental step-frames creation (init/append/commit)
|
|
352
|
+
-n, --nodetoframe Section 14: node_to_frame autonomous navigation
|
|
353
|
+
-q, --quiz Section 15: quiz — inline scoring + deferred wait_quiz (v1.8)
|
|
300
354
|
-a, --all All sections
|
|
301
355
|
-p, --port <port> Server port (default: 3000)
|
|
302
356
|
-d, --delay <ms> Delay between slides (default: 5000)
|
|
357
|
+
--no-intro Skip the intro card shown before each use case (quick smoke run)
|
|
358
|
+
--intro-delay <ms> How long each intro card stays up (default: 4000)
|
|
303
359
|
```
|
|
304
360
|
|
|
305
361
|
## Tests
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{g as p,r as u,d as a}from"./chunk-SVP7TREG-COvqzqaO.js";import{p as f}from"./chunk-JWPE2WC7-tyYPyD43.js";import{_ as n,l as o}from"./mermaid.core-CLvSDmrA.js";import{M as c,b as d}from"./cynefin-OW5HDTMX-PWlPYnpn.js";import"./index-CIhkXT_z.js";import"./step-BEQzeS8V.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},F={parser:R,db:a,renderer:u,styles:p};export{F as diagram};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{w as ln,
|
|
1
|
+
import{w as ln,h as an,e as g,t as tn,a as G,s as j,m as _,b as un,p as rn,d as K,f as o,g as z,i as sn,j as on,k as fn}from"./step-BEQzeS8V.js";function cn(l){return l.innerRadius}function gn(l){return l.outerRadius}function yn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,E,q,v,A,B,a){var I=E-l,i=q-h,n=B-v,d=a-A,u=d*I-n*i;if(!(u*u<g))return u=(n*(h-A)-d*(l-v))/u,[l+u*I,h+u*i]}function V(l,h,E,q,v,A,B){var a=l-E,I=h-q,i=(B?A:-A)/K(a*a+I*I),n=i*I,d=-i*a,u=l+n,s=h+d,f=E+n,c=q+d,C=(u+f)/2,t=(s+c)/2,m=f-u,y=c-s,R=m*m+y*y,T=v-A,P=u*c-f*s,O=(y<0?-1:1)*K(fn(0,T*T*R-P*P)),S=(P*y-m*O)/R,k=(-P*m-y*O)/R,w=(P*y+m*O)/R,p=(-P*m+y*O)/R,x=S-C,e=k-t,r=w-C,F=p-t;return x*x+e*e>r*r+F*F&&(S=w,k=p),{cx:S,cy:k,x01:-n,y01:-d,x11:S*(v/T-1),y11:k*(v/T-1)}}function hn(){var l=cn,h=gn,E=z(0),q=null,v=yn,A=dn,B=mn,a=null,I=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-an,c=A.apply(this,arguments)-an,C=un(c-f),t=c>f;if(a||(a=n=I()),s<u&&(d=s,s=u,u=d),!(s>g))a.moveTo(0,0);else if(C>tn-g)a.moveTo(s*G(f),s*j(f)),a.arc(0,0,s,f,c,!t),u>g&&(a.moveTo(u*G(c),u*j(c)),a.arc(0,0,u,c,f,t));else{var m=f,y=c,R=f,T=c,P=C,O=C,S=B.apply(this,arguments)/2,k=S>g&&(q?+q.apply(this,arguments):K(u*u+s*s)),w=_(un(s-u)/2,+E.apply(this,arguments)),p=w,x=w,e,r;if(k>g){var F=sn(k/u*j(S)),L=sn(k/s*j(S));(P-=F*2)>g?(F*=t?1:-1,R+=F,T-=F):(P=0,R=T=(f+c)/2),(O-=L*2)>g?(L*=t?1:-1,m+=L,y-=L):(O=0,m=y=(f+c)/2)}var H=s*G(m),J=s*j(m),M=u*G(T),N=u*j(T);if(w>g){var Q=s*G(y),U=s*j(y),W=u*G(R),X=u*j(R),D;if(C<rn)if(D=pn(H,J,W,X,Q,U,M,N)){var Y=H-D[0],Z=J-D[1],$=Q-D[0],b=U-D[1],nn=1/j(on((Y*$+Z*b)/(K(Y*Y+Z*Z)*K($*$+b*b)))/2),en=K(D[0]*D[0]+D[1]*D[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}O>g?x>g?(e=V(W,X,H,J,s,x,t),r=V(Q,U,M,N,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(H,J),a.arc(0,0,s,m,y,!t)):a.moveTo(H,J),!(u>g)||!(P>g)?a.lineTo(M,N):p>g?(e=V(M,N,Q,U,u,-p,t),r=V(H,J,W,X,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-rn/2;return[G(d)*n,j(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:z(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:z(+n),i):h},i.cornerRadius=function(n){return arguments.length?(E=typeof n=="function"?n:z(+n),i):E},i.padRadius=function(n){return arguments.length?(q=n==null?null:typeof n=="function"?n:z(+n),i):q},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:z(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:z(+n),i):A},i.padAngle=function(n){return arguments.length?(B=typeof n=="function"?n:z(+n),i):B},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|