@graphty/graph-io 0.3.48 → 0.3.50

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.
Files changed (600) hide show
  1. package/dist/chunks/{approximations-C2riR4NT.js → approximations-BRXsll0T.js} +2 -2
  2. package/dist/chunks/{approximations-C2riR4NT.js.map → approximations-BRXsll0T.js.map} +1 -1
  3. package/dist/chunks/bends-BGXjv_U4.js +129 -0
  4. package/dist/chunks/bends-BGXjv_U4.js.map +1 -0
  5. package/dist/chunks/{block-Cu22ewuS.js → block-yl0f_qLO.js} +931 -164
  6. package/dist/chunks/block-yl0f_qLO.js.map +1 -0
  7. package/dist/chunks/choose-CczZJaX_.js +717 -0
  8. package/dist/chunks/choose-CczZJaX_.js.map +1 -0
  9. package/dist/chunks/{codes-CV_Bhcyi.js → codes-BE2xir2k.js} +6 -2
  10. package/dist/chunks/codes-BE2xir2k.js.map +1 -0
  11. package/dist/chunks/{constants-D0LqcsLJ.js → constants-CKkYLPtt.js} +2 -2
  12. package/dist/chunks/{constants-D0LqcsLJ.js.map → constants-CKkYLPtt.js.map} +1 -1
  13. package/dist/chunks/{context-cgmmOxZ2.js → context-DizmppHy.js} +5 -4
  14. package/dist/chunks/{context-cgmmOxZ2.js.map → context-DizmppHy.js.map} +1 -1
  15. package/dist/chunks/{convert-BcQ8_jMo.js → convert-Ct2zelbQ.js} +7 -2
  16. package/dist/chunks/convert-Ct2zelbQ.js.map +1 -0
  17. package/dist/chunks/{credentials-BiQsnMTb.js → credentials-CmvJXYeH.js} +67 -16
  18. package/dist/chunks/credentials-CmvJXYeH.js.map +1 -0
  19. package/dist/chunks/{cx-export-BEeF6ymo.js → cx-export-Bt6NoqTQ.js} +3 -3
  20. package/dist/chunks/{cx-export-BEeF6ymo.js.map → cx-export-Bt6NoqTQ.js.map} +1 -1
  21. package/dist/chunks/descriptor-B28U25x4.js +60 -0
  22. package/dist/chunks/descriptor-B28U25x4.js.map +1 -0
  23. package/dist/chunks/{descriptor-BJ-W0Jd-.js → descriptor-Dhez_Cew.js} +11 -2
  24. package/dist/chunks/descriptor-Dhez_Cew.js.map +1 -0
  25. package/dist/chunks/{emit-CD8xnY8s.js → emit-7Jkpg0yd.js} +1788 -133
  26. package/dist/chunks/emit-7Jkpg0yd.js.map +1 -0
  27. package/dist/chunks/{escape-DH2Egt-l.js → escape-CuCIzNMB.js} +8 -8
  28. package/dist/chunks/{escape-DH2Egt-l.js.map → escape-CuCIzNMB.js.map} +1 -1
  29. package/dist/chunks/{export-CZ7VRm4I.js → export-Cha4dRFz.js} +45 -387
  30. package/dist/chunks/export-Cha4dRFz.js.map +1 -0
  31. package/dist/chunks/{exporter-ChjL5fmP.js → exporter-1Ajud15J.js} +9 -8
  32. package/dist/chunks/{exporter-ChjL5fmP.js.map → exporter-1Ajud15J.js.map} +1 -1
  33. package/dist/chunks/{exporter-BIdaoIgh.js → exporter-B6yvL3wy.js} +7 -7
  34. package/dist/chunks/{exporter-BIdaoIgh.js.map → exporter-B6yvL3wy.js.map} +1 -1
  35. package/dist/chunks/{exporter-Dcw7Ldtq.js → exporter-BP6otMO6.js} +6 -6
  36. package/dist/chunks/{exporter-Dcw7Ldtq.js.map → exporter-BP6otMO6.js.map} +1 -1
  37. package/dist/chunks/{exporter-DtVU80Fa.js → exporter-CA5q8ROP.js} +9 -8
  38. package/dist/chunks/{exporter-DtVU80Fa.js.map → exporter-CA5q8ROP.js.map} +1 -1
  39. package/dist/chunks/{exporter-bxxjOrDZ.js → exporter-CQQZFJ2v.js} +1284 -156
  40. package/dist/chunks/exporter-CQQZFJ2v.js.map +1 -0
  41. package/dist/chunks/format-BXiAb-1h.js +408 -0
  42. package/dist/chunks/format-BXiAb-1h.js.map +1 -0
  43. package/dist/chunks/{generic-CP6YD34u.js → generic-Dc21MAdg.js} +387 -31
  44. package/dist/chunks/generic-Dc21MAdg.js.map +1 -0
  45. package/dist/chunks/{importer-B87_fmVx.js → importer-C6NVpCXp.js} +327 -84
  46. package/dist/chunks/importer-C6NVpCXp.js.map +1 -0
  47. package/dist/chunks/{importer-DPZDN40-.js → importer-CKFe4q54.js} +25 -17
  48. package/dist/chunks/importer-CKFe4q54.js.map +1 -0
  49. package/dist/chunks/{importer-Bji2MeWr.js → importer-CKbUbsJA.js} +13 -10
  50. package/dist/chunks/{importer-Bji2MeWr.js.map → importer-CKbUbsJA.js.map} +1 -1
  51. package/dist/chunks/{importer-CB_zV5_L.js → importer-ClCsdgTJ.js} +208 -1104
  52. package/dist/chunks/importer-ClCsdgTJ.js.map +1 -0
  53. package/dist/chunks/{importer-e3huMZbm.js → importer-CyVZTrf0.js} +14 -10
  54. package/dist/chunks/importer-CyVZTrf0.js.map +1 -0
  55. package/dist/chunks/{importer-CVT-JyDE.js → importer-DgG4v1Ms.js} +1537 -355
  56. package/dist/chunks/importer-DgG4v1Ms.js.map +1 -0
  57. package/dist/chunks/{importer-Cyy6UseH.js → importer-asFUrIsj.js} +66 -13
  58. package/dist/chunks/importer-asFUrIsj.js.map +1 -0
  59. package/dist/chunks/{input-Ccg9I22A.js → input-CAxt_aV-.js} +4 -4
  60. package/dist/chunks/{input-Ccg9I22A.js.map → input-CAxt_aV-.js.map} +1 -1
  61. package/dist/chunks/{json-elements-By172SVq.js → json-elements-Cq76WjnZ.js} +4 -4
  62. package/dist/chunks/{json-elements-By172SVq.js.map → json-elements-Cq76WjnZ.js.map} +1 -1
  63. package/dist/chunks/{lexicon-u2SI5xcx.js → lexicon-IVRq2Uoo.js} +282 -74
  64. package/dist/chunks/lexicon-IVRq2Uoo.js.map +1 -0
  65. package/dist/chunks/{ontology-export-BwgMyuF0.js → ontology-export-OMo91mb0.js} +4 -4
  66. package/dist/chunks/{ontology-export-BwgMyuF0.js.map → ontology-export-OMo91mb0.js.map} +1 -1
  67. package/dist/chunks/{records-Cgvxz-Vx.js → records-NJiYvekn.js} +2 -2
  68. package/dist/chunks/{records-Cgvxz-Vx.js.map → records-NJiYvekn.js.map} +1 -1
  69. package/dist/chunks/{report-CgdsL9Hb.js → report-CwMfYa9J.js} +23 -23
  70. package/dist/chunks/{report-CgdsL9Hb.js.map → report-CwMfYa9J.js.map} +1 -1
  71. package/dist/chunks/round-trip-Cy3_SHRZ.js +79 -0
  72. package/dist/chunks/round-trip-Cy3_SHRZ.js.map +1 -0
  73. package/dist/chunks/{sigv4-LkAzsLAu.js → sigv4-BXwVbza3.js} +5 -5
  74. package/dist/chunks/sigv4-BXwVbza3.js.map +1 -0
  75. package/dist/chunks/{styles-CaABMoO1.js → styles-CAVmBLgp.js} +1237 -138
  76. package/dist/chunks/styles-CAVmBLgp.js.map +1 -0
  77. package/dist/chunks/styles-CPczw1Jz.js +1928 -0
  78. package/dist/chunks/styles-CPczw1Jz.js.map +1 -0
  79. package/dist/chunks/styles-boK6Ln54.js +2847 -0
  80. package/dist/chunks/styles-boK6Ln54.js.map +1 -0
  81. package/dist/chunks/{styles-yed-Cs1nmrjn.js → styles-yed-XeFBQ6VS.js} +506 -175
  82. package/dist/chunks/styles-yed-XeFBQ6VS.js.map +1 -0
  83. package/dist/chunks/{text-BuKQVMz2.js → text-ystE2bYi.js} +2 -2
  84. package/dist/chunks/{text-BuKQVMz2.js.map → text-ystE2bYi.js.map} +1 -1
  85. package/dist/csv.js +7 -7
  86. package/dist/cx.js +2 -2
  87. package/dist/cx2.js +2 -2
  88. package/dist/cys.js +1 -1
  89. package/dist/dot.js +8 -4
  90. package/dist/dot.js.map +1 -1
  91. package/dist/gexf.js +42 -12
  92. package/dist/gexf.js.map +1 -1
  93. package/dist/gml.js +848 -194
  94. package/dist/gml.js.map +1 -1
  95. package/dist/graph-io.js +230 -193
  96. package/dist/graph-io.js.map +1 -1
  97. package/dist/graphml.js +3 -3
  98. package/dist/json.js +3 -2
  99. package/dist/neo4j.js +6 -6
  100. package/dist/obo.js +1 -1
  101. package/dist/pajek.js +1 -1
  102. package/dist/positions.js +45 -3
  103. package/dist/positions.js.map +1 -1
  104. package/dist/sources-cypher.js +141 -23
  105. package/dist/sources-cypher.js.map +1 -1
  106. package/dist/sources-ndex.js +2 -2
  107. package/dist/sources-rest.js +32 -6
  108. package/dist/sources-rest.js.map +1 -1
  109. package/dist/sources-sparql.d.ts +1 -0
  110. package/dist/sources-sparql.js +439 -0
  111. package/dist/sources-sparql.js.map +1 -0
  112. package/dist/src/formats/cx/importer.d.ts.map +1 -1
  113. package/dist/src/formats/cx/importer.js +5 -1
  114. package/dist/src/formats/cx/importer.js.map +1 -1
  115. package/dist/src/formats/cx/styles.d.ts +24 -1
  116. package/dist/src/formats/cx/styles.d.ts.map +1 -1
  117. package/dist/src/formats/cx/styles.js +117 -11
  118. package/dist/src/formats/cx/styles.js.map +1 -1
  119. package/dist/src/formats/cx2/importer.d.ts.map +1 -1
  120. package/dist/src/formats/cx2/importer.js +1 -0
  121. package/dist/src/formats/cx2/importer.js.map +1 -1
  122. package/dist/src/formats/cx2/styles.d.ts.map +1 -1
  123. package/dist/src/formats/cx2/styles.js +3 -1
  124. package/dist/src/formats/cx2/styles.js.map +1 -1
  125. package/dist/src/formats/cys/importer.d.ts.map +1 -1
  126. package/dist/src/formats/cys/importer.js +1 -0
  127. package/dist/src/formats/cys/importer.js.map +1 -1
  128. package/dist/src/formats/cys/styles.d.ts +2 -0
  129. package/dist/src/formats/cys/styles.d.ts.map +1 -1
  130. package/dist/src/formats/cys/styles.js +15 -7
  131. package/dist/src/formats/cys/styles.js.map +1 -1
  132. package/dist/src/formats/dot/drawing.d.ts +98 -0
  133. package/dist/src/formats/dot/drawing.d.ts.map +1 -0
  134. package/dist/src/formats/dot/drawing.js +734 -0
  135. package/dist/src/formats/dot/drawing.js.map +1 -0
  136. package/dist/src/formats/dot/exporter.d.ts.map +1 -1
  137. package/dist/src/formats/dot/exporter.js +34 -2
  138. package/dist/src/formats/dot/exporter.js.map +1 -1
  139. package/dist/src/formats/dot/importer.d.ts +6 -1
  140. package/dist/src/formats/dot/importer.d.ts.map +1 -1
  141. package/dist/src/formats/dot/importer.js +170 -33
  142. package/dist/src/formats/dot/importer.js.map +1 -1
  143. package/dist/src/formats/dot/index.d.ts +3 -1
  144. package/dist/src/formats/dot/index.d.ts.map +1 -1
  145. package/dist/src/formats/dot/index.js +2 -1
  146. package/dist/src/formats/dot/index.js.map +1 -1
  147. package/dist/src/formats/dot/label-places.d.ts +41 -0
  148. package/dist/src/formats/dot/label-places.d.ts.map +1 -0
  149. package/dist/src/formats/dot/label-places.js +185 -0
  150. package/dist/src/formats/dot/label-places.js.map +1 -0
  151. package/dist/src/formats/dot/names.d.ts +13 -0
  152. package/dist/src/formats/dot/names.d.ts.map +1 -1
  153. package/dist/src/formats/dot/names.js +13 -0
  154. package/dist/src/formats/dot/names.js.map +1 -1
  155. package/dist/src/formats/dot/record.d.ts +50 -0
  156. package/dist/src/formats/dot/record.d.ts.map +1 -0
  157. package/dist/src/formats/dot/record.js +275 -0
  158. package/dist/src/formats/dot/record.js.map +1 -0
  159. package/dist/src/formats/dot/styles.d.ts +72 -2
  160. package/dist/src/formats/dot/styles.d.ts.map +1 -1
  161. package/dist/src/formats/dot/styles.js +1500 -119
  162. package/dist/src/formats/dot/styles.js.map +1 -1
  163. package/dist/src/formats/gexf/dialog-metrics.d.ts +15 -0
  164. package/dist/src/formats/gexf/dialog-metrics.d.ts.map +1 -0
  165. package/dist/src/formats/gexf/dialog-metrics.js +59 -0
  166. package/dist/src/formats/gexf/dialog-metrics.js.map +1 -0
  167. package/dist/src/formats/gexf/importer.d.ts +7 -0
  168. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  169. package/dist/src/formats/gexf/importer.js +41 -5
  170. package/dist/src/formats/gexf/importer.js.map +1 -1
  171. package/dist/src/formats/gexf/preview.d.ts +77 -0
  172. package/dist/src/formats/gexf/preview.d.ts.map +1 -0
  173. package/dist/src/formats/gexf/preview.js +191 -0
  174. package/dist/src/formats/gexf/preview.js.map +1 -0
  175. package/dist/src/formats/gexf/schema.d.ts.map +1 -1
  176. package/dist/src/formats/gexf/schema.js +2 -1
  177. package/dist/src/formats/gexf/schema.js.map +1 -1
  178. package/dist/src/formats/gexf/styles.d.ts +58 -31
  179. package/dist/src/formats/gexf/styles.d.ts.map +1 -1
  180. package/dist/src/formats/gexf/styles.js +848 -225
  181. package/dist/src/formats/gexf/styles.js.map +1 -1
  182. package/dist/src/formats/gml/importer.d.ts +5 -2
  183. package/dist/src/formats/gml/importer.d.ts.map +1 -1
  184. package/dist/src/formats/gml/importer.js +258 -71
  185. package/dist/src/formats/gml/importer.js.map +1 -1
  186. package/dist/src/formats/gml/index.d.ts +1 -1
  187. package/dist/src/formats/gml/index.d.ts.map +1 -1
  188. package/dist/src/formats/gml/index.js +1 -1
  189. package/dist/src/formats/gml/index.js.map +1 -1
  190. package/dist/src/formats/gml/positions.d.ts +7 -0
  191. package/dist/src/formats/gml/positions.d.ts.map +1 -1
  192. package/dist/src/formats/gml/positions.js +29 -7
  193. package/dist/src/formats/gml/positions.js.map +1 -1
  194. package/dist/src/formats/gml/styles.d.ts.map +1 -1
  195. package/dist/src/formats/gml/styles.js +754 -102
  196. package/dist/src/formats/gml/styles.js.map +1 -1
  197. package/dist/src/formats/graphml/styles-plain.d.ts.map +1 -1
  198. package/dist/src/formats/graphml/styles-plain.js +6 -10
  199. package/dist/src/formats/graphml/styles-plain.js.map +1 -1
  200. package/dist/src/formats/graphml/styles-yed.d.ts +21 -15
  201. package/dist/src/formats/graphml/styles-yed.d.ts.map +1 -1
  202. package/dist/src/formats/graphml/styles-yed.js +695 -164
  203. package/dist/src/formats/graphml/styles-yed.js.map +1 -1
  204. package/dist/src/formats/graphml/styles-yfiles3.d.ts +1 -1
  205. package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -1
  206. package/dist/src/formats/graphml/styles-yfiles3.js +431 -64
  207. package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -1
  208. package/dist/src/formats/graphml/yfiles.d.ts +3 -3
  209. package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
  210. package/dist/src/formats/graphml/yfiles.js +16 -6
  211. package/dist/src/formats/graphml/yfiles.js.map +1 -1
  212. package/dist/src/formats/json/dialect.d.ts +3 -2
  213. package/dist/src/formats/json/dialect.d.ts.map +1 -1
  214. package/dist/src/formats/json/dialect.js +39 -5
  215. package/dist/src/formats/json/dialect.js.map +1 -1
  216. package/dist/src/formats/json/importer.d.ts +13 -2
  217. package/dist/src/formats/json/importer.d.ts.map +1 -1
  218. package/dist/src/formats/json/importer.js +42 -6
  219. package/dist/src/formats/json/importer.js.map +1 -1
  220. package/dist/src/formats/json/index.d.ts +1 -1
  221. package/dist/src/formats/json/index.d.ts.map +1 -1
  222. package/dist/src/formats/json/index.js +1 -1
  223. package/dist/src/formats/json/index.js.map +1 -1
  224. package/dist/src/formats/json/positions.d.ts +5 -2
  225. package/dist/src/formats/json/positions.d.ts.map +1 -1
  226. package/dist/src/formats/json/positions.js +20 -4
  227. package/dist/src/formats/json/positions.js.map +1 -1
  228. package/dist/src/formats/json/styles.d.ts +12 -1
  229. package/dist/src/formats/json/styles.d.ts.map +1 -1
  230. package/dist/src/formats/json/styles.js +1843 -136
  231. package/dist/src/formats/json/styles.js.map +1 -1
  232. package/dist/src/formats/pajek/draw.d.ts +104 -0
  233. package/dist/src/formats/pajek/draw.d.ts.map +1 -0
  234. package/dist/src/formats/pajek/draw.js +382 -0
  235. package/dist/src/formats/pajek/draw.js.map +1 -0
  236. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  237. package/dist/src/formats/pajek/exporter.js +70 -51
  238. package/dist/src/formats/pajek/exporter.js.map +1 -1
  239. package/dist/src/formats/pajek/importer.d.ts +9 -0
  240. package/dist/src/formats/pajek/importer.d.ts.map +1 -1
  241. package/dist/src/formats/pajek/importer.js +233 -102
  242. package/dist/src/formats/pajek/importer.js.map +1 -1
  243. package/dist/src/formats/pajek/index.d.ts +1 -0
  244. package/dist/src/formats/pajek/index.d.ts.map +1 -1
  245. package/dist/src/formats/pajek/index.js.map +1 -1
  246. package/dist/src/formats/pajek/styles.d.ts +15 -7
  247. package/dist/src/formats/pajek/styles.d.ts.map +1 -1
  248. package/dist/src/formats/pajek/styles.js +1395 -114
  249. package/dist/src/formats/pajek/styles.js.map +1 -1
  250. package/dist/src/formats/pajek/syntax.d.ts +10 -1
  251. package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
  252. package/dist/src/formats/pajek/syntax.js +26 -2
  253. package/dist/src/formats/pajek/syntax.js.map +1 -1
  254. package/dist/src/formats/xgmml/emit.d.ts +1 -0
  255. package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
  256. package/dist/src/formats/xgmml/emit.js +65 -5
  257. package/dist/src/formats/xgmml/emit.js.map +1 -1
  258. package/dist/src/formats/xgmml/styles.d.ts +23 -3
  259. package/dist/src/formats/xgmml/styles.d.ts.map +1 -1
  260. package/dist/src/formats/xgmml/styles.js +277 -60
  261. package/dist/src/formats/xgmml/styles.js.map +1 -1
  262. package/dist/src/index.d.ts +6 -5
  263. package/dist/src/index.d.ts.map +1 -1
  264. package/dist/src/index.js +6 -5
  265. package/dist/src/index.js.map +1 -1
  266. package/dist/src/positions/convert.d.ts.map +1 -1
  267. package/dist/src/positions/convert.js +11 -1
  268. package/dist/src/positions/convert.js.map +1 -1
  269. package/dist/src/positions/extract.d.ts.map +1 -1
  270. package/dist/src/positions/extract.js +53 -1
  271. package/dist/src/positions/extract.js.map +1 -1
  272. package/dist/src/positions/index.d.ts +1 -1
  273. package/dist/src/positions/index.d.ts.map +1 -1
  274. package/dist/src/positions/types.d.ts +59 -0
  275. package/dist/src/positions/types.d.ts.map +1 -1
  276. package/dist/src/sources/builtins.d.ts +1 -1
  277. package/dist/src/sources/builtins.d.ts.map +1 -1
  278. package/dist/src/sources/builtins.js +8 -2
  279. package/dist/src/sources/builtins.js.map +1 -1
  280. package/dist/src/sources/context.d.ts.map +1 -1
  281. package/dist/src/sources/context.js +1 -0
  282. package/dist/src/sources/context.js.map +1 -1
  283. package/dist/src/sources/credentials.d.ts +50 -1
  284. package/dist/src/sources/credentials.d.ts.map +1 -1
  285. package/dist/src/sources/credentials.js +69 -0
  286. package/dist/src/sources/credentials.js.map +1 -1
  287. package/dist/src/sources/cypher/descriptor.d.ts.map +1 -1
  288. package/dist/src/sources/cypher/descriptor.js +5 -0
  289. package/dist/src/sources/cypher/descriptor.js.map +1 -1
  290. package/dist/src/sources/cypher/index.d.ts.map +1 -1
  291. package/dist/src/sources/cypher/index.js +125 -18
  292. package/dist/src/sources/cypher/index.js.map +1 -1
  293. package/dist/src/sources/cypher/params.d.ts +1 -1
  294. package/dist/src/sources/cypher/params.d.ts.map +1 -1
  295. package/dist/src/sources/cypher/query-builder.d.ts +17 -0
  296. package/dist/src/sources/cypher/query-builder.d.ts.map +1 -1
  297. package/dist/src/sources/cypher/query-builder.js +31 -0
  298. package/dist/src/sources/cypher/query-builder.js.map +1 -1
  299. package/dist/src/sources/ids.d.ts +5 -0
  300. package/dist/src/sources/ids.d.ts.map +1 -1
  301. package/dist/src/sources/ids.js +5 -0
  302. package/dist/src/sources/ids.js.map +1 -1
  303. package/dist/src/sources/params.d.ts +3 -0
  304. package/dist/src/sources/params.d.ts.map +1 -1
  305. package/dist/src/sources/rest/descriptor.d.ts.map +1 -1
  306. package/dist/src/sources/rest/descriptor.js +9 -0
  307. package/dist/src/sources/rest/descriptor.js.map +1 -1
  308. package/dist/src/sources/rest/openalex.d.ts +4 -2
  309. package/dist/src/sources/rest/openalex.d.ts.map +1 -1
  310. package/dist/src/sources/rest/openalex.js +4 -2
  311. package/dist/src/sources/rest/openalex.js.map +1 -1
  312. package/dist/src/sources/rest/source.d.ts.map +1 -1
  313. package/dist/src/sources/rest/source.js +38 -0
  314. package/dist/src/sources/rest/source.js.map +1 -1
  315. package/dist/src/sources/run.d.ts +2 -0
  316. package/dist/src/sources/run.d.ts.map +1 -1
  317. package/dist/src/sources/run.js +30 -3
  318. package/dist/src/sources/run.js.map +1 -1
  319. package/dist/src/sources/sigv4.d.ts +5 -3
  320. package/dist/src/sources/sigv4.d.ts.map +1 -1
  321. package/dist/src/sources/sigv4.js +6 -4
  322. package/dist/src/sources/sigv4.js.map +1 -1
  323. package/dist/src/sources/sparql/descriptor.d.ts +10 -0
  324. package/dist/src/sources/sparql/descriptor.d.ts.map +1 -0
  325. package/dist/src/sources/sparql/descriptor.js +64 -0
  326. package/dist/src/sources/sparql/descriptor.js.map +1 -0
  327. package/dist/src/sources/sparql/index.d.ts +28 -0
  328. package/dist/src/sources/sparql/index.d.ts.map +1 -0
  329. package/dist/src/sources/sparql/index.js +327 -0
  330. package/dist/src/sources/sparql/index.js.map +1 -0
  331. package/dist/src/sources/sparql/ntriples.d.ts +69 -0
  332. package/dist/src/sources/sparql/ntriples.d.ts.map +1 -0
  333. package/dist/src/sources/sparql/ntriples.js +180 -0
  334. package/dist/src/sources/sparql/ntriples.js.map +1 -0
  335. package/dist/src/sources/sparql/params.d.ts +27 -0
  336. package/dist/src/sources/sparql/params.d.ts.map +1 -0
  337. package/dist/src/sources/sparql/params.js +5 -0
  338. package/dist/src/sources/sparql/params.js.map +1 -0
  339. package/dist/src/sources/sparql/query.d.ts +73 -0
  340. package/dist/src/sources/sparql/query.d.ts.map +1 -0
  341. package/dist/src/sources/sparql/query.js +164 -0
  342. package/dist/src/sources/sparql/query.js.map +1 -0
  343. package/dist/src/sources/types.d.ts +60 -0
  344. package/dist/src/sources/types.d.ts.map +1 -1
  345. package/dist/src/styles/annotations.d.ts +61 -5
  346. package/dist/src/styles/annotations.d.ts.map +1 -1
  347. package/dist/src/styles/annotations.js +130 -22
  348. package/dist/src/styles/annotations.js.map +1 -1
  349. package/dist/src/styles/canon/bends.d.ts +46 -0
  350. package/dist/src/styles/canon/bends.d.ts.map +1 -1
  351. package/dist/src/styles/canon/bends.js +123 -0
  352. package/dist/src/styles/canon/bends.js.map +1 -1
  353. package/dist/src/styles/canon/color-tables.d.ts +6 -1
  354. package/dist/src/styles/canon/color-tables.d.ts.map +1 -1
  355. package/dist/src/styles/canon/color-tables.js +10 -4
  356. package/dist/src/styles/canon/color-tables.js.map +1 -1
  357. package/dist/src/styles/canon/color.d.ts +3 -1
  358. package/dist/src/styles/canon/color.d.ts.map +1 -1
  359. package/dist/src/styles/canon/color.js +103 -3
  360. package/dist/src/styles/canon/color.js.map +1 -1
  361. package/dist/src/styles/canon/enums.d.ts +2 -2
  362. package/dist/src/styles/canon/enums.d.ts.map +1 -1
  363. package/dist/src/styles/canon/enums.js +89 -7
  364. package/dist/src/styles/canon/enums.js.map +1 -1
  365. package/dist/src/styles/canon/graphviz.d.ts +12 -3
  366. package/dist/src/styles/canon/graphviz.d.ts.map +1 -1
  367. package/dist/src/styles/canon/graphviz.js +57 -9
  368. package/dist/src/styles/canon/graphviz.js.map +1 -1
  369. package/dist/src/styles/canon/number.d.ts +8 -0
  370. package/dist/src/styles/canon/number.d.ts.map +1 -1
  371. package/dist/src/styles/canon/number.js +18 -0
  372. package/dist/src/styles/canon/number.js.map +1 -1
  373. package/dist/src/styles/canon/text.d.ts +29 -2
  374. package/dist/src/styles/canon/text.d.ts.map +1 -1
  375. package/dist/src/styles/canon/text.js +156 -16
  376. package/dist/src/styles/canon/text.js.map +1 -1
  377. package/dist/src/styles/codes.d.ts +2 -0
  378. package/dist/src/styles/codes.d.ts.map +1 -1
  379. package/dist/src/styles/codes.js +2 -0
  380. package/dist/src/styles/codes.js.map +1 -1
  381. package/dist/src/styles/cyjs/cascade.d.ts.map +1 -1
  382. package/dist/src/styles/cyjs/cascade.js +1139 -79
  383. package/dist/src/styles/cyjs/cascade.js.map +1 -1
  384. package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -1
  385. package/dist/src/styles/cyjs/defaults-table.js +4 -0
  386. package/dist/src/styles/cyjs/defaults-table.js.map +1 -1
  387. package/dist/src/styles/cyjs/emit.d.ts.map +1 -1
  388. package/dist/src/styles/cyjs/emit.js +232 -10
  389. package/dist/src/styles/cyjs/emit.js.map +1 -1
  390. package/dist/src/styles/cyjs/face.d.ts +20 -0
  391. package/dist/src/styles/cyjs/face.d.ts.map +1 -0
  392. package/dist/src/styles/cyjs/face.js +332 -0
  393. package/dist/src/styles/cyjs/face.js.map +1 -0
  394. package/dist/src/styles/cyjs/mappers.d.ts +44 -1
  395. package/dist/src/styles/cyjs/mappers.d.ts.map +1 -1
  396. package/dist/src/styles/cyjs/mappers.js +475 -26
  397. package/dist/src/styles/cyjs/mappers.js.map +1 -1
  398. package/dist/src/styles/cyjs/routes.d.ts +53 -0
  399. package/dist/src/styles/cyjs/routes.d.ts.map +1 -0
  400. package/dist/src/styles/cyjs/routes.js +360 -0
  401. package/dist/src/styles/cyjs/routes.js.map +1 -0
  402. package/dist/src/styles/cytoscape/charts.d.ts +23 -0
  403. package/dist/src/styles/cytoscape/charts.d.ts.map +1 -0
  404. package/dist/src/styles/cytoscape/charts.js +560 -0
  405. package/dist/src/styles/cytoscape/charts.js.map +1 -0
  406. package/dist/src/styles/cytoscape/emit.d.ts.map +1 -1
  407. package/dist/src/styles/cytoscape/emit.js +156 -48
  408. package/dist/src/styles/cytoscape/emit.js.map +1 -1
  409. package/dist/src/styles/cytoscape/lexicon.d.ts +91 -3
  410. package/dist/src/styles/cytoscape/lexicon.d.ts.map +1 -1
  411. package/dist/src/styles/cytoscape/lexicon.js +387 -77
  412. package/dist/src/styles/cytoscape/lexicon.js.map +1 -1
  413. package/dist/src/styles/cytoscape/mappings.d.ts +30 -0
  414. package/dist/src/styles/cytoscape/mappings.d.ts.map +1 -1
  415. package/dist/src/styles/cytoscape/mappings.js +92 -0
  416. package/dist/src/styles/cytoscape/mappings.js.map +1 -1
  417. package/dist/src/styles/cytoscape/resolve.d.ts +42 -2
  418. package/dist/src/styles/cytoscape/resolve.d.ts.map +1 -1
  419. package/dist/src/styles/cytoscape/resolve.js +623 -17
  420. package/dist/src/styles/cytoscape/resolve.js.map +1 -1
  421. package/dist/src/styles/defaults.d.ts +46 -26
  422. package/dist/src/styles/defaults.d.ts.map +1 -1
  423. package/dist/src/styles/defaults.js +308 -71
  424. package/dist/src/styles/defaults.js.map +1 -1
  425. package/dist/src/styles/index.d.ts +4 -3
  426. package/dist/src/styles/index.d.ts.map +1 -1
  427. package/dist/src/styles/index.js +3 -2
  428. package/dist/src/styles/index.js.map +1 -1
  429. package/dist/src/styles/internal.d.ts +1 -0
  430. package/dist/src/styles/internal.d.ts.map +1 -1
  431. package/dist/src/styles/internal.js +1 -0
  432. package/dist/src/styles/internal.js.map +1 -1
  433. package/dist/src/styles/io/generic.d.ts.map +1 -1
  434. package/dist/src/styles/io/generic.js +56 -16
  435. package/dist/src/styles/io/generic.js.map +1 -1
  436. package/dist/src/styles/io/round-trip.d.ts +56 -0
  437. package/dist/src/styles/io/round-trip.d.ts.map +1 -0
  438. package/dist/src/styles/io/round-trip.js +138 -0
  439. package/dist/src/styles/io/round-trip.js.map +1 -0
  440. package/dist/src/styles/io/writer.d.ts.map +1 -1
  441. package/dist/src/styles/io/writer.js +6 -1
  442. package/dist/src/styles/io/writer.js.map +1 -1
  443. package/dist/src/styles/notes.d.ts.map +1 -1
  444. package/dist/src/styles/notes.js +1 -0
  445. package/dist/src/styles/notes.js.map +1 -1
  446. package/dist/src/styles/plan/features.d.ts +1 -1
  447. package/dist/src/styles/plan/features.d.ts.map +1 -1
  448. package/dist/src/styles/plan/features.js +22 -2
  449. package/dist/src/styles/plan/features.js.map +1 -1
  450. package/dist/src/styles/plan/index.d.ts +1 -1
  451. package/dist/src/styles/plan/index.d.ts.map +1 -1
  452. package/dist/src/styles/plan/index.js +1 -1
  453. package/dist/src/styles/plan/index.js.map +1 -1
  454. package/dist/src/styles/plan/plan.d.ts.map +1 -1
  455. package/dist/src/styles/plan/plan.js +61 -4
  456. package/dist/src/styles/plan/plan.js.map +1 -1
  457. package/dist/src/styles/plan/resolve.d.ts +11 -0
  458. package/dist/src/styles/plan/resolve.d.ts.map +1 -1
  459. package/dist/src/styles/plan/resolve.js +40 -4
  460. package/dist/src/styles/plan/resolve.js.map +1 -1
  461. package/dist/src/styles/slots.d.ts +14 -0
  462. package/dist/src/styles/slots.d.ts.map +1 -0
  463. package/dist/src/styles/slots.js +20 -0
  464. package/dist/src/styles/slots.js.map +1 -0
  465. package/dist/src/styles/types.d.ts +687 -6
  466. package/dist/src/styles/types.d.ts.map +1 -1
  467. package/dist/src/styles/vocabulary.d.ts +3 -2
  468. package/dist/src/styles/vocabulary.d.ts.map +1 -1
  469. package/dist/src/styles/vocabulary.js +224 -0
  470. package/dist/src/styles/vocabulary.js.map +1 -1
  471. package/dist/styles.js +30 -239
  472. package/dist/styles.js.map +1 -1
  473. package/dist/xgmml.js +3 -3
  474. package/package.json +27 -7
  475. package/src/formats/cx/importer.ts +6 -1
  476. package/src/formats/cx/styles.ts +143 -9
  477. package/src/formats/cx2/importer.ts +1 -0
  478. package/src/formats/cx2/styles.ts +3 -1
  479. package/src/formats/cys/importer.ts +1 -0
  480. package/src/formats/cys/styles.ts +27 -5
  481. package/src/formats/dot/drawing.ts +799 -0
  482. package/src/formats/dot/exporter.ts +37 -1
  483. package/src/formats/dot/importer.ts +192 -36
  484. package/src/formats/dot/index.ts +3 -1
  485. package/src/formats/dot/label-places.ts +241 -0
  486. package/src/formats/dot/names.ts +15 -0
  487. package/src/formats/dot/record.ts +336 -0
  488. package/src/formats/dot/styles.ts +1702 -149
  489. package/src/formats/gexf/dialog-metrics.ts +61 -0
  490. package/src/formats/gexf/importer.ts +50 -5
  491. package/src/formats/gexf/preview.ts +217 -0
  492. package/src/formats/gexf/schema.ts +2 -1
  493. package/src/formats/gexf/styles.ts +1099 -245
  494. package/src/formats/gml/importer.ts +291 -76
  495. package/src/formats/gml/index.ts +1 -1
  496. package/src/formats/gml/positions.ts +29 -7
  497. package/src/formats/gml/styles.ts +852 -110
  498. package/src/formats/graphml/styles-plain.ts +6 -15
  499. package/src/formats/graphml/styles-yed.ts +765 -176
  500. package/src/formats/graphml/styles-yfiles3.ts +487 -63
  501. package/src/formats/graphml/yfiles.ts +19 -6
  502. package/src/formats/json/dialect.ts +41 -5
  503. package/src/formats/json/importer.ts +59 -7
  504. package/src/formats/json/index.ts +1 -0
  505. package/src/formats/json/positions.ts +22 -5
  506. package/src/formats/json/styles.ts +2123 -171
  507. package/src/formats/pajek/draw.ts +469 -0
  508. package/src/formats/pajek/exporter.ts +81 -49
  509. package/src/formats/pajek/importer.ts +278 -103
  510. package/src/formats/pajek/index.ts +1 -0
  511. package/src/formats/pajek/styles.ts +1506 -133
  512. package/src/formats/pajek/syntax.ts +27 -2
  513. package/src/formats/xgmml/emit.ts +73 -5
  514. package/src/formats/xgmml/styles.ts +330 -71
  515. package/src/index.ts +12 -1
  516. package/src/positions/convert.ts +12 -1
  517. package/src/positions/extract.ts +55 -1
  518. package/src/positions/index.ts +1 -0
  519. package/src/positions/types.ts +60 -0
  520. package/src/sources/builtins.ts +8 -1
  521. package/src/sources/context.ts +1 -0
  522. package/src/sources/credentials.ts +90 -1
  523. package/src/sources/cypher/descriptor.ts +7 -1
  524. package/src/sources/cypher/index.ts +149 -18
  525. package/src/sources/cypher/params.ts +1 -1
  526. package/src/sources/cypher/query-builder.ts +33 -0
  527. package/src/sources/ids.ts +6 -0
  528. package/src/sources/params.ts +3 -0
  529. package/src/sources/rest/descriptor.ts +9 -0
  530. package/src/sources/rest/openalex.ts +4 -2
  531. package/src/sources/rest/source.ts +41 -0
  532. package/src/sources/run.ts +35 -3
  533. package/src/sources/sigv4.ts +6 -4
  534. package/src/sources/sparql/descriptor.ts +67 -0
  535. package/src/sources/sparql/index.ts +371 -0
  536. package/src/sources/sparql/ntriples.ts +219 -0
  537. package/src/sources/sparql/params.ts +28 -0
  538. package/src/sources/sparql/query.ts +216 -0
  539. package/src/sources/types.ts +68 -0
  540. package/src/styles/annotations.ts +201 -27
  541. package/src/styles/canon/bends.ts +137 -0
  542. package/src/styles/canon/color-tables.ts +10 -4
  543. package/src/styles/canon/color.ts +105 -3
  544. package/src/styles/canon/enums.ts +107 -8
  545. package/src/styles/canon/graphviz.ts +71 -10
  546. package/src/styles/canon/number.ts +19 -0
  547. package/src/styles/canon/text.ts +187 -16
  548. package/src/styles/codes.ts +2 -0
  549. package/src/styles/cyjs/cascade.ts +1303 -84
  550. package/src/styles/cyjs/defaults-table.ts +4 -0
  551. package/src/styles/cyjs/emit.ts +268 -14
  552. package/src/styles/cyjs/face.ts +372 -0
  553. package/src/styles/cyjs/mappers.ts +506 -27
  554. package/src/styles/cyjs/routes.ts +432 -0
  555. package/src/styles/cytoscape/charts.ts +684 -0
  556. package/src/styles/cytoscape/emit.ts +170 -48
  557. package/src/styles/cytoscape/lexicon.ts +431 -78
  558. package/src/styles/cytoscape/mappings.ts +108 -0
  559. package/src/styles/cytoscape/resolve.ts +741 -17
  560. package/src/styles/defaults.ts +417 -108
  561. package/src/styles/index.ts +5 -0
  562. package/src/styles/internal.ts +1 -0
  563. package/src/styles/io/generic.ts +79 -23
  564. package/src/styles/io/round-trip.ts +202 -0
  565. package/src/styles/io/writer.ts +6 -1
  566. package/src/styles/notes.ts +1 -0
  567. package/src/styles/plan/features.ts +23 -2
  568. package/src/styles/plan/index.ts +1 -1
  569. package/src/styles/plan/plan.ts +69 -5
  570. package/src/styles/plan/resolve.ts +47 -4
  571. package/src/styles/slots.ts +25 -0
  572. package/src/styles/types.ts +698 -5
  573. package/src/styles/vocabulary.ts +232 -2
  574. package/dist/chunks/block-Cu22ewuS.js.map +0 -1
  575. package/dist/chunks/choose-Cdx6Enl2.js +0 -57
  576. package/dist/chunks/choose-Cdx6Enl2.js.map +0 -1
  577. package/dist/chunks/codes-CV_Bhcyi.js.map +0 -1
  578. package/dist/chunks/convert-BcQ8_jMo.js.map +0 -1
  579. package/dist/chunks/credentials-BiQsnMTb.js.map +0 -1
  580. package/dist/chunks/descriptor-BJ-W0Jd-.js.map +0 -1
  581. package/dist/chunks/emit-CD8xnY8s.js.map +0 -1
  582. package/dist/chunks/export-CZ7VRm4I.js.map +0 -1
  583. package/dist/chunks/exporter-bxxjOrDZ.js.map +0 -1
  584. package/dist/chunks/format-BMS-1Nxb.js +0 -64
  585. package/dist/chunks/format-BMS-1Nxb.js.map +0 -1
  586. package/dist/chunks/generic-CP6YD34u.js.map +0 -1
  587. package/dist/chunks/importer-B87_fmVx.js.map +0 -1
  588. package/dist/chunks/importer-CB_zV5_L.js.map +0 -1
  589. package/dist/chunks/importer-CVT-JyDE.js.map +0 -1
  590. package/dist/chunks/importer-Cyy6UseH.js.map +0 -1
  591. package/dist/chunks/importer-DPZDN40-.js.map +0 -1
  592. package/dist/chunks/importer-e3huMZbm.js.map +0 -1
  593. package/dist/chunks/lexicon-u2SI5xcx.js.map +0 -1
  594. package/dist/chunks/sigv4-LkAzsLAu.js.map +0 -1
  595. package/dist/chunks/styles-CaABMoO1.js.map +0 -1
  596. package/dist/chunks/styles-P_gmFAOi.js +0 -770
  597. package/dist/chunks/styles-P_gmFAOi.js.map +0 -1
  598. package/dist/chunks/styles-yed-Cs1nmrjn.js.map +0 -1
  599. package/dist/chunks/writer-DvHTl36J.js +0 -449
  600. package/dist/chunks/writer-DvHTl36J.js.map +0 -1
