create-harness-vibe-coding 0.8.19 → 0.9.3
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 +49 -0
- package/README-CN.md +4 -2
- package/README.md +23 -4
- package/package.json +17 -2
- package/src/generator.js +19 -6
- package/src/index.js +531 -38
- package/src/prompts.js +4 -4
- package/src/ui/dist/assets/AgentsRoute-BGGs90wH.js +7 -0
- package/src/ui/dist/assets/Assistant-Bold-gm-uSS1B.woff2 +0 -0
- package/src/ui/dist/assets/Assistant-Medium-DrcxCXg3.woff2 +0 -0
- package/src/ui/dist/assets/Assistant-Regular-DVxZuzxb.woff2 +0 -0
- package/src/ui/dist/assets/Assistant-SemiBold-SCI4bEL9.woff2 +0 -0
- package/src/ui/dist/assets/Combination-DhBLEmof.js +42 -0
- package/src/ui/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/src/ui/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/src/ui/dist/assets/RolesRoute-DnodCSz0.js +2 -0
- package/src/ui/dist/assets/RuntimePicker-CucD8TGn.js +7 -0
- package/src/ui/dist/assets/SettingsRoute-DquMWjac.js +2 -0
- package/src/ui/dist/assets/TaskList-Bxc0Uktx.js +7 -0
- package/src/ui/dist/assets/TerminalDrawer-CDusYltc.js +36 -0
- package/src/ui/dist/assets/WorkflowRoute-BN8yHmUs.css +1 -0
- package/src/ui/dist/assets/WorkflowRoute-D-0wVDpY.js +334 -0
- package/src/ui/dist/assets/abnfDiagram-VRR7QNED-zkRKkaxO.js +2 -0
- package/src/ui/dist/assets/apl-B4CMkyY2.js +2 -0
- package/src/ui/dist/assets/ar-SA-G6X2FPQ2-BiHaArx5.js +11 -0
- package/src/ui/dist/assets/arc-CBnLtOFO.js +2 -0
- package/src/ui/dist/assets/architectureDiagram-ZJ3FMSHR-DWXRXsh1.js +37 -0
- package/src/ui/dist/assets/asciiarmor-Df11BRmG.js +2 -0
- package/src/ui/dist/assets/asn1-EdZsLKOL.js +2 -0
- package/src/ui/dist/assets/asterisk-B-8jnY81.js +2 -0
- package/src/ui/dist/assets/az-AZ-76LH7QW2-CiT05voO.js +2 -0
- package/src/ui/dist/assets/bg-BG-XCXSNQG7-L-UM9tNf.js +6 -0
- package/src/ui/dist/assets/blockDiagram-677ZJIJ3-BIyh3Bry.js +133 -0
- package/src/ui/dist/assets/bn-BD-2XOGV67Q-NdgbA0bC.js +6 -0
- package/src/ui/dist/assets/brainfuck-C4LP7Hcl.js +2 -0
- package/src/ui/dist/assets/c4Diagram-LMCZKHZV-CA54vWWM.js +11 -0
- package/src/ui/dist/assets/ca-ES-6MX7JW3Y-BKZZsQ5J.js +9 -0
- package/src/ui/dist/assets/channel-Da_-zcAu.js +2 -0
- package/src/ui/dist/assets/chunk-2Q5K7J3B-DE4cxr1v.js +2 -0
- package/src/ui/dist/assets/chunk-32BRIVSS-i7FwbEU9.js +2 -0
- package/src/ui/dist/assets/chunk-5VM5RSS4-BhIcOYSu.js +16 -0
- package/src/ui/dist/assets/chunk-EX3LRPZG-Ci9zZRIs.js +232 -0
- package/src/ui/dist/assets/chunk-JWPE2WC7-BFdkcz-G.js +2 -0
- package/src/ui/dist/assets/chunk-MOJQB5TN-Dzmapl_S.js +89 -0
- package/src/ui/dist/assets/chunk-RYQCIY6F-RTmp7FOy.js +2 -0
- package/src/ui/dist/assets/chunk-V7JOEXUC-DInU3v3D.js +207 -0
- package/src/ui/dist/assets/chunk-VR4S4FIN-BHoyGBQO.js +2 -0
- package/src/ui/dist/assets/chunk-XXDRQBXY-DfeLZNZx.js +2 -0
- package/src/ui/dist/assets/classDiagram-OUVF2IWQ-FKbUS0bu.js +2 -0
- package/src/ui/dist/assets/classDiagram-v2-EOCWNBFH-FKbUS0bu.js +2 -0
- package/src/ui/dist/assets/clike-B9uivgTg.js +2 -0
- package/src/ui/dist/assets/clojure-BMjYHr_A.js +2 -0
- package/src/ui/dist/assets/cmake-BQqOBYOt.js +2 -0
- package/src/ui/dist/assets/cobol-CWcv1MsR.js +2 -0
- package/src/ui/dist/assets/coffeescript-S37ZYGWr.js +2 -0
- package/src/ui/dist/assets/commonlisp-DBKNyK5s.js +2 -0
- package/src/ui/dist/assets/cose-bilkent-JH36ORCC-_5PUJ615.js +2 -0
- package/src/ui/dist/assets/crystal-SjHAIU92.js +2 -0
- package/src/ui/dist/assets/cs-CZ-2BRQDIVT-DnkfnhDU.js +12 -0
- package/src/ui/dist/assets/css-BnMrqG3P.js +2 -0
- package/src/ui/dist/assets/cynefin-VYW2F7L2-CmkcFv-b.js +179 -0
- package/src/ui/dist/assets/cynefinDiagram-TSTJHNR4-D72VZn8H.js +63 -0
- package/src/ui/dist/assets/cypher-C_CwsFkJ.js +2 -0
- package/src/ui/dist/assets/cytoscape.esm-DTSO7Bv0.js +332 -0
- package/src/ui/dist/assets/d-pRatUO7H.js +2 -0
- package/src/ui/dist/assets/da-DK-5WZEPLOC-C54tQUgd.js +6 -0
- package/src/ui/dist/assets/dagre-VKFMJZFB-0BsAkrmo.js +5 -0
- package/src/ui/dist/assets/de-DE-XR44H4JA-DGfZlR10.js +9 -0
- package/src/ui/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
- package/src/ui/dist/assets/diagram-FQU43EPY-C1ZJDOca.js +4 -0
- package/src/ui/dist/assets/diagram-G47NLZAW-D3pr3FSP.js +25 -0
- package/src/ui/dist/assets/diagram-NH7WQ7WH-Bwry0NV-.js +25 -0
- package/src/ui/dist/assets/diagram-OA4YK3LP-L6g09cmV.js +31 -0
- package/src/ui/dist/assets/diagram-WEI45ONY-Bh1puYVX.js +42 -0
- package/src/ui/dist/assets/diff-DbItnlRl.js +2 -0
- package/src/ui/dist/assets/directory-open-01563666-DWU9wJ6I.js +2 -0
- package/src/ui/dist/assets/directory-open-4ed118d0-CunoC1EB.js +2 -0
- package/src/ui/dist/assets/dockerfile-BKs6k2Af.js +2 -0
- package/src/ui/dist/assets/dtd-DF_7sFjM.js +2 -0
- package/src/ui/dist/assets/dylan-DwRh75JA.js +2 -0
- package/src/ui/dist/assets/ebnf-CDyGwa7X.js +2 -0
- package/src/ui/dist/assets/ebnfDiagram-CCIWWBDH-BC_mBRR5.js +2 -0
- package/src/ui/dist/assets/ecl-Cabwm37j.js +2 -0
- package/src/ui/dist/assets/eiffel-CnydiIhH.js +2 -0
- package/src/ui/dist/assets/el-GR-BZB4AONW-DsnwWweL.js +11 -0
- package/src/ui/dist/assets/elm-vLlmbW-K.js +2 -0
- package/src/ui/dist/assets/erDiagram-Q63AITRT-Bkxam36l.js +86 -0
- package/src/ui/dist/assets/erlang-BNw1qcRV.js +2 -0
- package/src/ui/dist/assets/es-ES-U4NZUMDT-CRxBcQgC.js +10 -0
- package/src/ui/dist/assets/eu-ES-A7QVB2H4-CTd_E7YR.js +12 -0
- package/src/ui/dist/assets/excalidraw-default-bMWIgNCy.svg +1 -0
- package/src/ui/dist/assets/fa-IR-HGAKTJCU-DO56k7eG.js +9 -0
- package/src/ui/dist/assets/factor-kuTfRLto.js +2 -0
- package/src/ui/dist/assets/fcl-Kvtd6kyn.js +2 -0
- package/src/ui/dist/assets/fi-FI-Z5N7JZ37-DEDCF9WB.js +7 -0
- package/src/ui/dist/assets/file-open-002ab408-DIuFHtCF.js +2 -0
- package/src/ui/dist/assets/file-open-7c801643-684qeFg4.js +2 -0
- package/src/ui/dist/assets/file-save-3189631c-C1wFhQhH.js +2 -0
- package/src/ui/dist/assets/file-save-745eba88-Bb9F9Kg7.js +2 -0
- package/src/ui/dist/assets/flowDiagram-23GEKE2U-C3SAEao7.js +157 -0
- package/src/ui/dist/assets/forth-Ffai-XNe.js +2 -0
- package/src/ui/dist/assets/fortran-DYz_wnZ1.js +2 -0
- package/src/ui/dist/assets/fr-FR-RHASNOE6-DE2nPyDt.js +10 -0
- package/src/ui/dist/assets/ganttDiagram-NO4QXBWP-U-J_Xi-_.js +293 -0
- package/src/ui/dist/assets/gas-Bneqetm1.js +2 -0
- package/src/ui/dist/assets/gherkin-heZmZLOM.js +2 -0
- package/src/ui/dist/assets/gitGraphDiagram-IHSO6WYX-BADVbD-a.js +107 -0
- package/src/ui/dist/assets/gl-ES-HMX3MZ6V-mCqVgQHC.js +11 -0
- package/src/ui/dist/assets/graph-C9eacEi8.js +2 -0
- package/src/ui/dist/assets/groovy-D9Dt4D0W.js +2 -0
- package/src/ui/dist/assets/haskell-Cw1EW3IL.js +2 -0
- package/src/ui/dist/assets/haxe-H-WmDvRZ.js +2 -0
- package/src/ui/dist/assets/he-IL-6SHJWFNN-KnaLrCzw.js +11 -0
- package/src/ui/dist/assets/hi-IN-IWLTKZ5I-wuqiaaWt.js +5 -0
- package/src/ui/dist/assets/http-DBlCnlav.js +2 -0
- package/src/ui/dist/assets/hu-HU-A5ZG7DT2-DLg_zm22.js +8 -0
- package/src/ui/dist/assets/id-ID-SAP4L64H-MB2mXqcR.js +11 -0
- package/src/ui/dist/assets/idl-BEugSyMb.js +2 -0
- package/src/ui/dist/assets/image-blob-reduce.esm-HwCm7IZ0.js +8 -0
- package/src/ui/dist/assets/index-39SJtrt0.js +2 -0
- package/src/ui/dist/assets/index-BQ0LIrW-.js +2 -0
- package/src/ui/dist/assets/index-BcAUh2Nm.css +1 -0
- package/src/ui/dist/assets/index-BcThOK-i.js +331 -0
- package/src/ui/dist/assets/index-BnxV3k_S.js +3 -0
- package/src/ui/dist/assets/index-C7MZBHbT.js +117 -0
- package/src/ui/dist/assets/index-CCvKI2rM.js +2 -0
- package/src/ui/dist/assets/index-CZvmcCab.js +8 -0
- package/src/ui/dist/assets/index-Cp3hLKNl.js +2 -0
- package/src/ui/dist/assets/index-D7ayFxcx.js +2 -0
- package/src/ui/dist/assets/index-DAYxSQGU.js +2 -0
- package/src/ui/dist/assets/index-DD-HeMV0.css +1 -0
- package/src/ui/dist/assets/index-DPMxd3Qn.js +2 -0
- package/src/ui/dist/assets/index-DSe2jx3k.js +4 -0
- package/src/ui/dist/assets/index-DUirCkgW.js +2 -0
- package/src/ui/dist/assets/index-DaKPQ2hN.js +159 -0
- package/src/ui/dist/assets/index-DzpuR0Jg.js +2 -0
- package/src/ui/dist/assets/index-POndwsCI.js +2 -0
- package/src/ui/dist/assets/index-R8NM9I_9.js +2 -0
- package/src/ui/dist/assets/index-TCDuDhBj.js +2 -0
- package/src/ui/dist/assets/index-TckzASdM.js +2 -0
- package/src/ui/dist/assets/index-Yi5ytFFr.js +2 -0
- package/src/ui/dist/assets/infoDiagram-FWYZ7A6U-CiGBlFqH.js +3 -0
- package/src/ui/dist/assets/init-Gi6I4Gst.js +2 -0
- package/src/ui/dist/assets/ishikawaDiagram-FXEZZL3T-B1aheRbz.js +71 -0
- package/src/ui/dist/assets/it-IT-JPQ66NNP-DT3HSU7X.js +12 -0
- package/src/ui/dist/assets/ja-JP-DBVTYXUO-CsfJjjWt.js +9 -0
- package/src/ui/dist/assets/javascript-iXu5QeM3.js +2 -0
- package/src/ui/dist/assets/journeyDiagram-5HDEW3XC-DtnJPxub.js +140 -0
- package/src/ui/dist/assets/julia-DuME0IfC.js +2 -0
- package/src/ui/dist/assets/kaa-6HZHGXH3-DQz-1-Gn.js +2 -0
- package/src/ui/dist/assets/kab-KAB-ZGHBKWFO-CaH-LSoK.js +9 -0
- package/src/ui/dist/assets/kanban-definition-HUTT4EX6-CZatSm3e.js +90 -0
- package/src/ui/dist/assets/katex-C5jXJg4s.js +258 -0
- package/src/ui/dist/assets/kk-KZ-P5N5QNE5-BMXmcm55.js +2 -0
- package/src/ui/dist/assets/km-KH-HSX4SM5Z-ZeoJNBpN.js +12 -0
- package/src/ui/dist/assets/ko-KR-MTYHY66A-BeaLRKph.js +10 -0
- package/src/ui/dist/assets/ku-TR-6OUDTVRD-B_28KrkR.js +10 -0
- package/src/ui/dist/assets/layout-DEXfKzaS.js +2 -0
- package/src/ui/dist/assets/linear-CpEDnQRC.js +2 -0
- package/src/ui/dist/assets/livescript-BwQOo05w.js +2 -0
- package/src/ui/dist/assets/lt-LT-XHIRWOB4-BxDcnUhp.js +4 -0
- package/src/ui/dist/assets/lua-VAEuO923.js +2 -0
- package/src/ui/dist/assets/lv-LV-5QDEKY6T-DpKDl1_y.js +8 -0
- package/src/ui/dist/assets/map-Czzmt4hB.js +2 -0
- package/src/ui/dist/assets/mathematica-DTrFuWx2.js +2 -0
- package/src/ui/dist/assets/mbox-CNhZ1qSd.js +2 -0
- package/src/ui/dist/assets/mermaidLoader-CBO31t0J.js +305 -0
- package/src/ui/dist/assets/mermaidLoader-CEK31ho9.css +1 -0
- package/src/ui/dist/assets/mindmap-definition-LN4V7U3C-CQ0TccEr.js +97 -0
- package/src/ui/dist/assets/mirc-CjQqDB4T.js +2 -0
- package/src/ui/dist/assets/mllike-CXdrOF99.js +2 -0
- package/src/ui/dist/assets/modelica-Dc1JOy9r.js +2 -0
- package/src/ui/dist/assets/mr-IN-CRQNXWMA-BKCZT58p.js +14 -0
- package/src/ui/dist/assets/mscgen-BA5vi2Kp.js +2 -0
- package/src/ui/dist/assets/mumps-BT43cFF4.js +2 -0
- package/src/ui/dist/assets/my-MM-5M5IBNSE-D34k0G9T.js +2 -0
- package/src/ui/dist/assets/nb-NO-T6EIAALU-DXr_KW8J.js +11 -0
- package/src/ui/dist/assets/nginx-DdIZxoE0.js +2 -0
- package/src/ui/dist/assets/nl-NL-IS3SIHDZ-C1L0aVDR.js +9 -0
- package/src/ui/dist/assets/nn-NO-6E72VCQL-CLWVSxbZ.js +9 -0
- package/src/ui/dist/assets/nsis-LdVXkNf5.js +2 -0
- package/src/ui/dist/assets/ntriples-BfvgReVJ.js +2 -0
- package/src/ui/dist/assets/oc-FR-POXYY2M6-nf8_J3eR.js +9 -0
- package/src/ui/dist/assets/octave-Ck1zUtKM.js +2 -0
- package/src/ui/dist/assets/ordinal-Cboi1Yqb.js +2 -0
- package/src/ui/dist/assets/oz-BzwKVEFT.js +2 -0
- package/src/ui/dist/assets/pa-IN-N4M65BXN-CAMJ-xd6.js +5 -0
- package/src/ui/dist/assets/pascal--L3eBynH.js +2 -0
- package/src/ui/dist/assets/pegDiagram-2B236MQR-Bj2RcmRd.js +2 -0
- package/src/ui/dist/assets/percentages-BXMCSKIN-s9yWFdr6.js +160 -0
- package/src/ui/dist/assets/perl-CdXCOZ3F.js +2 -0
- package/src/ui/dist/assets/pica-BGv_AhAB.js +8 -0
- package/src/ui/dist/assets/pieDiagram-ENE6RG2P-obpzz8dC.js +40 -0
- package/src/ui/dist/assets/pig-CevX1Tat.js +2 -0
- package/src/ui/dist/assets/pl-PL-T2D74RX3-DKJ5H85v.js +10 -0
- package/src/ui/dist/assets/powershell-CFHJl5sT.js +2 -0
- package/src/ui/dist/assets/properties-C78fOPTZ.js +2 -0
- package/src/ui/dist/assets/protobuf-ChK-085T.js +2 -0
- package/src/ui/dist/assets/{proxy-D0vUbds5.js → proxy-BSmwef4H.js} +2 -2
- package/src/ui/dist/assets/pt-BR-5N22H2LF-B5kow-kN.js +10 -0
- package/src/ui/dist/assets/pt-PT-UZXXM6DQ-B2G34fso.js +10 -0
- package/src/ui/dist/assets/pug-DeIclll2.js +2 -0
- package/src/ui/dist/assets/puppet-DMA9R1ak.js +2 -0
- package/src/ui/dist/assets/python-BuPzkPfP.js +2 -0
- package/src/ui/dist/assets/q-pXgVlZs6.js +2 -0
- package/src/ui/dist/assets/quadrantDiagram-ABIIQ3AL-Cl-T7NkJ.js +8 -0
- package/src/ui/dist/assets/r-B6wPVr8A.js +2 -0
- package/src/ui/dist/assets/railroadDiagram-RFXS5EU6-DMq82RHg.js +2 -0
- package/src/ui/dist/assets/{refresh-cw-JNmExijd.js → refresh-cw-CbFd7guV.js} +2 -2
- package/src/ui/dist/assets/requirementDiagram-TGXJPOKE-tGInMDgU.js +85 -0
- package/src/ui/dist/assets/ro-RO-JPDTUUEW-BwLdZW91.js +12 -0
- package/src/ui/dist/assets/roundRect-0PYZxl1G.js +2 -0
- package/src/ui/dist/assets/rpm-CTu-6PCP.js +2 -0
- package/src/ui/dist/assets/ru-RU-B4JR7IUQ-DpdAgIJc.js +10 -0
- package/src/ui/dist/assets/ruby-B2Rjki9n.js +2 -0
- package/src/ui/dist/assets/runtimeBrand-BoKPimsz.js +2 -0
- package/src/ui/dist/assets/sankeyDiagram-HTMAVEWB-C7X-ET4m.js +41 -0
- package/src/ui/dist/assets/sas-B4kiWyti.js +2 -0
- package/src/ui/dist/assets/save-D1CXHwBw.js +12 -0
- package/src/ui/dist/assets/scheme-C41bIUwD.js +2 -0
- package/src/ui/dist/assets/sequenceDiagram-DBY2YBRQ-Bn24i_nY.js +163 -0
- package/src/ui/dist/assets/settings-2-DtSNAAcU.js +22 -0
- package/src/ui/dist/assets/shell-CjFT_Tl9.js +2 -0
- package/src/ui/dist/assets/shield-check-BVbJ8T_N.js +7 -0
- package/src/ui/dist/assets/si-LK-N5RQ5JYF-naJaUy5u.js +2 -0
- package/src/ui/dist/assets/sieve-C3Gn_uJK.js +2 -0
- package/src/ui/dist/assets/simple-mode-GW_nhZxv.js +2 -0
- package/src/ui/dist/assets/sizeCapture-X5ZJPWSS-DAQHfm82.js +2 -0
- package/src/ui/dist/assets/sk-SK-C5VTKIMK-1NPz1J_d.js +7 -0
- package/src/ui/dist/assets/sl-SI-NN7IZMDC-D8RfmNQw.js +7 -0
- package/src/ui/dist/assets/smalltalk-CnHTOXQT.js +2 -0
- package/src/ui/dist/assets/solr-DehyRSwq.js +2 -0
- package/src/ui/dist/assets/sparql-DkYu6x3z.js +2 -0
- package/src/ui/dist/assets/spreadsheet-BCZA_wO0.js +2 -0
- package/src/ui/dist/assets/sql-D0XecflT.js +2 -0
- package/src/ui/dist/assets/stateDiagram-2N3HPSRC-Ck48D1ck.js +2 -0
- package/src/ui/dist/assets/stateDiagram-v2-6OUMAXLB-CdfRCJAJ.js +2 -0
- package/src/ui/dist/assets/stex-C3f8Ysf7.js +2 -0
- package/src/ui/dist/assets/style-C3QI7VPf.css +1 -0
- package/src/ui/dist/assets/stylus-B533Al4x.js +2 -0
- package/src/ui/dist/assets/subset-shared.chunk-eAtLHmbi.js +85 -0
- package/src/ui/dist/assets/subset-worker.chunk-DeHCTnTk.js +2 -0
- package/src/ui/dist/assets/sv-SE-XGPEYMSR-BeOYCU-8.js +11 -0
- package/src/ui/dist/assets/swift-BzpIVaGY.js +2 -0
- package/src/ui/dist/assets/swimlanes-5IMT3BWC-DOy22UjP.js +3 -0
- package/src/ui/dist/assets/swimlanesDiagram-G3AALYLV-sicPbVOg.js +9 -0
- package/src/ui/dist/assets/ta-IN-2NMHFXQM-CtS68WDd.js +10 -0
- package/src/ui/dist/assets/tcl-DVfN8rqt.js +2 -0
- package/src/ui/dist/assets/{terminal-BBQvnOAK.js → terminal-DEby5Y_P.js} +2 -2
- package/src/ui/dist/assets/terminalControl-BkFeDVXo.js +134 -0
- package/src/ui/dist/assets/textile-CnDTJFAw.js +2 -0
- package/src/ui/dist/assets/th-TH-HPSO5L25-DCHY9-Co.js +3 -0
- package/src/ui/dist/assets/tiddlywiki-DO-Gjzrf.js +2 -0
- package/src/ui/dist/assets/tiki-DGYXhP31.js +2 -0
- package/src/ui/dist/assets/timeline-definition-FHXFAJF6-DVkG72qO.js +121 -0
- package/src/ui/dist/assets/toml-Bm5Em-hy.js +2 -0
- package/src/ui/dist/assets/tr-TR-DEFEU3FU-y5-vjTv8.js +8 -0
- package/src/ui/dist/assets/troff-wAsdV37c.js +2 -0
- package/src/ui/dist/assets/ttcn-CfJYG6tj.js +2 -0
- package/src/ui/dist/assets/ttcn-cfg-B9xdYoR4.js +2 -0
- package/src/ui/dist/assets/turtle-B1tBg_DP.js +2 -0
- package/src/ui/dist/assets/uk-UA-QMV73CPH-BRbR5It6.js +7 -0
- package/src/ui/dist/assets/useReducedMotion-D07XP52Q.js +12 -0
- package/src/ui/dist/assets/vb-CmGdzxic.js +2 -0
- package/src/ui/dist/assets/vbscript-BuJXcnF6.js +2 -0
- package/src/ui/dist/assets/velocity-D8B20fx6.js +2 -0
- package/src/ui/dist/assets/vennDiagram-L72KCM5P-DwMwBlf-.js +35 -0
- package/src/ui/dist/assets/verilog-C6RDOZhf.js +2 -0
- package/src/ui/dist/assets/vhdl-lSbBsy5d.js +2 -0
- package/src/ui/dist/assets/vi-VN-M7AON7JQ-s6wSBv2s.js +6 -0
- package/src/ui/dist/assets/wardleyDiagram-EHGQE667-CNuuvzEg.js +79 -0
- package/src/ui/dist/assets/webidl-ZXfAyPTL.js +2 -0
- package/src/ui/dist/assets/{x-DoIOGAJz.js → x-B84wq78k.js} +2 -2
- package/src/ui/dist/assets/xquery-DzFWVndE.js +2 -0
- package/src/ui/dist/assets/xychartDiagram-FW5EYKEG-dUpZxmn6.js +8 -0
- package/src/ui/dist/assets/yacas-BJ4BC0dw.js +2 -0
- package/src/ui/dist/assets/z80-Hz9HOZM7.js +2 -0
- package/src/ui/dist/assets/zh-CN-LNUGB5OW-Cu_-POQN.js +11 -0
- package/src/ui/dist/assets/zh-HK-E62DVLB3-BCx4W4qZ.js +2 -0
- package/src/ui/dist/assets/zh-TW-RAJ6MFWO-02wx_Mrj.js +10 -0
- package/src/ui/dist/icons/excalidraw.svg +1 -0
- package/src/ui/dist/icons/markdown.svg +1 -0
- package/src/ui/dist/index.html +2 -2
- package/src/ui/package.json +10 -0
- package/src/ui/playwright.config.ts +36 -0
- package/src/ui/pnpm-workspace.yaml +2 -0
- package/src/ui/public/icons/excalidraw.svg +1 -0
- package/src/ui/public/icons/markdown.svg +1 -0
- package/src/ui/tsconfig.json +2 -1
- package/src/ui/vite.config.ts +1 -0
- package/src/wf-ui-server/__tests__/a2a-store.test.mjs +432 -15
- package/src/wf-ui-server/__tests__/action-registry-loader.test.mjs +156 -0
- package/src/wf-ui-server/__tests__/claude-stream-json.test.mjs +397 -0
- package/src/wf-ui-server/__tests__/codex-appserver.test.mjs +392 -0
- package/src/wf-ui-server/__tests__/component-node-api.test.mjs +881 -0
- package/src/wf-ui-server/__tests__/component-node-store.test.mjs +401 -0
- package/src/wf-ui-server/__tests__/control-plane-acceptance.test.mjs +2027 -0
- package/src/wf-ui-server/__tests__/control-plane-cli-smoke.test.mjs +1014 -0
- package/src/wf-ui-server/__tests__/dispatch-lifecycle.test.mjs +330 -0
- package/src/wf-ui-server/__tests__/effort-compatibility-red-092.test.mjs +151 -0
- package/src/wf-ui-server/__tests__/graph-layout-agent-tree.test.mjs +254 -0
- package/src/wf-ui-server/__tests__/graph-layout-tidy.test.mjs +260 -0
- package/src/wf-ui-server/__tests__/graph-layout.test.mjs +84 -0
- package/src/wf-ui-server/__tests__/node-config-api.test.mjs +384 -0
- package/src/wf-ui-server/__tests__/opencode-server.test.mjs +526 -0
- package/src/wf-ui-server/__tests__/peer-capsule.test.mjs +2 -2
- package/src/wf-ui-server/__tests__/pty-adapter.test.mjs +71 -1
- package/src/wf-ui-server/__tests__/runtime-config.test.mjs +3 -3
- package/src/wf-ui-server/__tests__/runtime-detector.test.mjs +21 -0
- package/src/wf-ui-server/__tests__/runtime-review.test.mjs +828 -0
- package/src/wf-ui-server/__tests__/server.integration.test.mjs +1463 -11
- package/src/wf-ui-server/__tests__/session-cleanup.test.mjs +159 -0
- package/src/wf-ui-server/__tests__/session-registry.test.mjs +52 -3
- package/src/wf-ui-server/__tests__/settings.test.mjs +12 -2
- package/src/wf-ui-server/__tests__/spawn-gate.test.mjs +78 -0
- package/src/wf-ui-server/__tests__/task-group-parser.test.mjs +120 -0
- package/src/wf-ui-server/__tests__/task-group-tag.test.mjs +264 -0
- package/src/wf-ui-server/__tests__/task-parser.test.mjs +49 -3
- package/src/wf-ui-server/__tests__/terminal-context-hardening.test.mjs +122 -0
- package/src/wf-ui-server/__tests__/terminal-context.test.mjs +164 -0
- package/src/wf-ui-server/__tests__/terminal-store.test.mjs +4 -4
- package/src/wf-ui-server/__tests__/wf-browser-api.test.mjs +599 -0
- package/src/wf-ui-server/__tests__/wf-browser-store.test.mjs +184 -0
- package/src/wf-ui-server/__tests__/workflow-action-registry.test.mjs +282 -0
- package/src/wf-ui-server/__tests__/workflow-agent-communication.test.mjs +411 -0
- package/src/wf-ui-server/__tests__/workflow-agent-context.test.mjs +323 -0
- package/src/wf-ui-server/__tests__/workflow-agent-cooperation-permissions.test.mjs +318 -0
- package/src/wf-ui-server/__tests__/workflow-agent-messages-structured.test.mjs +458 -0
- package/src/wf-ui-server/__tests__/workflow-agent-routing.test.mjs +434 -0
- package/src/wf-ui-server/__tests__/workflow-agent-runtime.test.mjs +324 -0
- package/src/wf-ui-server/__tests__/workflow-api-integration.test.mjs +263 -0
- package/src/wf-ui-server/__tests__/workflow-api-phase3.test.mjs +1213 -0
- package/src/wf-ui-server/__tests__/workflow-claude-materialize.test.mjs +209 -0
- package/src/wf-ui-server/__tests__/workflow-cli-registry-manuals.test.mjs +173 -0
- package/src/wf-ui-server/__tests__/workflow-cli-surface.test.mjs +176 -0
- package/src/wf-ui-server/__tests__/workflow-codex-resume-id.test.mjs +556 -0
- package/src/wf-ui-server/__tests__/workflow-cooperation-enforcement.test.mjs +553 -0
- package/src/wf-ui-server/__tests__/workflow-depth-resume-dock.test.mjs +846 -0
- package/src/wf-ui-server/__tests__/workflow-display-node.test.mjs +150 -0
- package/src/wf-ui-server/__tests__/workflow-edge-update.test.mjs +296 -0
- package/src/wf-ui-server/__tests__/workflow-file-cache-lock-watch.test.mjs +380 -0
- package/src/wf-ui-server/__tests__/workflow-file-formats.test.mjs +994 -0
- package/src/wf-ui-server/__tests__/workflow-file-node-actions.test.mjs +294 -0
- package/src/wf-ui-server/__tests__/workflow-goal-actions.test.mjs +357 -0
- package/src/wf-ui-server/__tests__/workflow-goal-task-autoclose.test.mjs +256 -0
- package/src/wf-ui-server/__tests__/workflow-graceful-stop.test.mjs +108 -0
- package/src/wf-ui-server/__tests__/workflow-graph-undo.test.mjs +524 -0
- package/src/wf-ui-server/__tests__/workflow-layout.test.mjs +732 -0
- package/src/wf-ui-server/__tests__/workflow-magnetic-topology.test.mjs +222 -0
- package/src/wf-ui-server/__tests__/workflow-markdown-lock.test.mjs +359 -0
- package/src/wf-ui-server/__tests__/workflow-mcp-hub.test.mjs +174 -0
- package/src/wf-ui-server/__tests__/workflow-node-manual-schema.test.mjs +241 -0
- package/src/wf-ui-server/__tests__/workflow-node-runtime.test.mjs +1991 -0
- package/src/wf-ui-server/__tests__/workflow-node-store-utils.test.mjs +125 -0
- package/src/wf-ui-server/__tests__/workflow-registry-drift.test.mjs +296 -0
- package/src/wf-ui-server/__tests__/workflow-resource-handle-four-side.test.mjs +217 -0
- package/src/wf-ui-server/__tests__/workflow-role-profile.test.mjs +234 -0
- package/src/wf-ui-server/__tests__/workflow-set-model.test.mjs +261 -0
- package/src/wf-ui-server/__tests__/workflow-skill-group-context-enabled.test.mjs +131 -0
- package/src/wf-ui-server/__tests__/workflow-skill-group-enable-disable.test.mjs +186 -0
- package/src/wf-ui-server/__tests__/workflow-skills-hub.test.mjs +125 -0
- package/src/wf-ui-server/__tests__/workflow-state-reconcile.test.mjs +139 -0
- package/src/wf-ui-server/__tests__/workflow-subagent-strategy-matrix.test.mjs +529 -0
- package/src/wf-ui-server/__tests__/workflow-team-flow.test.mjs +278 -0
- package/src/wf-ui-server/__tests__/workflow-terminal-control-live.test.mjs +294 -0
- package/src/wf-ui-server/__tests__/workflow-terminal-control.test.mjs +631 -0
- package/src/wf-ui-server/__tests__/workflow-timer-wakeup.test.mjs +318 -0
- package/src/wf-ui-server/__tests__/workflow-unified-resume.test.mjs +537 -0
- package/src/wf-ui-server/__tests__/workspace-api-hardening.test.mjs +187 -0
- package/src/wf-ui-server/__tests__/workspace-api.test.mjs +382 -0
- package/src/wf-ui-server/__tests__/workspace-reveal.test.mjs +138 -0
- package/src/wf-ui-server/__tests__/workspace-store-hardening.test.mjs +255 -0
- package/src/wf-ui-server/__tests__/workspace-store.test.mjs +208 -0
- package/src/wf-ui-server/__tests__/ws-events.integration.test.mjs +52 -25
- package/src/wf-ui-server/__tests__/ws-terminal.integration.test.mjs +12 -6
- package/src/wf-ui-server/__tests__/ws-wf-browser.integration.test.mjs +488 -0
- package/src/wf-ui-server/a2a-store.mjs +3007 -89
- package/src/wf-ui-server/action-registry.mjs +277 -0
- package/src/wf-ui-server/bridge-store.mjs +276 -0
- package/src/wf-ui-server/chat-driver.mjs +220 -0
- package/src/wf-ui-server/chat-store.mjs +109 -0
- package/src/wf-ui-server/claude-transcript-materializer.mjs +235 -0
- package/src/wf-ui-server/codex-update-prompt.mjs +53 -0
- package/src/wf-ui-server/component-node-store.mjs +689 -0
- package/src/wf-ui-server/composition-api.mjs +436 -0
- package/src/wf-ui-server/control-plane-token.mjs +17 -0
- package/src/wf-ui-server/dispatch-contract.mjs +150 -0
- package/src/wf-ui-server/drivers/claude-stream-json.mjs +371 -0
- package/src/wf-ui-server/drivers/codex-appserver.mjs +513 -0
- package/src/wf-ui-server/drivers/opencode-server.mjs +486 -0
- package/src/wf-ui-server/excalidraw-svg.mjs +141 -0
- package/src/wf-ui-server/file-format-adapters.mjs +511 -0
- package/src/wf-ui-server/file-watcher.mjs +215 -0
- package/src/wf-ui-server/graph-layout.mjs +778 -0
- package/src/wf-ui-server/harness-env.mjs +46 -0
- package/src/wf-ui-server/json-store-utils.mjs +48 -0
- package/src/wf-ui-server/model-capability.mjs +773 -0
- package/src/wf-ui-server/node-config-store.mjs +233 -0
- package/src/wf-ui-server/node-store-utils.mjs +88 -0
- package/src/wf-ui-server/peer-capsule.mjs +56 -1
- package/src/wf-ui-server/pty-adapter.mjs +513 -11
- package/src/wf-ui-server/reveal-foreground.mjs +69 -0
- package/src/wf-ui-server/runtime-detector.mjs +52 -6
- package/src/wf-ui-server/server.mjs +5613 -403
- package/src/wf-ui-server/session-cleanup.mjs +363 -0
- package/src/wf-ui-server/session-registry.mjs +179 -3
- package/src/wf-ui-server/settings.mjs +25 -0
- package/src/wf-ui-server/spawn-gate.mjs +46 -0
- package/src/wf-ui-server/task-parser.mjs +88 -3
- package/src/wf-ui-server/terminal-store.mjs +225 -11
- package/src/wf-ui-server/timer-wakeup-scheduler.mjs +226 -0
- package/src/wf-ui-server/wf-browser-store.mjs +733 -0
- package/src/wf-ui-server/workflow-agent-context.mjs +1731 -0
- package/src/wf-ui-server/workflow-capability-node-store.mjs +676 -0
- package/src/wf-ui-server/workflow-event-node-store.mjs +623 -0
- package/src/wf-ui-server/workflow-goal-node-store.mjs +328 -0
- package/src/wf-ui-server/workflow-graph-store.mjs +623 -0
- package/src/wf-ui-server/workflow-mcp-hub.mjs +286 -0
- package/src/wf-ui-server/workflow-node-runtime.mjs +1767 -0
- package/src/wf-ui-server/workflow-node-settings-store.mjs +339 -0
- package/src/wf-ui-server/workflow-node-types/agent-node.mjs +920 -0
- package/src/wf-ui-server/workflow-node-types/display-node.mjs +151 -0
- package/src/wf-ui-server/workflow-node-types/excalidraw-node.mjs +145 -0
- package/src/wf-ui-server/workflow-node-types/file-node.mjs +484 -0
- package/src/wf-ui-server/workflow-node-types/github-trigger-node.mjs +207 -0
- package/src/wf-ui-server/workflow-node-types/goal-node.mjs +491 -0
- package/src/wf-ui-server/workflow-node-types/markdown-node.mjs +225 -0
- package/src/wf-ui-server/workflow-node-types/mcp-connector-node.mjs +71 -0
- package/src/wf-ui-server/workflow-node-types/role-profile-store.mjs +191 -0
- package/src/wf-ui-server/workflow-node-types/skill-group-node.mjs +98 -0
- package/src/wf-ui-server/workflow-node-types/timer-node.mjs +607 -0
- package/src/wf-ui-server/workflow-ontology.mjs +681 -0
- package/src/wf-ui-server/workflow-operation-store.mjs +115 -0
- package/src/wf-ui-server/workflow-skills-hub.mjs +366 -0
- package/src/wf-ui-server/workflow-skills-market.mjs +723 -0
- package/src/wf-ui-server/workspace-store.mjs +951 -0
- package/src/wf-ui-server/ws-chat.mjs +299 -0
- package/src/wf-ui-server/ws-events.mjs +45 -20
- package/src/wf-ui-server/ws-terminal.mjs +165 -22
- package/src/wf-ui-server/ws-wf-browser.mjs +419 -0
- package/templates/common/.claude/agents/review-manager.md +33 -29
- package/templates/common/.claude/commands/wf-browser.md +1 -1
- package/templates/common/.claude/commands/wf-help.md +5 -3
- package/templates/common/.claude/commands/wf-init.md +44 -0
- package/templates/common/.claude/commands/wf-learn.md +34 -8
- package/templates/common/.claude/commands/wf-max.md +19 -9
- package/templates/common/.claude/commands/wf-review.md +3 -3
- package/templates/common/.claude/commands/wf-search.md +55 -0
- package/templates/common/.claude/commands/wf-task-archive.md +5 -4
- package/templates/common/.claude/commands/wf-task-list.md +6 -3
- package/templates/common/.claude/commands/wf-task-record.md +10 -2
- package/templates/common/.claude/commands/wf-ui.md +12 -5
- package/templates/common/.claude/commands/wf-update.md +4 -0
- package/templates/common/.claude/commands/wf.md +12 -1
- package/templates/common/.claude/rules/ecc/common.md +10 -1
- package/templates/common/.claude/skills/wf/SKILL.md +36 -0
- package/templates/common/.claude/skills/wf-browser/SKILL.md +427 -122
- package/templates/common/.claude/skills/wf-browser/references/BROWSER_CONTROL_PATTERNS.md +43 -0
- package/templates/common/.claude/skills/wf-init/SKILL.md +33 -0
- package/templates/common/.claude/skills/wf-learn/SKILL.md +106 -26
- package/templates/common/.claude/skills/wf-max/SKILL.md +45 -11
- package/templates/common/.claude/skills/wf-review/SKILL.md +26 -54
- package/templates/common/.claude/skills/wf-search/SKILL.md +122 -0
- package/templates/common/.claude/skills/wf-search/references/evidence-contract.md +77 -0
- package/templates/common/.claude/skills/wf-search/references/policies/compare.md +38 -0
- package/templates/common/.claude/skills/wf-search/references/policies/fact.md +39 -0
- package/templates/common/.claude/skills/wf-search/references/policies/troubleshoot.md +39 -0
- package/templates/common/.claude/skills/wf-search/references/policies/verify.md +44 -0
- package/templates/common/.claude/skills/wf-search/references/source-routing.md +40 -0
- package/templates/common/.claude/skills/wf-search/references/tool-adapters.md +38 -0
- package/templates/common/.claude/skills/wf-ui/SKILL.md +146 -12
- package/templates/common/.claude/skills/wf-update/SKILL.md +5 -1
- package/templates/common/.harness-version +188 -85
- package/templates/common/.opencode/agents/review-manager.md +31 -27
- package/templates/common/.opencode/commands/wf-browser.md +1 -1
- package/templates/common/.opencode/commands/wf-help.md +5 -3
- package/templates/common/.opencode/commands/wf-init.md +44 -0
- package/templates/common/.opencode/commands/wf-learn.md +34 -8
- package/templates/common/.opencode/commands/wf-max.md +19 -9
- package/templates/common/.opencode/commands/wf-review.md +3 -3
- package/templates/common/.opencode/commands/wf-search.md +55 -0
- package/templates/common/.opencode/commands/wf-task-archive.md +5 -7
- package/templates/common/.opencode/commands/wf-task-list.md +6 -6
- package/templates/common/.opencode/commands/wf-task-record.md +10 -5
- package/templates/common/.opencode/commands/wf-ui.md +12 -5
- package/templates/common/.opencode/commands/wf-update.md +4 -0
- package/templates/common/.opencode/commands/wf.md +12 -1
- package/templates/common/CLAUDE.md +61 -3
- package/templates/common/Harness/MEMORY.md +8 -5
- package/templates/common/Harness/README.md +14 -10
- package/templates/common/Harness/a2a/action-registry.json +4397 -0
- package/templates/common/Harness/a2a/role-graph.json +16 -3
- package/templates/common/Harness/a2a/skills/terminal-control.json +45 -1
- package/templates/common/Harness/a2a/skills/wf-ui-map.json +50 -0
- package/templates/common/Harness/a2a/skills/workflow-agent-node.json +143 -0
- package/templates/common/Harness/a2a/skills/workflow-context.json +95 -0
- package/templates/common/Harness/a2a/skills/workflow-diagram-node.json +60 -0
- package/templates/common/Harness/a2a/skills/workflow-display-node.json +69 -0
- package/templates/common/Harness/a2a/skills/workflow-file-node.json +92 -0
- package/templates/common/Harness/a2a/skills/workflow-goal-node.json +76 -0
- package/templates/common/Harness/a2a/skills/workflow-markdown-node.json +79 -0
- package/templates/common/Harness/a2a/skills/workflow-mcp-connector-node.json +59 -0
- package/templates/common/Harness/a2a/skills/workflow-node-actions.json +105 -0
- package/templates/common/Harness/a2a/skills/workflow-node-map.json +71 -0
- package/templates/common/Harness/a2a/skills/workflow-ontology.json +54 -0
- package/templates/common/Harness/a2a/skills/workflow-resource-node.json +68 -0
- package/templates/common/Harness/a2a/skills/workflow-skill-group-node.json +56 -0
- package/templates/common/Harness/a2a/skills/workflow-timer-node.json +81 -0
- package/templates/common/Harness/a2a/workflows.json +33 -0
- package/templates/common/Harness/ownership.manifest.json +263 -2
- package/templates/common/Harness/project/architecture.md +499 -0
- package/templates/common/Harness/research/README.md +20 -0
- package/templates/common/Harness/scripts/README.md +70 -6
- package/templates/common/Harness/scripts/a2a-terminal.mjs +12 -4
- package/templates/common/Harness/scripts/context-budget.mjs +7 -3
- package/templates/common/Harness/scripts/lib/memory-context.mjs +400 -0
- package/templates/common/Harness/scripts/lib/path-scope.mjs +77 -0
- package/templates/common/Harness/scripts/lib/research-policy.mjs +99 -0
- package/templates/common/Harness/scripts/lib/wf-search.mjs +189 -0
- package/templates/common/Harness/scripts/memory-context.mjs +63 -0
- package/templates/common/Harness/scripts/research-policy.mjs +27 -0
- package/templates/common/Harness/scripts/task-context.mjs +547 -0
- package/templates/common/Harness/scripts/task-group-index.mjs +242 -0
- package/templates/common/Harness/scripts/task-state.mjs +659 -95
- package/templates/common/Harness/scripts/validate-harness.mjs +70 -28
- package/templates/common/Harness/scripts/wf-auto-update-prompt.mjs +24 -0
- package/templates/common/Harness/scripts/wf-remove.mjs +10 -0
- package/templates/common/Harness/scripts/wf-search.mjs +10 -0
- package/templates/common/Harness/scripts/wf-ui-control.mjs +3144 -0
- package/templates/common/Harness/scripts/wf-update-check.mjs +274 -51
- package/templates/common/Harness/scripts/wf-update-runner.mjs +53 -16
- package/templates/common/Harness/settings.json +10 -0
- package/templates/common/Harness/specs/guides/SETUP.md +4 -3
- package/templates/common/Harness/specs/protocols/HARNESS_BRIDGE.md +429 -48
- package/templates/common/Harness/specs/protocols/MEMORY_PROTOCOL.md +24 -0
- package/templates/common/Harness/specs/protocols/TASK_ARCHIVE.md +14 -11
- package/templates/common/Harness/specs/runtime/command-surface.json +28 -0
- package/templates/common/Harness/specs/runtime/context-loading.md +6 -0
- package/templates/common/Harness/specs/runtime/dispatch.md +9 -1
- package/templates/common/Harness/specs/runtime/subagents.md +20 -5
- package/templates/common/Harness/specs/workflows/WF-AUTO-SPARK.md +19 -0
- package/templates/common/Harness/specs/workflows/WF-AUTO.md +21 -0
- package/templates/common/Harness/specs/workflows/WF-KERNEL.md +4 -2
- package/templates/common/Harness/specs/workflows/WF-MAX.md +39 -12
- package/templates/common/Harness/specs/workflows/WF-STATE.md +62 -14
- package/templates/common/Harness/specs/workflows/WF.md +32 -0
- package/templates/common/Harness/tasks/INDEX.json +7 -0
- package/templates/common/Harness/tasks/INDEX.md +12 -0
- package/templates/common/Harness/tasks/_template/PLAN.md +6 -0
- package/templates/common/Harness/tasks/_template/PROGRESS.md +5 -0
- package/templates/common/Harness/tasks/_template/STATE.json +22 -0
- package/templates/common/Harness/templates/TEST_PLAN.template.md +5 -4
- package/templates/common/Harness/templates/UI_CONTRACT.template.md +7 -5
- package/templates/common/Harness/wf-browser/README.md +69 -0
- package/templates/common/README.md +17 -0
- package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js +0 -17
- package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js.map +0 -1
- package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js +0 -7
- package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js.map +0 -1
- package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js +0 -12
- package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js.map +0 -1
- package/src/ui/dist/assets/TaskList-BN4r8s1i.js +0 -50
- package/src/ui/dist/assets/TaskList-BN4r8s1i.js.map +0 -1
- package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js +0 -74
- package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js.map +0 -1
- package/src/ui/dist/assets/WorkflowRoute-BnuhLJ6X.css +0 -1
- package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js +0 -39
- package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js.map +0 -1
- package/src/ui/dist/assets/index-BOVYWntB.js +0 -117
- package/src/ui/dist/assets/index-BOVYWntB.js.map +0 -1
- package/src/ui/dist/assets/index-DaUObq0H.css +0 -1
- package/src/ui/dist/assets/maximize-2-LNrr_rKr.js +0 -7
- package/src/ui/dist/assets/maximize-2-LNrr_rKr.js.map +0 -1
- package/src/ui/dist/assets/plus-QuTJyoT3.js +0 -7
- package/src/ui/dist/assets/plus-QuTJyoT3.js.map +0 -1
- package/src/ui/dist/assets/proxy-D0vUbds5.js.map +0 -1
- package/src/ui/dist/assets/refresh-cw-JNmExijd.js.map +0 -1
- package/src/ui/dist/assets/terminal-BBQvnOAK.js.map +0 -1
- package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js +0 -7
- package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js.map +0 -1
- package/src/ui/dist/assets/x-DoIOGAJz.js.map +0 -1
- package/src/ui/pnpm-lock.yaml +0 -1582
- package/src/ui/src/App.tsx +0 -86
- package/src/ui/src/api.ts +0 -93
- package/src/ui/src/components/AgentsRoute.tsx +0 -503
- package/src/ui/src/components/Footer.tsx +0 -69
- package/src/ui/src/components/Header.tsx +0 -175
- package/src/ui/src/components/LoadingView.tsx +0 -22
- package/src/ui/src/components/RolesRoute.tsx +0 -169
- package/src/ui/src/components/SettingsRoute.tsx +0 -166
- package/src/ui/src/components/TaskList.tsx +0 -388
- package/src/ui/src/components/TerminalDrawer.tsx +0 -611
- package/src/ui/src/components/WorkflowRoute.tsx +0 -670
- package/src/ui/src/hooks/useReducedMotion.ts +0 -17
- package/src/ui/src/hooks/useServerConnection.ts +0 -114
- package/src/ui/src/index.css +0 -281
- package/src/ui/src/main.tsx +0 -11
- package/src/ui/src/types.ts +0 -108
- /package/src/ui/dist/assets/{TerminalDrawer-6GBZ9nXN.css → terminalControl-6GBZ9nXN.css} +0 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { agentTreePositions, GRAPH_SLOT_W, GRAPH_SLOT_H } from '../graph-layout.mjs';
|
|
4
|
+
|
|
5
|
+
// Agent-tree compact layout contract (task-agent-tree-layout W1 pins, RED first).
|
|
6
|
+
// Round-2 user requirements: main agent = core anchor centered; subagents one
|
|
7
|
+
// layer BELOW main arranged as an agent TREE (multi-level); non-agent nodes
|
|
8
|
+
// ("scattered") arranged as a compact MATRIX BAND ABOVE their owner agent;
|
|
9
|
+
// layout NEVER creates or modifies edges (pure positions).
|
|
10
|
+
//
|
|
11
|
+
// Pinned API:
|
|
12
|
+
// agentTreePositions(nodes, edges, opts)
|
|
13
|
+
// nodes: Array<{id, width?, height?, agentKind?, parentAgentId?, parentNodeId?}>
|
|
14
|
+
// agentKind: 'main' | 'subagent' (any other value or absent = non-agent)
|
|
15
|
+
// width/height default to GRAPH_SLOT_W/H (280x180)
|
|
16
|
+
// edges: Array<{from, to, direction?, relation?}> — used ONLY for
|
|
17
|
+
// agent-tree parenting (first edge to another agent); direction ignored.
|
|
18
|
+
// opts: {
|
|
19
|
+
// origin?: {x, y}, default {260, 220} — origin.x is the CENTER x of
|
|
20
|
+
// the main agent; origin.y is the TOP of its band.
|
|
21
|
+
// gapX?: number, default 64 (edge-to-edge horizontal spacing)
|
|
22
|
+
// gapY?: number, default 48 (edge-to-edge vertical spacing)
|
|
23
|
+
// dockedPairs?: Array<{aId, bId, offset: {x,y}}> — glued exactly:
|
|
24
|
+
// positions[b] = positions[a] + offset.
|
|
25
|
+
// }
|
|
26
|
+
// returns {[nodeId]: {x, y}} — every input node placed exactly once;
|
|
27
|
+
// deterministic (same input -> deep-equal output).
|
|
28
|
+
//
|
|
29
|
+
// Geometry rules pinned below:
|
|
30
|
+
// - emitted positions are TOP-LEFT corners (graph-map/UI convention);
|
|
31
|
+
// opts.origin.x anchors the main agent's CENTER x, origin.y the band top.
|
|
32
|
+
// - agent children sorted by id asc; subtree width = max(unit width,
|
|
33
|
+
// sum(child subtree widths) + gapX*(n-1)); parent centered over children.
|
|
34
|
+
// - asset matrix: cols = clamp(ceil(sqrt(n)), 1, 6), row-major by id asc,
|
|
35
|
+
// row height = tallest unit + gapY, each row centered on owner center;
|
|
36
|
+
// band sits ABOVE the owner (bandTop = parentRowBottom + gapY; owner y =
|
|
37
|
+
// bandTop + bandH + (bandH > 0 ? gapY : 0)).
|
|
38
|
+
// - clearance floor: no two rects closer than 16px (shared AC contract).
|
|
39
|
+
|
|
40
|
+
const ORIGIN = { x: 0, y: 0 };
|
|
41
|
+
const NODE = { width: 100, height: 50 };
|
|
42
|
+
const S = (id, extra = {}) => ({ id, ...NODE, ...extra });
|
|
43
|
+
const E = (from, to, direction = 'bidirectional') => ({ from, to, direction });
|
|
44
|
+
const AGENT = { agentKind: 'subagent' };
|
|
45
|
+
|
|
46
|
+
function minRectGap(positions, nodes) {
|
|
47
|
+
const rects = nodes.map((node) => {
|
|
48
|
+
const p = positions[node.id];
|
|
49
|
+
assert.ok(p, `agentTreePositions must place ${node.id}`);
|
|
50
|
+
assert.ok(Number.isFinite(Number(p.x)) && Number.isFinite(Number(p.y)), `position for ${node.id} must be numeric`);
|
|
51
|
+
return {
|
|
52
|
+
id: node.id,
|
|
53
|
+
x: Number(p.x),
|
|
54
|
+
y: Number(p.y),
|
|
55
|
+
w: Number.isFinite(Number(node.width)) ? Number(node.width) : GRAPH_SLOT_W,
|
|
56
|
+
h: Number.isFinite(Number(node.height)) ? Number(node.height) : GRAPH_SLOT_H,
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
let minGap = Infinity;
|
|
60
|
+
for (let i = 0; i < rects.length; i++) {
|
|
61
|
+
for (let j = i + 1; j < rects.length; j++) {
|
|
62
|
+
const a = rects[i];
|
|
63
|
+
const b = rects[j];
|
|
64
|
+
const sepX = Math.max(a.x, b.x) - Math.min(a.x + a.w, b.x + b.w);
|
|
65
|
+
const sepY = Math.max(a.y, b.y) - Math.min(a.y + a.h, b.y + b.h);
|
|
66
|
+
minGap = Math.min(minGap, Math.max(sepX, sepY));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return minGap;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function assertNoOverlap16(positions, nodes) {
|
|
73
|
+
const gap = minRectGap(positions, nodes);
|
|
74
|
+
assert.ok(gap >= 16, `all rects must keep >= 16px clearance (min gap was ${gap})`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
test('A1: main centered; subagents one row below, centered under main, sorted by id', () => {
|
|
78
|
+
const nodes = [
|
|
79
|
+
S('M', { agentKind: 'main' }),
|
|
80
|
+
S('S1', AGENT),
|
|
81
|
+
S('S2', AGENT),
|
|
82
|
+
S('S3', AGENT),
|
|
83
|
+
];
|
|
84
|
+
const edges = [E('M', 'S1'), E('M', 'S2'), E('M', 'S3')];
|
|
85
|
+
const positions = agentTreePositions(nodes, edges, { origin: ORIGIN });
|
|
86
|
+
// Positions are TOP-LEFT; origin.x=0 anchors main's CENTER, so main's left
|
|
87
|
+
// is -50 (width 100). Row width 3*100 + 2*64 = 428 centered on 0 ->
|
|
88
|
+
// centers -164, 0, 164 -> lefts -214, -50, 114.
|
|
89
|
+
assert.deepEqual(positions.M, { x: -50, y: 0 }, 'main center sits on origin.x');
|
|
90
|
+
assert.ok(
|
|
91
|
+
positions.S1.y === positions.S2.y && positions.S2.y === positions.S3.y,
|
|
92
|
+
'subagents share ONE layer below main',
|
|
93
|
+
);
|
|
94
|
+
assert.ok(positions.S1.y > positions.M.y, 'subagent layer is BELOW main');
|
|
95
|
+
assert.equal(positions.S2.x, positions.M.x, 'middle subagent centers under main');
|
|
96
|
+
assert.equal(positions.S1.x, -214, 'first subagent left of center');
|
|
97
|
+
assert.equal(positions.S3.x, 114, 'last subagent right of center');
|
|
98
|
+
assertNoOverlap16(positions, nodes);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('A2: scattered assets form a matrix band ABOVE main, centered, row-major', () => {
|
|
102
|
+
const nodes = [S('M', { agentKind: 'main' }), S('a1'), S('a2'), S('a3')];
|
|
103
|
+
const positions = agentTreePositions(nodes, [], { origin: ORIGIN });
|
|
104
|
+
// cols = clamp(ceil(sqrt(3)), 1, 6) = 2 -> row1: a1,a2; row2: a3.
|
|
105
|
+
// Band height = 50 + 48 + 50 = 148; M.y = bandTop(0) + 148 + 48 = 196.
|
|
106
|
+
assert.ok(positions.a1.y < positions.M.y && positions.a2.y < positions.M.y && positions.a3.y < positions.M.y, 'assets ABOVE main');
|
|
107
|
+
assert.equal(positions.a1.y, 0, 'first matrix row at band top');
|
|
108
|
+
assert.equal(positions.a2.y, 0, 'second item on first matrix row');
|
|
109
|
+
assert.equal(positions.a3.y, 98, 'third item wraps to the second matrix row');
|
|
110
|
+
assert.equal(positions.M.x, -50, 'main center keeps the origin x (top-left -50)');
|
|
111
|
+
assert.equal(positions.M.y, 196, 'main sits below its own band');
|
|
112
|
+
// Row1 width = 2*100 + 64 = 264 centered on 0 -> lefts -132, 32.
|
|
113
|
+
assert.equal(positions.a1.x, -132, 'row1 left item');
|
|
114
|
+
assert.equal(positions.a2.x, 32, 'row1 right item');
|
|
115
|
+
assert.equal(positions.a3.x, -50, 'row2 single item centered on main');
|
|
116
|
+
assertNoOverlap16(positions, nodes);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('A3: multi-level agent tree: nested subagent below its parent, siblings side by side', () => {
|
|
120
|
+
const nodes = [
|
|
121
|
+
S('M', { agentKind: 'main' }),
|
|
122
|
+
S('S1', AGENT),
|
|
123
|
+
S('S1b', AGENT),
|
|
124
|
+
S('S2', AGENT),
|
|
125
|
+
];
|
|
126
|
+
const edges = [E('M', 'S1'), E('S1', 'S1b'), E('M', 'S2')];
|
|
127
|
+
const positions = agentTreePositions(nodes, edges, { origin: ORIGIN });
|
|
128
|
+
assert.equal(positions.S1.y, 98, 'first agent row below main (50 + 48)');
|
|
129
|
+
assert.equal(positions.S2.y, 98, 'sibling shares the first agent row');
|
|
130
|
+
assert.equal(positions.S1b.y, 196, 'nested subagent one row deeper');
|
|
131
|
+
assert.equal(positions.S1.x, -132, 'left sibling subtree (center -82 -> left -132)');
|
|
132
|
+
assert.equal(positions.S2.x, 32, 'right sibling subtree (center 82 -> left 32)');
|
|
133
|
+
assert.equal(positions.S1b.x, positions.S1.x, 'nested subagent centers under its parent');
|
|
134
|
+
assertNoOverlap16(positions, nodes);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('A4: explicit parentAgentId wins over bridge edges', () => {
|
|
138
|
+
const nodes = [
|
|
139
|
+
S('M', { agentKind: 'main' }),
|
|
140
|
+
S('S1', AGENT),
|
|
141
|
+
S('S2', { ...AGENT, parentAgentId: 'S1' }),
|
|
142
|
+
];
|
|
143
|
+
const edges = [E('M', 'S1'), E('M', 'S2')];
|
|
144
|
+
const positions = agentTreePositions(nodes, edges, { origin: ORIGIN });
|
|
145
|
+
assert.equal(positions.S1.x, positions.M.x, 'S1 (only child) centers under M');
|
|
146
|
+
assert.ok(positions.S2.y > positions.S1.y, 'S2 sits BELOW S1 despite bridging M');
|
|
147
|
+
assert.equal(positions.S2.x, positions.S1.x, 'S2 centers under its declared parent');
|
|
148
|
+
assertNoOverlap16(positions, nodes);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('A5: asset with parentAgentId lands in the band ABOVE its owner subagent', () => {
|
|
152
|
+
const nodes = [
|
|
153
|
+
S('M', { agentKind: 'main' }),
|
|
154
|
+
S('S1', AGENT),
|
|
155
|
+
S('X', { parentAgentId: 'S1' }),
|
|
156
|
+
];
|
|
157
|
+
const edges = [E('M', 'S1')];
|
|
158
|
+
const positions = agentTreePositions(nodes, edges, { origin: ORIGIN });
|
|
159
|
+
assert.ok(positions.X.y < positions.S1.y, 'asset band is ABOVE the owner agent');
|
|
160
|
+
assert.ok(positions.X.y > positions.M.y, 'subagent band sits below main');
|
|
161
|
+
assert.equal(positions.X.x, positions.S1.x, 'band centers on the owner');
|
|
162
|
+
// X at bandTop 98, S1.y = 98 + 50 + 48 = 196.
|
|
163
|
+
assert.equal(positions.X.y, 98, 'band top = main bottom + gapY');
|
|
164
|
+
assert.equal(positions.S1.y, 196, 'owner below its own band');
|
|
165
|
+
assertNoOverlap16(positions, nodes);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('A5b: parentNodeId chain resolves ownership to an agent', () => {
|
|
169
|
+
const nodes = [
|
|
170
|
+
S('M', { agentKind: 'main' }),
|
|
171
|
+
S('S1', AGENT),
|
|
172
|
+
S('child', { parentNodeId: 'X' }),
|
|
173
|
+
S('X', { parentNodeId: 'S1' }),
|
|
174
|
+
];
|
|
175
|
+
const edges = [E('M', 'S1')];
|
|
176
|
+
const positions = agentTreePositions(nodes, edges, { origin: ORIGIN });
|
|
177
|
+
assert.ok(positions.X.y < positions.S1.y, 'chain tail asset above the owner agent');
|
|
178
|
+
assert.ok(positions.child.y < positions.S1.y, 'chain head asset above the owner agent');
|
|
179
|
+
assertNoOverlap16(positions, nodes);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('A6: no main agent -> flat fallback: agents in one root row, assets band above, centered', () => {
|
|
183
|
+
const nodes = [S('A', AGENT), S('B', AGENT), S('x1'), S('x2')];
|
|
184
|
+
const edges = [E('A', 'B')];
|
|
185
|
+
const positions = agentTreePositions(nodes, edges, { origin: ORIGIN });
|
|
186
|
+
assert.equal(positions.A.y, positions.B.y, 'agents share one root row');
|
|
187
|
+
assert.ok(positions.x1.y < positions.A.y && positions.x2.y < positions.A.y, 'assets above the agent row');
|
|
188
|
+
assert.equal(positions.x1.y, 0, 'band top at origin');
|
|
189
|
+
assert.equal(positions.A.y, 98, 'agent row below the band (50 + 48)');
|
|
190
|
+
assert.equal(positions.A.x, -132, 'agents centered as a row (center -82 -> left -132)');
|
|
191
|
+
assert.equal(positions.B.x, 32, 'agents centered as a row (center 82 -> left 32)');
|
|
192
|
+
assert.equal(positions.x1.x, -132, 'asset matrix centered on the agent row');
|
|
193
|
+
assert.equal(positions.x2.x, 32, 'asset matrix centered on the agent row');
|
|
194
|
+
assertNoOverlap16(positions, nodes);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('A7: docked pair glued exactly; child row clears the glued unit extent', () => {
|
|
198
|
+
const nodes = [
|
|
199
|
+
S('M', { agentKind: 'main' }),
|
|
200
|
+
S('S1', AGENT),
|
|
201
|
+
S('X'),
|
|
202
|
+
];
|
|
203
|
+
const edges = [E('M', 'S1')];
|
|
204
|
+
const dockedPairs = [{ aId: 'M', bId: 'X', offset: { x: 12, y: 196 } }];
|
|
205
|
+
const positions = agentTreePositions(nodes, edges, { origin: ORIGIN, dockedPairs });
|
|
206
|
+
assert.equal(positions.X.x, positions.M.x + 12, 'dock keeps its x offset exactly');
|
|
207
|
+
assert.equal(positions.X.y, positions.M.y + 196, 'dock keeps its y offset exactly');
|
|
208
|
+
// X extends M's unit down to y=246; S1 must clear 246 + 16.
|
|
209
|
+
assert.ok(positions.S1.y >= positions.M.y + 246 + 16, 'child row clears the glued unit');
|
|
210
|
+
assertNoOverlap16(positions, nodes);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('A8: deterministic: same input -> deep-equal output', () => {
|
|
214
|
+
const nodes = [
|
|
215
|
+
S('M', { agentKind: 'main' }),
|
|
216
|
+
S('S1', AGENT),
|
|
217
|
+
S('S1b', AGENT),
|
|
218
|
+
S('S2', AGENT),
|
|
219
|
+
S('a1', { parentAgentId: 'S1' }),
|
|
220
|
+
S('a2'),
|
|
221
|
+
];
|
|
222
|
+
const edges = [E('M', 'S1'), E('S1', 'S1b'), E('M', 'S2')];
|
|
223
|
+
const first = agentTreePositions(nodes, edges, { origin: ORIGIN });
|
|
224
|
+
const second = agentTreePositions(nodes, edges, { origin: ORIGIN });
|
|
225
|
+
assert.deepEqual(second, first);
|
|
226
|
+
assertNoOverlap16(first, nodes);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test('A9: agent parenting cycle is safe: both agents placed on the root row', () => {
|
|
230
|
+
const nodes = [
|
|
231
|
+
S('A', { ...AGENT, parentAgentId: 'B' }),
|
|
232
|
+
S('B', { ...AGENT, parentAgentId: 'A' }),
|
|
233
|
+
];
|
|
234
|
+
const positions = agentTreePositions(nodes, [], { origin: ORIGIN });
|
|
235
|
+
assert.equal(positions.A.y, positions.B.y, 'cycle members share the root row');
|
|
236
|
+
assert.ok(positions.A.x !== positions.B.x, 'never stacked on one point');
|
|
237
|
+
assertNoOverlap16(positions, nodes);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('A10: compact: subagent row sits right below main (no slack gap)', () => {
|
|
241
|
+
const nodes = [S('M', { agentKind: 'main' }), S('S1', AGENT)];
|
|
242
|
+
const edges = [E('M', 'S1')];
|
|
243
|
+
const positions = agentTreePositions(nodes, edges, { origin: ORIGIN });
|
|
244
|
+
// main bottom 50 + gapY 48 = 98 — exactly one gap, nothing extra.
|
|
245
|
+
assert.equal(positions.S1.y, 98, 'subagent row advances by main height + gapY only');
|
|
246
|
+
assertNoOverlap16(positions, nodes);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test('A11: defaults pinned: origin {260,220} centers a lone main agent there', () => {
|
|
250
|
+
const positions = agentTreePositions([S('M', { agentKind: 'main' })], [], {});
|
|
251
|
+
// origin.x anchors the CENTER (width 100 -> top-left 210); origin.y is the
|
|
252
|
+
// band top = the agent's top when no assets exist.
|
|
253
|
+
assert.deepEqual(positions.M, { x: 210, y: 220 }, 'default origin centers main at {260,220}');
|
|
254
|
+
});
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { tidyPositions, GRAPH_SLOT_W, GRAPH_SLOT_H } from '../graph-layout.mjs';
|
|
4
|
+
|
|
5
|
+
// Tidy full-graph layout contract (AC-003): hierarchical re-layout driven by
|
|
6
|
+
// EDGE DIRECTION (not parentNodeId), deterministic, zero overlaps for cyclic /
|
|
7
|
+
// multi-component / isolated graphs, docked pairs glued.
|
|
8
|
+
//
|
|
9
|
+
// Pinned API (W1 synthesis; implementers add tidyPositions to graph-layout.mjs):
|
|
10
|
+
// tidyPositions(nodes, edges, opts)
|
|
11
|
+
// nodes: Array<{id, width?, height?}> size fallback 280x180 (GRAPH_SLOT_W/H)
|
|
12
|
+
// edges: Array<{from, to, direction?}> 'source-to-target' | 'bidirectional'
|
|
13
|
+
// (default 'source-to-target')
|
|
14
|
+
// opts: {
|
|
15
|
+
// origin?: {x, y}, default {x: 260, y: 220}
|
|
16
|
+
// gapX?: number, default 420
|
|
17
|
+
// gapY?: number, default 140
|
|
18
|
+
// parentNodeId?: string, optional root anchor (unused by layer order)
|
|
19
|
+
// dockedPairs?: Array<{aId, bId, offset: {x, y}}>
|
|
20
|
+
// // b is docked to a; output must satisfy EXACTLY:
|
|
21
|
+
// // positions[b] = { x: positions[a].x + offset.x, y: positions[a].y + offset.y }
|
|
22
|
+
// }
|
|
23
|
+
// returns {[nodeId]: {x, y}} — every input node placed exactly once;
|
|
24
|
+
// deterministic (same input -> deep-equal output).
|
|
25
|
+
//
|
|
26
|
+
// Rects use node width/height when given, else GRAPH_SLOT_W/GRAPH_SLOT_H.
|
|
27
|
+
// AC-003 minimum clearance between any two rects: 16px.
|
|
28
|
+
|
|
29
|
+
const DEFAULT_ORIGIN = { x: 260, y: 220 };
|
|
30
|
+
const DEFAULT_GAP = { x: 420, y: 140 };
|
|
31
|
+
|
|
32
|
+
// Minimum pairwise rect gap across all nodes; negative when any pair overlaps.
|
|
33
|
+
function minRectGap(positions, nodes) {
|
|
34
|
+
const rects = nodes.map(node => {
|
|
35
|
+
const p = positions[node.id];
|
|
36
|
+
assert.ok(p, `tidyPositions must place ${node.id}`);
|
|
37
|
+
assert.ok(Number.isFinite(Number(p.x)) && Number.isFinite(Number(p.y)), `position for ${node.id} must be numeric`);
|
|
38
|
+
return {
|
|
39
|
+
id: node.id,
|
|
40
|
+
x: Number(p.x),
|
|
41
|
+
y: Number(p.y),
|
|
42
|
+
w: Number.isFinite(Number(node.width)) ? Number(node.width) : GRAPH_SLOT_W,
|
|
43
|
+
h: Number.isFinite(Number(node.height)) ? Number(node.height) : GRAPH_SLOT_H,
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
let minGap = Infinity;
|
|
47
|
+
for (let i = 0; i < rects.length; i++) {
|
|
48
|
+
for (let j = i + 1; j < rects.length; j++) {
|
|
49
|
+
const a = rects[i];
|
|
50
|
+
const b = rects[j];
|
|
51
|
+
const sepX = Math.max(a.x, b.x) - Math.min(a.x + a.w, b.x + b.w);
|
|
52
|
+
const sepY = Math.max(a.y, b.y) - Math.min(a.y + a.h, b.y + b.h);
|
|
53
|
+
minGap = Math.min(minGap, Math.max(sepX, sepY));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return minGap;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function assertNoOverlap16(positions, nodes) {
|
|
60
|
+
const gap = minRectGap(positions, nodes);
|
|
61
|
+
assert.ok(
|
|
62
|
+
gap >= 16,
|
|
63
|
+
`AC-003: all rects must keep >= 16px clearance (min gap was ${gap})`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
test('T1 (AC-003) chain A->B->C is layered by edge direction and deterministic', () => {
|
|
68
|
+
const nodes = [{ id: 'A' }, { id: 'B' }, { id: 'C' }];
|
|
69
|
+
const edges = [
|
|
70
|
+
{ from: 'A', to: 'B', direction: 'source-to-target' },
|
|
71
|
+
{ from: 'B', to: 'C', direction: 'source-to-target' },
|
|
72
|
+
];
|
|
73
|
+
const first = tidyPositions(nodes, edges, {});
|
|
74
|
+
const second = tidyPositions(nodes, edges, {});
|
|
75
|
+
assert.deepEqual(second, first, 'deterministic: same input -> same output');
|
|
76
|
+
|
|
77
|
+
const ids = Object.keys(first).sort();
|
|
78
|
+
assert.deepEqual(ids, ['A', 'B', 'C'], 'every node must be placed exactly once');
|
|
79
|
+
assert.ok(
|
|
80
|
+
first.A.y < first.B.y && first.B.y < first.C.y,
|
|
81
|
+
`layers ordered by edge direction: A.y=${first.A.y} < B.y=${first.B.y} < C.y=${first.C.y}`,
|
|
82
|
+
);
|
|
83
|
+
assertNoOverlap16(first, nodes);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('T2 (AC-003) bidirectional edge keeps both nodes on the same layer', () => {
|
|
87
|
+
const nodes = [{ id: 'A' }, { id: 'B' }];
|
|
88
|
+
const edges = [{ from: 'A', to: 'B', direction: 'bidirectional' }];
|
|
89
|
+
const positions = tidyPositions(nodes, edges, {});
|
|
90
|
+
assert.equal(
|
|
91
|
+
positions.A.y,
|
|
92
|
+
positions.B.y,
|
|
93
|
+
'a bidirectional edge imposes no layer order, so both nodes share one layer',
|
|
94
|
+
);
|
|
95
|
+
assert.notEqual(positions.A.x, positions.B.x, 'same-layer nodes are side by side');
|
|
96
|
+
assertNoOverlap16(positions, nodes);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('T3 (AC-003) cyclic graph A->B->C->A: all nodes placed, no overlaps, deterministic', () => {
|
|
100
|
+
const nodes = [{ id: 'A' }, { id: 'B' }, { id: 'C' }];
|
|
101
|
+
const edges = [
|
|
102
|
+
{ from: 'A', to: 'B' },
|
|
103
|
+
{ from: 'B', to: 'C' },
|
|
104
|
+
{ from: 'C', to: 'A' },
|
|
105
|
+
];
|
|
106
|
+
const first = tidyPositions(nodes, edges, {});
|
|
107
|
+
const second = tidyPositions(nodes, edges, {});
|
|
108
|
+
assert.deepEqual(second, first);
|
|
109
|
+
assert.deepEqual(Object.keys(first).sort(), ['A', 'B', 'C'], 'cycle back-edges must not drop nodes');
|
|
110
|
+
assertNoOverlap16(first, nodes);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('T4 (AC-003) multi-component + isolated graphs: internal chain order kept, no overlaps', () => {
|
|
114
|
+
const nodes = [{ id: 'A' }, { id: 'B' }, { id: 'C' }, { id: 'D' }, { id: 'E' }];
|
|
115
|
+
const edges = [
|
|
116
|
+
{ from: 'A', to: 'B' },
|
|
117
|
+
{ from: 'C', to: 'D' },
|
|
118
|
+
];
|
|
119
|
+
const positions = tidyPositions(nodes, edges, {});
|
|
120
|
+
assert.deepEqual(Object.keys(positions).sort(), ['A', 'B', 'C', 'D', 'E']);
|
|
121
|
+
assert.ok(positions.A.y < positions.B.y, 'chain A->B keeps its layer order');
|
|
122
|
+
assert.ok(positions.C.y < positions.D.y, 'chain C->D keeps its layer order');
|
|
123
|
+
assertNoOverlap16(positions, nodes);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('T5 (AC-003) size variance: a 560x358 node never overlaps a default-size neighbor', () => {
|
|
127
|
+
const nodes = [
|
|
128
|
+
{ id: 'A', width: 560, height: 358 },
|
|
129
|
+
{ id: 'B' },
|
|
130
|
+
];
|
|
131
|
+
const edges = [{ from: 'A', to: 'B' }];
|
|
132
|
+
const positions = tidyPositions(nodes, edges, {});
|
|
133
|
+
assert.ok(positions.A.y < positions.B.y, 'chain order kept with mixed sizes');
|
|
134
|
+
assertNoOverlap16(positions, nodes);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('T6 (AC-003) docked pairs are glued: relative offset preserved exactly', () => {
|
|
138
|
+
const nodes = [{ id: 'A' }, { id: 'B' }, { id: 'C' }];
|
|
139
|
+
const edges = [{ from: 'A', to: 'C' }];
|
|
140
|
+
// y-offset 196 = one node height (180) + 16px clearance, so the glued pair
|
|
141
|
+
// itself also satisfies the min-gap rule (the no-overlap contract applies
|
|
142
|
+
// between layout units; a docked pair is one unit).
|
|
143
|
+
const dockedPairs = [{ aId: 'A', bId: 'B', offset: { x: 12, y: 196 } }];
|
|
144
|
+
const positions = tidyPositions(nodes, edges, { dockedPairs });
|
|
145
|
+
// Pinned dockedPairs shape: positions[b] = positions[a] + offset exactly.
|
|
146
|
+
assert.equal(positions.B.x, positions.A.x + 12, 'docked pair keeps its x offset');
|
|
147
|
+
assert.equal(positions.B.y, positions.A.y + 196, 'docked pair keeps its y offset');
|
|
148
|
+
assertNoOverlap16(positions, nodes);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('T7 (AC-003) defaults: opts optional, plain chain still ordered and clean', () => {
|
|
152
|
+
const nodes = [{ id: 'X' }, { id: 'Y' }];
|
|
153
|
+
const edges = [{ from: 'X', to: 'Y' }];
|
|
154
|
+
const positions = tidyPositions(nodes, edges);
|
|
155
|
+
assert.ok(positions.X.y < positions.Y.y, 'default origin/gap still order the chain');
|
|
156
|
+
assertNoOverlap16(positions, nodes);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('T8 (AC-003) opts.origin and opts.gapY are honored', () => {
|
|
160
|
+
const nodes = [{ id: 'A' }, { id: 'B' }];
|
|
161
|
+
const edges = [{ from: 'A', to: 'B' }];
|
|
162
|
+
const opts = { origin: { x: 0, y: 0 }, gapX: 300, gapY: 400 };
|
|
163
|
+
const positions = tidyPositions(nodes, edges, opts);
|
|
164
|
+
assert.deepEqual(
|
|
165
|
+
positions.A,
|
|
166
|
+
{ x: 0, y: 0 },
|
|
167
|
+
'first layer starts at opts.origin',
|
|
168
|
+
);
|
|
169
|
+
assert.equal(
|
|
170
|
+
positions.B.y,
|
|
171
|
+
0 + 400,
|
|
172
|
+
'layer rows advance by opts.gapY',
|
|
173
|
+
);
|
|
174
|
+
assertNoOverlap16(positions, nodes);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('T9 (AC-003) defaults match the pinned constants', () => {
|
|
178
|
+
// Locks the documented defaults so server.mjs GRAPH_LAYOUT_DEFAULTS stays in
|
|
179
|
+
// sync with the unit API.
|
|
180
|
+
const nodes = [{ id: 'A' }];
|
|
181
|
+
const positions = tidyPositions(nodes, [], {});
|
|
182
|
+
assert.deepEqual(positions.A, DEFAULT_ORIGIN, 'origin defaults to {260,220}');
|
|
183
|
+
assert.ok(DEFAULT_GAP.x >= GRAPH_SLOT_W + 16 && DEFAULT_GAP.y >= 16, 'default gaps keep clearance');
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// ── W5 review pins (task-optimize-graph-layout) ──────────────────────────────
|
|
187
|
+
// The four cases below pin W4 review findings MED-3 / MED-4 / LOW-9. All are
|
|
188
|
+
// RED against the current engine and must go GREEN with the W5 fix wave:
|
|
189
|
+
// T10 (MED-3): a top-docked capsule (negative y offset) extends ABOVE its
|
|
190
|
+
// anchor; the anchor row must advance enough that the glued pair's union
|
|
191
|
+
// clears the row above.
|
|
192
|
+
// T11 (MED-4a): chained docks (B is A's slave AND C's anchor) must be laid
|
|
193
|
+
// out as one glued chain — every node placed, glue exact, no overlaps.
|
|
194
|
+
// T12 (MED-4b): an A<->B dock cycle must place both nodes, never overlap,
|
|
195
|
+
// and stay deterministic.
|
|
196
|
+
// T13 (LOW-9): opts.gapX below the 16px minimum clearance must be clamped.
|
|
197
|
+
|
|
198
|
+
test('T10 (AC-003/MED-3) top-docked capsule (negative offset) never overlaps the row above', () => {
|
|
199
|
+
// TALL (600 tall) on the row above; A anchors a capsule docked 600px UP
|
|
200
|
+
// (offset y:-600). The pair's union bbox reaches from A.y-600 to A.y+180,
|
|
201
|
+
// so the anchor row must advance for that upward extent: B must clear TALL
|
|
202
|
+
// by >= 16px.
|
|
203
|
+
const nodes = [
|
|
204
|
+
{ id: 'TALL', width: 280, height: 600 },
|
|
205
|
+
{ id: 'A' },
|
|
206
|
+
{ id: 'B' },
|
|
207
|
+
];
|
|
208
|
+
const edges = [{ from: 'TALL', to: 'A', direction: 'source-to-target' }];
|
|
209
|
+
const dockedPairs = [{ aId: 'A', bId: 'B', offset: { x: 0, y: -600 } }];
|
|
210
|
+
const positions = tidyPositions(nodes, edges, { dockedPairs });
|
|
211
|
+
assert.deepEqual(Object.keys(positions).sort(), ['A', 'B', 'TALL'], 'every node placed exactly once');
|
|
212
|
+
assert.equal(positions.B.x, positions.A.x, 'docked pair keeps its x offset');
|
|
213
|
+
assert.equal(positions.B.y, positions.A.y - 600, 'docked pair keeps its y offset');
|
|
214
|
+
assertNoOverlap16(positions, nodes);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test('T11 (AC-003/MED-4a) chained docks (slave-as-anchor) place all nodes with no overlaps', () => {
|
|
218
|
+
// Chain A -> B -> C: B is A's dock slave AND the anchor of C's pair.
|
|
219
|
+
// The chain must behave as ONE glued unit (extent 196+196+180 tall), so a
|
|
220
|
+
// node on the row below the anchor (X via edge A->X) never collides with C.
|
|
221
|
+
const nodes = [{ id: 'A' }, { id: 'B' }, { id: 'C' }, { id: 'X' }];
|
|
222
|
+
const edges = [{ from: 'A', to: 'X', direction: 'source-to-target' }];
|
|
223
|
+
const dockedPairs = [
|
|
224
|
+
{ aId: 'A', bId: 'B', offset: { x: 0, y: 196 } },
|
|
225
|
+
{ aId: 'B', bId: 'C', offset: { x: 0, y: 196 } },
|
|
226
|
+
];
|
|
227
|
+
const positions = tidyPositions(nodes, edges, { dockedPairs });
|
|
228
|
+
assert.deepEqual(Object.keys(positions).sort(), ['A', 'B', 'C', 'X'], 'every node placed exactly once');
|
|
229
|
+
// Glue relations stay exact wherever the chain is resolvable.
|
|
230
|
+
assert.equal(positions.B.x, positions.A.x, 'chain link 1 keeps its x offset');
|
|
231
|
+
assert.equal(positions.B.y, positions.A.y + 196, 'chain link 1 keeps its y offset');
|
|
232
|
+
assert.equal(positions.C.x, positions.B.x, 'chain link 2 keeps its x offset');
|
|
233
|
+
assert.equal(positions.C.y, positions.B.y + 196, 'chain link 2 keeps its y offset');
|
|
234
|
+
assertNoOverlap16(positions, nodes);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test('T12 (AC-003/MED-4b) dock cycle A<->B: both placed, no overlap, deterministic', () => {
|
|
238
|
+
// Two dock links that agree on the same geometry (B = A + (300,0) and
|
|
239
|
+
// A = B + (-300,0)) must resolve deterministically instead of collapsing
|
|
240
|
+
// both nodes onto one spot.
|
|
241
|
+
const nodes = [{ id: 'A' }, { id: 'B' }];
|
|
242
|
+
const dockedPairs = [
|
|
243
|
+
{ aId: 'A', bId: 'B', offset: { x: 300, y: 0 } },
|
|
244
|
+
{ aId: 'B', bId: 'A', offset: { x: -300, y: 0 } },
|
|
245
|
+
];
|
|
246
|
+
const first = tidyPositions(nodes, [], { dockedPairs });
|
|
247
|
+
const second = tidyPositions(nodes, [], { dockedPairs });
|
|
248
|
+
assert.deepEqual(second, first, 'deterministic: same input -> same output');
|
|
249
|
+
assert.deepEqual(Object.keys(first).sort(), ['A', 'B'], 'both nodes placed exactly once');
|
|
250
|
+
assertNoOverlap16(first, nodes);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test('T13 (AC-003/LOW-9) gapX below the 16px minimum clearance is clamped', () => {
|
|
254
|
+
// opts.gapX = 4 would let two same-row units sit only 4px apart; the engine
|
|
255
|
+
// must clamp the effective clearance so minRectGap never drops below 16.
|
|
256
|
+
const nodes = [{ id: 'A' }, { id: 'B' }];
|
|
257
|
+
const edges = [{ from: 'A', to: 'B', direction: 'bidirectional' }];
|
|
258
|
+
const positions = tidyPositions(nodes, edges, { gapX: 4 });
|
|
259
|
+
assertNoOverlap16(positions, nodes);
|
|
260
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {
|
|
4
|
+
autoPlaceNode,
|
|
5
|
+
layeredTreePositions,
|
|
6
|
+
GRAPH_SLOT_W,
|
|
7
|
+
GRAPH_SLOT_H,
|
|
8
|
+
GRAPH_ORIGIN,
|
|
9
|
+
} from '../graph-layout.mjs';
|
|
10
|
+
|
|
11
|
+
// Occupancy-aware auto-layout contract: new auto-placed nodes never overlap
|
|
12
|
+
// any existing node (auto or dragged); children sit below their parents; the
|
|
13
|
+
// full-graph relayout is a deterministic layered tree.
|
|
14
|
+
|
|
15
|
+
test('G1 sequential auto-placement never overlaps (20 nodes)', () => {
|
|
16
|
+
const positions = {};
|
|
17
|
+
const placed = [];
|
|
18
|
+
for (let i = 0; i < 20; i++) {
|
|
19
|
+
const pos = autoPlaceNode(positions, {});
|
|
20
|
+
placed.push(pos);
|
|
21
|
+
positions[`n-${i}`] = pos;
|
|
22
|
+
}
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
for (const { x, y } of placed) {
|
|
25
|
+
const key = `${x},${y}`;
|
|
26
|
+
assert.ok(!seen.has(key), `duplicate slot ${key}`);
|
|
27
|
+
seen.add(key);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('G2 children land on the row below the parent, near the parent column', () => {
|
|
32
|
+
const parent = { x: GRAPH_ORIGIN.x + 2 * GRAPH_SLOT_W, y: GRAPH_ORIGIN.y };
|
|
33
|
+
const child1 = autoPlaceNode({ parent }, { parent });
|
|
34
|
+
assert.equal(child1.y, GRAPH_ORIGIN.y + GRAPH_SLOT_H);
|
|
35
|
+
assert.equal(child1.x, parent.x, 'first child takes the parent column');
|
|
36
|
+
const child2 = autoPlaceNode({ parent, child1 }, { parent });
|
|
37
|
+
assert.equal(child2.y, child1.y);
|
|
38
|
+
assert.equal(Math.abs(child2.x - parent.x), GRAPH_SLOT_W, 'second child takes the adjacent column');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('G3 a dragged node occupying a cell is avoided', () => {
|
|
42
|
+
const dragged = { x: GRAPH_ORIGIN.x, y: GRAPH_ORIGIN.y };
|
|
43
|
+
const pos = autoPlaceNode({ dragged }, {});
|
|
44
|
+
assert.notDeepEqual(pos, dragged);
|
|
45
|
+
// The first free root slot with col 0 taken is the next column.
|
|
46
|
+
assert.equal(pos.x, GRAPH_ORIGIN.x + GRAPH_SLOT_W);
|
|
47
|
+
assert.equal(pos.y, GRAPH_ORIGIN.y);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('G4 layered tree: roots side by side, children below, no collisions', () => {
|
|
51
|
+
const nodes = [
|
|
52
|
+
{ nodeId: 'root-a' },
|
|
53
|
+
{ nodeId: 'child-a1', parentNodeId: 'root-a' },
|
|
54
|
+
{ nodeId: 'child-a2', parentNodeId: 'root-a' },
|
|
55
|
+
{ nodeId: 'grand-a1', parentNodeId: 'child-a1' },
|
|
56
|
+
{ nodeId: 'root-b' },
|
|
57
|
+
{ nodeId: 'child-b1', parentNodeId: 'root-b' },
|
|
58
|
+
];
|
|
59
|
+
const positions = layeredTreePositions(nodes, {});
|
|
60
|
+
const seen = new Set();
|
|
61
|
+
for (const [id, { x, y }] of Object.entries(positions)) {
|
|
62
|
+
assert.ok(!seen.has(`${x},${y}`), `collision at ${x},${y} for ${id}`);
|
|
63
|
+
seen.add(`${x},${y}`);
|
|
64
|
+
}
|
|
65
|
+
assert.equal(positions['root-a'].y, positions['root-b'].y, 'roots share row 0');
|
|
66
|
+
assert.ok(positions['root-b'].x > positions['root-a'].x, 'roots side by side');
|
|
67
|
+
assert.equal(positions['child-a1'].y, positions['root-a'].y + GRAPH_SLOT_H, 'children one row below');
|
|
68
|
+
assert.equal(positions['child-a2'].y, positions['root-a'].y + GRAPH_SLOT_H);
|
|
69
|
+
assert.equal(positions['grand-a1'].y, positions['root-a'].y + 2 * GRAPH_SLOT_H, 'grandchildren two rows below');
|
|
70
|
+
// The two children are centered under the root's x.
|
|
71
|
+
const midX = (positions['child-a1'].x + positions['child-a2'].x) / 2;
|
|
72
|
+
assert.equal(midX, positions['root-a'].x, 'children block centered under parent');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('G5 layered tree is deterministic', () => {
|
|
76
|
+
const nodes = [
|
|
77
|
+
{ nodeId: 'd1' },
|
|
78
|
+
{ nodeId: 'd2', parentNodeId: 'd1' },
|
|
79
|
+
{ nodeId: 'd3', parentNodeId: 'd1' },
|
|
80
|
+
];
|
|
81
|
+
const first = layeredTreePositions(nodes, {});
|
|
82
|
+
const second = layeredTreePositions(nodes, {});
|
|
83
|
+
assert.deepEqual(first, second);
|
|
84
|
+
});
|