@graphty/graph-io 0.3.43 → 0.3.45

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 (674) hide show
  1. package/dist/chunks/approximations-DJMHdDvv.js +40 -0
  2. package/dist/chunks/approximations-DJMHdDvv.js.map +1 -0
  3. package/dist/chunks/block-CPCHkX42.js +2777 -0
  4. package/dist/chunks/block-CPCHkX42.js.map +1 -0
  5. package/dist/chunks/{choose-DvpbdRuz.js → choose--jM0yYCo.js} +3 -2
  6. package/dist/chunks/{choose-DvpbdRuz.js.map → choose--jM0yYCo.js.map} +1 -1
  7. package/dist/chunks/{constants-fT3E8JYY.js → constants-BKwqelYD.js} +6 -2
  8. package/dist/chunks/constants-BKwqelYD.js.map +1 -0
  9. package/dist/chunks/convert-BcQ8_jMo.js +59 -0
  10. package/dist/chunks/convert-BcQ8_jMo.js.map +1 -0
  11. package/dist/chunks/emit-DZy6JgdA.js +3456 -0
  12. package/dist/chunks/emit-DZy6JgdA.js.map +1 -0
  13. package/dist/chunks/{escape-B62ZGM_a.js → escape-CgzxiYPg.js} +44 -21
  14. package/dist/chunks/escape-CgzxiYPg.js.map +1 -0
  15. package/dist/chunks/{export-rKLeGQMo.js → export-BvqAXCKh.js} +196 -792
  16. package/dist/chunks/export-BvqAXCKh.js.map +1 -0
  17. package/dist/chunks/{exporter-yLxKBvLE.js → exporter--Mk9gXCJ.js} +4 -3
  18. package/dist/chunks/{exporter-yLxKBvLE.js.map → exporter--Mk9gXCJ.js.map} +1 -1
  19. package/dist/chunks/{exporter-RVtXVetw.js → exporter-C-9cgZKa.js} +1805 -287
  20. package/dist/chunks/exporter-C-9cgZKa.js.map +1 -0
  21. package/dist/chunks/{exporter-8hLNemFk.js → exporter-DFqJhBnj.js} +133 -74
  22. package/dist/chunks/exporter-DFqJhBnj.js.map +1 -0
  23. package/dist/chunks/{exporter-BWwEuOPT.js → exporter-DteVeJki.js} +22 -12
  24. package/dist/chunks/exporter-DteVeJki.js.map +1 -0
  25. package/dist/chunks/format-BpQBx8MZ.js +676 -0
  26. package/dist/chunks/format-BpQBx8MZ.js.map +1 -0
  27. package/dist/chunks/generic-BGO71QBv.js +1347 -0
  28. package/dist/chunks/generic-BGO71QBv.js.map +1 -0
  29. package/dist/chunks/{importer-C98dpSH4.js → importer-B-9Rc1zO.js} +141 -82
  30. package/dist/chunks/importer-B-9Rc1zO.js.map +1 -0
  31. package/dist/chunks/{importer-D794XUTe.js → importer-BVVR740x.js} +31 -8
  32. package/dist/chunks/importer-BVVR740x.js.map +1 -0
  33. package/dist/chunks/{importer-WrGRZb0N.js → importer-C0AjA3Ax.js} +449 -102
  34. package/dist/chunks/importer-C0AjA3Ax.js.map +1 -0
  35. package/dist/chunks/{importer-BbNb7eNg.js → importer-C7pvJe1L.js} +1398 -129
  36. package/dist/chunks/importer-C7pvJe1L.js.map +1 -0
  37. package/dist/chunks/{importer-xZ-3suY1.js → importer-ClwoVx3O.js} +791 -135
  38. package/dist/chunks/importer-ClwoVx3O.js.map +1 -0
  39. package/dist/chunks/{importer-Ht7LNg8L.js → importer-D0sC_6bO.js} +116 -296
  40. package/dist/chunks/importer-D0sC_6bO.js.map +1 -0
  41. package/dist/chunks/{importer-Dz7tI8Sa.js → importer-EHQk1jbM.js} +617 -115
  42. package/dist/chunks/importer-EHQk1jbM.js.map +1 -0
  43. package/dist/chunks/{json-elements-DKSqpNW1.js → json-elements-DJZRZ6jd.js} +123 -32
  44. package/dist/chunks/json-elements-DJZRZ6jd.js.map +1 -0
  45. package/dist/chunks/lexicon-WMsNddyU.js +509 -0
  46. package/dist/chunks/lexicon-WMsNddyU.js.map +1 -0
  47. package/dist/chunks/{ontology-export-CtRXjg_i.js → ontology-export-G4MqRdux.js} +3 -2
  48. package/dist/chunks/{ontology-export-CtRXjg_i.js.map → ontology-export-G4MqRdux.js.map} +1 -1
  49. package/dist/chunks/{records-Cu4bxYOm.js → records-CiaWZrFH.js} +2 -2
  50. package/dist/chunks/{records-Cu4bxYOm.js.map → records-CiaWZrFH.js.map} +1 -1
  51. package/dist/chunks/styles-DvoFBKCK.js +690 -0
  52. package/dist/chunks/styles-DvoFBKCK.js.map +1 -0
  53. package/dist/chunks/styles-yed-Cz9rLBCP.js +842 -0
  54. package/dist/chunks/styles-yed-Cz9rLBCP.js.map +1 -0
  55. package/dist/chunks/styles-zNTmz5R7.js +2102 -0
  56. package/dist/chunks/styles-zNTmz5R7.js.map +1 -0
  57. package/dist/chunks/{text-DHOJXnED.js → text-CzJNFOS9.js} +2 -2
  58. package/dist/chunks/{text-DHOJXnED.js.map → text-CzJNFOS9.js.map} +1 -1
  59. package/dist/chunks/units-XMZFIFe1.js +19 -0
  60. package/dist/chunks/units-XMZFIFe1.js.map +1 -0
  61. package/dist/chunks/writer-gQ8kx99H.js +448 -0
  62. package/dist/chunks/writer-gQ8kx99H.js.map +1 -0
  63. package/dist/csv.js +5 -4
  64. package/dist/csv.js.map +1 -1
  65. package/dist/cx.js +1 -1
  66. package/dist/cx2.js +2 -2
  67. package/dist/cys.js +1 -1
  68. package/dist/dot.js +5 -2
  69. package/dist/dot.js.map +1 -1
  70. package/dist/gexf.js +292 -412
  71. package/dist/gexf.js.map +1 -1
  72. package/dist/gml.js +927 -74
  73. package/dist/gml.js.map +1 -1
  74. package/dist/graph-io.js +351 -236
  75. package/dist/graph-io.js.map +1 -1
  76. package/dist/graphml.js +4 -2
  77. package/dist/graphml.js.map +1 -1
  78. package/dist/json.js +1 -1
  79. package/dist/neo4j.js +4 -3
  80. package/dist/neo4j.js.map +1 -1
  81. package/dist/obo.js +1 -1
  82. package/dist/pajek.js +4 -2
  83. package/dist/positions.d.ts +1 -0
  84. package/dist/positions.js +427 -0
  85. package/dist/positions.js.map +1 -0
  86. package/dist/src/common/codes.d.ts +6 -0
  87. package/dist/src/common/codes.d.ts.map +1 -1
  88. package/dist/src/common/codes.js +6 -0
  89. package/dist/src/common/codes.js.map +1 -1
  90. package/dist/src/common/escape.d.ts.map +1 -1
  91. package/dist/src/common/escape.js +4 -2
  92. package/dist/src/common/escape.js.map +1 -1
  93. package/dist/src/common/export.d.ts +10 -0
  94. package/dist/src/common/export.d.ts.map +1 -1
  95. package/dist/src/common/export.js +123 -100
  96. package/dist/src/common/export.js.map +1 -1
  97. package/dist/src/common/input.d.ts.map +1 -1
  98. package/dist/src/common/input.js +8 -1
  99. package/dist/src/common/input.js.map +1 -1
  100. package/dist/src/common/json-elements.d.ts +12 -9
  101. package/dist/src/common/json-elements.d.ts.map +1 -1
  102. package/dist/src/common/json-elements.js +160 -37
  103. package/dist/src/common/json-elements.js.map +1 -1
  104. package/dist/src/common/notes.d.ts +42 -0
  105. package/dist/src/common/notes.d.ts.map +1 -0
  106. package/dist/src/common/notes.js +72 -0
  107. package/dist/src/common/notes.js.map +1 -0
  108. package/dist/src/common/report.d.ts +18 -0
  109. package/dist/src/common/report.d.ts.map +1 -1
  110. package/dist/src/common/report.js +36 -1
  111. package/dist/src/common/report.js.map +1 -1
  112. package/dist/src/common/units.d.ts +24 -0
  113. package/dist/src/common/units.d.ts.map +1 -0
  114. package/dist/src/common/units.js +26 -0
  115. package/dist/src/common/units.js.map +1 -0
  116. package/dist/src/common/xml.d.ts +18 -0
  117. package/dist/src/common/xml.d.ts.map +1 -1
  118. package/dist/src/common/xml.js +42 -5
  119. package/dist/src/common/xml.js.map +1 -1
  120. package/dist/src/formats/cx/exporter.d.ts +6 -0
  121. package/dist/src/formats/cx/exporter.d.ts.map +1 -1
  122. package/dist/src/formats/cx/exporter.js +71 -8
  123. package/dist/src/formats/cx/exporter.js.map +1 -1
  124. package/dist/src/formats/cx/importer.d.ts +5 -2
  125. package/dist/src/formats/cx/importer.d.ts.map +1 -1
  126. package/dist/src/formats/cx/importer.js +102 -72
  127. package/dist/src/formats/cx/importer.js.map +1 -1
  128. package/dist/src/formats/cx/styles.d.ts +102 -0
  129. package/dist/src/formats/cx/styles.d.ts.map +1 -0
  130. package/dist/src/formats/cx/styles.js +225 -0
  131. package/dist/src/formats/cx/styles.js.map +1 -0
  132. package/dist/src/formats/cx2/exporter.d.ts +5 -0
  133. package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
  134. package/dist/src/formats/cx2/exporter.js +181 -71
  135. package/dist/src/formats/cx2/exporter.js.map +1 -1
  136. package/dist/src/formats/cx2/importer.d.ts +4 -1
  137. package/dist/src/formats/cx2/importer.d.ts.map +1 -1
  138. package/dist/src/formats/cx2/importer.js +19 -1
  139. package/dist/src/formats/cx2/importer.js.map +1 -1
  140. package/dist/src/formats/cx2/styles.d.ts +18 -0
  141. package/dist/src/formats/cx2/styles.d.ts.map +1 -0
  142. package/dist/src/formats/cx2/styles.js +24 -0
  143. package/dist/src/formats/cx2/styles.js.map +1 -0
  144. package/dist/src/formats/cys/constants.d.ts +2 -0
  145. package/dist/src/formats/cys/constants.d.ts.map +1 -1
  146. package/dist/src/formats/cys/constants.js +2 -0
  147. package/dist/src/formats/cys/constants.js.map +1 -1
  148. package/dist/src/formats/cys/exporter.d.ts +6 -2
  149. package/dist/src/formats/cys/exporter.d.ts.map +1 -1
  150. package/dist/src/formats/cys/exporter.js +215 -53
  151. package/dist/src/formats/cys/exporter.js.map +1 -1
  152. package/dist/src/formats/cys/importer.d.ts +2 -1
  153. package/dist/src/formats/cys/importer.d.ts.map +1 -1
  154. package/dist/src/formats/cys/importer.js +119 -6
  155. package/dist/src/formats/cys/importer.js.map +1 -1
  156. package/dist/src/formats/cys/session.d.ts +2 -0
  157. package/dist/src/formats/cys/session.d.ts.map +1 -1
  158. package/dist/src/formats/cys/session.js +5 -0
  159. package/dist/src/formats/cys/session.js.map +1 -1
  160. package/dist/src/formats/cys/styles.d.ts +38 -0
  161. package/dist/src/formats/cys/styles.d.ts.map +1 -0
  162. package/dist/src/formats/cys/styles.js +213 -0
  163. package/dist/src/formats/cys/styles.js.map +1 -0
  164. package/dist/src/formats/cys/tables.d.ts +8 -3
  165. package/dist/src/formats/cys/tables.d.ts.map +1 -1
  166. package/dist/src/formats/cys/tables.js +88 -34
  167. package/dist/src/formats/cys/tables.js.map +1 -1
  168. package/dist/src/formats/dot/exporter.d.ts +2 -0
  169. package/dist/src/formats/dot/exporter.d.ts.map +1 -1
  170. package/dist/src/formats/dot/exporter.js +195 -81
  171. package/dist/src/formats/dot/exporter.js.map +1 -1
  172. package/dist/src/formats/dot/importer.d.ts.map +1 -1
  173. package/dist/src/formats/dot/importer.js +100 -20
  174. package/dist/src/formats/dot/importer.js.map +1 -1
  175. package/dist/src/formats/dot/index.d.ts +1 -0
  176. package/dist/src/formats/dot/index.d.ts.map +1 -1
  177. package/dist/src/formats/dot/index.js +1 -0
  178. package/dist/src/formats/dot/index.js.map +1 -1
  179. package/dist/src/formats/dot/positions.d.ts +14 -0
  180. package/dist/src/formats/dot/positions.d.ts.map +1 -0
  181. package/dist/src/formats/dot/positions.js +70 -0
  182. package/dist/src/formats/dot/positions.js.map +1 -0
  183. package/dist/src/formats/dot/styles.d.ts +82 -0
  184. package/dist/src/formats/dot/styles.d.ts.map +1 -0
  185. package/dist/src/formats/dot/styles.js +1496 -0
  186. package/dist/src/formats/dot/styles.js.map +1 -0
  187. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  188. package/dist/src/formats/gexf/exporter.js +20 -5
  189. package/dist/src/formats/gexf/exporter.js.map +1 -1
  190. package/dist/src/formats/gexf/importer.d.ts +8 -1
  191. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  192. package/dist/src/formats/gexf/importer.js +251 -131
  193. package/dist/src/formats/gexf/importer.js.map +1 -1
  194. package/dist/src/formats/gexf/index.d.ts +8 -0
  195. package/dist/src/formats/gexf/index.d.ts.map +1 -1
  196. package/dist/src/formats/gexf/index.js +10 -2
  197. package/dist/src/formats/gexf/index.js.map +1 -1
  198. package/dist/src/formats/gexf/schema.d.ts +5 -0
  199. package/dist/src/formats/gexf/schema.d.ts.map +1 -1
  200. package/dist/src/formats/gexf/schema.js +5 -0
  201. package/dist/src/formats/gexf/schema.js.map +1 -1
  202. package/dist/src/formats/gexf/styles.d.ts +74 -0
  203. package/dist/src/formats/gexf/styles.d.ts.map +1 -0
  204. package/dist/src/formats/gexf/styles.js +604 -0
  205. package/dist/src/formats/gexf/styles.js.map +1 -0
  206. package/dist/src/formats/gml/exporter.d.ts +1 -5
  207. package/dist/src/formats/gml/exporter.d.ts.map +1 -1
  208. package/dist/src/formats/gml/exporter.js +19 -8
  209. package/dist/src/formats/gml/exporter.js.map +1 -1
  210. package/dist/src/formats/gml/importer.d.ts.map +1 -1
  211. package/dist/src/formats/gml/importer.js +4 -0
  212. package/dist/src/formats/gml/importer.js.map +1 -1
  213. package/dist/src/formats/gml/index.d.ts +2 -1
  214. package/dist/src/formats/gml/index.d.ts.map +1 -1
  215. package/dist/src/formats/gml/index.js +2 -1
  216. package/dist/src/formats/gml/index.js.map +1 -1
  217. package/dist/src/formats/gml/positions.d.ts +14 -0
  218. package/dist/src/formats/gml/positions.d.ts.map +1 -0
  219. package/dist/src/formats/gml/positions.js +46 -0
  220. package/dist/src/formats/gml/positions.js.map +1 -0
  221. package/dist/src/formats/gml/styles.d.ts +59 -0
  222. package/dist/src/formats/gml/styles.d.ts.map +1 -0
  223. package/dist/src/formats/gml/styles.js +1056 -0
  224. package/dist/src/formats/gml/styles.js.map +1 -0
  225. package/dist/src/formats/gml/syntax.d.ts +11 -0
  226. package/dist/src/formats/gml/syntax.d.ts.map +1 -1
  227. package/dist/src/formats/gml/syntax.js +151 -62
  228. package/dist/src/formats/gml/syntax.js.map +1 -1
  229. package/dist/src/formats/graphml/constants.d.ts +4 -0
  230. package/dist/src/formats/graphml/constants.d.ts.map +1 -1
  231. package/dist/src/formats/graphml/constants.js +5 -1
  232. package/dist/src/formats/graphml/constants.js.map +1 -1
  233. package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
  234. package/dist/src/formats/graphml/exporter.js +133 -65
  235. package/dist/src/formats/graphml/exporter.js.map +1 -1
  236. package/dist/src/formats/graphml/importer.d.ts +4 -1
  237. package/dist/src/formats/graphml/importer.d.ts.map +1 -1
  238. package/dist/src/formats/graphml/importer.js +100 -44
  239. package/dist/src/formats/graphml/importer.js.map +1 -1
  240. package/dist/src/formats/graphml/index.d.ts +1 -0
  241. package/dist/src/formats/graphml/index.d.ts.map +1 -1
  242. package/dist/src/formats/graphml/index.js +1 -0
  243. package/dist/src/formats/graphml/index.js.map +1 -1
  244. package/dist/src/formats/graphml/styles-plain.d.ts +19 -0
  245. package/dist/src/formats/graphml/styles-plain.d.ts.map +1 -0
  246. package/dist/src/formats/graphml/styles-plain.js +187 -0
  247. package/dist/src/formats/graphml/styles-plain.js.map +1 -0
  248. package/dist/src/formats/graphml/styles-yed.d.ts +179 -0
  249. package/dist/src/formats/graphml/styles-yed.d.ts.map +1 -0
  250. package/dist/src/formats/graphml/styles-yed.js +1086 -0
  251. package/dist/src/formats/graphml/styles-yed.js.map +1 -0
  252. package/dist/src/formats/graphml/styles-yfiles3.d.ts +19 -0
  253. package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -0
  254. package/dist/src/formats/graphml/styles-yfiles3.js +508 -0
  255. package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -0
  256. package/dist/src/formats/graphml/tree.d.ts.map +1 -1
  257. package/dist/src/formats/graphml/tree.js +34 -19
  258. package/dist/src/formats/graphml/tree.js.map +1 -1
  259. package/dist/src/formats/graphml/yfiles.d.ts +16 -2
  260. package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
  261. package/dist/src/formats/graphml/yfiles.js +83 -12
  262. package/dist/src/formats/graphml/yfiles.js.map +1 -1
  263. package/dist/src/formats/json/exporter.d.ts +5 -0
  264. package/dist/src/formats/json/exporter.d.ts.map +1 -1
  265. package/dist/src/formats/json/exporter.js +68 -25
  266. package/dist/src/formats/json/exporter.js.map +1 -1
  267. package/dist/src/formats/json/importer.d.ts +22 -3
  268. package/dist/src/formats/json/importer.d.ts.map +1 -1
  269. package/dist/src/formats/json/importer.js +439 -264
  270. package/dist/src/formats/json/importer.js.map +1 -1
  271. package/dist/src/formats/json/positions.d.ts +16 -0
  272. package/dist/src/formats/json/positions.d.ts.map +1 -0
  273. package/dist/src/formats/json/positions.js +44 -0
  274. package/dist/src/formats/json/positions.js.map +1 -0
  275. package/dist/src/formats/json/styles.d.ts +102 -0
  276. package/dist/src/formats/json/styles.d.ts.map +1 -0
  277. package/dist/src/formats/json/styles.js +1753 -0
  278. package/dist/src/formats/json/styles.js.map +1 -0
  279. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
  280. package/dist/src/formats/neo4j/exporter.js +2 -1
  281. package/dist/src/formats/neo4j/exporter.js.map +1 -1
  282. package/dist/src/formats/pajek/exporter.d.ts +2 -0
  283. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  284. package/dist/src/formats/pajek/exporter.js +142 -76
  285. package/dist/src/formats/pajek/exporter.js.map +1 -1
  286. package/dist/src/formats/pajek/importer.d.ts +4 -0
  287. package/dist/src/formats/pajek/importer.d.ts.map +1 -1
  288. package/dist/src/formats/pajek/importer.js +60 -6
  289. package/dist/src/formats/pajek/importer.js.map +1 -1
  290. package/dist/src/formats/pajek/index.d.ts +2 -0
  291. package/dist/src/formats/pajek/index.d.ts.map +1 -1
  292. package/dist/src/formats/pajek/index.js +2 -0
  293. package/dist/src/formats/pajek/index.js.map +1 -1
  294. package/dist/src/formats/pajek/styles.d.ts +50 -0
  295. package/dist/src/formats/pajek/styles.d.ts.map +1 -0
  296. package/dist/src/formats/pajek/styles.js +569 -0
  297. package/dist/src/formats/pajek/styles.js.map +1 -0
  298. package/dist/src/formats/pajek/syntax.d.ts +6 -0
  299. package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
  300. package/dist/src/formats/pajek/syntax.js +6 -0
  301. package/dist/src/formats/pajek/syntax.js.map +1 -1
  302. package/dist/src/formats/xgmml/columns.d.ts.map +1 -1
  303. package/dist/src/formats/xgmml/columns.js +34 -9
  304. package/dist/src/formats/xgmml/columns.js.map +1 -1
  305. package/dist/src/formats/xgmml/constants.d.ts +2 -0
  306. package/dist/src/formats/xgmml/constants.d.ts.map +1 -1
  307. package/dist/src/formats/xgmml/constants.js +3 -1
  308. package/dist/src/formats/xgmml/constants.js.map +1 -1
  309. package/dist/src/formats/xgmml/document.d.ts +12 -8
  310. package/dist/src/formats/xgmml/document.d.ts.map +1 -1
  311. package/dist/src/formats/xgmml/document.js +56 -13
  312. package/dist/src/formats/xgmml/document.js.map +1 -1
  313. package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
  314. package/dist/src/formats/xgmml/emit.js +12 -6
  315. package/dist/src/formats/xgmml/emit.js.map +1 -1
  316. package/dist/src/formats/xgmml/exporter.d.ts +3 -1
  317. package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
  318. package/dist/src/formats/xgmml/exporter.js +22 -9
  319. package/dist/src/formats/xgmml/exporter.js.map +1 -1
  320. package/dist/src/formats/xgmml/importer.d.ts +2 -1
  321. package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
  322. package/dist/src/formats/xgmml/importer.js +31 -24
  323. package/dist/src/formats/xgmml/importer.js.map +1 -1
  324. package/dist/src/formats/xgmml/styles.d.ts +104 -0
  325. package/dist/src/formats/xgmml/styles.d.ts.map +1 -0
  326. package/dist/src/formats/xgmml/styles.js +310 -0
  327. package/dist/src/formats/xgmml/styles.js.map +1 -0
  328. package/dist/src/index.d.ts +9 -7
  329. package/dist/src/index.d.ts.map +1 -1
  330. package/dist/src/index.js +7 -7
  331. package/dist/src/index.js.map +1 -1
  332. package/dist/src/positions/codes.d.ts +31 -0
  333. package/dist/src/positions/codes.d.ts.map +1 -0
  334. package/dist/src/positions/codes.js +26 -0
  335. package/dist/src/positions/codes.js.map +1 -0
  336. package/dist/src/positions/convert.d.ts +58 -0
  337. package/dist/src/positions/convert.d.ts.map +1 -0
  338. package/dist/src/positions/convert.js +107 -0
  339. package/dist/src/positions/convert.js.map +1 -0
  340. package/dist/src/positions/extract.d.ts +27 -0
  341. package/dist/src/positions/extract.d.ts.map +1 -0
  342. package/dist/src/positions/extract.js +526 -0
  343. package/dist/src/positions/extract.js.map +1 -0
  344. package/dist/src/positions/index.d.ts +10 -0
  345. package/dist/src/positions/index.d.ts.map +1 -0
  346. package/dist/src/positions/index.js +9 -0
  347. package/dist/src/positions/index.js.map +1 -0
  348. package/dist/src/positions/types.d.ts +208 -0
  349. package/dist/src/positions/types.d.ts.map +1 -0
  350. package/dist/src/positions/types.js +10 -0
  351. package/dist/src/positions/types.js.map +1 -0
  352. package/dist/src/registry.d.ts +30 -0
  353. package/dist/src/registry.d.ts.map +1 -1
  354. package/dist/src/registry.js +130 -9
  355. package/dist/src/registry.js.map +1 -1
  356. package/dist/src/styles/annotations.d.ts +132 -0
  357. package/dist/src/styles/annotations.d.ts.map +1 -0
  358. package/dist/src/styles/annotations.js +320 -0
  359. package/dist/src/styles/annotations.js.map +1 -0
  360. package/dist/src/styles/budget.d.ts +45 -0
  361. package/dist/src/styles/budget.d.ts.map +1 -0
  362. package/dist/src/styles/budget.js +58 -0
  363. package/dist/src/styles/budget.js.map +1 -0
  364. package/dist/src/styles/canon/bends.d.ts +32 -0
  365. package/dist/src/styles/canon/bends.d.ts.map +1 -0
  366. package/dist/src/styles/canon/bends.js +74 -0
  367. package/dist/src/styles/canon/bends.js.map +1 -0
  368. package/dist/src/styles/canon/color-tables.d.ts +22 -0
  369. package/dist/src/styles/canon/color-tables.d.ts.map +1 -0
  370. package/dist/src/styles/canon/color-tables.js +369 -0
  371. package/dist/src/styles/canon/color-tables.js.map +1 -0
  372. package/dist/src/styles/canon/color.d.ts +66 -0
  373. package/dist/src/styles/canon/color.d.ts.map +1 -0
  374. package/dist/src/styles/canon/color.js +548 -0
  375. package/dist/src/styles/canon/color.js.map +1 -0
  376. package/dist/src/styles/canon/enums.d.ts +25 -0
  377. package/dist/src/styles/canon/enums.d.ts.map +1 -0
  378. package/dist/src/styles/canon/enums.js +187 -0
  379. package/dist/src/styles/canon/enums.js.map +1 -0
  380. package/dist/src/styles/canon/graphviz.d.ts +48 -0
  381. package/dist/src/styles/canon/graphviz.d.ts.map +1 -0
  382. package/dist/src/styles/canon/graphviz.js +160 -0
  383. package/dist/src/styles/canon/graphviz.js.map +1 -0
  384. package/dist/src/styles/canon/index.d.ts +13 -0
  385. package/dist/src/styles/canon/index.d.ts.map +1 -0
  386. package/dist/src/styles/canon/index.js +13 -0
  387. package/dist/src/styles/canon/index.js.map +1 -0
  388. package/dist/src/styles/canon/number.d.ts +59 -0
  389. package/dist/src/styles/canon/number.d.ts.map +1 -0
  390. package/dist/src/styles/canon/number.js +142 -0
  391. package/dist/src/styles/canon/number.js.map +1 -0
  392. package/dist/src/styles/canon/site.d.ts +51 -0
  393. package/dist/src/styles/canon/site.d.ts.map +1 -0
  394. package/dist/src/styles/canon/site.js +63 -0
  395. package/dist/src/styles/canon/site.js.map +1 -0
  396. package/dist/src/styles/canon/text.d.ts +61 -0
  397. package/dist/src/styles/canon/text.d.ts.map +1 -0
  398. package/dist/src/styles/canon/text.js +199 -0
  399. package/dist/src/styles/canon/text.js.map +1 -0
  400. package/dist/src/styles/codes.d.ts +95 -0
  401. package/dist/src/styles/codes.d.ts.map +1 -0
  402. package/dist/src/styles/codes.js +97 -0
  403. package/dist/src/styles/codes.js.map +1 -0
  404. package/dist/src/styles/cyjs/cascade.d.ts +134 -0
  405. package/dist/src/styles/cyjs/cascade.d.ts.map +1 -0
  406. package/dist/src/styles/cyjs/cascade.js +2252 -0
  407. package/dist/src/styles/cyjs/cascade.js.map +1 -0
  408. package/dist/src/styles/cyjs/defaults-table.d.ts +25 -0
  409. package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -0
  410. package/dist/src/styles/cyjs/defaults-table.js +76 -0
  411. package/dist/src/styles/cyjs/defaults-table.js.map +1 -0
  412. package/dist/src/styles/cyjs/emit.d.ts +82 -0
  413. package/dist/src/styles/cyjs/emit.d.ts.map +1 -0
  414. package/dist/src/styles/cyjs/emit.js +791 -0
  415. package/dist/src/styles/cyjs/emit.js.map +1 -0
  416. package/dist/src/styles/cyjs/index.d.ts +13 -0
  417. package/dist/src/styles/cyjs/index.d.ts.map +1 -0
  418. package/dist/src/styles/cyjs/index.js +13 -0
  419. package/dist/src/styles/cyjs/index.js.map +1 -0
  420. package/dist/src/styles/cyjs/live.d.ts +46 -0
  421. package/dist/src/styles/cyjs/live.d.ts.map +1 -0
  422. package/dist/src/styles/cyjs/live.js +108 -0
  423. package/dist/src/styles/cyjs/live.js.map +1 -0
  424. package/dist/src/styles/cyjs/mappers.d.ts +147 -0
  425. package/dist/src/styles/cyjs/mappers.d.ts.map +1 -0
  426. package/dist/src/styles/cyjs/mappers.js +744 -0
  427. package/dist/src/styles/cyjs/mappers.js.map +1 -0
  428. package/dist/src/styles/cyjs/selector.d.ts +113 -0
  429. package/dist/src/styles/cyjs/selector.d.ts.map +1 -0
  430. package/dist/src/styles/cyjs/selector.js +611 -0
  431. package/dist/src/styles/cyjs/selector.js.map +1 -0
  432. package/dist/src/styles/cyjs/stylesheet-text.d.ts +37 -0
  433. package/dist/src/styles/cyjs/stylesheet-text.d.ts.map +1 -0
  434. package/dist/src/styles/cyjs/stylesheet-text.js +181 -0
  435. package/dist/src/styles/cyjs/stylesheet-text.js.map +1 -0
  436. package/dist/src/styles/cytoscape/emit.d.ts +125 -0
  437. package/dist/src/styles/cytoscape/emit.d.ts.map +1 -0
  438. package/dist/src/styles/cytoscape/emit.js +1025 -0
  439. package/dist/src/styles/cytoscape/emit.js.map +1 -0
  440. package/dist/src/styles/cytoscape/index.d.ts +10 -0
  441. package/dist/src/styles/cytoscape/index.d.ts.map +1 -0
  442. package/dist/src/styles/cytoscape/index.js +10 -0
  443. package/dist/src/styles/cytoscape/index.js.map +1 -0
  444. package/dist/src/styles/cytoscape/lexicon.d.ts +49 -0
  445. package/dist/src/styles/cytoscape/lexicon.d.ts.map +1 -0
  446. package/dist/src/styles/cytoscape/lexicon.js +605 -0
  447. package/dist/src/styles/cytoscape/lexicon.js.map +1 -0
  448. package/dist/src/styles/cytoscape/mappings.d.ts +171 -0
  449. package/dist/src/styles/cytoscape/mappings.d.ts.map +1 -0
  450. package/dist/src/styles/cytoscape/mappings.js +460 -0
  451. package/dist/src/styles/cytoscape/mappings.js.map +1 -0
  452. package/dist/src/styles/cytoscape/resolve.d.ts +137 -0
  453. package/dist/src/styles/cytoscape/resolve.d.ts.map +1 -0
  454. package/dist/src/styles/cytoscape/resolve.js +381 -0
  455. package/dist/src/styles/cytoscape/resolve.js.map +1 -0
  456. package/dist/src/styles/cytoscape/translate.d.ts +50 -0
  457. package/dist/src/styles/cytoscape/translate.d.ts.map +1 -0
  458. package/dist/src/styles/cytoscape/translate.js +156 -0
  459. package/dist/src/styles/cytoscape/translate.js.map +1 -0
  460. package/dist/src/styles/defaults.d.ts +90 -0
  461. package/dist/src/styles/defaults.d.ts.map +1 -0
  462. package/dist/src/styles/defaults.js +414 -0
  463. package/dist/src/styles/defaults.js.map +1 -0
  464. package/dist/src/styles/index.d.ts +15 -0
  465. package/dist/src/styles/index.d.ts.map +1 -0
  466. package/dist/src/styles/index.js +14 -0
  467. package/dist/src/styles/index.js.map +1 -0
  468. package/dist/src/styles/internal.d.ts +17 -0
  469. package/dist/src/styles/internal.d.ts.map +1 -0
  470. package/dist/src/styles/internal.js +16 -0
  471. package/dist/src/styles/internal.js.map +1 -0
  472. package/dist/src/styles/io/approximations.d.ts +24 -0
  473. package/dist/src/styles/io/approximations.d.ts.map +1 -0
  474. package/dist/src/styles/io/approximations.js +51 -0
  475. package/dist/src/styles/io/approximations.js.map +1 -0
  476. package/dist/src/styles/io/block.d.ts +166 -0
  477. package/dist/src/styles/io/block.d.ts.map +1 -0
  478. package/dist/src/styles/io/block.js +930 -0
  479. package/dist/src/styles/io/block.js.map +1 -0
  480. package/dist/src/styles/io/generic.d.ts +88 -0
  481. package/dist/src/styles/io/generic.d.ts.map +1 -0
  482. package/dist/src/styles/io/generic.js +516 -0
  483. package/dist/src/styles/io/generic.js.map +1 -0
  484. package/dist/src/styles/io/index.d.ts +8 -0
  485. package/dist/src/styles/io/index.d.ts.map +1 -0
  486. package/dist/src/styles/io/index.js +7 -0
  487. package/dist/src/styles/io/index.js.map +1 -0
  488. package/dist/src/styles/io/writer.d.ts +89 -0
  489. package/dist/src/styles/io/writer.d.ts.map +1 -0
  490. package/dist/src/styles/io/writer.js +626 -0
  491. package/dist/src/styles/io/writer.js.map +1 -0
  492. package/dist/src/styles/notes.d.ts +47 -0
  493. package/dist/src/styles/notes.d.ts.map +1 -0
  494. package/dist/src/styles/notes.js +140 -0
  495. package/dist/src/styles/notes.js.map +1 -0
  496. package/dist/src/styles/plan/classes.d.ts +31 -0
  497. package/dist/src/styles/plan/classes.d.ts.map +1 -0
  498. package/dist/src/styles/plan/classes.js +139 -0
  499. package/dist/src/styles/plan/classes.js.map +1 -0
  500. package/dist/src/styles/plan/continuous.d.ts +32 -0
  501. package/dist/src/styles/plan/continuous.d.ts.map +1 -0
  502. package/dist/src/styles/plan/continuous.js +165 -0
  503. package/dist/src/styles/plan/continuous.js.map +1 -0
  504. package/dist/src/styles/plan/discrete.d.ts +39 -0
  505. package/dist/src/styles/plan/discrete.d.ts.map +1 -0
  506. package/dist/src/styles/plan/discrete.js +127 -0
  507. package/dist/src/styles/plan/discrete.js.map +1 -0
  508. package/dist/src/styles/plan/families.d.ts +31 -0
  509. package/dist/src/styles/plan/families.d.ts.map +1 -0
  510. package/dist/src/styles/plan/families.js +95 -0
  511. package/dist/src/styles/plan/families.js.map +1 -0
  512. package/dist/src/styles/plan/features.d.ts +58 -0
  513. package/dist/src/styles/plan/features.d.ts.map +1 -0
  514. package/dist/src/styles/plan/features.js +185 -0
  515. package/dist/src/styles/plan/features.js.map +1 -0
  516. package/dist/src/styles/plan/index.d.ts +7 -0
  517. package/dist/src/styles/plan/index.d.ts.map +1 -0
  518. package/dist/src/styles/plan/index.js +7 -0
  519. package/dist/src/styles/plan/index.js.map +1 -0
  520. package/dist/src/styles/plan/plan.d.ts +71 -0
  521. package/dist/src/styles/plan/plan.d.ts.map +1 -0
  522. package/dist/src/styles/plan/plan.js +893 -0
  523. package/dist/src/styles/plan/plan.js.map +1 -0
  524. package/dist/src/styles/plan/resolve.d.ts +137 -0
  525. package/dist/src/styles/plan/resolve.d.ts.map +1 -0
  526. package/dist/src/styles/plan/resolve.js +587 -0
  527. package/dist/src/styles/plan/resolve.js.map +1 -0
  528. package/dist/src/styles/types.d.ts +702 -0
  529. package/dist/src/styles/types.d.ts.map +1 -0
  530. package/dist/src/styles/types.js +10 -0
  531. package/dist/src/styles/types.js.map +1 -0
  532. package/dist/src/styles/vocabulary.d.ts +41 -0
  533. package/dist/src/styles/vocabulary.d.ts.map +1 -0
  534. package/dist/src/styles/vocabulary.js +140 -0
  535. package/dist/src/styles/vocabulary.js.map +1 -0
  536. package/dist/src/types.d.ts +64 -1
  537. package/dist/src/types.d.ts.map +1 -1
  538. package/dist/src/types.js.map +1 -1
  539. package/dist/styles.d.ts +1 -0
  540. package/dist/styles.js +322 -0
  541. package/dist/styles.js.map +1 -0
  542. package/dist/xgmml.js +5 -4
  543. package/dist/xgmml.js.map +1 -1
  544. package/package.json +13 -2
  545. package/src/common/codes.ts +7 -0
  546. package/src/common/escape.ts +5 -2
  547. package/src/common/export.ts +179 -140
  548. package/src/common/input.ts +8 -4
  549. package/src/common/json-elements.ts +169 -38
  550. package/src/common/notes.ts +94 -0
  551. package/src/common/report.ts +41 -1
  552. package/src/common/units.ts +42 -0
  553. package/src/common/xml.ts +45 -5
  554. package/src/formats/cx/exporter.ts +95 -11
  555. package/src/formats/cx/importer.ts +126 -90
  556. package/src/formats/cx/styles.ts +304 -0
  557. package/src/formats/cx2/exporter.ts +234 -77
  558. package/src/formats/cx2/importer.ts +19 -1
  559. package/src/formats/cx2/styles.ts +29 -0
  560. package/src/formats/cys/constants.ts +3 -0
  561. package/src/formats/cys/exporter.ts +246 -60
  562. package/src/formats/cys/importer.ts +125 -9
  563. package/src/formats/cys/session.ts +6 -0
  564. package/src/formats/cys/styles.ts +254 -0
  565. package/src/formats/cys/tables.ts +117 -48
  566. package/src/formats/dot/exporter.ts +262 -126
  567. package/src/formats/dot/importer.ts +111 -20
  568. package/src/formats/dot/index.ts +1 -0
  569. package/src/formats/dot/positions.ts +77 -0
  570. package/src/formats/dot/styles.ts +1755 -0
  571. package/src/formats/gexf/exporter.ts +27 -11
  572. package/src/formats/gexf/importer.ts +308 -158
  573. package/src/formats/gexf/index.ts +10 -0
  574. package/src/formats/gexf/schema.ts +5 -0
  575. package/src/formats/gexf/styles.ts +719 -0
  576. package/src/formats/gml/exporter.ts +33 -14
  577. package/src/formats/gml/importer.ts +4 -0
  578. package/src/formats/gml/index.ts +2 -1
  579. package/src/formats/gml/positions.ts +52 -0
  580. package/src/formats/gml/styles.ts +1248 -0
  581. package/src/formats/gml/syntax.ts +173 -62
  582. package/src/formats/graphml/constants.ts +5 -0
  583. package/src/formats/graphml/exporter.ts +163 -71
  584. package/src/formats/graphml/importer.ts +119 -45
  585. package/src/formats/graphml/index.ts +1 -0
  586. package/src/formats/graphml/styles-plain.ts +236 -0
  587. package/src/formats/graphml/styles-yed.ts +1221 -0
  588. package/src/formats/graphml/styles-yfiles3.ts +573 -0
  589. package/src/formats/graphml/tree.ts +36 -20
  590. package/src/formats/graphml/yfiles.ts +89 -13
  591. package/src/formats/json/exporter.ts +76 -26
  592. package/src/formats/json/importer.ts +541 -322
  593. package/src/formats/json/positions.ts +50 -0
  594. package/src/formats/json/styles.ts +2082 -0
  595. package/src/formats/neo4j/exporter.ts +2 -1
  596. package/src/formats/pajek/exporter.ts +195 -106
  597. package/src/formats/pajek/importer.ts +78 -9
  598. package/src/formats/pajek/index.ts +2 -0
  599. package/src/formats/pajek/styles.ts +685 -0
  600. package/src/formats/pajek/syntax.ts +7 -0
  601. package/src/formats/xgmml/columns.ts +39 -9
  602. package/src/formats/xgmml/constants.ts +3 -0
  603. package/src/formats/xgmml/document.ts +54 -12
  604. package/src/formats/xgmml/emit.ts +11 -6
  605. package/src/formats/xgmml/exporter.ts +32 -13
  606. package/src/formats/xgmml/importer.ts +43 -29
  607. package/src/formats/xgmml/styles.ts +400 -0
  608. package/src/index.ts +16 -0
  609. package/src/positions/codes.ts +32 -0
  610. package/src/positions/convert.ts +123 -0
  611. package/src/positions/extract.ts +619 -0
  612. package/src/positions/index.ts +23 -0
  613. package/src/positions/types.ts +221 -0
  614. package/src/registry.ts +179 -8
  615. package/src/styles/annotations.ts +478 -0
  616. package/src/styles/budget.ts +60 -0
  617. package/src/styles/canon/bends.ts +84 -0
  618. package/src/styles/canon/color-tables.ts +386 -0
  619. package/src/styles/canon/color.ts +624 -0
  620. package/src/styles/canon/enums.ts +211 -0
  621. package/src/styles/canon/graphviz.ts +189 -0
  622. package/src/styles/canon/index.ts +13 -0
  623. package/src/styles/canon/number.ts +152 -0
  624. package/src/styles/canon/site.ts +87 -0
  625. package/src/styles/canon/text.ts +221 -0
  626. package/src/styles/codes.ts +104 -0
  627. package/src/styles/cyjs/cascade.ts +2670 -0
  628. package/src/styles/cyjs/defaults-table.ts +82 -0
  629. package/src/styles/cyjs/emit.ts +952 -0
  630. package/src/styles/cyjs/index.ts +37 -0
  631. package/src/styles/cyjs/live.ts +147 -0
  632. package/src/styles/cyjs/mappers.ts +879 -0
  633. package/src/styles/cyjs/selector.ts +778 -0
  634. package/src/styles/cyjs/stylesheet-text.ts +214 -0
  635. package/src/styles/cytoscape/emit.ts +1264 -0
  636. package/src/styles/cytoscape/index.ts +40 -0
  637. package/src/styles/cytoscape/lexicon.ts +658 -0
  638. package/src/styles/cytoscape/mappings.ts +603 -0
  639. package/src/styles/cytoscape/resolve.ts +521 -0
  640. package/src/styles/cytoscape/translate.ts +237 -0
  641. package/src/styles/defaults.ts +504 -0
  642. package/src/styles/index.ts +57 -0
  643. package/src/styles/internal.ts +17 -0
  644. package/src/styles/io/approximations.ts +47 -0
  645. package/src/styles/io/block.ts +1138 -0
  646. package/src/styles/io/generic.ts +687 -0
  647. package/src/styles/io/index.ts +18 -0
  648. package/src/styles/io/writer.ts +780 -0
  649. package/src/styles/notes.ts +165 -0
  650. package/src/styles/plan/classes.ts +166 -0
  651. package/src/styles/plan/continuous.ts +218 -0
  652. package/src/styles/plan/discrete.ts +165 -0
  653. package/src/styles/plan/families.ts +115 -0
  654. package/src/styles/plan/features.ts +234 -0
  655. package/src/styles/plan/index.ts +7 -0
  656. package/src/styles/plan/plan.ts +1129 -0
  657. package/src/styles/plan/resolve.ts +685 -0
  658. package/src/styles/types.ts +814 -0
  659. package/src/styles/vocabulary.ts +172 -0
  660. package/src/types.ts +76 -1
  661. package/dist/chunks/constants-fT3E8JYY.js.map +0 -1
  662. package/dist/chunks/escape-B62ZGM_a.js.map +0 -1
  663. package/dist/chunks/export-rKLeGQMo.js.map +0 -1
  664. package/dist/chunks/exporter-8hLNemFk.js.map +0 -1
  665. package/dist/chunks/exporter-BWwEuOPT.js.map +0 -1
  666. package/dist/chunks/exporter-RVtXVetw.js.map +0 -1
  667. package/dist/chunks/importer-BbNb7eNg.js.map +0 -1
  668. package/dist/chunks/importer-C98dpSH4.js.map +0 -1
  669. package/dist/chunks/importer-D794XUTe.js.map +0 -1
  670. package/dist/chunks/importer-Dz7tI8Sa.js.map +0 -1
  671. package/dist/chunks/importer-Ht7LNg8L.js.map +0 -1
  672. package/dist/chunks/importer-WrGRZb0N.js.map +0 -1
  673. package/dist/chunks/importer-xZ-3suY1.js.map +0 -1
  674. package/dist/chunks/json-elements-DKSqpNW1.js.map +0 -1