@@ -42,18 +42,26 @@ import type {
42
42
  StyleWorkBudget,
43
43
  } from "../types.js";
44
44
  import { CYJS_DEFAULT_STYLESHEET, CYJS_PROPERTY_DEFAULTS } from "./defaults-table.js";
45
+ import { combineFace } from "./face.js";
45
46
  import {
46
47
  applyCyjsMapper,
48
+ CYJS_CURVE_STYLES,
47
49
  CYJS_THROWS,
50
+ cyjsAliasOf,
51
+ cyjsDashLook,
48
52
  type CyjsFlat,
49
53
  cyjsNeutral,
50
54
  type CyjsParsed,
55
+ cyjsPolygonShapes,
51
56
  cyjsProperties,
52
57
  cyjsPropertyName,
53
58
  type CyjsPropertySpec,
54
59
  cyjsRendered,
60
+ gradientStopColors,
61
+ gradientStops,
55
62
  parseCyjsValue,
56
63
  } from "./mappers.js";
64
+ import { cyjsRoute, type CyjsRouteEdge, type CyjsRouteNode } from "./routes.js";
57
65
  import { type CyjsCheck, type CyjsQuery, type CyjsSelector, parseCyjsSelector } from "./selector.js";
58
66
  import { type CyjsSheetEntry, parseCyjsStylesheetText } from "./stylesheet-text.js";
59
67
 
@@ -1136,6 +1144,18 @@ const POSITIONS: ReadonlyMap<string, string> = new Map([
1136
1144
  ["bottom,right", "bottom-right"],
1137
1145
  ]);
1138
1146
 
1147
+ /** The positions inside the node, by the sides the `-inside` alignments name (`center` for neither). */
1148
+ const INSIDE_POSITIONS: ReadonlyMap<string, string> = new Map([
1149
+ ["top,left", "inside-top-left"],
1150
+ ["top,center", "inside-top"],
1151
+ ["top,right", "inside-top-right"],
1152
+ ["center,left", "inside-left"],
1153
+ ["center,right", "inside-right"],
1154
+ ["bottom,left", "inside-bottom-left"],
1155
+ ["bottom,center", "inside-bottom"],
1156
+ ["bottom,right", "inside-bottom-right"],
1157
+ ]);
1158
+
1139
1159
  /**
1140
1160
  * Applies a Cytoscape.js stylesheet to its elements as Cytoscape.js 3.34 does.
1141
1161
  * @param input - the elements, the stylesheet, the graph and the options
@@ -1186,8 +1206,8 @@ export function evaluateCytoscapeJs(input: CyjsStyleInput): CyjsStyles {
1186
1206
  notes.add(STYLE_ISSUE.SELECTOR, null, "selector", { count: deep, sample: "nested too deep" });
1187
1207
  }
1188
1208
  const matcher = new Matcher(graph, notes);
1189
- const node = evaluateTarget("node", graph, sheet, matcher, input, notes, budget);
1190
- const edge = evaluateTarget("edge", graph, sheet, matcher, input, notes, budget);
1209
+ const node = evaluateTarget("node", graph, sheet, matcher, input, { notes, budget, nodes: null });
1210
+ const edge = evaluateTarget("edge", graph, sheet, matcher, input, { notes, budget, nodes: node });
1191
1211
  return { node, edge, graph };
1192
1212
  }
1193
1213
 
@@ -1289,8 +1309,10 @@ function cellsOf(cells: Cells, name: string, n: number): (Cell | undefined)[] {
1289
1309
  * @param sheet - the entries
1290
1310
  * @param matcher - the selector evaluator
1291
1311
  * @param input - the input
1292
- * @param notes - where problems are noted
1293
- * @param budget - the counted budget
1312
+ * @param env - where problems are noted, the counted budget, and for edges the nodes' values
1313
+ * @param env.notes - where problems are noted
1314
+ * @param env.budget - the counted budget
1315
+ * @param env.nodes - the nodes' values, which edge routes are measured against (null for nodes)
1294
1316
  * @returns the target's values and rules
1295
1317
  */