@@ -0,0 +1,1248 @@
1
+ /**
2
+ * GML styles: each node's and edge's `graphics [ ... ]` and `LabelGraphics [ ... ]` records read the way the program
3
+ * that wrote the file draws them, and a graph's styles written back as yEd GML graphics.
4
+ *
5
+ * GML has one syntax and many vocabularies. The `Creator` line (top level, or inside the graph as Gephi writes it,
6
+ * quoted or not) names the writer: yEd and yFiles, Cytoscape, Gephi, OGDF and igraph are recognized; every other file
7
+ * (NetworkX writes no `Creator`) is read with the keys of the GML draft (Graphlet). A recognized drawing program's
8
+ * defaults go beneath the file's own values; igraph and unknown writers get none.
9
+ *
10
+ * Writing: the yEd keys (`fill`, `outline`, `outlineWidth`, `outlineStyle`, `w`, `h`, `type`; edge `fill`, `width`,
11
+ * `style`, `sourceArrow`, `targetArrow`, `smoothBends`; `LabelGraphics` `text`, `color`, `fontSize`, `fontName`,
12
+ * `fontStyle`) replace every style key any reader here reads, inside the records the graph already has, so positions,
13
+ * bends and other keys stay. The `graphty.styles` block and the per-element values its rules read are keys too,
14
+ * named by the GML writer's rule for names that are not GML keys (`graphty_styles`, `graphty_style_node_<key>`).
15
+ */
16
+
17
+ import type { GraphSnapshot } from "@graphty/graph-format";
18
+
19
+ import { type BendPoint, bendsToPoints, canonBends } from "../../styles/canon/bends.js";
20
+ import { canonColor, colorOpacity } from "../../styles/canon/color.js";
21
+ import { canonEnum } from "../../styles/canon/enums.js";
22
+ import { canonNumber } from "../../styles/canon/number.js";
23
+ import { type CanonSite, MAX_STYLE_VALUE_LENGTH, noteAt, scalarText } from "../../styles/canon/site.js";
24
+ import { canonText } from "../../styles/canon/text.js";
25
+ import { STYLE_ISSUE } from "../../styles/codes.js";
26
+ import { CYTOSCAPE_LEXICON } from "../../styles/cytoscape/lexicon.js";
27
+ import { StyleApproximations } from "../../styles/io/approximations.js";
28
+ import {
29
+ blockStyles,
30
+ parseStyleBlock,
31
+ STYLE_BLOCK_NAME,
32
+ styleBlockLosses,
33
+ styleTag,
34
+ writeStyleBlock,
35
+ } from "../../styles/io/block.js";
36
+ import { dataRead, type ExporterStyleInput } from "../../styles/io/writer.js";
37
+ import { StyleNotes } from "../../styles/notes.js";
38
+ import { styleFingerprint } from "../../styles/plan/features.js";
39
+ import { plainCell, resolveStyles } from "../../styles/plan/resolve.js";
40
+ import type {
41
+ ImporterStyles,
42
+ StyleColumn,
43
+ StyleDialect,
44
+ StyleReadContext,
45
+ StyleTarget,
46
+ StyleTool,
47
+ StyleValue,
48
+ StyleWriteCapabilities,
49
+ } from "../../styles/types.js";
50
+ import type { LossNote } from "../../types.js";
51
+ import { gephiRules } from "../gexf/styles.js";
52
+ import {
53
+ flowchartShape,
54
+ FONT_STYLES,
55
+ type ValueColumns,
56
+ valueColumns,
57
+ YED_ARROWS,
58
+ YED_LINES,
59
+ YED_SHAPES,
60
+ yedColor,
61
+ yedSpelled,
62
+ yedValue,
63
+ } from "../graphml/styles-yed.js";
64
+ import { KEY_MANGLED_CODE, mangleGmlKey } from "./syntax.js";
65
+
66
+ /** A GML record as the importer keeps it (a json cell). */
67
+ type Rec = Record<string, unknown>;
68
+
69
+ /** The key vocabulary a file's graphics are read with. */
70
+ type Vocabulary = "yed" | "cytoscape" | "ogdf" | "graphlet";
71
+
72
+ /** Who wrote a GML file, as far as its styles go. */
73
+ interface GmlWriter {
74
+ /** The dialect recorded in the styles. */
75
+ readonly dialect: StyleDialect;
76
+ /** The program whose defaults apply, or null. */
77
+ readonly tool: StyleTool | null;
78
+ /** The keys its graphics are read with. */
79
+ readonly vocabulary: Vocabulary;
80
+ /** How its colors and names are spelled. */
81
+ readonly spelling: StyleDialect;
82
+ /** The unit of its lengths. */
83
+ readonly units: "px" | "relative";
84
+ /** Whether it draws the `label` key of an element that has graphics (the GML draft does; yEd and Gephi do not). */
85
+ readonly labelKey: boolean;
86
+ }
87
+
88
+ /** The writers recognized by their `Creator` text, first match wins. */
89
+ const WRITERS: readonly (readonly [RegExp, GmlWriter])[] = [
90
+ [
91
+ /^\s*(yfiles|yed)\b/i,
92
+ { dialect: "yed", tool: "yed", vocabulary: "yed", spelling: "yed", units: "px", labelKey: false },
93
+ ],
94
+ [
95
+ /^\s*cytoscape\b/i,
96
+ {
97
+ dialect: "cytoscape",
98
+ tool: "cytoscape",
99
+ vocabulary: "cytoscape",
100
+ spelling: "cytoscape",
101
+ units: "px",
102
+ labelKey: true,
103
+ },
104
+ ],
105
+ [
106
+ /^\s*gephi\b/i,
107
+ {
108
+ dialect: "gephi",
109
+ tool: "gephi",
110
+ vocabulary: "graphlet",
111
+ spelling: "css",
112
+ units: "relative",
113
+ labelKey: false,
114
+ },
115
+ ],
116
+ [
117
+ /^\s*ogdf::/i,
118
+ { dialect: "ogdf", tool: "ogdf", vocabulary: "ogdf", spelling: "css", units: "px", labelKey: true },
119
+ ],
120
+ // igraph writes a Creator line, but no drawing defaults of igraph's are stated by a primary source
121
+ [
122
+ /^\s*igraph\b/i,
123
+ { dialect: "igraph", tool: null, vocabulary: "graphlet", spelling: "css", units: "px", labelKey: true },
124
+ ],
125
+ ];
126
+
127
+ /** Any other writer (NetworkX, SNDlib, hand-written files): the GML draft's keys, no defaults. */
128
+ const UNKNOWN_WRITER: GmlWriter = {
129
+ dialect: "gml",
130
+ tool: null,
131
+ vocabulary: "graphlet",
132
+ spelling: "css",
133
+ units: "px",
134
+ labelKey: true,
135
+ };
136
+
137
+ /** A file graphty wrote: yEd keys, and the `graphty.styles` block gives the rules. */
138
+ const GRAPHTY_WRITER: GmlWriter = { ...WRITERS[0][1], tool: null };
139
+
140
+ /**
141
+ * The graph key holding the `graphty.styles` block: the block's name under the GML writer's rule for names that are
142
+ * not GML keys.
143
+ * @category Built-in formats
144
+ */
145
+ export const GML_STYLE_BLOCK_KEY = mangleGmlKey(STYLE_BLOCK_NAME);
146
+
147
+ /** The key prefix of the per-element style values (`graphty.style.` under the same rule). */
148
+ const TAG_PREFIX = mangleGmlKey("graphty.style.");
149
+
150
+ /**
151
+ * The GML key of one style column's per-element values.
152
+ * @param target - nodes or edges
153
+ * @param key - the style column's key
154
+ * @returns the key
155
+ */
156
+ function tagKey(target: StyleTarget, key: string): string {
157
+ return mangleGmlKey(styleTag(target, key));
158
+ }
159
+
160
+ /**
161
+ * A graph value as text, or null.
162
+ * @param snapshot - the graph
163
+ * @param name - the graph column
164
+ * @returns the text
165
+ */
166
+ function graphText(snapshot: GraphSnapshot, name: string): string | null {
167
+ if (!snapshot.graph.has(name)) {
168
+ return null;
169
+ }
170
+ const v = plainCell(snapshot.graph.value(name, 0));
171
+ if (typeof v === "string") {
172
+ return v;
173
+ }
174
+ return typeof v === "number" ? String(v) : null;
175
+ }
176
+
177
+ /**
178
+ * Who wrote a GML file: the `Creator` line (top level, or a key of the graph block as Gephi writes it), or graphty when
179
+ * the file holds a `graphty.styles` block.
180
+ * @param snapshot - the graph
181
+ * @returns the writer
182
+ */
183
+ function writerOf(snapshot: GraphSnapshot): GmlWriter {
184
+ if (snapshot.graph.has(GML_STYLE_BLOCK_KEY)) {
185
+ return GRAPHTY_WRITER;
186
+ }
187
+ const creator = snapshot.meta.creator ?? graphText(snapshot, "Creator") ?? "";
188
+ for (const [pattern, writer] of WRITERS) {
189
+ if (pattern.test(creator)) {
190
+ return writer;
191
+ }
192
+ }
193
+ return UNKNOWN_WRITER;
194
+ }
195
+
196
+ // ============================================================ reading
197
+
198
+ /** One element's canonical value of a property; null leaves it unset. */
199
+ type Put = (property: string, value: StyleValue | null) => void;
200
+
201
+ /** Where a value of an element was read, by property. */
202
+ type At = (property: string) => CanonSite;
203
+
204
+ /** The edge's `arrow` key of the GML draft: which ends have an arrow. */
205
+ const ARROW_ENDS: ReadonlyMap<string, readonly [string, string]> = new Map([
206
+ ["none", ["none", "none"]],
207
+ ["last", ["none", "triangle"]],
208
+ ["first", ["triangle", "none"]],
209
+ ["both", ["triangle", "triangle"]],
210
+ ]);
211
+
212
+ /** OGDF's stroke types (`stipple`), by number. */
213
+ const OGDF_STROKES: readonly string[] = ["none", "solid", "dashed", "dotted", "dash-dot", "dash-dot"];
214
+
215
+ /** OGDF's shape names the common table does not know. */
216
+ const OGDF_SHAPES: ReadonlyMap<string, string> = new Map([
217
+ ["rect", "rectangle"],
218
+ ["rhomb", "diamond"],
219
+ ["trapeze", "trapezoid"],
220
+ ["invtrapeze", "trapezoid"],
221
+ ["invtriangle", "triangle"],
222
+ ["invparallelogram", "parallelogram"],
223
+ ]);
224
+
225
+ /** Keys only yEd writes in a label record: its `fill` is the label's background, not the text color. */
226
+ const YED_LABEL_KEYS: readonly string[] = [
227
+ "outline",
228
+ "autoSizePolicy",
229
+ "model",
230
+ "configuration",
231
+ "borderDistance",
232
+ "modelName",
233
+ "modelPosition",
234
+ ];
235
+
236
+ /** Cytoscape's legacy numbered arrows (`source_arrow 3`), read as Cytoscape reads them. */
237
+ const cytoscapeArrow = CYTOSCAPE_LEXICON.edge.get("targetArrow")?.[0]?.[1];
238
+
239
+ /**
240
+ * A short sample of a value for a note.
241
+ * @param raw - the value
242
+ * @returns its text, or "a record" for a record
243
+ */
244
+ function sampleOf(raw: unknown): string {
245
+ return typeof raw === "string" || typeof raw === "number"
246
+ ? String(raw).slice(0, MAX_STYLE_VALUE_LENGTH)
247
+ : "a record";
248
+ }
249
+
250
+ /**
251
+ * Whether a value is a GML record.
252
+ * @param v - the value
253
+ * @returns true for a non-array object
254
+ */
255
+ function isRec(v: unknown): v is Rec {
256
+ return typeof v === "object" && v !== null && !Array.isArray(v);
257
+ }
258
+
259
+ /**
260
+ * The first defined value of some keys of a record.
261
+ * @param r - the record
262
+ * @param keys - the keys, in order of preference
263
+ * @returns the value, or undefined
264
+ */
265
+ function first(r: Rec, ...keys: string[]): unknown {
266
+ for (const k of keys) {
267
+ if (Object.hasOwn(r, k) && r[k] !== undefined) {
268
+ return r[k];
269
+ }
270
+ }
271
+ return undefined;
272
+ }
273
+
274
+ /**
275
+ * Reads a color and its alpha.
276
+ * @param raw - the value
277
+ * @param property - the color property
278
+ * @param opacity - its opacity property, or null
279
+ * @param writer - the writer
280
+ * @param put - receives the values
281
+ * @param at - the sites
282
+ */
283
+ function readColor(raw: unknown, property: string, opacity: string | null, writer: GmlWriter, put: Put, at: At): void {
284
+ if (raw === undefined) {
285
+ return;
286
+ }
287
+ const c = canonColor(raw, writer.spelling, at(property));
288
+ if (c === null) {
289
+ return;
290
+ }
291
+ put(property, c.color);
292
+ const alpha = colorOpacity(c, null, at(property), opacity !== null);
293
+ if (opacity !== null) {
294
+ put(opacity, alpha);
295
+ }
296
+ }
297
+
298
+ /**
299
+ * A stroke style: OGDF's numbered `stipple`, or a line type name.
300
+ * @param raw - the value
301
+ * @param kind - border or line style
302
+ * @param writer - the writer
303
+ * @param at - the site
304
+ * @returns the style, or null
305
+ */
306
+ function strokeStyle(raw: unknown, kind: "borderStyle" | "lineStyle", writer: GmlWriter, at: CanonSite): string | null {
307
+ if (raw === undefined) {
308
+ return null;
309
+ }
310
+ if (writer.vocabulary === "ogdf") {
311
+ const stroke = OGDF_STROKES[Number(raw)] as string | undefined;
312
+ if (stroke === undefined || stroke === "none") {
313
+ noteAt(at, STYLE_ISSUE.UNKNOWN_VALUE, sampleOf(raw));
314
+ return null;
315
+ }
316
+ return stroke;
317
+ }
318
+ return canonEnum(kind, raw, writer.spelling, at);
319
+ }
320
+
321
+ /**
322
+ * Reads one node's `graphics` record.
323
+ * @param g - the record
324
+ * @param writer - the writer
325
+ * @param put - receives the values
326
+ * @param at - the sites
327
+ */
328
+ function readNodeGraphics(g: Rec, writer: GmlWriter, put: Put, at: At): void {
329
+ const v = writer.vocabulary;
330
+ readNodeFill(g, writer, put, at);
331
+ readNodeOutline(g, writer, put, at);
332
+ put(
333
+ "borderStyle",
334
+ strokeStyle(v === "ogdf" ? g.stipple : g.outlineStyle, "borderStyle", writer, at("borderStyle")),
335
+ );
336
+ put("width", g.w === undefined ? null : canonNumber(g.w, at("width")));
337
+ put("height", g.h === undefined ? null : canonNumber(g.h, at("height")));
338
+ if (g.type !== undefined) {
339
+ const raw =
340
+ v === "ogdf" && typeof g.type === "string" ? (OGDF_SHAPES.get(g.type.toLowerCase()) ?? g.type) : g.type;
341
+ put("shape", canonEnum("shape", raw, writer.spelling, at("shape")));
342
+ } else if (v === "yed" && typeof g.customconfiguration === "string") {
343
+ // yEd's flowchart palette keeps a node's shape as a configuration, not a `type`.
344
+ put("shape", flowchartShape(g.customconfiguration));
345
+ }
346
+ }
347
+
348
+ /**
349
+ * Reads the fill of one node's `graphics` record.
350
+ * @param g - the record
351
+ * @param writer - the writer
352
+ * @param put - receives the values
353
+ * @param at - the sites
354
+ */
355
+ function readNodeFill(g: Rec, writer: GmlWriter, put: Put, at: At): void {
356
+ if (writer.vocabulary === "yed" && Number(g.hasFill) === 0) {
357
+ noteAt(at("fill"), STYLE_ISSUE.NO_FILL, "hasFill 0");
358
+ put("fillOpacity", 0);
359
+ return;
360
+ }
361
+ readColor(g.fill, "fill", "fillOpacity", writer, put, at);
362
+ if (g.fill2 !== undefined) {
363
+ noteAt(at("fill"), STYLE_ISSUE.GRADIENT, sampleOf(g.fill2));
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Reads the outline color and width of one node's `graphics` record.
369
+ * @param g - the record
370
+ * @param writer - the writer
371
+ * @param put - receives the values
372
+ * @param at - the sites
373
+ */
374
+ function readNodeOutline(g: Rec, writer: GmlWriter, put: Put, at: At): void {
375
+ const v = writer.vocabulary;
376
+ if (v === "yed" && Number(g.hasOutline) === 0) {
377
+ put("borderWidth", 0);
378
+ return;
379
+ }
380
+ readColor(v === "ogdf" ? g.line : g.outline, "borderColor", null, writer, put, at);
381
+ const width = outlineWidthOf(g, v);
382
+ put("borderWidth", width === undefined ? null : canonNumber(width, at("borderWidth")));
383
+ }
384
+
385
+ /**
386
+ * The outline width of a node's `graphics` record, under the key its writer uses.
387
+ * @param g - the record
388
+ * @param v - the writer's vocabulary
389
+ * @returns the raw width, or undefined
390
+ */
391
+ function outlineWidthOf(g: Rec, v: GmlWriter["vocabulary"]): unknown {
392
+ switch (v) {
393
+ case "yed":
394
+ return g.outlineWidth;
395
+ case "cytoscape":
396
+ return first(g, "outline_width", "width");
397
+ case "ogdf":
398
+ return g.lineWidth;
399
+ default:
400
+ return first(g, "width", "outlineWidth");
401
+ }
402
+ }
403
+
404
+ /**
405
+ * Reads one edge's `graphics` record.
406
+ * @param g - the record
407
+ * @param writer - the writer
408
+ * @param put - receives the values
409
+ * @param at - the sites
410
+ */
411
+ function readEdgeGraphics(g: Rec, writer: GmlWriter, put: Put, at: At): void {
412
+ const v = writer.vocabulary;
413
+ readColor(g.fill, "color", "opacity", writer, put, at);
414
+ const width = v === "ogdf" ? first(g, "lineWidth", "width") : g.width;
415
+ put("width", width === undefined ? null : canonNumber(width, at("width")));
416
+ put("lineStyle", strokeStyle(v === "ogdf" ? g.stipple : g.style, "lineStyle", writer, at("lineStyle")));
417
+ if (typeof g.arrow === "string") {
418
+ const ends = ARROW_ENDS.get(g.arrow.trim().toLowerCase());
419
+ if (ends === undefined) {
420
+ noteAt(at("targetArrow"), STYLE_ISSUE.UNKNOWN_VALUE, g.arrow.slice(0, MAX_STYLE_VALUE_LENGTH));
421
+ } else {
422
+ put("sourceArrow", ends[0]);
423
+ put("targetArrow", ends[1]);
424
+ }
425
+ }
426
+ for (const [keys, property] of [
427
+ [["sourceArrow", "source_arrow"], "sourceArrow"],
428
+ [["targetArrow", "target_arrow"], "targetArrow"],
429
+ ] as const) {
430
+ const raw = first(g, ...keys);
431
+ if (raw === undefined) {
432
+ continue;
433
+ }
434
+ put(
435
+ property,
436
+ v === "cytoscape" && cytoscapeArrow !== undefined
437
+ ? cytoscapeArrow(raw, at(property))
438
+ : canonEnum("arrow", raw, writer.spelling, at(property)),
439
+ );
440
+ }
441
+ if (v === "yed") {
442
+ readYedCurve(g, put);
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Reads whether yEd draws an edge curved: `smoothBends` rounds the bends of its `Line`, whose points run from the
448
+ * source through the bends to the target, so a line of two points (or none) stays straight.
449
+ * @param g - the edge's `graphics` record
450
+ * @param put - receives the value
451
+ */
452
+ function readYedCurve(g: Rec, put: Put): void {
453
+ if (g.smoothBends !== undefined) {
454
+ const points = isRec(g.Line) ? g.Line.point : undefined;
455
+ put("curved", Number(g.smoothBends) === 1 && Array.isArray(points) && points.length > 2);
456
+ }
457
+ }
458
+
459
+ /**
460
+ * Reads an element's label records: the first visible one with text; further visible ones are noted.
461
+ * @param raw - the `LabelGraphics` value (one record or several)
462
+ * @param writer - the writer
463
+ * @param put - receives the values
464
+ * @param at - the sites
465
+ */
466
+ function readLabelGraphics(raw: unknown, writer: GmlWriter, put: Put, at: At): void {
467
+ const visible = (Array.isArray(raw) ? raw : [raw]).filter((l): l is Rec => isRec(l) && Number(l.visible) !== 0);
468
+ const lg = visible.at(0);
469
+ if (lg === undefined) {
470
+ return;
471
+ }
472
+ const extra = visible.slice(1).filter((l) => l.text !== undefined).length;
473
+ if (extra > 0) {
474
+ noteAt(at("label"), STYLE_ISSUE.EXTRA_LABELS, `${String(extra + 1)} labels`);
475
+ }
476
+ if (lg.text !== undefined) {
477
+ put("label", canonText(lg.text, at("label")));
478
+ }
479
+ // the GML draft's `fill` is the text color; yEd's is the label's background box, its `color` the text color
480
+ const yedLabel = writer.vocabulary === "yed" || YED_LABEL_KEYS.some((k) => Object.hasOwn(lg, k));
481
+ if (lg.color !== undefined) {
482
+ readColor(lg.color, "labelColor", null, writer, put, at);
483
+ } else if (lg.fill !== undefined && !yedLabel) {
484
+ readColor(lg.fill, "labelColor", null, writer, put, at);
485
+ }
486
+ if (lg.fill !== undefined && yedLabel) {
487
+ noteAt(at("label"), STYLE_ISSUE.NOT_CARRIED, "label background");
488
+ }
489
+ readLabelFont(lg, put, at);
490
+ }
491
+
492
+ /**
493
+ * Reads a label record's font: its size, name, weight and slant.
494
+ * @param lg - the record
495
+ * @param put - receives the values
496
+ * @param at - the sites
497
+ */
498
+ function readLabelFont(lg: Rec, put: Put, at: At): void {
499
+ put("labelSize", lg.fontSize === undefined ? null : canonNumber(lg.fontSize, at("labelSize")));
500
+ put("labelFont", lg.fontName === undefined ? null : canonText(lg.fontName, at("labelFont")));
501
+ const style = scalarText(lg.fontStyle, at("labelWeight"))?.trim().toLowerCase();
502
+ if (style !== undefined) {
503
+ const known = FONT_STYLES.get(style);
504
+ if (known === undefined) {
505
+ noteAt(at("labelWeight"), STYLE_ISSUE.UNKNOWN_VALUE, style.slice(0, MAX_STYLE_VALUE_LENGTH));
506
+ } else {
507
+ put("labelWeight", known[0]);
508
+ put("labelItalic", known[1]);
509
+ }
510
+ }
511
+ }
512
+
513
+ /**
514
+ * Reads one element: its `graphics` and `LabelGraphics`, and the `label` key where the writer draws it.
515
+ * @param target - nodes or edges
516
+ * @param g - the graphics value
517
+ * @param lg - the LabelGraphics value
518
+ * @param label - the `label` key's value
519
+ * @param writer - the writer
520
+ * @param put - receives the values
521
+ * @param at - the sites
522
+ */
523
+ function readElement(
524
+ target: StyleTarget,
525
+ g: unknown,
526
+ lg: unknown,
527
+ label: unknown,
528
+ writer: GmlWriter,
529
+ put: Put,
530
+ at: At,
531
+ ): void {
532
+ let text = false;
533
+ const putLabel: Put = (property, value) => {
534
+ text ||= property === "label" && value !== null;
535
+ put(property, value);
536
+ };
537
+ if (isRec(g)) {
538
+ (target === "node" ? readNodeGraphics : readEdgeGraphics)(g, writer, put, at);
539
+ }
540
+ if (lg !== null && lg !== undefined) {
541
+ readLabelGraphics(lg, writer, putLabel, at);
542
+ }
543
+ // the GML draft draws the label key; Cytoscape reads it as the node's name, which its default style shows
544
+ const drawn =
545
+ writer.vocabulary === "cytoscape" ? target === "node" : writer.labelKey && (isRec(g) || lg !== undefined);
546
+ if (!text && drawn && label !== null && label !== undefined) {
547
+ put("label", canonText(label, at("label")));
548
+ }
549
+ }
550
+
551
+ /**
552
+ * The value of a column, or undefined when the table has no such column.
553
+ * @param table - the node or edge table
554
+ * @param name - the column
555
+ * @param i - the row
556
+ * @returns the value
557
+ */
558
+ function cellOf(table: GraphSnapshot["nodes"], name: string, i: number): unknown {
559
+ if (!table.has(name)) {
560
+ return undefined;
561
+ }
562
+ const v = plainCell(table.value(name, i));
563
+ return v ?? undefined;
564
+ }
565
+
566
+ /**
567
+ * A node's center: the `graphics` `x` and `y` the importer moved into the position column.
568
+ * @param snapshot - the graph
569
+ * @param i - the node's index
570
+ * @returns the center; NaN where the node has none
571
+ */
572
+ function centerOf(snapshot: GraphSnapshot, i: number): BendPoint {
573
+ const column = snapshot.nodes.byRole("position");
574
+ const v = column?.isSet(i) === true ? (column.value(i) as ArrayLike<number>) : null;
575
+ return v === null ? [Number.NaN, Number.NaN] : [v[0], v[1]];
576
+ }
577
+
578
+ /**
579
+ * An edge's bends: the points of its `graphics` `Line` between the first and the last, which are where it leaves
580
+ * the source and meets the target.
581
+ * @param snapshot - the graph
582
+ * @param g - the edge's `graphics` value
583
+ * @param e - the edge's row
584
+ * @returns the `bends` value, or null without bends
585
+ */
586
+ function lineBends(snapshot: GraphSnapshot, g: unknown, e: number): string | null {
587
+ const raw = isRec(g) && isRec(g.Line) ? g.Line.point : undefined;
588
+ if (!Array.isArray(raw) || raw.length < 3) {
589
+ return null;
590
+ }
591
+ const points = raw.slice(1, -1).map((p): BendPoint => (isRec(p) ? [Number(p.x), Number(p.y)] : [Number.NaN, 0]));
592
+ return canonBends(
593
+ centerOf(snapshot, snapshot.edgeSource(e)),
594
+ centerOf(snapshot, snapshot.edgeTarget(e)),
595
+ points,
596
+ true,
597
+ );
598
+ }
599
+
600
+ /**
601
+ * Every element's values, read with a writer's vocabulary.
602
+ * @param snapshot - the graph
603
+ * @param writer - the writer
604
+ * @param notes - where problems are noted
605
+ * @param context - the work budget
606
+ * @returns the values per target, and whether anything was read
607
+ */
608
+ function readValues(
609
+ snapshot: GraphSnapshot,
610
+ writer: GmlWriter,
611
+ notes: StyleNotes,
612
+ context: Pick<StyleReadContext, "budget">,
613
+ ): { readonly node: ValueColumns; readonly edge: ValueColumns; readonly found: boolean } {
614
+ let found = false;
615
+ const read = (target: StyleTarget): ValueColumns => {
616
+ const table = target === "node" ? snapshot.nodes : snapshot.edges;
617
+ const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
618
+ const out = valueColumns(n);
619
+ if (!table.has("graphics") && !table.has("LabelGraphics") && writer.vocabulary !== "cytoscape") {
620
+ return out.values;
621
+ }
622
+ for (let i = 0; i < n; i++) {
623
+ context.budget.spend(target, 1);
624
+ const at: At = (property) => ({ notes, target, property, element: i, budget: context.budget });
625
+ readElement(
626
+ target,
627
+ cellOf(table, "graphics", i),
628
+ cellOf(table, "LabelGraphics", i),
629
+ cellOf(table, "label", i),
630
+ writer,
631
+ (property, value) => {
632
+ if (value !== null) {
633
+ found = true;
634
+ out.put(i, property, value);
635
+ }
636
+ },
637
+ at,
638
+ );
639
+ if (target === "edge") {
640
+ const bends = lineBends(snapshot, cellOf(table, "graphics", i), i);
641
+ if (bends !== null) {
642
+ found = true;
643
+ out.put(i, "bends", bends);
644
+ }
645
+ }
646
+ }
647
+ return out.values;
648
+ };
649
+ const node = read("node");
650
+ const edge = read("edge");
651
+ return { node, edge, found };
652
+ }
653
+
654
+ /**
655
+ * The `graphty.styles` block of a file graphty wrote, with the per-element values its rules read moved inside it from
656
+ * their `graphty_style_*` keys; the block as written when it is ignored anyway (graph-io then says why).
657
+ * @param snapshot - the graph
658
+ * @param text - the block's text
659
+ * @returns the block's text for graph-io to read
660
+ */
661
+ function blockWithValues(snapshot: GraphSnapshot, text: string): string {
662
+ const parsed = parseStyleBlock(text, styleFingerprint(snapshot));
663
+ if (!parsed.ok || parsed.block.values !== undefined) {
664
+ return text;
665
+ }
666
+ const node = tagValues(snapshot, "node", parsed.block.columns.node);
667
+ const edge = tagValues(snapshot, "edge", parsed.block.columns.edge);
668
+ if (node === null || edge === null) {
669
+ // a style column is missing: graph-io ignores the block with a note
670
+ return text;
671
+ }
672
+ const raw = JSON.parse(text) as Record<string, unknown>;
673
+ return JSON.stringify({ ...raw, values: { node, edge } });
674
+ }
675
+
676
+ /**
677
+ * The per-element values of a target's style columns, read from their `graphty_style_*` keys.
678
+ * @param snapshot - the graph
679
+ * @param target - nodes or edges
680
+ * @param keys - the style columns the block names
681
+ * @returns the values by key, or null when a key is missing
682
+ */
683
+ function tagValues(
684
+ snapshot: GraphSnapshot,
685
+ target: StyleTarget,
686
+ keys: readonly string[],
687
+ ): Record<string, StyleColumn> | null {
688
+ const table = target === "node" ? snapshot.nodes : snapshot.edges;
689
+ const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
690
+ const values: Record<string, StyleColumn> = {};
691
+ for (const key of keys) {
692
+ const name = tagKey(target, key);
693
+ if (!table.has(name)) {
694
+ return null;
695
+ }
696
+ values[key] = Array.from({ length: n }, (_, i) => {
697
+ const v = plainCell(table.value(name, i));
698
+ return typeof v === "string" || typeof v === "number" || typeof v === "boolean" ? v : null;
699
+ });
700
+ }
701
+ return values;
702
+ }
703
+
704
+ /**
705
+ * The columns of a table that hold styles: the graphics records and graphty's per-element style values.
706
+ * @param table - the node or edge table
707
+ * @returns the column names
708
+ */
709
+ function styleColumns(table: GraphSnapshot["nodes"]): string[] {
710
+ return [...table.names()].filter((c) => c === "graphics" || c === "LabelGraphics" || c.startsWith(TAG_PREFIX));
711
+ }
712
+
713
+ /**
714
+ * The styles of a GML file, read the way the program that wrote it draws them, with that program's defaults beneath:
715
+ * yEd, Cytoscape, Gephi and OGDF are recognized by the `Creator` line; igraph and every other writer get the GML
716
+ * draft's keys and no defaults. A file graphty wrote gives back its `graphty.styles` block.
717
+ * @param snapshot - the graph the import built
718
+ * @param context - the work budget
719
+ * @returns the styles, or null when the file has none
720
+ */
721
+ export function gmlStyles(snapshot: GraphSnapshot, context: StyleReadContext): ImporterStyles | null {
722
+ const notes = new StyleNotes();
723
+ const writer = writerOf(snapshot);
724
+ const values = readValues(snapshot, writer, notes, context);
725
+ const blockText = graphText(snapshot, GML_STYLE_BLOCK_KEY);
726
+ const block = blockText === null ? null : blockWithValues(snapshot, blockText);
727
+ if (block !== null) {
728
+ exactFromBlock(snapshot, block, values);
729
+ }
730
+ if (writer.tool === null && !values.found && block === null) {
731
+ return null;
732
+ }
733
+ const gephi =
734
+ writer.tool === "gephi"
735
+ ? gephiRules(snapshot, values.node.fill, values.edge.color, context.budget, values.edge.width)
736
+ : { rules: [], columns: new Map<string, StyleColumn>() };
737
+ return {
738
+ canonical: true,
739
+ dialect: writer.dialect,
740
+ tool: writer.tool,
741
+ units: writer.units,
742
+ node: { values: values.node },
743
+ edge: { values: values.edge, rules: gephi.rules, columns: gephi.columns },
744
+ notes: notes.list(),
745
+ opacityInColor: true,
746
+ block,
747
+ // the graphics records and the per-element style values of a file graphty wrote hold styles, not data
748
+ readFrom: { node: styleColumns(snapshot.nodes), edge: styleColumns(snapshot.edges) },
749
+ };
750
+ }
751
+
752
+ // ============================================================ writing
753
+
754
+ /**
755
+ * What the GML writer holds natively: yEd GML `graphics` and `LabelGraphics`. The `graphty.styles` block and the
756
+ * per-element values its rules read are keys of the graph and of each element.
757
+ * @category Built-in formats
758
+ */
759
+ export const GML_STYLE_CAPABILITIES: StyleWriteCapabilities = Object.freeze({
760
+ node: Object.freeze([
761
+ "fill",
762
+ "fillOpacity",
763
+ "borderColor",
764
+ "borderWidth",
765
+ "borderStyle",
766
+ "shape",
767
+ "width",
768
+ "height",
769
+ "label",
770
+ "labelColor",
771
+ "labelSize",
772
+ "labelFont",
773
+ "labelWeight",
774
+ "labelItalic",
775
+ ] as const),
776
+ edge: Object.freeze([
777
+ "color",
778
+ "opacity",
779
+ "width",
780
+ "lineStyle",
781
+ "sourceArrow",
782
+ "targetArrow",
783
+ "curved",
784
+ "bends",
785
+ "label",
786
+ "labelColor",
787
+ "labelSize",
788
+ "labelFont",
789
+ "labelWeight",
790
+ "labelItalic",
791
+ ] as const),
792
+ block: "native",
793
+ });
794
+
795
+ /** Every style key a reader here reads, per record: the writer replaces them all. */
796
+ const OWNED: Readonly<Record<"node" | "edge" | "label", ReadonlySet<string>>> = {
797
+ node: new Set([
798
+ "fill",
799
+ "fill2",
800
+ "hasFill",
801
+ "outline",
802
+ "hasOutline",
803
+ "outlineWidth",
804
+ "outline_width",
805
+ "outlineStyle",
806
+ "width",
807
+ "line",
808
+ "lineWidth",
809
+ "stipple",
810
+ "w",
811
+ "h",
812
+ "type",
813
+ ]),
814
+ edge: new Set([
815
+ "fill",
816
+ "width",
817
+ "lineWidth",
818
+ "style",
819
+ "stipple",
820
+ "arrow",
821
+ "sourceArrow",
822
+ "targetArrow",
823
+ "source_arrow",
824
+ "target_arrow",
825
+ "smoothBends",
826
+ ]),
827
+ label: new Set(["text", "color", "fill", "fontSize", "fontName", "fontStyle", "visible"]),
828
+ };
829
+
830
+ /** One element's value of a property. */
831
+ type Get = (property: string) => StyleValue | null;
832
+
833
+ /** Records a value written as the nearest one yEd has. */
834
+ type Approximate = (property: string, value: StyleValue) => void;
835
+
836
+ /**
837
+ * A node's yEd `graphics` keys from its values.
838
+ * @param get - the node's values
839
+ * @param approximate - records approximations
840
+ * @returns the keys
841
+ */
842
+ function nodeGraphics(get: Get, approximate: Approximate): Rec {
843
+ const g: Rec = {};
844
+ const width = get("width");
845
+ const height = get("height");
846
+ if (typeof width === "number") {
847
+ g.w = width;
848
+ }
849
+ if (typeof height === "number") {
850
+ g.h = height;
851
+ }
852
+ const shape = get("shape");
853
+ if (typeof shape === "string") {
854
+ const w = yedValue(YED_SHAPES, shape, shape === "vee" ? "triangle" : "rectangle");
855
+ if (!w.exact) {
856
+ approximate("shape", shape);
857
+ }
858
+ g.type = w.value;
859
+ }
860
+ const fill = get("fill");
861
+ const fillOpacity = get("fillOpacity");
862
+ if (typeof fill === "string") {
863
+ g.fill = yedColor(fill, fillOpacity);
864
+ } else if (fillOpacity === 0) {
865
+ g.hasFill = 0;
866
+ }
867
+ const borderColor = get("borderColor");
868
+ const borderWidth = get("borderWidth");
869
+ const borderStyle = get("borderStyle");
870
+ if (typeof borderColor === "string") {
871
+ g.outline = borderColor.toUpperCase();
872
+ }
873
+ if (typeof borderStyle === "string") {
874
+ const w = yedValue(YED_LINES, borderStyle, "line");
875
+ if (!w.exact) {
876
+ approximate("borderStyle", borderStyle);
877
+ }
878
+ g.outlineStyle = w.value;
879
+ }
880
+ if (borderWidth === 0) {
881
+ g.hasOutline = 0;
882
+ } else if (typeof borderWidth === "number") {
883
+ g.outlineWidth = borderWidth;
884
+ }
885
+ return g;
886
+ }
887
+
888
+ /**
889
+ * An edge's yEd `graphics` keys from its values.
890
+ * @param get - the edge's values
891
+ * @param approximate - records approximations
892
+ * @returns the keys
893
+ */
894
+ function edgeGraphics(get: Get, approximate: Approximate): Rec {
895
+ const g: Rec = {};
896
+ const color = get("color");
897
+ if (typeof color === "string") {
898
+ g.fill = yedColor(color, get("opacity"));
899
+ }
900
+ const width = get("width");
901
+ if (typeof width === "number") {
902
+ g.width = width;
903
+ }
904
+ const lineStyle = get("lineStyle");
905
+ if (typeof lineStyle === "string") {
906
+ g.style = yedSpelled(
907
+ YED_LINES,
908
+ lineStyle,
909
+ lineStyle === "long-dash" ? "dashed" : "line",
910
+ "lineStyle",
911
+ approximate,
912
+ );
913
+ }
914
+ for (const property of ["sourceArrow", "targetArrow"]) {
915
+ const arrow = get(property);
916
+ if (typeof arrow === "string") {
917
+ g[property] = yedSpelled(YED_ARROWS, arrow, "standard", property, approximate);
918
+ }
919
+ }
920
+ const curved = get("curved");
921
+ if (typeof curved === "boolean") {
922
+ g.smoothBends = curved ? 1 : 0;
923
+ }
924
+ return g;
925
+ }
926
+
927
+ /**
928
+ * An element's yEd `LabelGraphics` keys from its values, or null when it has no label text.
929
+ * @param get - the element's values
930
+ * @returns the keys
931
+ */
932
+ function labelGraphics(get: Get): Rec | null {
933
+ const text = get("label");
934
+ if (typeof text !== "string" || text.trim().length === 0) {
935
+ return null;
936
+ }
937
+ const lg: Rec = { text };
938
+ const color = get("labelColor");
939
+ if (typeof color === "string") {
940
+ lg.color = color.toUpperCase();
941
+ }
942
+ const size = get("labelSize");
943
+ if (typeof size === "number") {
944
+ lg.fontSize = size;
945
+ }
946
+ const font = get("labelFont");
947
+ if (typeof font === "string") {
948
+ lg.fontName = font;
949
+ }
950
+ const weight = get("labelWeight");
951
+ const italic = get("labelItalic");
952
+ if (weight !== null || italic !== null) {
953
+ const style = (weight === "bold" ? "bold" : "") + (italic === true ? "italic" : "");
954
+ lg.fontStyle = style.length === 0 ? "plain" : style;
955
+ }
956
+ return lg;
957
+ }
958
+
959
+ /**
960
+ * A record with the style keys replaced: the kept keys of the old record, then the written ones.
961
+ * @param old - the record the element had, or anything else
962
+ * @param owned - the style keys
963
+ * @param written - the new style keys
964
+ * @returns the record, or undefined (an unset cell) when it is empty
965
+ */
966
+ function merged(old: unknown, owned: ReadonlySet<string>, written: Rec): Rec | undefined {
967
+ const out: Rec = {};
968
+ if (isRec(old)) {
969
+ for (const [k, v] of Object.entries(old)) {
970
+ if (!owned.has(k)) {
971
+ out[k] = v;
972
+ }
973
+ }
974
+ }
975
+ Object.assign(out, written);
976
+ return Object.keys(out).length === 0 ? undefined : out;
977
+ }
978
+
979
+ /**
980
+ * An element's `LabelGraphics` value with its first label's style keys replaced; further labels are kept.
981
+ * @param old - the value the element had
982
+ * @param written - the new label keys, or null for no label
983
+ * @returns the value, or undefined (an unset cell)
984
+ */
985
+ function mergedLabels(old: unknown, written: Rec | null): unknown {
986
+ const labels = Array.isArray(old) ? old : [old];
987
+ const head = merged(labels[0], OWNED.label, written ?? {});
988
+ const rest = labels.slice(1).filter((l) => l !== null && l !== undefined);
989
+ if (rest.length === 0) {
990
+ return head;
991
+ }
992
+ return head === undefined ? rest : [head, ...rest];
993
+ }
994
+
995
+ /**
996
+ * Each element's values of the properties the writer writes, as a getter per element.
997
+ * @param picture - each element's value of a property
998
+ * @param target - nodes or edges
999
+ * @returns the getter
1000
+ */
1001
+ function getter(
1002
+ picture: (target: StyleTarget, property: string) => readonly (StyleValue | null)[],
1003
+ target: StyleTarget,
1004
+ ): (i: number) => Get {
1005
+ const columns = new Map<string, readonly (StyleValue | null)[]>();
1006
+ for (const p of GML_STYLE_CAPABILITIES[target]) {
1007
+ columns.set(p, picture(target, p));
1008
+ }
1009
+ return (i) => (property) => columns.get(property)?.[i] ?? null;
1010
+ }
1011
+
1012
+ /**
1013
+ * For a file graphty wrote: where a value read from the graphics is what the writer writes for the block's own value
1014
+ * (a pentagon written as a rectangle, an opacity rounded to 255 steps), the block's exact value is the truth, so a
1015
+ * re-import gives back the same rules; a value another program changed since stays the truth.
1016
+ * @param snapshot - the graph
1017
+ * @param text - the block's text
1018
+ * @param read - the values read, changed in place
1019
+ * @param read.node - the node values
1020
+ * @param read.edge - the edge values
1021
+ */
1022
+ function exactFromBlock(
1023
+ snapshot: GraphSnapshot,
1024
+ text: string,
1025
+ read: { readonly node: ValueColumns; readonly edge: ValueColumns },
1026
+ ): void {
1027
+ const parsed = parseStyleBlock(text, styleFingerprint(snapshot));
1028
+ const ex = parsed.ok ? blockStyles(parsed.block, snapshot, "gml") : null;
1029
+ if (ex === null) {
1030
+ return;
1031
+ }
1032
+ const notes = new StyleNotes();
1033
+ for (const target of ["node", "edge"] as const) {
1034
+ const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
1035
+ const get = getter((t, p) => resolveStyles(ex, t, p as never, snapshot), target);
1036
+ const values = read[target];
1037
+ for (let i = 0; i < n; i++) {
1038
+ const want = get(i);
1039
+ const back = writtenAndReadBack(target, want, notes);
1040
+ for (const property of GML_STYLE_CAPABILITIES[target]) {
1041
+ const native = values[property]?.[i] ?? null;
1042
+ const w = want(property);
1043
+ if (native !== w && native === (back.get(property) ?? null)) {
1044
+ values[property] ??= new Array<StyleValue | null>(n).fill(null);
1045
+ values[property][i] = w;
1046
+ }
1047
+ }
1048
+ }
1049
+ }
1050
+ }
1051
+
1052
+ /**
1053
+ * An element's values written as the writer writes them and read back as the reader reads them.
1054
+ * @param target - nodes or edges
1055
+ * @param want - its values
1056
+ * @param notes - a scratch for the notes the round trip makes
1057
+ * @returns the values read back, by property
1058
+ */
1059
+ function writtenAndReadBack(target: StyleTarget, want: Get, notes: StyleNotes): Map<string, StyleValue> {
1060
+ const ignore: Approximate = () => undefined;
1061
+ const g = target === "node" ? nodeGraphics(want, ignore) : edgeGraphics(want, ignore);
1062
+ const back = new Map<string, StyleValue>();
1063
+ readElement(
1064
+ target,
1065
+ g,
1066
+ labelGraphics(want) ?? undefined,
1067
+ undefined,
1068
+ GRAPHTY_WRITER,
1069
+ (property, value) => {
1070
+ if (value !== null) {
1071
+ back.set(property, value);
1072
+ }
1073
+ },
1074
+ (property) => ({ notes, target, property }),
1075
+ );
1076
+ return back;
1077
+ }
1078
+
1079
+ /** A GML export with styles worked out. */
1080
+ interface GmlStyledExport {
1081
+ /** The graph to write: yEd graphics written from the styles, the block and the per-element style values added. */
1082
+ readonly snapshot: GraphSnapshot;
1083
+ /** W_STYLE_APPROXIMATED for every value written as the nearest one yEd has. */
1084
+ readonly notes: readonly LossNote[];
1085
+ }
1086
+
1087
+ /**
1088
+ * Works out a GML export with styles: every node's and edge's `graphics` and `LabelGraphics` records get yEd's keys
1089
+ * for what graphty draws in place of every style key they had (positions, bends and other keys stay), the
1090
+ * `graphty.styles` block becomes the graph key `graphty_styles` and the per-element values its rules read become
1091
+ * element keys. Should two style columns map to one key, the values go inside the block instead.
1092
+ * @param snapshot - the graph handed to the exporter
1093
+ * @param input - the styles, or undefined when none are written
1094
+ * @returns the styled export, or null without styles
1095
+ */
1096
+ export function gmlStyledExport(
1097
+ snapshot: GraphSnapshot,
1098
+ input: ExporterStyleInput | undefined,
1099
+ ): GmlStyledExport | null {
1100
+ if (input === undefined) {
1101
+ return null;
1102
+ }
1103
+ const approximated = new StyleApproximations();
1104
+ const out = snapshot.withColumns();
1105
+ const renames: LossNote[] = [];
1106
+ for (const target of ["node", "edge"] as const) {
1107
+ const table = target === "node" ? out.nodes : out.edges;
1108
+ writeGraphicsColumns(snapshot, input, target, table, approximated);
1109
+ // the per-element style values of an earlier write go; a data column the rules read, or any other whose key
1110
+ // would read back as a style value, is renamed out of the keys the style values take
1111
+ const read = dataRead(input.rules.styles.rules, target);
1112
+ for (const name of table.names().filter((c) => mangleGmlKey(c).startsWith(TAG_PREFIX))) {
1113
+ const col = table.get(name);
1114
+ table.remove(name);
1115
+ if (col !== null && !(name.startsWith(TAG_PREFIX) && !read.has(name))) {
1116
+ renames.push(renameDataColumn(table, target, name, col));
1117
+ }
1118
+ }
1119
+ }
1120
+ let { block } = input;
1121
+ const keys = [...input.rules.columns.node.keys()].map((k) => tagKey("node", k));
1122
+ const edgeKeys = [...input.rules.columns.edge.keys()].map((k) => tagKey("edge", k));
1123
+ if (new Set(keys).size !== keys.length || new Set(edgeKeys).size !== edgeKeys.length) {
1124
+ block = writeStyleBlock(input.rules, { inlineColumns: true, snapshot });
1125
+ } else {
1126
+ setTagColumns(out, input);
1127
+ }
1128
+ out.graph.set(GML_STYLE_BLOCK_KEY, [block], { dtype: "string" });
1129
+ const notes: LossNote[] = [
1130
+ ...renames,
1131
+ ...(block === input.block ? [] : styleBlockLosses(block, input.rules.styles.rules.length, "size")),
1132
+ ...approximated.notes("yEd's GML"),
1133
+ ];
1134
+ return { snapshot: out, notes };
1135
+ }
1136
+
1137
+ /**
1138
+ * Writes every element's `graphics` and `LabelGraphics` records of a target with yEd's keys for what graphty draws,
1139
+ * keeping the keys that are not style keys.
1140
+ * @param snapshot - the graph handed to the exporter
1141
+ * @param input - the styles
1142
+ * @param target - nodes or edges
1143
+ * @param table - the target's table being written, changed in place
1144
+ * @param approximated - where approximations are counted
1145
+ */
1146
+ function writeGraphicsColumns(
1147
+ snapshot: GraphSnapshot,
1148
+ input: ExporterStyleInput,
1149
+ target: StyleTarget,
1150
+ table: GraphSnapshot["nodes"],
1151
+ approximated: StyleApproximations,
1152
+ ): void {
1153
+ const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
1154
+ const approximate: Approximate = (property, value) => {
1155
+ approximated.add(target, property, value);
1156
+ };
1157
+ const get = getter((t, p) => input.picture(t, p as never), target);
1158
+ const graphics: unknown[] = [];
1159
+ const labels: unknown[] = [];
1160
+ for (let i = 0; i < n; i++) {
1161
+ const g = target === "node" ? nodeGraphics(get(i), approximate) : edgeGraphics(get(i), approximate);
1162
+ if (target === "edge") {
1163
+ const line = bendsLine(snapshot, get(i)("bends"), i);
1164
+ if (line !== null) {
1165
+ g.Line = line;
1166
+ }
1167
+ }
1168
+ graphics.push(merged(cellOf(table, "graphics", i), OWNED[target], g));
1169
+ labels.push(mergedLabels(cellOf(table, "LabelGraphics", i), labelGraphics(get(i))));
1170
+ }
1171
+ for (const [name, cells] of [
1172
+ ["graphics", graphics],
1173
+ ["LabelGraphics", labels],
1174
+ ] as const) {
1175
+ if (cells.some((c) => c !== undefined)) {
1176
+ table.set(name, cells, { dtype: "json", nullable: true });
1177
+ } else if (table.has(name)) {
1178
+ table.remove(name);
1179
+ }
1180
+ }
1181
+ }
1182
+
1183
+ /**
1184
+ * An edge's `Line` from its `bends` value: the node centers it is written between, with the bends placed between them,
1185
+ * so the bends read back as written.
1186
+ * @param snapshot - the graph handed to the exporter
1187
+ * @param bends - the edge's `bends` value
1188
+ * @param e - the edge's row
1189
+ * @returns the `Line` record, or null without a value or without both centers
1190
+ */
1191
+ function bendsLine(snapshot: GraphSnapshot, bends: StyleValue | null, e: number): Rec | null {
1192
+ const source = centerOf(snapshot, snapshot.edgeSource(e));
1193
+ const target = centerOf(snapshot, snapshot.edgeTarget(e));
1194
+ const points = typeof bends === "string" ? bendsToPoints(source, target, bends, true) : null;
1195
+ if (points === null || ![...source, ...target].every(Number.isFinite)) {
1196
+ return null;
1197
+ }
1198
+ return { point: [source, ...points, target].map(([x, y]) => ({ x, y })) };
1199
+ }
1200
+
1201
+ /**
1202
+ * Writes a data column under a name out of the keys the style values take.
1203
+ * @param table - the table, changed in place
1204
+ * @param target - nodes or edges
1205
+ * @param name - the column's name
1206
+ * @param col - the column
1207
+ * @returns the note saying so
1208
+ */
1209
+ function renameDataColumn(
1210
+ table: GraphSnapshot["nodes"],
1211
+ target: StyleTarget,
1212
+ name: string,
1213
+ col: NonNullable<ReturnType<GraphSnapshot["nodes"]["get"]>>,
1214
+ ): LossNote {
1215
+ let renamed = `data_${name}`;
1216
+ let k = 2;
1217
+ while (table.has(renamed)) {
1218
+ renamed = `data_${name}_${String(k)}`;
1219
+ k++;
1220
+ }
1221
+ table.set(renamed, col);
1222
+ return Object.freeze({
1223
+ code: KEY_MANGLED_CODE,
1224
+ message: `${target} column "${name}" is written as "${renamed}": keys starting with ${TAG_PREFIX} hold graphty's style values`,
1225
+ column: name,
1226
+ count: null,
1227
+ });
1228
+ }
1229
+
1230
+ /**
1231
+ * Sets the per-element values the rules read as element keys.
1232
+ * @param out - the graph, changed in place
1233
+ * @param input - the styles
1234
+ */
1235
+ function setTagColumns(out: GraphSnapshot, input: ExporterStyleInput): void {
1236
+ for (const target of ["node", "edge"] as const) {
1237
+ const table = target === "node" ? out.nodes : out.edges;
1238
+ for (const [key, col] of input.rules.columns[target]) {
1239
+ const kinds = new Set(col.filter((v) => v !== null).map((v) => typeof v));
1240
+ const text = kinds.size > 1 || kinds.has("string");
1241
+ let dtype: "string" | "bool" | "f64" = kinds.has("boolean") ? "bool" : "f64";
1242
+ if (text) {
1243
+ dtype = "string";
1244
+ }
1245
+ table.set(tagKey(target, key), text ? col.map((v) => (v === null ? null : String(v))) : col, { dtype });
1246
+ }
1247
+ }
1248
+ }