1296
1318
  function evaluateTarget(
@@ -1299,9 +1321,9 @@ function evaluateTarget(
1299
1321
  sheet: readonly Entry[],
1300
1322
  matcher: Matcher,
1301
1323
  input: CyjsStyleInput,
1302
- notes: StyleNotes,
1303
- budget: StyleWorkBudget,
1324
+ env: { readonly notes: StyleNotes; readonly budget: StyleWorkBudget; readonly nodes: CyjsTargetStyles | null },
1304
1325
  ): CyjsTargetStyles {
1326
+ const { notes, budget } = env;
1305
1327
  const els = kind === "node" ? graph.nodes : graph.edges;
1306
1328
  const spend = (ops: number): void => {
1307
1329
  budget.spend(kind, ops);
@@ -1319,6 +1341,22 @@ function evaluateTarget(
1319
1341
  forceLoopCurves(graph, cells.get("curve-style"));
1320
1342
  }
1321
1343
  const drawn = drawnValues(kind, els, cells, order, input.rendered, notes, spend);
1344
+ // a file read with Cytoscape.js's own defaults draws an edge no rule curves as a haystack (a file graphty wrote
1345
+ // is read with graphty's)
1346
+ if (kind === "edge" && input.toolDefaults !== false) {
1347
+ const read: DrawnRead = { kind, els, cells, order, rendered: input.rendered, notes };
1348
+ const curves = drawnColumn(read, "curve-style", CURVE_PART);
1349
+ noArrowsOnHaystacks(curves, drawn);
1350
+ if (env.nodes !== null) {
1351
+ edgeRoutes(graph, read, curves, env.nodes, drawn);
1352
+ }
1353
+ }
1354
+ if (kind === "node") {
1355
+ const read: DrawnRead = { kind, els, cells, order, rendered: input.rendered, notes };
1356
+ nodeSizes(graph, read, drawn);
1357
+ groupBoxes(graph, read, drawn);
1358
+ mixedLabelPlaces(graph, read, drawn);
1359
+ }
1322
1360
  if (kind === "edge") {
1323
1361
  straightenUnpairedBeziers(graph, curveStyles(graph, cells, input.rendered), coreToOut, drawn.curved);
1324
1362
  }
@@ -1501,7 +1539,7 @@ function drawnValues(
1501
1539
  if (spec.property.startsWith("_")) {
1502
1540
  words.set(
1503
1541
  spec.property,
1504
- col.map((v) => (typeof v === "string" ? v : null)),
1542
+ col.map((v) => (typeof v === "string" || typeof v === "number" ? String(v) : null)),
1505
1543
  );
1506
1544
  continue;
1507
1545
  }
@@ -1514,6 +1552,9 @@ function drawnValues(
1514
1552
  });
1515
1553
  }
1516
1554
  combineParts(kind, out, words, values, notes);
1555
+ if (kind === "node") {
1556
+ cyjsPolygonShapes(values);
1557
+ }
1517
1558
  const drawn: Record<string, (StyleValue | null)[]> = {};
1518
1559
  for (const [p, col] of Object.entries(values)) {
1519
1560
  if (col.some((v) => v !== null)) {
@@ -1695,7 +1736,7 @@ function entryProperties(
1695
1736
  ): EntryProperty[] {
1696
1737
  const out: EntryProperty[] = [];
1697
1738
  for (const [name0, value] of entry.properties) {
1698
- const name = entry.dashOnly ? dashOnlyName(name0) : cyjsPropertyName(name0);
1739
+ const name = entry.dashOnly ? cyjsAliasOf(name0) : cyjsPropertyName(name0);
1699
1740
  const spec = specs.get(name);
1700
1741
  if (spec === undefined) {
1701
1742
  if (kind === "node" && !CYJS_EDGE_ONLY.has(name) && !entry.tool) {
@@ -1716,29 +1757,39 @@ function entryProperties(
1716
1757
  return out;
1717
1758
  }
1718
1759
 
1719
- /**
1720
- * A property name of an entry Cytoscape.js reads in dash form only: `content` is `label`, any other name as it is.
1721
- * @param name - the name as written
1722
- * @returns the name
1723
- */
1724
- function dashOnlyName(name: string): string {
1725
- return name === "content" ? "label" : name;
1726
- }
1727
-
1728
1760
  /** Edge properties a node never carries: not noted as dropped on nodes. */
1729
1761
  const CYJS_EDGE_ONLY: ReadonlySet<string> = new Set([
1730
1762
  "line-color",
1731
1763
  "line-style",
1764
+ "line-outline-width",
1765
+ "line-outline-color",
1766
+ "line-fill",
1767
+ "line-gradient-stop-colors",
1768
+ "line-gradient-stop-positions",
1732
1769
  "target-arrow-shape",
1733
1770
  "source-arrow-shape",
1734
1771
  "target-arrow-color",
1735
1772
  "source-arrow-color",
1773
+ "mid-target-arrow-shape",
1774
+ "mid-source-arrow-shape",
1775
+ "mid-target-arrow-color",
1776
+ "mid-source-arrow-color",
1736
1777
  "target-arrow-fill",
1737
1778
  "source-arrow-fill",
1738
1779
  "curve-style",
1739
1780
  "arrow-scale",
1740
1781
  "source-label",
1741
1782
  "target-label",
1783
+ "source-text-offset",
1784
+ "target-text-offset",
1785
+ "source-text-margin-x",
1786
+ "source-text-margin-y",
1787
+ "target-text-margin-x",
1788
+ "target-text-margin-y",
1789
+ "source-text-rotation",
1790
+ "target-text-rotation",
1791
+ "source-distance-from-node",
1792
+ "target-distance-from-node",
1742
1793
  ]);
1743
1794
 
1744
1795
  /**
@@ -1862,7 +1913,8 @@ function neutralOfCell(spec: CyjsPropertySpec, name: string, c: Cell, site: Cano
1862
1913
  }
1863
1914
 
1864
1915
  /**
1865
- * Combines the label alignments into `labelPosition` and `visibility` with `display` into `visible`.
1916
+ * Combines the label alignments into `labelPosition`, `text-wrap` with `text-max-width` and `text-overflow-wrap` into
1917
+ * `labelWrapWidth`, `labelWrapEllipsis` and `labelWrapAnywhere`, and `visibility` with `display` into `visible`.
1866
1918
  * @param kind - the target
1867
1919
  * @param n - the element count
1868
1920
  * @param words - the parts by internal name
@@ -1876,116 +1928,1279 @@ function combineParts(
1876
1928
  values: Record<string, (StyleValue | null)[]>,
1877
1929
  notes: StyleNotes,
1878
1930
  ): void {
1931
+ // the element's own opacity, before an edge's line opacity is multiplied in: its label is drawn at it
1932
+ const whole = [...(values.opacity ?? new Array<StyleValue | null>(n).fill(null))];
1933
+ combineLabelOpacity(n, words, values);
1934
+ if (kind === "node") {
1935
+ combineNodeOpacity(n, words, values);
1936
+ }
1879
1937
  const va = words.get("_valign");
1880
1938
  const ha = words.get("_halign");
1881
1939
  if (kind === "node" && (va !== undefined || ha !== undefined)) {
1882
- values.labelPosition = Array.from({ length: n }, (_, i) =>
1883
- labelPosition(va?.[i] ?? null, ha?.[i] ?? null, (sample) => {
1884
- notes.add(STYLE_ISSUE.UNKNOWN_VALUE, kind, "labelPosition", { sample, element: i });
1885
- }),
1940
+ values.labelPosition = Array.from({ length: n }, (_, i) => labelPosition(va?.[i] ?? null, ha?.[i] ?? null));
1941
+ }
1942
+ // `text-max-width` wraps a label only under `text-wrap: wrap`; unset there, it is 9999 (cytoscape.js 3.34.3
1943
+ // dist/cytoscape.cjs.js line 18484), which wraps nothing but the label's own line breaks.
1944
+ // `text-wrap: ellipsis` cuts the label at the same width instead (3.34.3 line 25254), and
1945
+ // `text-overflow-wrap: anywhere` breaks a wrapped one between any two letters (line 25204).
1946
+ const wrap = words.get("_wrap");
1947
+ const maxWidth = words.get("_maxWidth");
1948
+ const overflow = words.get("_overflowWrap");
1949
+ if (wrap !== undefined) {
1950
+ const cut = (i: number): boolean => wrap[i] === "wrap" || wrap[i] === "ellipsis";
1951
+ values.labelWrapWidth = Array.from({ length: n }, (_, i) =>
1952
+ cut(i) && typeof maxWidth?.[i] === "string" ? Number(maxWidth[i]) : null,
1886
1953
  );
1954
+ if (wrap.includes("ellipsis")) {
1955
+ values.labelWrapEllipsis = wrap.map((w) => (w === "ellipsis" ? true : null));
1956
+ }
1957
+ if (overflow?.includes("anywhere") === true) {
1958
+ values.labelWrapAnywhere = wrap.map((w, i) => (w === "wrap" && overflow[i] === "anywhere" ? true : null));
1959
+ }
1887
1960
  }
1961
+ if (kind === "node") {
1962
+ combineFace(n, words, values);
1963
+ combineBackgroundGradient(n, words, values, notes);
1964
+ } else {
1965
+ combineArrowFills(n, words, values, notes);
1966
+ combineLineOpacity(n, words.get("_lineOpacity"), values);
1967
+ }
1968
+ combineDashes(kind, n, words, values);
1969
+ combineLabelText(kind, n, words, values);
1970
+ combineLabelDress(kind, n, words, values, notes, whole);
1971
+ combineVisibility(kind, n, words, values);
1972
+ }
1973
+
1974
+ /**
1975
+ * A label's opacity as Cytoscape.js draws it: its `text-opacity` times the element's `opacity` (cytoscape.js 3.34.3
1976
+ * `drawText`: `effectiveOpacity() * text-opacity`), which the label's box and its line round it are drawn at too.
1977
+ * ponytail: a compound parent's opacity, which Cytoscape.js multiplies in as well, is left out
1978
+ * @param n - the element count
1979
+ * @param words - the parts by internal name
1980
+ * @param values - the values, `opacity` still the element's own; changed in place
1981
+ */
1982
+ function combineLabelOpacity(
1983
+ n: number,
1984
+ words: ReadonlyMap<string, (string | null)[]>,
1985
+ values: Record<string, (StyleValue | null)[]>,
1986
+ ): void {
1987
+ const text = words.get("_textOpacity");
1988
+ const whole = values.opacity;
1989
+ if (text === undefined && whole === undefined) {
1990
+ return;
1991
+ }
1992
+ const col = new Array<StyleValue | null>(n).fill(null);
1993
+ for (let i = 0; i < n; i++) {
1994
+ const t = text?.[i] ?? null;
1995
+ const o = whole?.[i];
1996
+ const v = Math.round((t === null ? 1 : Number(t)) * (typeof o === "number" ? o : 1) * 1e6) / 1e6;
1997
+ // a label at full strength is left unset, as one no rule fades
1998
+ col[i] = v === 1 || !Number.isFinite(v) ? null : v;
1999
+ }
2000
+ values.labelOpacity = col;
2001
+ }
2002
+
2003
+ /**
2004
+ * A node's border and outline as Cytoscape.js draws them: at their own `border-opacity` / `outline-opacity` times the
2005
+ * node's `opacity` (cytoscape.js 3.34.3 `drawNode`: `parentOpacity * borderOpacity`). A ring at full strength is left
2006
+ * unset.
2007
+ * @param n - the element count
2008
+ * @param words - the parts by internal name
2009
+ * @param values - the values, `opacity` still the node's own; changed in place
2010
+ */
2011
+ function combineNodeOpacity(
2012
+ n: number,
2013
+ words: ReadonlyMap<string, (string | null)[]>,
2014
+ values: Record<string, (StyleValue | null)[]>,
2015
+ ): void {
2016
+ const whole = values.opacity;
2017
+ for (const [part, p] of [
2018
+ ["_ringOpacity", "borderOpacity"],
2019
+ ["_outlineRingOpacity", "outerBorderOpacity"],
2020
+ ] as const) {
2021
+ const own = words.get(part);
2022
+ if (own === undefined && whole === undefined) {
2023
+ continue;
2024
+ }
2025
+ values[p] = Array.from({ length: n }, (_, i) => {
2026
+ const a = own?.[i] ?? null;
2027
+ const o = whole?.[i];
2028
+ const v = Math.round((a === null ? 1 : Number(a)) * (typeof o === "number" ? o : 1) * 1e6) / 1e6;
2029
+ return v === 1 || !Number.isFinite(v) ? null : v;
2030
+ });
2031
+ }
2032
+ }
2033
+
2034
+ /**
2035
+ * A dashed line or border as the line it looks like. Cytoscape.js draws `dashed` with `line-dash-pattern` (6 3) or
2036
+ * `border-dash-pattern` (4 2) in pixels whatever the width, and `dotted` as 1 1 (cytoscape.js 3.34.3 `drawEdgePath`,
2037
+ * `drawNode`), so a wide dashed line shows dots and a pattern of long and short dashes a dash-dot. The class is read
2038
+ * from the dashes and gaps against the width, a round or square cap lengthening each dash by the width.
2039
+ * ponytail: the neutral line styles name a class, not a pattern; a dash pattern slot would carry the lengths exactly
2040
+ * @param kind - the target
2041
+ * @param n - the element count
2042
+ * @param words - the parts by internal name
2043
+ * @param values - the values; changed in place
2044
+ */
2045
+ function combineDashes(
2046
+ kind: StyleTarget,
2047
+ n: number,
2048
+ words: ReadonlyMap<string, (string | null)[]>,
2049
+ values: Record<string, (StyleValue | null)[]>,
2050
+ ): void {
2051
+ // the line or border, and a node's outline, which is always dashed 4 2 with butt ends (`drawOutline`)
2052
+ const lines: [string, string, number, string, boolean][] =
2053
+ kind === "edge"
2054
+ ? [["lineStyle", "width", 3, "6 3", true]]
2055
+ : [
2056
+ ["borderStyle", "borderWidth", 0, "4 2", true],
2057
+ ["outerBorderStyle", "outerBorderWidth", 0, "4 2", false],
2058
+ ];
2059
+ for (const [style, width, fallback, pattern, own] of lines) {
2060
+ const col = values[style];
2061
+ if (col !== undefined) {
2062
+ dashColumn(col, n, values[width], { fallback, pattern }, own ? words : null);
2063
+ }
2064
+ }
2065
+ }
2066
+
2067
+ /**
2068
+ * One line style's dashed lines as the lines they look like.
2069
+ * @param col - the line styles; changed in place
2070
+ * @param n - the element count
2071
+ * @param widths - the lines' widths, when set
2072
+ * @param drawn - what Cytoscape.js draws when they are not set: the width and the dash pattern
2073
+ * @param drawn.fallback - the width
2074
+ * @param drawn.pattern - the dash pattern
2075
+ * @param words - the parts by internal name, or null for an outline, which has no pattern or cap of its own
2076
+ */
2077
+ function dashColumn(
2078
+ col: (StyleValue | null)[],
2079
+ n: number,
2080
+ widths: readonly (StyleValue | null)[] | undefined,
2081
+ drawn: { fallback: number; pattern: string },
2082
+ words: ReadonlyMap<string, (string | null)[]> | null,
2083
+ ): void {
2084
+ for (let i = 0; i < n; i++) {
2085
+ if (col[i] !== "dashed") {
2086
+ continue;
2087
+ }
2088
+ const w = widths?.[i];
2089
+ const look = dashedLook(words, i, drawn.pattern, typeof w === "number" ? w : drawn.fallback);
2090
+ if (look !== null) {
2091
+ col[i] = look;
2092
+ }
2093
+ }
2094
+ }
2095
+
2096
+ /**
2097
+ * One dashed line's look, from its dash pattern (its own `line-dash-pattern` when it has one) and cap.
2098
+ * @param words - the parts by internal name, or null for an outline, which has no pattern or cap of its own
2099
+ * @param i - the element
2100
+ * @param pattern - the pattern Cytoscape.js draws when none is given
2101
+ * @param width - the line's width
2102
+ * @returns the line style, or null when the pattern is not a list of numbers
2103
+ */
2104
+ function dashedLook(
2105
+ words: ReadonlyMap<string, (string | null)[]> | null,
2106
+ i: number,
2107
+ pattern: string,
2108
+ width: number,
2109
+ ): string | null {
2110
+ const dashes = (words?.get("_dashPattern")?.[i] ?? pattern)
2111
+ .split(/[\s,[\]]+/)
2112
+ .filter((t) => t !== "")
2113
+ .map((t) => Number.parseFloat(t));
2114
+ if (dashes.length === 0 || !dashes.every((d) => Number.isFinite(d))) {
2115
+ return null;
2116
+ }
2117
+ return cyjsDashLook(dashes, width, words?.get("_dashCap")?.[i] ?? "butt");
2118
+ }
2119
+
2120
+ /**
2121
+ * A property's values, created (all null) when it has none yet.
2122
+ * @param values - the values by property; changed in place
2123
+ * @param property - the property
2124
+ * @param n - the element count
2125
+ * @returns its values
2126
+ */
2127
+ function columnOf(values: Record<string, (StyleValue | null)[]>, property: string, n: number): (StyleValue | null)[] {
2128
+ let column = values[property];
2129
+ if (column === undefined) {
2130
+ column = new Array<StyleValue | null>(n).fill(null);
2131
+ values[property] = column;
2132
+ }
2133
+ return column;
2134
+ }
2135
+
2136
+ /**
2137
+ * `visibility` with `display` into `visible`. Cytoscape.js leaves the edges of a node hidden with `visibility:
2138
+ * hidden` drawn (only `display: none` takes them away), so such a node is kept and drawn at opacity 0 (its border and
2139
+ * outline too), with no label.
2140
+ * @param kind - the target
2141
+ * @param n - the element count
2142
+ * @param words - the parts by internal name
2143
+ * @param values - the values; changed in place
2144
+ */
2145
+ function combineVisibility(
2146
+ kind: StyleTarget,
2147
+ n: number,
2148
+ words: ReadonlyMap<string, (string | null)[]>,
2149
+ values: Record<string, (StyleValue | null)[]>,
2150
+ ): void {
1888
2151
  const vis = words.get("_visibility");
1889
2152
  const disp = words.get("_display");
1890
- if (vis !== undefined || disp !== undefined) {
1891
- const col = new Array<StyleValue | null>(n).fill(null);
1892
- for (let i = 0; i < n; i++) {
1893
- const a = vis?.[i] ?? null;
1894
- const b = disp?.[i] ?? null;
1895
- if (a === null && b === null) {
1896
- continue;
2153
+ if (vis === undefined && disp === undefined) {
2154
+ return;
2155
+ }
2156
+ const col = new Array<StyleValue | null>(n).fill(null);
2157
+ for (let i = 0; i < n; i++) {
2158
+ const a = vis?.[i] ?? null;
2159
+ const b = disp?.[i] ?? null;
2160
+ if (a === null && b === null) {
2161
+ continue;
2162
+ }
2163
+ const keptInvisible = kind === "node" && a === "hidden" && b !== "none";
2164
+ col[i] = keptInvisible || (a !== "hidden" && b !== "none");
2165
+ if (keptInvisible) {
2166
+ // nothing of it is drawn: its body, its border, its outline or its label
2167
+ for (const p of ["opacity", "borderOpacity", "outerBorderOpacity", "labelOpacity"]) {
2168
+ columnOf(values, p, n)[i] = 0;
1897
2169
  }
1898
- col[i] = a !== "hidden" && b !== "none";
2170
+ columnOf(values, "label", n)[i] = "";
1899
2171
  }
1900
- values.visible = col;
1901
- }
1902
- if (kind === "edge") {
1903
- hollowArrows("target", words.get("_targetArrowFill"), values, notes);
1904
- hollowArrows("source", words.get("_sourceArrowFill"), values, notes);
1905
2172
  }
2173
+ values.visible = col;
1906
2174
  }
1907
2175
 
1908
2176
  /**
1909
- * An entry's arrows drawn open where the same entry sets them hollow. A fill set by another entry is left to the
1910
- * per-element values the rules are checked against.
1911
- * @param setting - the entry's constants; changed in place
1912
- * @param parts - every property of the entry
2177
+ * The label's words as Cytoscape.js draws them: `text-transform`, `text-justification`, and `text-rotation` (an
2178
+ * edge's end labels' `source-` / `target-text-rotation` too) as `labelRotation`, `sourceLabelRotation` and
2179
+ * `targetLabelRotation`.
2180
+ * @param kind - the target
2181
+ * @param n - the element count
2182
+ * @param words - the parts by internal name
2183
+ * @param values - the values; changed in place
1913
2184
  */
1914
- function openArrows(setting: Map<string, StyleValue>, parts: readonly EntryProperty[]): void {
1915
- for (const end of ["target", "source"]) {
1916
- // a later duplicate of a property replaces the earlier one
1917
- let fill: CyjsParsed | undefined;
1918
- for (const p of parts) {
1919
- if (p.spec.property === `_${end}ArrowFill`) {
1920
- fill = p.parsed;
2185
+ function combineLabelText(
2186
+ kind: StyleTarget,
2187
+ n: number,
2188
+ words: ReadonlyMap<string, (string | null)[]>,
2189
+ values: Record<string, (StyleValue | null)[]>,
2190
+ ): void {
2191
+ transformLabels(n, words.get("_transform"), values.label);
2192
+ // cytoscape.js 3.34.3 `labelJustification`: auto lines the lines up against the node, a label beside it on the
2193
+ // left to its right edge
2194
+ const justification = words.get("_justification");
2195
+ if (justification !== undefined && kind === "edge") {
2196
+ combineEdgeJustification(n, justification, words.get("_wrap"), values);
2197
+ } else if (justification !== undefined) {
2198
+ const halign = words.get("_halign");
2199
+ const AUTO: Readonly<Record<string, string>> = {
2200
+ left: "right",
2201
+ right: "left",
2202
+ "left-inside": "left",
2203
+ "right-inside": "right",
2204
+ };
2205
+ values.labelTextAlign = justification.map((j, i) => {
2206
+ if (j === null) {
2207
+ return null;
1921
2208
  }
2209
+ return j === "auto" ? (AUTO[halign?.[i] ?? "center"] ?? "center") : j;
2210
+ });
2211
+ }
2212
+ // an edge's `autorotate` follows its line, which the routes work out (`autorotateLabels`)
2213
+ const rotations: readonly (readonly [string, string])[] = [
2214
+ ["_rotation", "labelRotation"],
2215
+ ["_sourceRotation", "sourceLabelRotation"],
2216
+ ["_targetRotation", "targetLabelRotation"],
2217
+ ];
2218
+ for (const [part, property] of rotations) {
2219
+ const rotation = words.get(part);
2220
+ if (rotation !== undefined) {
2221
+ values[property] = rotation.map((r) => (r === null ? null : cyjsAngleDegrees(r)));
1922
2222
  }
1923
- const shape = setting.get(`${end}Arrow`);
1924
- const open = shape === undefined ? undefined : OPEN_ARROWS.get(shape);
1925
- if (fill?.kind === "word" && fill.value === "hollow" && open !== undefined) {
1926
- setting.set(`${end}Arrow`, open);
2223
+ }
2224
+ }
2225
+
2226
+ /**
2227
+ * A label's `text-transform`: `uppercase` and `lowercase` change its words; anything else leaves them.
2228
+ * @param n - the element count
2229
+ * @param transform - each element's transform
2230
+ * @param label - each element's label; changed in place
2231
+ */
2232
+ function transformLabels(
2233
+ n: number,
2234
+ transform: readonly (string | null)[] | undefined,
2235
+ label: (StyleValue | null)[] | undefined,
2236
+ ): void {
2237
+ if (transform === undefined || label === undefined) {
2238
+ return;
2239
+ }
2240
+ for (let i = 0; i < n; i++) {
2241
+ const text = label[i];
2242
+ if (typeof text === "string" && transform[i] === "uppercase") {
2243
+ label[i] = text.toUpperCase();
2244
+ } else if (typeof text === "string" && transform[i] === "lowercase") {
2245
+ label[i] = text.toLowerCase();
1927
2246
  }
1928
2247
  }
1929
2248
  }
1930
2249
 
1931
- /** The arrows the vocabulary has an open shape for, by their filled shape. */
1932
- const OPEN_ARROWS: ReadonlyMap<StyleValue, StyleValue> = new Map([
1933
- ["triangle", "triangle-open"],
1934
- ["circle", "circle-open"],
1935
- ["diamond", "diamond-open"],
2250
+ /**
2251
+ * An edge label's `text-justification` as Cytoscape.js draws it: every line centered (an edge's text is always
2252
+ * centered, cytoscape.js 3.34.3 `setupTextStyle`), the block moved half its width left for `left` and right for
2253
+ * `right`, and only for a wrapped label (`drawText`), as `labelShiftX`.
2254
+ * @param n - the element count
2255
+ * @param justification - each edge's justification
2256
+ * @param wrap - each edge's `text-wrap`
2257
+ * @param values - the values; changed in place
2258
+ */
2259
+ function combineEdgeJustification(
2260
+ n: number,
2261
+ justification: readonly (string | null)[],
2262
+ wrap: readonly (string | null)[] | undefined,
2263
+ values: Record<string, (StyleValue | null)[]>,
2264
+ ): void {
2265
+ const SHIFT: Readonly<Record<string, number>> = { left: -0.5, right: 0.5 };
2266
+ for (let i = 0; i < n; i++) {
2267
+ const shift = wrap?.[i] === "wrap" ? SHIFT[justification[i] ?? ""] : undefined;
2268
+ if (shift !== undefined) {
2269
+ columnOf(values, "labelShiftX", n)[i] = shift;
2270
+ }
2271
+ }
2272
+ }
2273
+
2274
+ /**
2275
+ * A label's outline and background box as Cytoscape.js draws them: the outline only with a `text-outline-width` and
2276
+ * a `text-outline-opacity` above 0, a stroke of twice that width centered on the letters' edges (cytoscape.js
2277
+ * 3.34.3 `drawText`: `lineWidth = 2 * text-outline-width`); the box only with a `text-background-opacity` above 0
2278
+ * (its default is 0), and the line round the box (`labelBorder`); the box and the line reach `text-background-padding`
2279
+ * past the words.
2280
+ * @param kind - the target
2281
+ * @param n - the element count
2282
+ * @param words - the parts by internal name
2283
+ * @param values - the values; changed in place
2284
+ * @param notes - where problems are noted
2285
+ * @param whole - each element's own opacity, which the box is drawn at too
2286
+ */
2287
+ function combineLabelDress(
2288
+ kind: StyleTarget,
2289
+ n: number,
2290
+ words: ReadonlyMap<string, (string | null)[]>,
2291
+ values: Record<string, (StyleValue | null)[]>,
2292
+ notes: StyleNotes,
2293
+ whole: readonly (StyleValue | null)[],
2294
+ ): void {
2295
+ const num = (part: string, i: number, fallback: number): number => {
2296
+ const v = words.get(part)?.[i];
2297
+ return v === null || v === undefined ? fallback : Number(v);
2298
+ };
2299
+ const col = (p: string): (StyleValue | null)[] => (values[p] ??= new Array<StyleValue | null>(n).fill(null));
2300
+ const outlined = words.has("_outlineWidth");
2301
+ const boxed = words.has("_backgroundOpacity");
2302
+ const bordered = words.has("_textBorderWidth") && words.has("_textBorderOpacity");
2303
+ // each part is drawn at its own opacity times the element's; the outline at the words' opacity too (drawText)
2304
+ const at = (v: StyleValue | null | undefined): number => (typeof v === "number" ? v : 1);
2305
+ const faded = (v: number): number | null => {
2306
+ const r = Math.round(v * 1e6) / 1e6;
2307
+ return r < 1 ? r : null;
2308
+ };
2309
+ for (let i = 0; i < n && (outlined || boxed || bordered); i++) {
2310
+ const width = num("_outlineWidth", i, 0);
2311
+ const outlineOpacity = num("_outlineOpacity", i, 1);
2312
+ if (outlined && width > 0 && outlineOpacity > 0) {
2313
+ col("labelOutlineColor")[i] = words.get("_outlineColor")?.[i] ?? "#000000";
2314
+ col("labelOutlineWidth")[i] = 2 * width;
2315
+ col("labelOutlineOpacity")[i] = faded(outlineOpacity * at(values.labelOpacity?.[i]));
2316
+ }
2317
+ const boxOpacity = num("_backgroundOpacity", i, 0);
2318
+ const box = boxed && boxOpacity > 0;
2319
+ if (box) {
2320
+ col("labelBackground")[i] = words.get("_backgroundColor")?.[i] ?? "#000000";
2321
+ col("labelBackgroundOpacity")[i] = faded(boxOpacity * at(whole[i]));
2322
+ }
2323
+ const border = bordered && labelBorder(kind, i, words, col, notes, at(whole[i]));
2324
+ if (box || border) {
2325
+ labelBoxShape(kind, i, words, col, notes);
2326
+ }
2327
+ }
2328
+ }
2329
+
2330
+ /**
2331
+ * A label's box or line round it, sized and shaped: it reaches `text-background-padding` past the words on every
2332
+ * side (0 unset), one font size tall a line (cytoscape.js 3.34.3 `calculateLabelDimensions`), its corners rounded
2333
+ * 2px for round-rectangle (`drawText`); a circle is noted.
2334
+ * @param kind - the target
2335
+ * @param i - the element
2336
+ * @param words - the parts by internal name
2337
+ * @param col - a property's values, created when absent
2338
+ * @param notes - where problems are noted
2339
+ */
2340
+ function labelBoxShape(
2341
+ kind: StyleTarget,
2342
+ i: number,
2343
+ words: ReadonlyMap<string, (string | null)[]>,
2344
+ col: (p: string) => (StyleValue | null)[],
2345
+ notes: StyleNotes,
2346
+ ): void {
2347
+ const padding = words.get("_backgroundPadding")?.[i];
2348
+ col("labelPadding")[i] = padding === null || padding === undefined ? 0 : Number(padding);
2349
+ col("labelLineHeight")[i] = 1;
2350
+ const shape = words.get("_backgroundShape")?.[i];
2351
+ if (shape === "round-rectangle" || shape === "roundrectangle") {
2352
+ col("labelCornerRadius")[i] = 2;
2353
+ } else if (shape === "circle") {
2354
+ // a disc as tall as the box, centered on it: no neutral box has that shape
2355
+ notes.add(STYLE_ISSUE.APPROXIMATED, kind, "labelBackground", { sample: "circle", element: i });
2356
+ }
2357
+ }
2358
+
2359
+ /**
2360
+ * The line Cytoscape.js draws round a label's box: only with a `text-border-width` and a `text-border-opacity` above
2361
+ * 0 (both default to 0), in `text-border-color` (black unset), at its opacity times the element's. A double one is
2362
+ * one line a quarter of the width, where `drawText` strokes two such lines half the width apart (cytoscape.js 3.34.3),
2363
+ * noted; dotted and dashed are drawn solid, noted.
2364
+ * @param kind - the target
2365
+ * @param i - the element
2366
+ * @param words - the parts by internal name
2367
+ * @param col - a property's values, created when absent
2368
+ * @param notes - where problems are noted
2369
+ * @param whole - the element's opacity
2370
+ * @returns whether a line is drawn
2371
+ */
2372
+ function labelBorder(
2373
+ kind: StyleTarget,
2374
+ i: number,
2375
+ words: ReadonlyMap<string, (string | null)[]>,
2376
+ col: (p: string) => (StyleValue | null)[],
2377
+ notes: StyleNotes,
2378
+ whole: number,
2379
+ ): boolean {
2380
+ const num = (part: string): number => Number(words.get(part)?.[i] ?? 0);
2381
+ const [width, opacity] = [num("_textBorderWidth"), num("_textBorderOpacity")];
2382
+ if (!(width > 0 && opacity > 0)) {
2383
+ return false;
2384
+ }
2385
+ const style = words.get("_textBorderStyle")?.[i] ?? "solid";
2386
+ col("labelBorderColor")[i] = words.get("_textBorderColor")?.[i] ?? "#000000";
2387
+ col("labelBorderWidth")[i] = style === "double" ? width / 4 : width;
2388
+ const drawn = Math.round(opacity * whole * 1e6) / 1e6;
2389
+ col("labelBorderOpacity")[i] = drawn < 1 ? drawn : null;
2390
+ if (style === "dotted" || style === "dashed" || style === "double") {
2391
+ notes.add(STYLE_ISSUE.APPROXIMATED, kind, "labelBorderColor", { sample: style, element: i });
2392
+ }
2393
+ return true;
2394
+ }
2395
+
2396
+ /**
2397
+ * An autorotated label's turn: along the line between two centers, kept upright (cytoscape.js 3.34.3
2398
+ * `lineAngleFromDelta`: atan(dy / dx), a vertical line turned to +90 whichever way it runs), from 0 up to 360.
2399
+ * @param a - the source's center
2400
+ * @param b - the target's center
2401
+ * @returns the degrees
2402
+ */
2403
+ function lineDegrees(a: readonly [number, number], b: readonly [number, number]): number {
2404
+ const dx = b[0] - a[0];
2405
+ const angle = Math.atan((b[1] - a[1]) / dx);
2406
+ const turn = ((dx === 0 ? Math.abs(angle) : angle) * 180) / Math.PI;
2407
+ return Number.isFinite(turn) ? Math.round((((turn % 360) + 360) % 360) * 1e6) / 1e6 + 0 : 0;
2408
+ }
2409
+
2410
+ /**
2411
+ * A Cytoscape.js angle (`30deg`, `0.5rad`, a number of radians) in degrees; `none` and `autorotate` (which turns
2412
+ * only an edge's label) are 0.
2413
+ * @param text - the value
2414
+ * @returns the degrees, or null when it is not an angle
2415
+ */
2416
+ function cyjsAngleDegrees(text: string): number | null {
2417
+ const t = text.trim();
2418
+ if (t === "none" || t === "autorotate") {
2419
+ return 0;
2420
+ }
2421
+ const m = /^(-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?)(deg|rad)?$/i.exec(t);
2422
+ if (m === null) {
2423
+ return null;
2424
+ }
2425
+ const x = Number(m[1]);
2426
+ const deg = m[2]?.toLowerCase() === "deg" ? x : (x * 180) / Math.PI;
2427
+ // the neutral rotation runs from 0 up to 360
2428
+ return Math.round((((deg % 360) + 360) % 360) * 1e6) / 1e6 + 0;
2429
+ }
2430
+
2431
+ /**
2432
+ * An edge's `line-opacity` times its `opacity`, which the line is drawn at (cytoscape.js 3.34.3 `drawEdge`:
2433
+ * `effectiveLineOpacity`).
2434
+ * @param n - the element count
2435
+ * @param line - the line opacities, as text
2436
+ * @param values - the values; changed in place
2437
+ */
2438
+ function combineLineOpacity(
2439
+ n: number,
2440
+ line: readonly (string | null)[] | undefined,
2441
+ values: Record<string, (StyleValue | null)[]>,
2442
+ ): void {
2443
+ if (line === undefined) {
2444
+ return;
2445
+ }
2446
+ const whole = values.opacity;
2447
+ values.opacity = Array.from({ length: n }, (_, i) => {
2448
+ const a = line[i] === null ? null : Number(line[i]);
2449
+ const b = whole?.[i];
2450
+ if (a === null || !Number.isFinite(a)) {
2451
+ return b ?? null;
2452
+ }
2453
+ return Math.round(a * (typeof b === "number" ? b : 1) * 1e6) / 1e6;
2454
+ });
2455
+ }
2456
+
2457
+ /** Where each `background-gradient-direction` runs, in the neutral `fillGradientAngle` degrees. */
2458
+ const GRADIENT_ANGLES: ReadonlyMap<string, number> = new Map([
2459
+ ["to-bottom", 90],
2460
+ ["to-top", 270],
2461
+ ["to-left", 180],
2462
+ ["to-right", 0],
2463
+ ["to-bottom-right", 45],
2464
+ ["to-right-bottom", 45],
2465
+ ["to-bottom-left", 135],
2466
+ ["to-left-bottom", 135],
2467
+ ["to-top-right", 315],
2468
+ ["to-right-top", 315],
2469
+ ["to-top-left", 225],
2470
+ ["to-left-top", 225],
1936
2471
  ]);
1937
2472
 
1938
2473
  /**
1939
- * Combines an end's arrow shape with its fill: a hollow arrow is the open shape. A hollow arrow the vocabulary has no
1940
- * open shape for keeps its filled shape and is noted.
1941
- * @param end - the edge end
1942
- * @param fill - the fill per element, or undefined when no rule sets it
2474
+ * A node's `background-fill: linear-gradient` or `radial-gradient` as `fill` (its first stop), `fillGradient` (its
2475
+ * last), `fillGradientAngle` (a linear one's direction), `fillGradientKind`, `fillGradientColors` (every stop, when
2476
+ * there are more than two) and `fillGradientStops` (`background-gradient-stop-positions`, when there is one per
2477
+ * color, as Cytoscape.js uses them).
2478
+ * @param n - the element count
2479
+ * @param words - the parts by internal name
1943
2480
  * @param values - the values; changed in place
1944
2481
  * @param notes - where problems are noted
1945
2482
  */
1946
- function hollowArrows(
1947
- end: "source" | "target",
1948
- fill: readonly (string | null)[] | undefined,
2483
+ function combineBackgroundGradient(
2484
+ n: number,
2485
+ words: ReadonlyMap<string, (string | null)[]>,
1949
2486
  values: Record<string, (StyleValue | null)[]>,
1950
2487
  notes: StyleNotes,
1951
2488
  ): void {
1952
- const shapes = values[`${end}Arrow`] as (StyleValue | null)[] | undefined;
1953
- if (fill === undefined || shapes === undefined) {
2489
+ const fill = words.get("_backgroundFill");
2490
+ if (fill === undefined) {
1954
2491
  return;
1955
2492
  }
1956
- for (let i = 0; i < shapes.length; i++) {
1957
- const shape = shapes[i];
1958
- if (fill[i] !== "hollow" || shape === null || shape === "none") {
2493
+ const stops = words.get("_backgroundStops");
2494
+ const positions = words.get("_backgroundPositions");
2495
+ const direction = words.get("_backgroundDirection");
2496
+ const col = (name: string): (StyleValue | null)[] => (values[name] ??= new Array<StyleValue | null>(n).fill(null));
2497
+ for (let i = 0; i < n; i++) {
2498
+ const radial = fill[i] === "radial-gradient";
2499
+ if (fill[i] !== "linear-gradient" && !radial) {
1959
2500
  continue;
1960
2501
  }
1961
- const open = OPEN_ARROWS.get(shape);
1962
- if (open === undefined) {
1963
- notes.add(STYLE_ISSUE.NOT_CARRIED, "edge", `${end}-arrow-fill`, {
1964
- sample: `hollow ${String(shape)}`,
1965
- element: i,
1966
- });
2502
+ // Cytoscape.js's default stop colors are one gray, which draws flat
2503
+ const colors = gradientStopColors(stops?.[i] ?? "#999", {
2504
+ notes,
2505
+ target: "node",
2506
+ property: "fill",
2507
+ element: i,
2508
+ });
2509
+ if (colors.length < 2) {
2510
+ continue;
2511
+ }
2512
+ col("fill")[i] = colors[0];
2513
+ col("fillGradient")[i] = colors.at(-1) ?? null;
2514
+ if (colors.length > 2) {
2515
+ col("fillGradientColors")[i] = colors.join(" ");
2516
+ }
2517
+ const placed = gradientStops(positions?.[i] ?? "");
2518
+ if (placed?.split(" ").length === colors.length) {
2519
+ col("fillGradientStops")[i] = placed;
2520
+ }
2521
+ if (radial) {
2522
+ col("fillGradientKind")[i] = "radial";
1967
2523
  } else {
1968
- shapes[i] = open;
2524
+ col("fillGradientAngle")[i] = GRADIENT_ANGLES.get(direction?.[i] ?? "to-bottom") ?? 90;
2525
+ }
2526
+ }
2527
+ }
2528
+
2529
+ /** An edge's routing settings, each read as Cytoscape.js spells it. */
2530
+ const ROUTE_PARTS: readonly (readonly [keyof CyjsRouteEdge, string, CyjsPropertySpec])[] = [
2531
+ ["segmentDistances", "segment-distances", { property: "_segmentDistances", type: "text" }],
2532
+ ["segmentWeights", "segment-weights", { property: "_segmentWeights", type: "text" }],
2533
+ ["controlPointDistances", "control-point-distances", { property: "_controlPointDistances", type: "text" }],
2534
+ ["controlPointWeights", "control-point-weights", { property: "_controlPointWeights", type: "text" }],
2535
+ ["controlPointStepSize", "control-point-step-size", { property: "_controlPointStepSize", type: "size" }],
2536
+ ["taxiDirection", "taxi-direction", { property: "_taxiDirection", type: "text" }],
2537
+ ["taxiTurn", "taxi-turn", { property: "_taxiTurn", type: "text" }],
2538
+ ["taxiTurnMinDistance", "taxi-turn-min-distance", { property: "_taxiTurnMinDistance", type: "size" }],
2539
+ ["edgeDistances", "edge-distances", { property: "_edgeDistances", type: "text" }],
2540
+ ["loopDirection", "loop-direction", { property: "_loopDirection", type: "text" }],
2541
+ ["loopSweep", "loop-sweep", { property: "_loopSweep", type: "text" }],
2542
+ ["segmentRadii", "segment-radii", { property: "_segmentRadii", type: "text" }],
2543
+ ["taxiRadius", "taxi-radius", { property: "_taxiRadius", type: "text" }],
2544
+ ["radiusType", "radius-type", { property: "_radiusType", type: "text" }],
2545
+ ];
2546
+
2547
+ /**
2548
+ * The routes of the edges Cytoscape.js does not draw straight (`segments`, `taxi`, an `unbundled-bezier` with
2549
+ * control points, a loop) as `bends`, drawn straight through them (`curved` false).
2550
+ * @param graph - the core's elements
2551
+ * @param read - the edges' cells
2552
+ * @param curves - each edge's curve style, in output order
2553
+ * @param nodes - the nodes' values
2554
+ * @param values - the edges' values; changed in place
2555
+ */
2556
+ function edgeRoutes(
2557
+ graph: CyjsGraph,
2558
+ read: DrawnRead,
2559
+ curves: readonly (StyleValue | null | undefined)[],
2560
+ nodes: CyjsTargetStyles,
2561
+ values: Record<string, (StyleValue | null)[]>,
2562
+ ): void {
2563
+ const words = new Map(ROUTE_PARTS.map(([key, name, spec]) => [key, drawnColumn(read, name, spec)] as const));
2564
+ const nodeAt = new Map<CyjsElement, number>();
2565
+ nodes.elements.forEach((el, k) => {
2566
+ if (el !== null) {
2567
+ nodeAt.set(el, k);
2568
+ }
2569
+ });
2570
+ const geometry = (core: number): CyjsRouteNode | null => routeNode(graph.nodes[core], nodeAt, nodes);
2571
+ // an end label's autorotate turns it as the main label's does (cytoscape.js 3.34.3 `calculateLabelAngle`)
2572
+ const turned: readonly (readonly [string, (StyleValue | null | undefined)[]])[] = [
2573
+ ["labelRotation", drawnColumn(read, "text-rotation", TEXT_PART)],
2574
+ ["sourceLabelRotation", drawnColumn(read, "source-text-rotation", TEXT_PART)],
2575
+ ["targetLabelRotation", drawnColumn(read, "target-text-rotation", TEXT_PART)],
2576
+ ];
2577
+ const ends = [drawnColumn(read, "source-endpoint", TEXT_PART), drawnColumn(read, "target-endpoint", TEXT_PART)];
2578
+ const loops = new Map<string, number>();
2579
+ const n = read.order.length;
2580
+ let routed: (StyleValue | null)[] | null = null;
2581
+ for (let k = 0; k < n; k++) {
2582
+ const e = read.order[k];
2583
+ const between = edgeNodes(graph, e, geometry);
2584
+ if (between === null) {
2585
+ continue;
2586
+ }
2587
+ const { src, tgt, loop } = between;
2588
+ if (!loop) {
2589
+ autorotateLabels(values, turned, k, n, lineDegrees(src.center, tgt.center));
2590
+ }
2591
+ const edge = routeSettings(words, k, curves[k]);
2592
+ const bends = cyjsRoute(edge, src, tgt, loop ? nextLoopIndex(loops, graph.source[e], edge) : 0);
2593
+ if (bends !== null) {
2594
+ routed ??= values.bends ?? new Array<StyleValue | null>(n).fill(null);
2595
+ routed[k] = bends;
2596
+ columnOf(values, "curved", n)[k] = false;
2597
+ }
2598
+ if (!loop) {
2599
+ edgePorts(edge.curveStyle, [ends[0][k], ends[1][k]], [src, tgt], (p, port) => {
2600
+ columnOf(values, p, n)[k] = port;
2601
+ });
2602
+ }
2603
+ }
2604
+ if (routed !== null) {
2605
+ values.bends = routed;
2606
+ }
2607
+ }
2608
+
2609
+ /**
2610
+ * A node's geometry as the routes measure it: its center, its outer size (its border taken in), its box and whether
2611
+ * it is an ellipse.
2612
+ * @param el - the node
2613
+ * @param nodeAt - each node's output index
2614
+ * @param nodes - the nodes' values
2615
+ * @returns the geometry, or null when the node has no position
2616
+ */
2617
+ function routeNode(
2618
+ el: CyjsElement,
2619
+ nodeAt: ReadonlyMap<CyjsElement, number>,
2620
+ nodes: CyjsTargetStyles,
2621
+ ): CyjsRouteNode | null {
2622
+ const k = nodeAt.get(el);
2623
+ const pos = el.json.position;
2624
+ if (k === undefined || !isObject(pos) || typeof pos.x !== "number" || typeof pos.y !== "number") {
2625
+ return null;
2626
+ }
2627
+ const num = (p: string, fallback: number): number => {
2628
+ const v = nodes.values[p]?.[k];
2629
+ return typeof v === "number" ? v : fallback;
2630
+ };
2631
+ const [w, h, bw] = [num("width", 30), num("height", 30), num("borderWidth", 0)];
2632
+ return {
2633
+ center: [pos.x, pos.y],
2634
+ size: [w + bw, h + bw],
2635
+ box: [w, h],
2636
+ ellipse: (nodes.values.shape?.[k] ?? "ellipse") === "ellipse",
2637
+ };
2638
+ }
2639
+
2640
+ /**
2641
+ * An edge's two nodes' geometry, and whether it is a loop.
2642
+ * @param graph - the core's elements
2643
+ * @param e - the edge's core index, below 0 for none
2644
+ * @param geometry - a node's geometry
2645
+ * @returns the source's and the target's geometry, or null when either is not known
2646
+ */
2647
+ function edgeNodes(
2648
+ graph: CyjsGraph,
2649
+ e: number,
2650
+ geometry: (core: number) => CyjsRouteNode | null,
2651
+ ): { src: CyjsRouteNode; tgt: CyjsRouteNode; loop: boolean } | null {
2652
+ if (e < 0) {
2653
+ return null;
2654
+ }
2655
+ const loop = graph.source[e] === graph.target[e];
2656
+ const src = geometry(graph.source[e]);
2657
+ const tgt = loop ? src : geometry(graph.target[e]);
2658
+ return src === null || tgt === null ? null : { src, tgt, loop };
2659
+ }
2660
+
2661
+ /**
2662
+ * Turns each of an edge's labels set to `autorotate` along its line.
2663
+ * @param values - the edges' values; changed in place
2664
+ * @param turned - each label's rotation property and its `text-rotation` words
2665
+ * @param k - the edge's output index
2666
+ * @param n - the edge count
2667
+ * @param degrees - the line's turn
2668
+ */
2669
+ function autorotateLabels(
2670
+ values: Record<string, (StyleValue | null)[]>,
2671
+ turned: readonly (readonly [string, (StyleValue | null | undefined)[]])[],
2672
+ k: number,
2673
+ n: number,
2674
+ degrees: number,
2675
+ ): void {
2676
+ for (const [property, column] of turned) {
2677
+ if (column[k] === "autorotate") {
2678
+ columnOf(values, property, n)[k] = degrees;
2679
+ }
2680
+ }
2681
+ }
2682
+
2683
+ /**
2684
+ * A loop's place among the loops drawn before it on the same node in the same direction and sweep, which fans them.
2685
+ * @param loops - how many loops each node, direction and sweep has so far; changed in place
2686
+ * @param source - the loop's node
2687
+ * @param edge - the loop's settings
2688
+ * @returns its place, from 0
2689
+ */
2690
+ function nextLoopIndex(loops: Map<string, number>, source: number, edge: CyjsRouteEdge): number {
2691
+ const key = `${String(source)} ${String(edge.loopDirection)} ${String(edge.loopSweep)}`;
2692
+ const index = loops.get(key) ?? 0;
2693
+ loops.set(key, index + 1);
2694
+ return index;
2695
+ }
2696
+
2697
+ /**
2698
+ * Where an edge's ends meet its nodes, when not where the line from the centers crosses the outlines (cytoscape.js
2699
+ * 3.34.3 `findEndpoints`, `manualEndptToPx`): `inside-to-node` at the center, `x y` offsets from the center (pixels,
2700
+ * or percentages of the node's outer size), an angle clockwise from 12 o'clock on the outline. A `haystack` edge
2701
+ * runs between points `haystack-radius` of the way from the centers to the outlines in a random direction
2702
+ * (`findHaystackPoints`), drawn from the centers, where a radius of 0 (the default) puts them; a taxi edge ignores its
2703
+ * endpoints.
2704
+ * ponytail: an angle meets a polygon's outline where it meets its box; per-shape outlines if a file angles one
2705
+ * @param curve - the curve style
2706
+ * @param words - the source endpoint and the target endpoint, as Cytoscape.js spells them
2707
+ * @param nodes - the source's and the target's geometry
2708
+ * @param put - sets `sourcePort` or `targetPort`
2709
+ */
2710
+ function edgePorts(
2711
+ curve: string,
2712
+ words: readonly (StyleValue | null | undefined)[],
2713
+ nodes: readonly [CyjsRouteNode, CyjsRouteNode],
2714
+ put: (property: string, port: string) => void,
2715
+ ): void {
2716
+ if (curve === "taxi" || curve === "round-taxi") {
2717
+ return;
2718
+ }
2719
+ (["sourcePort", "targetPort"] as const).forEach((p, a) => {
2720
+ const port = curve === "haystack" ? "0,0" : endpointPort(String(words[a] ?? ""), nodes[a]);
2721
+ if (port !== null) {
2722
+ put(p, port);
2723
+ }
2724
+ });
2725
+ }
2726
+
2727
+ /**
2728
+ * One `source-endpoint` / `target-endpoint` as a neutral port.
2729
+ * @param text - the value
2730
+ * @param node - the node's geometry
2731
+ * @returns the port, or null where the line meets the outline as it does by default
2732
+ */
2733
+ function endpointPort(text: string, node: CyjsRouteNode): string | null {
2734
+ const t = text.trim();
2735
+ if (t === "inside-to-node") {
2736
+ return "0,0";
2737
+ }
2738
+ const parts = t.split(/\s+/);
2739
+ const [w, h] = node.size;
2740
+ const r6 = (v: number): string => String(Math.round(v * 1e6) / 1e6 + 0);
2741
+ const fraction = (part: string, size: number): number | null => {
2742
+ const m = /^(-?(?:\d+(?:\.\d*)?|\.\d+))(%|px)?$/.exec(part);
2743
+ if (m === null || Number.isNaN(size) || size <= 0) {
2744
+ return null;
2745
+ }
2746
+ const px = m[2] === "%" ? (Number(m[1]) / 100) * size : Number(m[1]);
2747
+ return px / (size / 2);
2748
+ };
2749
+ if (parts.length === 2) {
2750
+ const [x, y] = [fraction(parts[0], w), fraction(parts[1], h)];
2751
+ return x === null || y === null ? null : `${r6(x)},${r6(-y)}`;
2752
+ }
2753
+ const m = /^(-?(?:\d+(?:\.\d*)?|\.\d+))(deg|rad)$/.exec(t);
2754
+ if (m === null) {
2755
+ return null;
2756
+ }
2757
+ const turn = m[2] === "deg" ? (Number(m[1]) * Math.PI) / 180 : Number(m[1]);
2758
+ // the direction in half sizes, y up, from 12 o'clock clockwise; scaled until it meets the outline
2759
+ const [dx, dy] = [Math.sin(turn) / (w / 2), Math.cos(turn) / (h / 2)];
2760
+ const reach = node.ellipse ? Math.hypot(dx, dy) : Math.max(Math.abs(dx), Math.abs(dy));
2761
+ return reach > 0 ? `${r6(dx / reach)},${r6(dy / reach)}` : null;
2762
+ }
2763
+
2764
+ /**
2765
+ * One edge's routing settings.
2766
+ * @param words - each setting's values, in output order
2767
+ * @param k - the edge's output index
2768
+ * @param curve - its curve style
2769
+ * @returns the settings
2770
+ */
2771
+ function routeSettings(
2772
+ words: ReadonlyMap<keyof CyjsRouteEdge, readonly (StyleValue | null | undefined)[]>,
2773
+ k: number,
2774
+ curve: StyleValue | null | undefined,
2775
+ ): CyjsRouteEdge {
2776
+ const text = (key: keyof CyjsRouteEdge): string | null => {
2777
+ const v = words.get(key)?.[k];
2778
+ return v === null || v === undefined ? null : String(v);
2779
+ };
2780
+ const num = (key: keyof CyjsRouteEdge): number | null => {
2781
+ const v = text(key);
2782
+ return v === null ? null : Number(v);
2783
+ };
2784
+ return {
2785
+ curveStyle: String(curve ?? "haystack"),
2786
+ segmentDistances: text("segmentDistances"),
2787
+ segmentWeights: text("segmentWeights"),
2788
+ controlPointDistances: text("controlPointDistances"),
2789
+ controlPointWeights: text("controlPointWeights"),
2790
+ controlPointStepSize: num("controlPointStepSize"),
2791
+ taxiDirection: text("taxiDirection"),
2792
+ taxiTurn: text("taxiTurn"),
2793
+ taxiTurnMinDistance: num("taxiTurnMinDistance"),
2794
+ edgeDistances: text("edgeDistances"),
2795
+ loopDirection: text("loopDirection"),
2796
+ loopSweep: text("loopSweep"),
2797
+ segmentRadii: text("segmentRadii"),
2798
+ taxiRadius: text("taxiRadius"),
2799
+ radiusType: text("radiusType"),
2800
+ };
2801
+ }
2802
+
2803
+ /** A padding read as Cytoscape.js spells it. */
2804
+ const TEXT_PART: CyjsPropertySpec = { property: "_text", type: "text" };
2805
+
2806
+ /**
2807
+ * A node's size as Cytoscape.js draws it: a node that is no compound parent is drawn `padding` larger on every side
2808
+ * (cytoscape.js 3.34.3 draws its shape `width() + 2 * padding()` across).
2809
+ * @param graph - the core's elements
2810
+ * @param read - the nodes' cells
2811
+ * @param values - the nodes' values; changed in place
2812
+ */
2813
+ function nodeSizes(graph: CyjsGraph, read: DrawnRead, values: Record<string, (StyleValue | null)[]>): void {
2814
+ const padding = drawnColumn(read, "padding", TEXT_PART);
2815
+ const toLabel = (["width", "height"] as const).map((p) => drawnColumn(read, p, SIZE_PART));
2816
+ const n = read.order.length;
2817
+ const col = (p: string): (StyleValue | null)[] => (values[p] ??= new Array<StyleValue | null>(n).fill(null));
2818
+ for (let k = 0; k < n; k++) {
2819
+ const core = read.order[k];
2820
+ const pad = core < 0 || graph.children[core].length > 0 ? 0 : paddingOf(padding[k]);
2821
+ const fitted = toLabel.map((c) => c[k] === "label");
2822
+ if (fitted[0] || fitted[1]) {
2823
+ // `width: label` sizes the node to its label's box (cytoscape.js 3.34.3 `width()` reads the label's
2824
+ // width), padding on each side: the label's side is a minimum of 0, the other keeps its own size
2825
+ col("labelFit")[k] = "bounds";
2826
+ col("labelPaddingX")[k] = pad;
2827
+ col("labelPaddingY")[k] = pad;
2828
+ ["width", "height"].forEach((p, a) => {
2829
+ const v = values[p]?.[k];
2830
+ const side = typeof v === "number" ? v : 30;
2831
+ col(p)[k] = fitted[a] ? 0 : side + 2 * pad;
2832
+ });
2833
+ continue;
1969
2834
  }
2835
+ if (pad <= 0) {
2836
+ continue;
2837
+ }
2838
+ for (const p of ["width", "height"]) {
2839
+ const v = values[p]?.[k];
2840
+ col(p)[k] = (typeof v === "number" ? v : 30) + 2 * pad;
2841
+ }
2842
+ }
2843
+ }
2844
+
2845
+ /** The words of a compound parent's sizing properties. */
2846
+ const PADDING_OF_PART: CyjsPropertySpec = {
2847
+ property: "_paddingOf",
2848
+ type: "enum",
2849
+ enums: ["width", "height", "average", "min", "max"],
2850
+ };
2851
+ const LABELS_PART: CyjsPropertySpec = { property: "_compoundLabels", type: "enum", enums: ["include", "exclude"] };
2852
+
2853
+ /**
2854
+ * A length or a percentage as Cytoscape.js spells it.
2855
+ * @param text - the value, or null
2856
+ * @returns the number and whether it is a percentage, or null for none or a non-number
2857
+ */
2858
+ function measureOf(text: StyleValue | null | undefined): { value: number; percent: boolean } | null {
2859
+ if (typeof text !== "string" && typeof text !== "number") {
2860
+ return null;
2861
+ }
2862
+ const value = Number.parseFloat(String(text));
2863
+ return Number.isFinite(value) ? { value, percent: String(text).trim().endsWith("%") } : null;
2864
+ }
2865
+
2866
+ /**
2867
+ * The share of a minimum's growth on the near side (left, top), as Cytoscape.js 3.34.3 `updateCompoundBounds`
2868
+ * splits it: each bias a percentage, a length read as its percentage of the minimum, the near side's share of
2869
+ * their sum; no bias on either side grows both sides alike.
2870
+ * @param near - the near side's bias
2871
+ * @param far - the far side's bias
2872
+ * @param min - the minimum
2873
+ * @returns the near side's share, or null for half
2874
+ */
2875
+ function biasShare(
2876
+ near: StyleValue | null | undefined,
2877
+ far: StyleValue | null | undefined,
2878
+ min: number,
2879
+ ): number | null {
2880
+ const percent = (text: StyleValue | null | undefined): number => {
2881
+ const m = measureOf(text);
2882
+ if (m === null || m.value <= 0) {
2883
+ return 0;
2884
+ }
2885
+ return m.percent ? m.value : (m.value * 100) / min;
2886
+ };
2887
+ const a = percent(near);
2888
+ const total = a + percent(far);
2889
+ return total > 0 ? a / total : null;
2890
+ }
2891
+
2892
+ /**
2893
+ * How Cytoscape.js sizes a compound parent round its children (cytoscape.js 3.34.3 `updateCompoundBounds`): its
2894
+ * `padding` past their box (a percentage is of the box, `padding-relative-to`), at least `min-width` by `min-height`
2895
+ * before the padding, grown by the biases' shares, its children's labels counted unless `compound-sizing-wrt-labels`
2896
+ * is `exclude`. Written for every compound parent, so the group is drawn as Cytoscape.js draws it.
2897
+ * @param graph - the core's elements
2898
+ * @param read - the nodes' cells
2899
+ * @param values - the nodes' values; changed in place
2900
+ */
2901
+ function groupBoxes(graph: CyjsGraph, read: DrawnRead, values: Record<string, (StyleValue | null)[]>): void {
2902
+ const word = (name: string, spec: CyjsPropertySpec = TEXT_PART): (StyleValue | null | undefined)[] =>
2903
+ drawnColumn(read, name, spec);
2904
+ const padding = word("padding");
2905
+ const paddingOf = word("padding-relative-to", PADDING_OF_PART);
2906
+ const labels = word("compound-sizing-wrt-labels", LABELS_PART);
2907
+ const axes = [
2908
+ { min: word("min-width"), near: word("min-width-bias-left"), far: word("min-width-bias-right") },
2909
+ { min: word("min-height"), near: word("min-height-bias-top"), far: word("min-height-bias-bottom") },
2910
+ ];
2911
+ const names = [
2912
+ ["groupMinWidth", "groupMinWidthBiasLeft"],
2913
+ ["groupMinHeight", "groupMinHeightBiasTop"],
2914
+ ];
2915
+ const n = read.order.length;
2916
+ const put = (p: string, k: number, v: StyleValue): void => {
2917
+ const column = values[p] ?? new Array<StyleValue | null>(n).fill(null);
2918
+ values[p] = column;
2919
+ column[k] = v;
2920
+ };
2921
+ for (let k = 0; k < n; k++) {
2922
+ const core = read.order[k];
2923
+ if (core < 0 || graph.children[core].length === 0) {
2924
+ continue;
2925
+ }
2926
+ for (const [p, v] of groupPaddingValues(padding[k], paddingOf[k])) {
2927
+ put(p, k, v);
2928
+ }
2929
+ put("groupIncludeLabels", k, labels[k] !== "exclude");
2930
+ axes.forEach((axis, a) => {
2931
+ for (const [p, v] of groupMinValues(axis.min[k], axis.near[k], axis.far[k], names[a])) {
2932
+ put(p, k, v);
2933
+ }
2934
+ });
1970
2935
  }
1971
2936
  }
1972
2937
 
1973
2938
  /**
1974
- * A node label's position from its vertical and horizontal alignments; an `-inside` alignment is read as its
1975
- * outside one, noted.
2939
+ * A compound parent's padding as neutral values: a length, or a fraction of the side `padding-relative-to` names.
2940
+ * @param padding - the padding as Cytoscape.js spells it
2941
+ * @param of - its `padding-relative-to`
2942
+ * @returns the properties and their values
2943
+ */
2944
+ function groupPaddingValues(
2945
+ padding: StyleValue | null | undefined,
2946
+ of: StyleValue | null | undefined,
2947
+ ): [string, StyleValue][] {
2948
+ const pad = measureOf(padding);
2949
+ if (pad?.percent !== true) {
2950
+ return [["groupPadding", Math.max(0, pad?.value ?? 0)]];
2951
+ }
2952
+ return [
2953
+ ["groupPaddingRatio", Math.max(0, pad.value / 100)],
2954
+ ["groupPaddingOf", typeof of === "string" ? of : "width"],
2955
+ ];
2956
+ }
2957
+
2958
+ /**
2959
+ * A compound parent's minimum size on one axis and the share of its growth on the near side, as neutral values.
2960
+ * @param min - the minimum as Cytoscape.js spells it
2961
+ * @param near - the near side's bias (left, top)
2962
+ * @param far - the far side's bias
2963
+ * @param names - the neutral names of the minimum and of the near side's share
2964
+ * @returns the properties and their values; none without a minimum
2965
+ */
2966
+ function groupMinValues(
2967
+ min: StyleValue | null | undefined,
2968
+ near: StyleValue | null | undefined,
2969
+ far: StyleValue | null | undefined,
2970
+ names: readonly string[],
2971
+ ): [string, StyleValue][] {
2972
+ const m = measureOf(min);
2973
+ if (m === null || m.percent || m.value <= 0) {
2974
+ return [];
2975
+ }
2976
+ const share = biasShare(near, far, m.value);
2977
+ return share === null
2978
+ ? [[names[0], m.value]]
2979
+ : [
2980
+ [names[0], m.value],
2981
+ [names[1], share],
2982
+ ];
2983
+ }
2984
+
2985
+ /** The label alignments read as Cytoscape.js's words. */
2986
+ const VALIGN_PART: CyjsPropertySpec = {
2987
+ property: "_valign",
2988
+ type: "enum",
2989
+ enums: ["top", "top-inside", "center", "bottom", "bottom-inside"],
2990
+ };
2991
+ const HALIGN_PART: CyjsPropertySpec = {
2992
+ property: "_halign",
2993
+ type: "enum",
2994
+ enums: ["left", "left-inside", "center", "right", "right-inside"],
2995
+ };
2996
+
2997
+ /**
2998
+ * Where Cytoscape.js pins a label on one axis (cytoscape.js 3.34.3 `recalculateNodeLabelProjection` and
2999
+ * `labelHalign` / `labelValign`): outside, `padding` past the node's side with the label's far edge there; inside,
3000
+ * `padding` short of it with the label's near edge there; centered, at the node's middle.
3001
+ * @param align - the alignment (`left`, `left-inside`, `top`, ...)
3002
+ * @param half - half the node's drawn size on that axis, `padding` included
3003
+ * @param pad - the node's padding
3004
+ * @returns the offset from the node's center and the shift by the label's own size
3005
+ */
3006
+ function pinnedAxis(align: string, half: number, pad: number): { offset: number; shift: number } {
3007
+ const near = /^(left|top)/.test(align);
3008
+ if (!near && !/^(right|bottom)/.test(align)) {
3009
+ return { offset: 0, shift: 0 };
3010
+ }
3011
+ const side = near ? -1 : 1;
3012
+ return align.endsWith("-inside")
3013
+ ? { offset: side * (half - 2 * pad), shift: -side / 2 }
3014
+ : { offset: side * half, shift: side / 2 };
3015
+ }
3016
+
3017
+ /**
3018
+ * A node label with one alignment inside the node and the other outside it (`text-valign: bottom-inside` with
3019
+ * `text-halign: left`): no neutral position says that, so the label is centered, moved to the point Cytoscape.js
3020
+ * puts it at by `labelOffsetX` / `labelOffsetY` (its `text-margin` added) and pinned there by `labelShiftX` /
3021
+ * `labelShiftY`. A node sized to its label keeps the outside side's position, noted.
3022
+ * @param graph - the core's elements
3023
+ * @param read - the nodes' cells
3024
+ * @param values - the nodes' values, sizes already drawn (`nodeSizes`); changed in place
3025
+ */
3026
+ function mixedLabelPlaces(graph: CyjsGraph, read: DrawnRead, values: Record<string, (StyleValue | null)[]>): void {
3027
+ const va = drawnColumn(read, "text-valign", VALIGN_PART);
3028
+ const ha = drawnColumn(read, "text-halign", HALIGN_PART);
3029
+ const padding = drawnColumn(read, "padding", TEXT_PART);
3030
+ const n = read.order.length;
3031
+ const col = (p: string): (StyleValue | null)[] => (values[p] ??= new Array<StyleValue | null>(n).fill(null));
3032
+ for (let k = 0; k < n; k++) {
3033
+ const v = typeof va[k] === "string" ? String(va[k]) : "top";
3034
+ const h = typeof ha[k] === "string" ? String(ha[k]) : "center";
3035
+ const inside = v.endsWith("-inside") || h.endsWith("-inside");
3036
+ const outside = v === "top" || v === "bottom" || h === "left" || h === "right";
3037
+ if (!inside || !outside) {
3038
+ continue;
3039
+ }
3040
+ if (values.labelFit?.[k] !== undefined && values.labelFit[k] !== null) {
3041
+ read.notes.add(STYLE_ISSUE.APPROXIMATED, "node", "labelPosition", { sample: `${v} ${h}`, element: k });
3042
+ continue;
3043
+ }
3044
+ const core = read.order[k];
3045
+ const pad = core < 0 || graph.children[core].length > 0 ? 0 : paddingOf(padding[k]);
3046
+ const size = (p: string): number => {
3047
+ const s = values[p]?.[k];
3048
+ return typeof s === "number" ? s : 30 + 2 * pad;
3049
+ };
3050
+ const x = pinnedAxis(h, size("width") / 2, pad);
3051
+ const y = pinnedAxis(v, size("height") / 2, pad);
3052
+ const add = (p: string, d: number): void => {
3053
+ const was = values[p]?.[k];
3054
+ col(p)[k] = (typeof was === "number" ? was : 0) + d;
3055
+ };
3056
+ col("labelPosition")[k] = "center";
3057
+ add("labelOffsetX", x.offset);
3058
+ add("labelOffsetY", y.offset);
3059
+ col("labelShiftX")[k] = x.shift;
3060
+ col("labelShiftY")[k] = y.shift;
3061
+ }
3062
+ }
3063
+
3064
+ /** A node size read as Cytoscape.js's word, so `label` is seen. */
3065
+ const SIZE_PART: CyjsPropertySpec = { property: "_size", type: "nodeSize" };
3066
+
3067
+ /**
3068
+ * A node's padding in pixels. A percentage is relative only on a compound parent; on any other node Cytoscape.js
3069
+ * draws its fraction as pixels (`padding()` returns the property's `pfValue`, 0.2 for `20%`).
3070
+ * @param text - the padding as Cytoscape.js spells it, or null
3071
+ * @returns the pixels, 0 for none
3072
+ */
3073
+ function paddingOf(text: StyleValue | null | undefined): number {
3074
+ if (typeof text !== "string") {
3075
+ return 0;
3076
+ }
3077
+ const x = Number.parseFloat(text);
3078
+ if (!Number.isFinite(x) || x <= 0) {
3079
+ return 0;
3080
+ }
3081
+ return text.trim().endsWith("%") ? x / 100 : x;
3082
+ }
3083
+
3084
+ /** The curve style read as Cytoscape.js's word. */
3085
+ const CURVE_PART: CyjsPropertySpec = { property: "_curve", type: "enum", enums: CYJS_CURVE_STYLES };
3086
+
3087
+ /**
3088
+ * Cytoscape.js draws no source or target arrow on a `haystack` edge, its default curve style (cytoscape.js 3.34.3
3089
+ * `drawArrowheads` skips them on haystacks, but still draws the mid arrows): each end arrow of one is `none`.
3090
+ * @param curves - each edge's curve style, null where no rule sets it
3091
+ * @param values - the values; changed in place
3092
+ */
3093
+ function noArrowsOnHaystacks(
3094
+ curves: readonly (StyleValue | null | undefined)[],
3095
+ values: Record<string, (StyleValue | null)[]>,
3096
+ ): void {
3097
+ const n = curves.length;
3098
+ for (const p of ["targetArrow", "sourceArrow"]) {
3099
+ const col = values[p];
3100
+ if (col === undefined) {
3101
+ continue;
3102
+ }
3103
+ for (let i = 0; i < n; i++) {
3104
+ if ((curves[i] ?? "haystack") === "haystack" && col[i] !== null) {
3105
+ col[i] = "none";
3106
+ }
3107
+ }
3108
+ }
3109
+ }
3110
+
3111
+ /**
3112
+ * The outline arrow a hollow `*-arrow-fill` draws for each filled shape that has one (cytoscape.js 3.34.3 strokes the
3113
+ * arrow's path instead of filling it). A hollow square has no outline cap of its own in the neutral vocabulary;
3114
+ * `other:open-square` is drawn as the nearest one.
3115
+ */
3116
+ const HOLLOW_ARROWS: ReadonlyMap<StyleValue, string> = new Map([
3117
+ ["triangle", "triangle-open"],
3118
+ ["diamond", "diamond-open"],
3119
+ ["circle", "circle-open"],
3120
+ ["square", "other:open-square"],
3121
+ ]);
3122
+
3123
+ /**
3124
+ * A rule that sets an arrow shape and makes it hollow sets the outline shape (`combineArrowFills` does the same per
3125
+ * element, for a fill and a shape set by different rules).
3126
+ * @param set - the rule's values; changed in place
3127
+ * @param all - every property of the rule
3128
+ */
3129
+ function hollowArrowsOfRule(set: Record<string, StyleValue>, all: readonly EntryProperty[]): void {
3130
+ for (const p of all) {
3131
+ const shapeProp = /^_(\w+Arrow)Fill$/.exec(p.spec.property)?.[1];
3132
+ const shape = shapeProp === undefined ? undefined : set[shapeProp];
3133
+ if (shapeProp === undefined || shape === undefined || p.parsed.kind !== "word") {
3134
+ continue;
3135
+ }
3136
+ const open = HOLLOW_ARROWS.get(shape);
3137
+ if (open !== undefined && p.parsed.value === "hollow") {
3138
+ set[shapeProp] = open;
3139
+ }
3140
+ }
3141
+ }
3142
+
3143
+ /**
3144
+ * Turns each arrow whose `*-arrow-fill` is `hollow` into its outline shape. A hollow arrow the vocabulary has no
3145
+ * outline shape for keeps its filled shape and is noted.
3146
+ * @param n - the element count
3147
+ * @param words - the parts by internal name
3148
+ * @param values - the values; changed in place
3149
+ * @param notes - where a hollow arrow drawn filled is noted
3150
+ */
3151
+ function combineArrowFills(
3152
+ n: number,
3153
+ words: ReadonlyMap<string, (string | null)[]>,
3154
+ values: Record<string, (StyleValue | null)[]>,
3155
+ notes: StyleNotes,
3156
+ ): void {
3157
+ for (const p of ["targetArrow", "sourceArrow", "midTargetArrow", "midSourceArrow"]) {
3158
+ const fill = words.get(`_${p}Fill`);
3159
+ const shapes = values[p];
3160
+ if (fill === undefined || shapes === undefined) {
3161
+ continue;
3162
+ }
3163
+ for (let i = 0; i < n; i++) {
3164
+ const shape = shapes[i];
3165
+ if (fill[i] !== "hollow" || shape === null || shape === "none") {
3166
+ continue;
3167
+ }
3168
+ const open = HOLLOW_ARROWS.get(shape);
3169
+ if (open !== undefined) {
3170
+ shapes[i] = open;
3171
+ } else if (!String(shape).includes("open")) {
3172
+ const property = p.replaceAll(/[A-Z]/g, (c) => `-${c.toLowerCase()}`).replace("-arrow", "-arrow-fill");
3173
+ notes.add(STYLE_ISSUE.NOT_CARRIED, "edge", property, { sample: `hollow ${String(shape)}`, element: i });
3174
+ }
3175
+ }
3176
+ }
3177
+ }
3178
+
3179
+ /**
3180
+ * A node label's position from its vertical and horizontal alignments. Cytoscape.js 3.34 places an `-inside`
3181
+ * alignment against that side of the node, inside it (`recalculateNodeLabelProjection`): `top-inside` with
3182
+ * `center` is `inside-top`, `top-inside` with `left-inside` is `inside-top-left`. One inside and one outside
3183
+ * alignment have no neutral position of their own: the outside side here, its inside part read as centered, which
3184
+ * `mixedLabelPlaces()` replaces with the pinned place once the node's size is known.
1976
3185
  * @param v - the vertical alignment, or null
1977
3186
  * @param h - the horizontal alignment, or null
1978
- * @param note - notes an alignment read as another
1979
3187
  * @returns the position, or null
1980
3188
  */
1981
- function labelPosition(v: string | null, h: string | null, note: (sample: string) => void): StyleValue | null {
3189
+ function labelPosition(v: string | null, h: string | null): StyleValue | null {
1982
3190
  if (v === null && h === null) {
1983
3191
  return null;
1984
3192
  }
3193
+ const vIn = (v ?? "").endsWith("-inside");
3194
+ const hIn = (h ?? "").endsWith("-inside");
1985
3195
  const vv = (v ?? "top").replace("-inside", "");
1986
3196
  const hh = (h ?? "center").replace("-inside", "");
1987
- if ((v ?? "").endsWith("-inside") || (h ?? "").endsWith("-inside")) {
1988
- note(`${v ?? ""} ${h ?? ""}`);
3197
+ if (vIn || hIn) {
3198
+ const vOut = vv !== "center" && !vIn;
3199
+ const hOut = hh !== "center" && !hIn;
3200
+ if (!vOut && !hOut) {
3201
+ return INSIDE_POSITIONS.get(`${vv},${hh}`) ?? null;
3202
+ }
3203
+ return POSITIONS.get(`${vIn ? "center" : vv},${hIn ? "center" : hh}`) ?? null;
1989
3204
  }
1990
3205
  return POSITIONS.get(`${vv},${hh}`) ?? null;
1991
3206
  }
@@ -2091,7 +3306,7 @@ interface Draft {
2091
3306
  * @param entry - the entry
2092
3307
  * @param list - the matched core elements
2093
3308
  * @param props - the carried properties
2094
- * @param parts - every property of the entry, the combined parts too: a hollow fill makes the entry's own arrow open
3309
+ * @param all - every property of the entry, the parts combined afterward too
2095
3310
  * @returns the draft
2096
3311
  */
2097
3312
  function draftOf(
@@ -2099,7 +3314,7 @@ function draftOf(
2099
3314
  entry: Entry,
2100
3315
  list: readonly number[],
2101
3316
  props: readonly EntryProperty[],
2102
- parts: readonly EntryProperty[],
3317
+ all: readonly EntryProperty[],
2103
3318
  ): Draft {
2104
3319
  const setting = new Map<string, StyleValue>();
2105
3320
  const mapping = new Map<string, Mapping>();
@@ -2131,8 +3346,12 @@ function draftOf(
2131
3346
  mapping.set(p.spec.property, mapperMapping(ctx, entry, list, p));
2132
3347
  }
2133
3348
  }
2134
- openArrows(setting, parts);
2135
- const set = Object.fromEntries(setting);
3349
+ const set: Record<string, StyleValue> = Object.fromEntries(setting);
3350
+ if (ctx.kind === "node") {
3351
+ cyjsPolygonShapes(set);
3352
+ } else {
3353
+ hollowArrowsOfRule(set, all);
3354
+ }
2136
3355
  const map = Object.fromEntries(mapping);
2137
3356
  const hasMap = mapping.size > 0;
2138
3357
  // the predicate is built only for a rule that is not collapsed into a run: a run's rules need no column each