react-cheminfo 0.64.1 → 0.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (604) hide show
  1. package/README.md +282 -0
  2. package/lib/card/core/cardAddress.d.ts +70 -0
  3. package/lib/card/core/cardAddress.d.ts.map +1 -0
  4. package/lib/card/core/cardAddress.js +59 -0
  5. package/lib/card/core/cardAddress.js.map +1 -0
  6. package/lib/card/core/cardPageUrl.d.ts +49 -0
  7. package/lib/card/core/cardPageUrl.d.ts.map +1 -0
  8. package/lib/card/core/cardPageUrl.js +59 -0
  9. package/lib/card/core/cardPageUrl.js.map +1 -0
  10. package/lib/card/core/index.d.ts +7 -0
  11. package/lib/card/core/index.d.ts.map +1 -0
  12. package/lib/card/core/index.js +4 -0
  13. package/lib/card/core/index.js.map +1 -0
  14. package/lib/card/core/panelRoom.d.ts +40 -0
  15. package/lib/card/core/panelRoom.d.ts.map +1 -0
  16. package/lib/card/core/panelRoom.js +40 -0
  17. package/lib/card/core/panelRoom.js.map +1 -0
  18. package/lib/card/ui/CardDetailsButton.d.ts +38 -0
  19. package/lib/card/ui/CardDetailsButton.d.ts.map +1 -0
  20. package/lib/card/ui/CardDetailsButton.js +50 -0
  21. package/lib/card/ui/CardDetailsButton.js.map +1 -0
  22. package/lib/card/ui/CardFigure.d.ts +51 -0
  23. package/lib/card/ui/CardFigure.d.ts.map +1 -0
  24. package/lib/card/ui/CardFigure.js +27 -0
  25. package/lib/card/ui/CardFigure.js.map +1 -0
  26. package/lib/card/ui/CardFrame.d.ts +30 -0
  27. package/lib/card/ui/CardFrame.d.ts.map +1 -0
  28. package/lib/card/ui/CardFrame.js +20 -0
  29. package/lib/card/ui/CardFrame.js.map +1 -0
  30. package/lib/card/ui/CopyTableButton.d.ts +37 -0
  31. package/lib/card/ui/CopyTableButton.d.ts.map +1 -0
  32. package/lib/card/ui/CopyTableButton.js +19 -0
  33. package/lib/card/ui/CopyTableButton.js.map +1 -0
  34. package/lib/card/ui/cardTooltip.d.ts +14 -0
  35. package/lib/card/ui/cardTooltip.d.ts.map +1 -0
  36. package/lib/card/ui/cardTooltip.js +15 -0
  37. package/lib/card/ui/cardTooltip.js.map +1 -0
  38. package/lib/card/ui/embed.d.ts +15 -0
  39. package/lib/card/ui/embed.d.ts.map +1 -0
  40. package/lib/card/ui/embed.js +24 -0
  41. package/lib/card/ui/embed.js.map +1 -0
  42. package/lib/card/ui/fitCardToFrame.d.ts +31 -0
  43. package/lib/card/ui/fitCardToFrame.d.ts.map +1 -0
  44. package/lib/card/ui/fitCardToFrame.js +66 -0
  45. package/lib/card/ui/fitCardToFrame.js.map +1 -0
  46. package/lib/card/ui/holdRoomForPanels.d.ts +16 -0
  47. package/lib/card/ui/holdRoomForPanels.d.ts.map +1 -0
  48. package/lib/card/ui/holdRoomForPanels.js +76 -0
  49. package/lib/card/ui/holdRoomForPanels.js.map +1 -0
  50. package/lib/card/ui/index.d.ts +11 -0
  51. package/lib/card/ui/index.d.ts.map +1 -0
  52. package/lib/card/ui/index.js +7 -0
  53. package/lib/card/ui/index.js.map +1 -0
  54. package/lib/card/ui/openPanels.d.ts +25 -0
  55. package/lib/card/ui/openPanels.d.ts.map +1 -0
  56. package/lib/card/ui/openPanels.js +85 -0
  57. package/lib/card/ui/openPanels.js.map +1 -0
  58. package/lib/card.d.ts +13 -0
  59. package/lib/card.d.ts.map +1 -0
  60. package/lib/card.js +13 -0
  61. package/lib/card.js.map +1 -0
  62. package/lib/conformers.d.ts +1 -0
  63. package/lib/conformers.d.ts.map +1 -1
  64. package/lib/conformers.js +1 -0
  65. package/lib/conformers.js.map +1 -1
  66. package/lib/core.d.ts +4 -0
  67. package/lib/core.d.ts.map +1 -1
  68. package/lib/core.js +5 -0
  69. package/lib/core.js.map +1 -1
  70. package/lib/fetch/core/apiError.d.ts +56 -0
  71. package/lib/fetch/core/apiError.d.ts.map +1 -0
  72. package/lib/fetch/core/apiError.js +43 -0
  73. package/lib/fetch/core/apiError.js.map +1 -0
  74. package/lib/fetch/core/fetchJson.d.ts +47 -0
  75. package/lib/fetch/core/fetchJson.d.ts.map +1 -0
  76. package/lib/fetch/core/fetchJson.js +109 -0
  77. package/lib/fetch/core/fetchJson.js.map +1 -0
  78. package/lib/fetch/core/index.d.ts +5 -0
  79. package/lib/fetch/core/index.d.ts.map +1 -0
  80. package/lib/fetch/core/index.js +3 -0
  81. package/lib/fetch/core/index.js.map +1 -0
  82. package/lib/formula/core/formulaText.d.ts +46 -0
  83. package/lib/formula/core/formulaText.d.ts.map +1 -0
  84. package/lib/formula/core/formulaText.js +74 -0
  85. package/lib/formula/core/formulaText.js.map +1 -0
  86. package/lib/formula/core/index.d.ts +6 -0
  87. package/lib/formula/core/index.d.ts.map +1 -0
  88. package/lib/formula/core/index.js +4 -0
  89. package/lib/formula/core/index.js.map +1 -0
  90. package/lib/formula/core/normalizeFormula.d.ts +22 -0
  91. package/lib/formula/core/normalizeFormula.d.ts.map +1 -0
  92. package/lib/formula/core/normalizeFormula.js +46 -0
  93. package/lib/formula/core/normalizeFormula.js.map +1 -0
  94. package/lib/formula/core/readFormula.d.ts +73 -0
  95. package/lib/formula/core/readFormula.d.ts.map +1 -0
  96. package/lib/formula/core/readFormula.js +132 -0
  97. package/lib/formula/core/readFormula.js.map +1 -0
  98. package/lib/formula/ui/CopyableFormula.d.ts +31 -0
  99. package/lib/formula/ui/CopyableFormula.d.ts.map +1 -0
  100. package/lib/formula/ui/CopyableFormula.js +19 -0
  101. package/lib/formula/ui/CopyableFormula.js.map +1 -0
  102. package/lib/formula/ui/FormulaInput.d.ts +29 -0
  103. package/lib/formula/ui/FormulaInput.d.ts.map +1 -0
  104. package/lib/formula/ui/FormulaInput.js +16 -0
  105. package/lib/formula/ui/FormulaInput.js.map +1 -0
  106. package/lib/formula/ui/QuestionFormula.d.ts +14 -0
  107. package/lib/formula/ui/QuestionFormula.d.ts.map +1 -0
  108. package/lib/formula/ui/QuestionFormula.js +13 -0
  109. package/lib/formula/ui/QuestionFormula.js.map +1 -0
  110. package/lib/formula/ui/index.d.ts +7 -0
  111. package/lib/formula/ui/index.d.ts.map +1 -0
  112. package/lib/formula/ui/index.js +4 -0
  113. package/lib/formula/ui/index.js.map +1 -0
  114. package/lib/formula.d.ts +11 -0
  115. package/lib/formula.d.ts.map +1 -0
  116. package/lib/formula.js +11 -0
  117. package/lib/formula.js.map +1 -0
  118. package/lib/hooks/ui/index.d.ts +1 -0
  119. package/lib/hooks/ui/index.d.ts.map +1 -1
  120. package/lib/hooks/ui/index.js.map +1 -1
  121. package/lib/hooks/ui/keyTargets.d.ts +4 -2
  122. package/lib/hooks/ui/keyTargets.d.ts.map +1 -1
  123. package/lib/hooks/ui/keyTargets.js +5 -3
  124. package/lib/hooks/ui/keyTargets.js.map +1 -1
  125. package/lib/hooks/ui/useAsync.d.ts +20 -5
  126. package/lib/hooks/ui/useAsync.d.ts.map +1 -1
  127. package/lib/hooks/ui/useAsync.js +29 -14
  128. package/lib/hooks/ui/useAsync.js.map +1 -1
  129. package/lib/i18n/core/chromeCatalog.d.ts +1 -1
  130. package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
  131. package/lib/locales/de.json +37 -0
  132. package/lib/locales/en.json +37 -0
  133. package/lib/locales/es.json +37 -0
  134. package/lib/locales/fr.json +37 -0
  135. package/lib/locales/it.json +37 -0
  136. package/lib/moleculesClient/core/index.d.ts +5 -0
  137. package/lib/moleculesClient/core/index.d.ts.map +1 -0
  138. package/lib/moleculesClient/core/index.js +3 -0
  139. package/lib/moleculesClient/core/index.js.map +1 -0
  140. package/lib/moleculesClient/core/moleculesClient.d.ts +12 -0
  141. package/lib/moleculesClient/core/moleculesClient.d.ts.map +1 -0
  142. package/lib/moleculesClient/core/moleculesClient.js +105 -0
  143. package/lib/moleculesClient/core/moleculesClient.js.map +1 -0
  144. package/lib/moleculesClient/core/moleculesClientApi.d.ts +100 -0
  145. package/lib/moleculesClient/core/moleculesClientApi.d.ts.map +1 -0
  146. package/lib/moleculesClient/core/moleculesClientApi.js +20 -0
  147. package/lib/moleculesClient/core/moleculesClientApi.js.map +1 -0
  148. package/lib/moleculesClient/core/moleculesTypes.d.ts +154 -0
  149. package/lib/moleculesClient/core/moleculesTypes.d.ts.map +1 -0
  150. package/lib/moleculesClient/core/moleculesTypes.js +2 -0
  151. package/lib/moleculesClient/core/moleculesTypes.js.map +1 -0
  152. package/lib/moleculesClient/core/requestRetry.d.ts +50 -0
  153. package/lib/moleculesClient/core/requestRetry.d.ts.map +1 -0
  154. package/lib/moleculesClient/core/requestRetry.js +108 -0
  155. package/lib/moleculesClient/core/requestRetry.js.map +1 -0
  156. package/lib/molstar/core/camera.d.ts +2 -1
  157. package/lib/molstar/core/camera.d.ts.map +1 -1
  158. package/lib/molstar/core/camera.js +3 -2
  159. package/lib/molstar/core/camera.js.map +1 -1
  160. package/lib/molstar/core/framing.d.ts +102 -0
  161. package/lib/molstar/core/framing.d.ts.map +1 -0
  162. package/lib/molstar/core/framing.js +120 -0
  163. package/lib/molstar/core/framing.js.map +1 -0
  164. package/lib/molstar/core/hover.d.ts +16 -0
  165. package/lib/molstar/core/hover.d.ts.map +1 -1
  166. package/lib/molstar/core/hover.js +14 -0
  167. package/lib/molstar/core/hover.js.map +1 -1
  168. package/lib/molstar/core/index.d.ts +4 -1
  169. package/lib/molstar/core/index.d.ts.map +1 -1
  170. package/lib/molstar/core/index.js +2 -1
  171. package/lib/molstar/core/index.js.map +1 -1
  172. package/lib/pedagogy/core/answers.d.ts +46 -0
  173. package/lib/pedagogy/core/answers.d.ts.map +1 -0
  174. package/lib/pedagogy/core/answers.js +77 -0
  175. package/lib/pedagogy/core/answers.js.map +1 -0
  176. package/lib/pedagogy/core/attempts.d.ts +86 -0
  177. package/lib/pedagogy/core/attempts.d.ts.map +1 -0
  178. package/lib/pedagogy/core/attempts.js +117 -0
  179. package/lib/pedagogy/core/attempts.js.map +1 -0
  180. package/lib/pedagogy/core/index.d.ts +8 -0
  181. package/lib/pedagogy/core/index.d.ts.map +1 -1
  182. package/lib/pedagogy/core/index.js +4 -0
  183. package/lib/pedagogy/core/index.js.map +1 -1
  184. package/lib/pedagogy/core/progressController.d.ts +64 -0
  185. package/lib/pedagogy/core/progressController.d.ts.map +1 -0
  186. package/lib/pedagogy/core/progressController.js +56 -0
  187. package/lib/pedagogy/core/progressController.js.map +1 -0
  188. package/lib/pedagogy/core/seed.d.ts +96 -0
  189. package/lib/pedagogy/core/seed.d.ts.map +1 -0
  190. package/lib/pedagogy/core/seed.js +128 -0
  191. package/lib/pedagogy/core/seed.js.map +1 -0
  192. package/lib/pedagogy/core/series.d.ts +77 -0
  193. package/lib/pedagogy/core/series.d.ts.map +1 -0
  194. package/lib/pedagogy/core/series.js +10 -0
  195. package/lib/pedagogy/core/series.js.map +1 -0
  196. package/lib/pedagogy/ui/AnswerFields.d.ts +21 -0
  197. package/lib/pedagogy/ui/AnswerFields.d.ts.map +1 -0
  198. package/lib/pedagogy/ui/AnswerFields.js +24 -0
  199. package/lib/pedagogy/ui/AnswerFields.js.map +1 -0
  200. package/lib/pedagogy/ui/ExerciseList.d.ts +82 -0
  201. package/lib/pedagogy/ui/ExerciseList.d.ts.map +1 -0
  202. package/lib/pedagogy/ui/ExerciseList.js +53 -0
  203. package/lib/pedagogy/ui/ExerciseList.js.map +1 -0
  204. package/lib/pedagogy/ui/ExerciseSeries.d.ts +95 -0
  205. package/lib/pedagogy/ui/ExerciseSeries.d.ts.map +1 -0
  206. package/lib/pedagogy/ui/ExerciseSeries.js +74 -0
  207. package/lib/pedagogy/ui/ExerciseSeries.js.map +1 -0
  208. package/lib/pedagogy/ui/QuestionCard.d.ts +64 -0
  209. package/lib/pedagogy/ui/QuestionCard.d.ts.map +1 -0
  210. package/lib/pedagogy/ui/QuestionCard.js +73 -0
  211. package/lib/pedagogy/ui/QuestionCard.js.map +1 -0
  212. package/lib/pedagogy/ui/index.d.ts +10 -0
  213. package/lib/pedagogy/ui/index.d.ts.map +1 -1
  214. package/lib/pedagogy/ui/index.js +6 -0
  215. package/lib/pedagogy/ui/index.js.map +1 -1
  216. package/lib/pedagogy/ui/seriesCounts.d.ts +17 -0
  217. package/lib/pedagogy/ui/seriesCounts.d.ts.map +1 -0
  218. package/lib/pedagogy/ui/seriesCounts.js +27 -0
  219. package/lib/pedagogy/ui/seriesCounts.js.map +1 -0
  220. package/lib/pedagogy/ui/seriesParts.d.ts +55 -0
  221. package/lib/pedagogy/ui/seriesParts.d.ts.map +1 -0
  222. package/lib/pedagogy/ui/seriesParts.js +38 -0
  223. package/lib/pedagogy/ui/seriesParts.js.map +1 -0
  224. package/lib/pedagogy/ui/useAttemptRecords.d.ts +10 -0
  225. package/lib/pedagogy/ui/useAttemptRecords.d.ts.map +1 -0
  226. package/lib/pedagogy/ui/useAttemptRecords.js +11 -0
  227. package/lib/pedagogy/ui/useAttemptRecords.js.map +1 -0
  228. package/lib/periodic/ui/PeriodicTable.d.ts.map +1 -1
  229. package/lib/periodic/ui/PeriodicTable.js +6 -3
  230. package/lib/periodic/ui/PeriodicTable.js.map +1 -1
  231. package/lib/property/core/copyableText.d.ts +14 -0
  232. package/lib/property/core/copyableText.d.ts.map +1 -0
  233. package/lib/property/core/copyableText.js +21 -0
  234. package/lib/property/core/copyableText.js.map +1 -0
  235. package/lib/property/core/index.d.ts +2 -0
  236. package/lib/property/core/index.d.ts.map +1 -0
  237. package/lib/property/core/index.js +2 -0
  238. package/lib/property/core/index.js.map +1 -0
  239. package/lib/property/ui/PropertyTable.d.ts +43 -0
  240. package/lib/property/ui/PropertyTable.d.ts.map +1 -0
  241. package/lib/property/ui/PropertyTable.js +19 -0
  242. package/lib/property/ui/PropertyTable.js.map +1 -0
  243. package/lib/property/ui/index.d.ts +3 -0
  244. package/lib/property/ui/index.d.ts.map +1 -0
  245. package/lib/property/ui/index.js +2 -0
  246. package/lib/property/ui/index.js.map +1 -0
  247. package/lib/router/core/history.d.ts +24 -3
  248. package/lib/router/core/history.d.ts.map +1 -1
  249. package/lib/router/core/history.js +55 -7
  250. package/lib/router/core/history.js.map +1 -1
  251. package/lib/router/core/index.d.ts +3 -3
  252. package/lib/router/core/index.d.ts.map +1 -1
  253. package/lib/router/core/index.js +2 -2
  254. package/lib/router/core/index.js.map +1 -1
  255. package/lib/router/core/languageRoute.d.ts +61 -0
  256. package/lib/router/core/languageRoute.d.ts.map +1 -0
  257. package/lib/router/core/languageRoute.js +96 -0
  258. package/lib/router/core/languageRoute.js.map +1 -0
  259. package/lib/router/core/siteAddresses.d.ts +23 -0
  260. package/lib/router/core/siteAddresses.d.ts.map +1 -1
  261. package/lib/router/core/siteAddresses.js +26 -0
  262. package/lib/router/core/siteAddresses.js.map +1 -1
  263. package/lib/router/core/tabRouter.d.ts +2 -108
  264. package/lib/router/core/tabRouter.d.ts.map +1 -1
  265. package/lib/router/core/tabRouter.js +38 -5
  266. package/lib/router/core/tabRouter.js.map +1 -1
  267. package/lib/router/core/tabRouterTypes.d.ts +154 -0
  268. package/lib/router/core/tabRouterTypes.d.ts.map +1 -0
  269. package/lib/router/core/tabRouterTypes.js +2 -0
  270. package/lib/router/core/tabRouterTypes.js.map +1 -0
  271. package/lib/seo/core/fitDescription.d.ts +40 -0
  272. package/lib/seo/core/fitDescription.d.ts.map +1 -0
  273. package/lib/seo/core/fitDescription.js +45 -0
  274. package/lib/seo/core/fitDescription.js.map +1 -0
  275. package/lib/seo/core/index.d.ts +7 -0
  276. package/lib/seo/core/index.d.ts.map +1 -1
  277. package/lib/seo/core/index.js +4 -0
  278. package/lib/seo/core/index.js.map +1 -1
  279. package/lib/seo/core/limits.d.ts +24 -0
  280. package/lib/seo/core/limits.d.ts.map +1 -0
  281. package/lib/seo/core/limits.js +24 -0
  282. package/lib/seo/core/limits.js.map +1 -0
  283. package/lib/seo/core/pageContent.d.ts +25 -0
  284. package/lib/seo/core/pageContent.d.ts.map +1 -0
  285. package/lib/seo/core/pageContent.js +25 -0
  286. package/lib/seo/core/pageContent.js.map +1 -0
  287. package/lib/seo/core/plainDescription.d.ts.map +1 -1
  288. package/lib/seo/core/plainDescription.js +3 -2
  289. package/lib/seo/core/plainDescription.js.map +1 -1
  290. package/lib/seo/core/routeProblems.d.ts.map +1 -1
  291. package/lib/seo/core/routeProblems.js +1 -6
  292. package/lib/seo/core/routeProblems.js.map +1 -1
  293. package/lib/seo/core/routeTable.d.ts +31 -0
  294. package/lib/seo/core/routeTable.d.ts.map +1 -0
  295. package/lib/seo/core/routeTable.js +96 -0
  296. package/lib/seo/core/routeTable.js.map +1 -0
  297. package/lib/seo/vite/index.d.ts +2 -0
  298. package/lib/seo/vite/index.d.ts.map +1 -1
  299. package/lib/seo/vite/index.js +1 -0
  300. package/lib/seo/vite/index.js.map +1 -1
  301. package/lib/seo/vite/prerender.d.ts +10 -0
  302. package/lib/seo/vite/prerender.d.ts.map +1 -1
  303. package/lib/seo/vite/prerender.js +12 -1
  304. package/lib/seo/vite/prerender.js.map +1 -1
  305. package/lib/seo/vite/routeTable.d.ts +40 -0
  306. package/lib/seo/vite/routeTable.d.ts.map +1 -0
  307. package/lib/seo/vite/routeTable.js +44 -0
  308. package/lib/seo/vite/routeTable.js.map +1 -0
  309. package/lib/server/answerAddresses.d.ts +51 -0
  310. package/lib/server/answerAddresses.d.ts.map +1 -0
  311. package/lib/server/answerAddresses.js +72 -0
  312. package/lib/server/answerAddresses.js.map +1 -0
  313. package/lib/server/cardMeta.d.ts +57 -0
  314. package/lib/server/cardMeta.d.ts.map +1 -0
  315. package/lib/server/cardMeta.js +52 -0
  316. package/lib/server/cardMeta.js.map +1 -0
  317. package/lib/server/config.d.ts +72 -0
  318. package/lib/server/config.d.ts.map +1 -0
  319. package/lib/server/config.js +67 -0
  320. package/lib/server/config.js.map +1 -0
  321. package/lib/server/envNumber.d.ts +35 -0
  322. package/lib/server/envNumber.d.ts.map +1 -0
  323. package/lib/server/envNumber.js +42 -0
  324. package/lib/server/envNumber.js.map +1 -0
  325. package/lib/server/healthRoute.d.ts +26 -0
  326. package/lib/server/healthRoute.d.ts.map +1 -0
  327. package/lib/server/healthRoute.js +22 -0
  328. package/lib/server/healthRoute.js.map +1 -0
  329. package/lib/server/listenSite.d.ts +38 -0
  330. package/lib/server/listenSite.d.ts.map +1 -0
  331. package/lib/server/listenSite.js +60 -0
  332. package/lib/server/listenSite.js.map +1 -0
  333. package/lib/server/mcpServer.d.ts +8 -0
  334. package/lib/server/mcpServer.d.ts.map +1 -0
  335. package/lib/server/mcpServer.js +11 -0
  336. package/lib/server/mcpServer.js.map +1 -0
  337. package/lib/server/repositoryVersion.d.ts +16 -0
  338. package/lib/server/repositoryVersion.d.ts.map +1 -0
  339. package/lib/server/repositoryVersion.js +33 -0
  340. package/lib/server/repositoryVersion.js.map +1 -0
  341. package/lib/server/routeOptions.d.ts +23 -0
  342. package/lib/server/routeOptions.d.ts.map +1 -0
  343. package/lib/server/routeOptions.js +20 -0
  344. package/lib/server/routeOptions.js.map +1 -0
  345. package/lib/server/routeTable.d.ts +14 -0
  346. package/lib/server/routeTable.d.ts.map +1 -0
  347. package/lib/server/routeTable.js +32 -0
  348. package/lib/server/routeTable.js.map +1 -0
  349. package/lib/server/siteView.d.ts +29 -0
  350. package/lib/server/siteView.d.ts.map +1 -0
  351. package/lib/server/siteView.js +21 -0
  352. package/lib/server/siteView.js.map +1 -0
  353. package/lib/server/spaPages.d.ts +32 -0
  354. package/lib/server/spaPages.d.ts.map +1 -0
  355. package/lib/server/spaPages.js +126 -0
  356. package/lib/server/spaPages.js.map +1 -0
  357. package/lib/server/spaSite.d.ts +30 -0
  358. package/lib/server/spaSite.d.ts.map +1 -0
  359. package/lib/server/spaSite.js +170 -0
  360. package/lib/server/spaSite.js.map +1 -0
  361. package/lib/server/spaSiteOptions.d.ts +126 -0
  362. package/lib/server/spaSiteOptions.d.ts.map +1 -0
  363. package/lib/server/spaSiteOptions.js +2 -0
  364. package/lib/server/spaSiteOptions.js.map +1 -0
  365. package/lib/server/staticSite.d.ts +9 -0
  366. package/lib/server/staticSite.d.ts.map +1 -1
  367. package/lib/server/staticSite.js +41 -19
  368. package/lib/server/staticSite.js.map +1 -1
  369. package/lib/server/toolSiteServer.d.ts +77 -0
  370. package/lib/server/toolSiteServer.d.ts.map +1 -0
  371. package/lib/server/toolSiteServer.js +58 -0
  372. package/lib/server/toolSiteServer.js.map +1 -0
  373. package/lib/server/trustProxy.d.ts +20 -0
  374. package/lib/server/trustProxy.d.ts.map +1 -0
  375. package/lib/server/trustProxy.js +32 -0
  376. package/lib/server/trustProxy.js.map +1 -0
  377. package/lib/server/types.d.ts +13 -0
  378. package/lib/server/types.d.ts.map +1 -0
  379. package/lib/server/types.js +2 -0
  380. package/lib/server/types.js.map +1 -0
  381. package/lib/server.d.ts +22 -0
  382. package/lib/server.d.ts.map +1 -1
  383. package/lib/server.js +12 -0
  384. package/lib/server.js.map +1 -1
  385. package/lib/share/core/config.d.ts +18 -0
  386. package/lib/share/core/config.d.ts.map +1 -1
  387. package/lib/share/core/config.js +33 -2
  388. package/lib/share/core/config.js.map +1 -1
  389. package/lib/share/core/index.d.ts +3 -1
  390. package/lib/share/core/index.d.ts.map +1 -1
  391. package/lib/share/core/index.js +2 -1
  392. package/lib/share/core/index.js.map +1 -1
  393. package/lib/share/core/keySetParam.d.ts +40 -0
  394. package/lib/share/core/keySetParam.d.ts.map +1 -0
  395. package/lib/share/core/keySetParam.js +60 -0
  396. package/lib/share/core/keySetParam.js.map +1 -0
  397. package/lib/share/core/vocabulary.d.ts +8 -0
  398. package/lib/share/core/vocabulary.d.ts.map +1 -1
  399. package/lib/shell/ui/ShareControl.d.ts +75 -0
  400. package/lib/shell/ui/ShareControl.d.ts.map +1 -0
  401. package/lib/shell/ui/ShareControl.js +40 -0
  402. package/lib/shell/ui/ShareControl.js.map +1 -0
  403. package/lib/shell/ui/ShellFrame.d.ts +14 -0
  404. package/lib/shell/ui/ShellFrame.d.ts.map +1 -0
  405. package/lib/shell/ui/ShellFrame.js +45 -0
  406. package/lib/shell/ui/ShellFrame.js.map +1 -0
  407. package/lib/shell/ui/SiteShell.d.ts +251 -0
  408. package/lib/shell/ui/SiteShell.d.ts.map +1 -0
  409. package/lib/shell/ui/SiteShell.js +39 -0
  410. package/lib/shell/ui/SiteShell.js.map +1 -0
  411. package/lib/shell/ui/index.d.ts +5 -0
  412. package/lib/shell/ui/index.d.ts.map +1 -0
  413. package/lib/shell/ui/index.js +3 -0
  414. package/lib/shell/ui/index.js.map +1 -0
  415. package/lib/state/core/index.d.ts +1 -0
  416. package/lib/state/core/index.d.ts.map +1 -1
  417. package/lib/state/core/index.js.map +1 -1
  418. package/lib/state/core/legacyEntries.d.ts +30 -0
  419. package/lib/state/core/legacyEntries.d.ts.map +1 -0
  420. package/lib/state/core/legacyEntries.js +51 -0
  421. package/lib/state/core/legacyEntries.js.map +1 -0
  422. package/lib/state/core/persistBucket.d.ts +26 -2
  423. package/lib/state/core/persistBucket.d.ts.map +1 -1
  424. package/lib/state/core/persistBucket.js +26 -9
  425. package/lib/state/core/persistBucket.js.map +1 -1
  426. package/lib/state/core/persistSignalBucket.d.ts +26 -0
  427. package/lib/state/core/persistSignalBucket.d.ts.map +1 -1
  428. package/lib/state/core/persistSignalBucket.js +31 -0
  429. package/lib/state/core/persistSignalBucket.js.map +1 -1
  430. package/lib/state/core/storageEntry.d.ts +15 -0
  431. package/lib/state/core/storageEntry.d.ts.map +1 -1
  432. package/lib/state/core/storageEntry.js +33 -0
  433. package/lib/state/core/storageEntry.js.map +1 -1
  434. package/lib/structure/ui/StructureExportButton.d.ts +27 -0
  435. package/lib/structure/ui/StructureExportButton.d.ts.map +1 -0
  436. package/lib/structure/ui/StructureExportButton.js +21 -0
  437. package/lib/structure/ui/StructureExportButton.js.map +1 -0
  438. package/lib/structure/ui/index.d.ts +4 -0
  439. package/lib/structure/ui/index.d.ts.map +1 -1
  440. package/lib/structure/ui/index.js +2 -0
  441. package/lib/structure/ui/index.js.map +1 -1
  442. package/lib/structure/ui/useStructureDraft.d.ts +32 -0
  443. package/lib/structure/ui/useStructureDraft.d.ts.map +1 -0
  444. package/lib/structure/ui/useStructureDraft.js +20 -0
  445. package/lib/structure/ui/useStructureDraft.js.map +1 -0
  446. package/lib/ui.d.ts +2 -0
  447. package/lib/ui.d.ts.map +1 -1
  448. package/lib/ui.js +2 -0
  449. package/lib/ui.js.map +1 -1
  450. package/lib/worker/core/index.d.ts +1 -1
  451. package/lib/worker/core/index.d.ts.map +1 -1
  452. package/lib/worker/core/messages.d.ts +4 -2
  453. package/lib/worker/core/messages.d.ts.map +1 -1
  454. package/lib/worker/core/messages.js.map +1 -1
  455. package/lib/worker/core/oclResourcesUrl.d.ts +16 -0
  456. package/lib/worker/core/oclResourcesUrl.d.ts.map +1 -0
  457. package/lib/worker/core/oclResourcesUrl.js +19 -0
  458. package/lib/worker/core/oclResourcesUrl.js.map +1 -0
  459. package/lib/worker/core/serveWorkerRequests.d.ts +8 -0
  460. package/lib/worker/core/serveWorkerRequests.d.ts.map +1 -1
  461. package/lib/worker/core/serveWorkerRequests.js +15 -2
  462. package/lib/worker/core/serveWorkerRequests.js.map +1 -1
  463. package/lib/worker/core/workerChannel.d.ts +3 -4
  464. package/lib/worker/core/workerChannel.d.ts.map +1 -1
  465. package/lib/worker/core/workerChannel.js +72 -67
  466. package/lib/worker/core/workerChannel.js.map +1 -1
  467. package/lib/worker/core/workerChannelParts.d.ts +17 -6
  468. package/lib/worker/core/workerChannelParts.d.ts.map +1 -1
  469. package/lib/worker/core/workerChannelParts.js +20 -4
  470. package/lib/worker/core/workerChannelParts.js.map +1 -1
  471. package/lib/worker/core/workerChannelTypes.d.ts +46 -2
  472. package/lib/worker/core/workerChannelTypes.d.ts.map +1 -1
  473. package/lib/worker/core/workerSlot.d.ts +66 -0
  474. package/lib/worker/core/workerSlot.d.ts.map +1 -0
  475. package/lib/worker/core/workerSlot.js +107 -0
  476. package/lib/worker/core/workerSlot.js.map +1 -0
  477. package/package.json +16 -1
  478. package/src/card/core/cardAddress.ts +100 -0
  479. package/src/card/core/cardPageUrl.ts +97 -0
  480. package/src/card/core/index.ts +6 -0
  481. package/src/card/core/panelRoom.ts +66 -0
  482. package/src/card/ui/CardDetailsButton.tsx +104 -0
  483. package/src/card/ui/CardFigure.tsx +86 -0
  484. package/src/card/ui/CardFrame.tsx +46 -0
  485. package/src/card/ui/CopyTableButton.tsx +81 -0
  486. package/src/card/ui/cardTooltip.ts +18 -0
  487. package/src/card/ui/embed.ts +27 -0
  488. package/src/card/ui/fitCardToFrame.ts +76 -0
  489. package/src/card/ui/holdRoomForPanels.ts +83 -0
  490. package/src/card/ui/index.ts +14 -0
  491. package/src/card/ui/openPanels.ts +106 -0
  492. package/src/card.ts +13 -0
  493. package/src/conformers.ts +4 -0
  494. package/src/core.ts +5 -0
  495. package/src/fetch/core/apiError.ts +65 -0
  496. package/src/fetch/core/fetchJson.ts +129 -0
  497. package/src/fetch/core/index.ts +4 -0
  498. package/src/formula/core/formulaText.ts +96 -0
  499. package/src/formula/core/index.ts +20 -0
  500. package/src/formula/core/normalizeFormula.ts +50 -0
  501. package/src/formula/core/readFormula.ts +211 -0
  502. package/src/formula/ui/CopyableFormula.tsx +51 -0
  503. package/src/formula/ui/FormulaInput.tsx +50 -0
  504. package/src/formula/ui/QuestionFormula.tsx +20 -0
  505. package/src/formula/ui/index.ts +6 -0
  506. package/src/formula.ts +11 -0
  507. package/src/hooks/ui/index.ts +1 -0
  508. package/src/hooks/ui/keyTargets.ts +5 -3
  509. package/src/hooks/ui/useAsync.ts +50 -14
  510. package/src/locales/de.json +37 -0
  511. package/src/locales/en.json +37 -0
  512. package/src/locales/es.json +37 -0
  513. package/src/locales/fr.json +37 -0
  514. package/src/locales/it.json +37 -0
  515. package/src/moleculesClient/core/index.ts +19 -0
  516. package/src/moleculesClient/core/moleculesClient.ts +175 -0
  517. package/src/moleculesClient/core/moleculesClientApi.ts +120 -0
  518. package/src/moleculesClient/core/moleculesTypes.ts +163 -0
  519. package/src/moleculesClient/core/requestRetry.ts +118 -0
  520. package/src/molstar/core/camera.ts +3 -2
  521. package/src/molstar/core/framing.ts +200 -0
  522. package/src/molstar/core/hover.ts +26 -0
  523. package/src/molstar/core/index.ts +21 -1
  524. package/src/pedagogy/core/answers.ts +94 -0
  525. package/src/pedagogy/core/attempts.ts +205 -0
  526. package/src/pedagogy/core/index.ts +36 -0
  527. package/src/pedagogy/core/progressController.ts +116 -0
  528. package/src/pedagogy/core/seed.ts +177 -0
  529. package/src/pedagogy/core/series.ts +82 -0
  530. package/src/pedagogy/ui/AnswerFields.tsx +63 -0
  531. package/src/pedagogy/ui/ExerciseList.tsx +202 -0
  532. package/src/pedagogy/ui/ExerciseSeries.tsx +250 -0
  533. package/src/pedagogy/ui/QuestionCard.tsx +231 -0
  534. package/src/pedagogy/ui/index.ts +18 -0
  535. package/src/pedagogy/ui/seriesCounts.ts +34 -0
  536. package/src/pedagogy/ui/seriesParts.tsx +127 -0
  537. package/src/pedagogy/ui/useAttemptRecords.ts +16 -0
  538. package/src/periodic/ui/PeriodicTable.tsx +6 -3
  539. package/src/property/core/copyableText.ts +20 -0
  540. package/src/property/core/index.ts +1 -0
  541. package/src/property/ui/PropertyTable.tsx +83 -0
  542. package/src/property/ui/index.ts +2 -0
  543. package/src/router/core/history.ts +61 -7
  544. package/src/router/core/index.ts +3 -2
  545. package/src/router/core/languageRoute.ts +136 -0
  546. package/src/router/core/siteAddresses.ts +38 -0
  547. package/src/router/core/tabRouter.ts +59 -117
  548. package/src/router/core/tabRouterTypes.ts +158 -0
  549. package/src/seo/core/fitDescription.ts +74 -0
  550. package/src/seo/core/index.ts +17 -0
  551. package/src/seo/core/limits.ts +27 -0
  552. package/src/seo/core/pageContent.ts +42 -0
  553. package/src/seo/core/plainDescription.ts +4 -2
  554. package/src/seo/core/routeProblems.ts +1 -7
  555. package/src/seo/core/routeTable.ts +103 -0
  556. package/src/seo/vite/index.ts +2 -0
  557. package/src/seo/vite/prerender.ts +26 -1
  558. package/src/seo/vite/routeTable.ts +79 -0
  559. package/src/server/answerAddresses.ts +92 -0
  560. package/src/server/cardMeta.ts +88 -0
  561. package/src/server/config.ts +125 -0
  562. package/src/server/envNumber.ts +65 -0
  563. package/src/server/healthRoute.ts +42 -0
  564. package/src/server/listenSite.ts +90 -0
  565. package/src/server/mcpServer.ts +14 -0
  566. package/src/server/repositoryVersion.ts +33 -0
  567. package/src/server/routeOptions.ts +26 -0
  568. package/src/server/routeTable.ts +35 -0
  569. package/src/server/siteView.ts +42 -0
  570. package/src/server/spaPages.ts +179 -0
  571. package/src/server/spaSite.ts +216 -0
  572. package/src/server/spaSiteOptions.ts +133 -0
  573. package/src/server/staticSite.ts +65 -35
  574. package/src/server/toolSiteServer.ts +127 -0
  575. package/src/server/trustProxy.ts +32 -0
  576. package/src/server/types.ts +25 -0
  577. package/src/server.ts +40 -0
  578. package/src/share/core/config.ts +45 -2
  579. package/src/share/core/index.ts +4 -0
  580. package/src/share/core/keySetParam.ts +92 -0
  581. package/src/share/core/vocabulary.ts +8 -0
  582. package/src/shell/ui/ShareControl.tsx +150 -0
  583. package/src/shell/ui/ShellFrame.tsx +180 -0
  584. package/src/shell/ui/SiteShell.tsx +297 -0
  585. package/src/shell/ui/index.ts +9 -0
  586. package/src/state/core/index.ts +1 -0
  587. package/src/state/core/legacyEntries.ts +76 -0
  588. package/src/state/core/persistBucket.ts +50 -10
  589. package/src/state/core/persistSignalBucket.ts +52 -0
  590. package/src/state/core/storageEntry.ts +32 -0
  591. package/src/structure/ui/StructureExportButton.tsx +57 -0
  592. package/src/structure/ui/index.ts +4 -0
  593. package/src/structure/ui/useStructureDraft.ts +47 -0
  594. package/src/ui.ts +2 -0
  595. package/src/worker/core/index.ts +1 -0
  596. package/src/worker/core/messages.ts +4 -3
  597. package/src/worker/core/oclResourcesUrl.ts +21 -0
  598. package/src/worker/core/serveWorkerRequests.ts +30 -2
  599. package/src/worker/core/workerChannel.ts +74 -74
  600. package/src/worker/core/workerChannelParts.ts +33 -13
  601. package/src/worker/core/workerChannelTypes.ts +47 -2
  602. package/src/worker/core/workerSlot.ts +181 -0
  603. package/styles/card.css +145 -0
  604. package/styles/chrome.css +248 -0
package/src/core.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export * from './about/core/index.ts';
2
2
  export * from './axis/core/index.ts';
3
3
  export * from './build/core/index.ts';
4
+ // The addresses of an answer's card, which a tool server writes in Node.
5
+ export * from './card/core/index.ts';
4
6
  export * from './chart/core/index.ts';
5
7
  export * from './citation/core/index.ts';
6
8
  export * from './clipboard/core/index.ts';
@@ -11,9 +13,11 @@ export * from './delimited/core/index.ts';
11
13
  export * from './download/core/index.ts';
12
14
  export * from './ecosystem/core/index.ts';
13
15
  export * from './error/core/index.ts';
16
+ export * from './fetch/core/index.ts';
14
17
  export * from './format/core/index.ts';
15
18
  export * from './i18n/core/index.ts';
16
19
  export * from './language/core/index.ts';
20
+ export * from './moleculesClient/core/index.ts';
17
21
  export * from './moleculesSources/core/index.ts';
18
22
  export * from './moleculesToken/core/index.ts';
19
23
  export * from './number/core/index.ts';
@@ -24,6 +28,7 @@ export * from './parallel/core/index.ts';
24
28
  export * from './pedagogy/core/index.ts';
25
29
  export * from './periodic/core/index.ts';
26
30
  export * from './projection/core/index.ts';
31
+ export * from './property/core/index.ts';
27
32
  export * from './range/core/index.ts';
28
33
  export * from './router/core/index.ts';
29
34
  export * from './scatter/core/index.ts';
@@ -0,0 +1,65 @@
1
+ /**
2
+ * A request a backend refused, or could not answer, with the reason it gave.
3
+ *
4
+ * `status` tells a refusal worth retrying from one that is final — a job still
5
+ * running from one that is gone — and `message` is written for the reader.
6
+ */
7
+ export class ApiError extends Error {
8
+ override name = 'ApiError';
9
+ /**
10
+ * The status of the refusal: the envelope's `statusCode` when it names one,
11
+ * else the response's. 0 when the server could not be reached at all.
12
+ */
13
+ readonly status: number;
14
+ /**
15
+ * The short name Fastify gives the status, e.g. `Not Found`; the response's
16
+ * status text when the body names none.
17
+ */
18
+ readonly error: string;
19
+ /**
20
+ * The body of the refusal as read — the parsed JSON, or the text when it was
21
+ * none — for a field the envelope does not name.
22
+ */
23
+ readonly body: unknown;
24
+
25
+ /**
26
+ * Name a refusal.
27
+ * @param message - The reason, written for the reader.
28
+ * @param options - The status, its name, the body and what caused it.
29
+ */
30
+ constructor(message: string, options: ApiErrorOptions) {
31
+ super(message, { cause: options.cause });
32
+ this.status = options.status;
33
+ this.error = options.error ?? '';
34
+ this.body = options.body;
35
+ }
36
+
37
+ /**
38
+ * The status, under the name Fastify's envelope gives it.
39
+ * @returns The same number as `status`.
40
+ */
41
+ get statusCode(): number {
42
+ return this.status;
43
+ }
44
+ }
45
+
46
+ /** What an {@link ApiError} is built from. */
47
+ export interface ApiErrorOptions {
48
+ /** The status of the refusal, 0 when the server could not be reached. */
49
+ status: number;
50
+ /**
51
+ * The short name of the status.
52
+ * @default ''
53
+ */
54
+ error?: string;
55
+ /**
56
+ * The body of the refusal as read.
57
+ * @default undefined
58
+ */
59
+ body?: unknown;
60
+ /**
61
+ * What the error was raised from.
62
+ * @default undefined
63
+ */
64
+ cause?: unknown;
65
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * One JSON request to one of our backends, and its refusal as an error a page
3
+ * can show.
4
+ *
5
+ * Our backends are Fastify, and a refusal comes back as `{ statusCode, error,
6
+ * message }`: the message is written for the reader, so it is what the error
7
+ * says. Nothing here needs more than `fetch`, so it runs in a page, a worker
8
+ * and Node alike.
9
+ */
10
+
11
+ import { ApiError } from './apiError.ts';
12
+
13
+ /**
14
+ * Ask a backend one question and read its JSON answer.
15
+ *
16
+ * A refusal rejects with an {@link ApiError} carrying the server's own words
17
+ * and its status; a server that cannot be reached rejects with one of status
18
+ * 0. An abort is passed on untouched, so a caller tells "nobody asked any
19
+ * more" from a failure by the signal it aborted. A `204`, or an empty body,
20
+ * answers `undefined`.
21
+ * @param url - The address, absolute or relative to the page.
22
+ * @param options - The request, plus a `json` body and the `fetch` to use.
23
+ * @returns The parsed answer.
24
+ * @throws {ApiError} When the server refuses, cannot be reached, or answers
25
+ * something that is not JSON.
26
+ */
27
+ export async function fetchJson<T>(
28
+ url: string,
29
+ options: FetchJsonOptions = {},
30
+ ): Promise<T> {
31
+ const { json, fetch: send = globalThis.fetch, ...init } = options;
32
+ const headers = new Headers(init.headers);
33
+ if (!headers.has('Accept')) headers.set('Accept', 'application/json');
34
+ if (json !== undefined && !headers.has('Content-Type')) {
35
+ headers.set('Content-Type', 'application/json');
36
+ }
37
+ const body = json === undefined ? init.body : JSON.stringify(json);
38
+
39
+ let response: Response;
40
+ try {
41
+ response = await send(url, { ...init, headers, body });
42
+ } catch (error) {
43
+ if (init.signal?.aborted === true) throw error;
44
+ throw new ApiError('the server could not be reached', {
45
+ status: 0,
46
+ error: 'Network Error',
47
+ cause: error,
48
+ });
49
+ }
50
+ if (!response.ok) throw await readApiError(response);
51
+
52
+ const text = await response.text();
53
+ if (response.status === 204 || text === '') return undefined as T;
54
+ try {
55
+ return JSON.parse(text) as T;
56
+ } catch (error) {
57
+ throw new ApiError('the server answered with something that is not JSON', {
58
+ status: response.status,
59
+ body: text,
60
+ cause: error,
61
+ });
62
+ }
63
+ }
64
+
65
+ /**
66
+ * The error a refusal stands for, in the server's words when it gave any: the
67
+ * `message` of a Fastify envelope, else a FastAPI `detail`, else the status.
68
+ * @param response - The response that was not ok.
69
+ * @returns The error, its body read.
70
+ */
71
+ export async function readApiError(response: Response): Promise<ApiError> {
72
+ let body: unknown;
73
+ try {
74
+ const text = await response.text();
75
+ body = text === '' ? undefined : parseOrKeep(text);
76
+ } catch {
77
+ body = undefined;
78
+ }
79
+ const envelope = isRecord(body) ? body : {};
80
+ const status =
81
+ typeof envelope.statusCode === 'number'
82
+ ? envelope.statusCode
83
+ : response.status;
84
+ const error =
85
+ typeof envelope.error === 'string' ? envelope.error : response.statusText;
86
+ return new ApiError(refusalMessage(envelope, response), {
87
+ status,
88
+ error,
89
+ body,
90
+ });
91
+ }
92
+
93
+ /** What {@link fetchJson} is handed: a `fetch` request, and two additions. */
94
+ export interface FetchJsonOptions extends RequestInit {
95
+ /**
96
+ * A value sent as the JSON body, with its content type; it replaces `body`.
97
+ * @default undefined — the request carries `body`, if any
98
+ */
99
+ json?: unknown;
100
+ /**
101
+ * The `fetch` to call: a session's own, which signs the request, or a
102
+ * stand-in in a test.
103
+ * @default globalThis.fetch
104
+ */
105
+ fetch?: typeof globalThis.fetch;
106
+ }
107
+
108
+ function refusalMessage(
109
+ envelope: Record<string, unknown>,
110
+ response: Response,
111
+ ): string {
112
+ const { message, detail } = envelope;
113
+ if (typeof message === 'string' && message !== '') return message;
114
+ if (typeof detail === 'string' && detail !== '') return detail;
115
+ const text = response.statusText === '' ? '' : ` ${response.statusText}`;
116
+ return `the server answered ${response.status}${text}`;
117
+ }
118
+
119
+ function parseOrKeep(text: string): unknown {
120
+ try {
121
+ return JSON.parse(text) as unknown;
122
+ } catch {
123
+ return text;
124
+ }
125
+ }
126
+
127
+ function isRecord(value: unknown): value is Record<string, unknown> {
128
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
129
+ }
@@ -0,0 +1,4 @@
1
+ export type { ApiErrorOptions } from './apiError.ts';
2
+ export { ApiError } from './apiError.ts';
3
+ export type { FetchJsonOptions } from './fetchJson.ts';
4
+ export { fetchJson, readApiError } from './fetchJson.ts';
@@ -0,0 +1,96 @@
1
+ /**
2
+ * One formula written several ways: canonically, so two spellings compare
3
+ * equal; as HTML, so a paste keeps its subscripts; in Unicode, for prose.
4
+ */
5
+
6
+ import { MF, ensureCase } from 'mf-parser';
7
+
8
+ import { DEFAULT_LANGUAGE } from '../../i18n/core/languages.ts';
9
+
10
+ import { normalizeFormula } from './normalizeFormula.ts';
11
+ import type { FormulaOptions, ParsedFormula } from './readFormula.ts';
12
+ import { FormulaError, parserMessage } from './readFormula.ts';
13
+
14
+ /** What {@link canonicalFormula} is told beyond the text. */
15
+ export interface CanonicalFormulaOptions extends FormulaOptions {
16
+ /**
17
+ * Whether a formula typed in the wrong case is fixed first, so `c9h6n4`
18
+ * reads as `C9H6N4`. A tool reading exact isotopes and charges leaves it
19
+ * off: case is what tells `Co` from `CO`.
20
+ * @default false
21
+ */
22
+ fixCase?: boolean;
23
+ }
24
+
25
+ /**
26
+ * The canonical spelling of a formula, so two spellings of one compound
27
+ * compare equal: `NaOH`, `HONa` and `NaHO` all give the same string.
28
+ * @param text - A formula as typed.
29
+ * @param options - Whether the case is fixed, and the language of a failure.
30
+ * @returns The canonical formula.
31
+ * @throws {FormulaError} When the text is not a formula.
32
+ */
33
+ export function canonicalFormula(
34
+ text: string,
35
+ options: CanonicalFormulaOptions = {},
36
+ ): string {
37
+ const { fixCase = false, language = DEFAULT_LANGUAGE } = options;
38
+ const normalized = normalizeFormula(text);
39
+ try {
40
+ return new MF(fixCase ? ensureCase(normalized) : normalized).toMF();
41
+ } catch (error) {
42
+ throw new FormulaError(parserMessage(text, error, language));
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Atom counts per element over a whole formula, isotopes folded into their
48
+ * element and every part added up.
49
+ * @param parsed - A formula as `readFormula` read it.
50
+ * @returns Symbol → count, in the order the formula first names each element.
51
+ */
52
+ export function atomCounts(parsed: ParsedFormula): Map<string, number> {
53
+ const counts = new Map<string, number>();
54
+ for (const part of parsed.parts) {
55
+ for (const atom of part.atoms) {
56
+ counts.set(atom.symbol, (counts.get(atom.symbol) ?? 0) + atom.count);
57
+ }
58
+ }
59
+ return counts;
60
+ }
61
+
62
+ /**
63
+ * A formula as HTML, its subscripts and its charge marked up, so a value
64
+ * copied from the page keeps them when it is pasted into a document.
65
+ * @param text - A formula as typed.
66
+ * @returns The markup, or the text itself when it is not a formula.
67
+ */
68
+ export function formulaHtml(text: string): string {
69
+ try {
70
+ return new MF(normalizeFormula(text)).toHtml();
71
+ } catch {
72
+ return text.trim();
73
+ }
74
+ }
75
+
76
+ /**
77
+ * A formula written for prose, with true subscripts and superscripts and its
78
+ * charge the way a chemist writes it: `NO₃⁻`, `Fe³⁺`, `²³⁸U¹⁹F₆`.
79
+ * @param text - A formula as typed.
80
+ * @returns The formula in Unicode, or the text itself when it is not one.
81
+ */
82
+ export function formulaText(text: string): string {
83
+ let written: string;
84
+ try {
85
+ written = new MF(normalizeFormula(text)).toText();
86
+ } catch {
87
+ return text.trim();
88
+ }
89
+ return written
90
+ .replaceAll(/\s+•\s+/gu, '·')
91
+ .replace(
92
+ /(?<sign>[⁺⁻])(?<digits>[⁰¹²³⁴⁵⁶⁷⁸⁹]+)$/u,
93
+ (_match, sign: string, digits: string) =>
94
+ `${digits === '¹' ? '' : digits}${sign}`,
95
+ );
96
+ }
@@ -0,0 +1,20 @@
1
+ export type { CanonicalFormulaOptions } from './formulaText.ts';
2
+ export {
3
+ atomCounts,
4
+ canonicalFormula,
5
+ formulaHtml,
6
+ formulaText,
7
+ } from './formulaText.ts';
8
+ export { normalizeFormula } from './normalizeFormula.ts';
9
+ export type {
10
+ FormulaAtom,
11
+ FormulaOptions,
12
+ FormulaPart,
13
+ ParsedFormula,
14
+ } from './readFormula.ts';
15
+ export {
16
+ FormulaError,
17
+ atomicNumber,
18
+ elementName,
19
+ readFormula,
20
+ } from './readFormula.ts';
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The ways a student types a charge or a hydrate, rewritten into the syntax
3
+ * `mf-parser` reads.
4
+ *
5
+ * The parser silently drops a trailing minus — `NO3-` would be read as neutral
6
+ * nitrate and `SO4--` as neutral sulfate — and reads the dot of `CuSO4.5H2O` as
7
+ * a decimal, giving `H2CuO5.5S`. Both are rewritten here, before the parser
8
+ * ever sees the text, rather than by every site that reads a formula.
9
+ */
10
+
11
+ /**
12
+ * Rewrite the ways a charge and a hydrate are commonly typed into the syntax
13
+ * the parser reads.
14
+ *
15
+ * - `NO3-`, `SO4--`, `S2O3-2`, `CO3 2-`, `CO3^2-` → `NO3(-1)`, `SO4(-2)`, …
16
+ * - `CuSO4.5H2O`, `CuSO4·5H2O`, `CuSO4*5H2O` → `CuSO4 . 5H2O`
17
+ *
18
+ * A charge already written in parentheses, `Fe(3+)`, is left as it is.
19
+ * @param text - The formula as typed.
20
+ * @returns The formula the parser is given.
21
+ */
22
+ export function normalizeFormula(text: string): string {
23
+ let formula = text.trim().replaceAll(/[·•*]/g, '.');
24
+ formula = formula.replaceAll(/\s*\.\s*/g, ' . ');
25
+
26
+ const separated =
27
+ /^(?<body>.*?\S)(?:\s+|\^)(?<digits>\d*)(?<signs>[+-]+)$/u.exec(formula);
28
+ if (separated?.groups) {
29
+ const { body = '', digits = '', signs = '' } = separated.groups;
30
+ return withCharge(body, signs, digits);
31
+ }
32
+ const signThenDigits =
33
+ /^(?<body>.*?[^\s(])(?<sign>[+-])(?<digits>\d+)$/u.exec(formula);
34
+ if (signThenDigits?.groups) {
35
+ const { body = '', sign = '', digits = '' } = signThenDigits.groups;
36
+ return withCharge(body, sign, digits);
37
+ }
38
+ const trailingSigns = /^(?<body>.*?[^\s+-])(?<signs>[+-]+)$/u.exec(formula);
39
+ if (trailingSigns?.groups) {
40
+ const { body = '', signs = '' } = trailingSigns.groups;
41
+ return withCharge(body, signs, '');
42
+ }
43
+ return formula;
44
+ }
45
+
46
+ function withCharge(body: string, signs: string, digits: string): string {
47
+ const sign = signs.startsWith('-') ? '-' : '+';
48
+ const magnitude = digits === '' ? signs.length : Number(digits);
49
+ return `${body.trim()}(${sign}${magnitude})`;
50
+ }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * A formula read into its parts, atoms and charges, with every failure
3
+ * explained in a sentence written for a student rather than for a parser.
4
+ */
5
+
6
+ import { MF } from 'mf-parser';
7
+
8
+ import { CHROME_CATALOG } from '../../i18n/core/chromeCatalog.ts';
9
+ import type { Language } from '../../i18n/core/languages.ts';
10
+ import { DEFAULT_LANGUAGE } from '../../i18n/core/languages.ts';
11
+ import { catalogTranslate } from '../../i18n/core/translate.ts';
12
+ import { elementBySymbol } from '../../periodic/core/elements.ts';
13
+
14
+ import { normalizeFormula } from './normalizeFormula.ts';
15
+
16
+ /** One kind of atom in one part of a formula. */
17
+ export interface FormulaAtom {
18
+ /** Element symbol, e.g. `Fe`. */
19
+ symbol: string;
20
+ /** Mass number when the formula names the isotope, e.g. 56 in `[56Fe]`. */
21
+ massNumber: number | null;
22
+ /** How many of them the part holds. */
23
+ count: number;
24
+ }
25
+
26
+ /** One `.`-separated part of a formula: its atoms and its charge. */
27
+ export interface FormulaPart {
28
+ /** The atoms of the part, in the order the formula first names them. */
29
+ atoms: FormulaAtom[];
30
+ /** The charge of the part. */
31
+ charge: number;
32
+ }
33
+
34
+ /** A formula as {@link readFormula} reads it. */
35
+ export interface ParsedFormula {
36
+ /** What was handed to the parser, after {@link normalizeFormula}. */
37
+ formula: string;
38
+ /** Its `.`-separated parts. */
39
+ parts: FormulaPart[];
40
+ /** Sum of the charges of every part. */
41
+ charge: number;
42
+ }
43
+
44
+ /** What the formula readers are told beyond the text. */
45
+ export interface FormulaOptions {
46
+ /**
47
+ * The language a failure is explained in.
48
+ * @default 'en'
49
+ */
50
+ language?: Language;
51
+ }
52
+
53
+ /** A formula that cannot be read, with a message written for a student. */
54
+ export class FormulaError extends Error {
55
+ override name = 'FormulaError';
56
+ }
57
+
58
+ interface ParserEntry {
59
+ kind: string;
60
+ value: unknown;
61
+ multiplier?: number;
62
+ }
63
+
64
+ /**
65
+ * Read a formula into its parts, atoms and charges.
66
+ * @param text - A formula as typed, e.g. `Fe(3+)`, `NO3-` or `[238U][19F]6`.
67
+ * @param options - The language a failure is explained in.
68
+ * @returns The parsed formula.
69
+ * @throws {FormulaError} When the text is empty or is not a formula.
70
+ */
71
+ export function readFormula(
72
+ text: string,
73
+ options: FormulaOptions = {},
74
+ ): ParsedFormula {
75
+ const { language = DEFAULT_LANGUAGE } = options;
76
+ const t = catalogTranslate(CHROME_CATALOG, language);
77
+ const formula = normalizeFormula(text);
78
+ if (formula === '') throw new FormulaError(t('formula.typeFirst'));
79
+
80
+ let rawParts: ParserEntry[][];
81
+ try {
82
+ rawParts = new MF(formula).toParts() as ParserEntry[][];
83
+ } catch (error) {
84
+ throw new FormulaError(parserMessage(text, error, language));
85
+ }
86
+
87
+ const order = writtenOrder(formula);
88
+ const parts: FormulaPart[] = [];
89
+ let total = 0;
90
+ for (const rawPart of rawParts) {
91
+ const part = readPart(text, rawPart, language);
92
+ part.atoms.sort(
93
+ (a, b) =>
94
+ (order.get(a.symbol) ?? order.size) -
95
+ (order.get(b.symbol) ?? order.size),
96
+ );
97
+ total += part.charge;
98
+ parts.push(part);
99
+ }
100
+ if (parts.every((part) => part.atoms.length === 0)) {
101
+ throw new FormulaError(t('formula.noAtom', { formula: text.trim() }));
102
+ }
103
+ return { formula, parts, charge: total };
104
+ }
105
+
106
+ /**
107
+ * The atomic number of an element.
108
+ * @param symbol - Its symbol.
109
+ * @param options - The language a failure is explained in.
110
+ * @returns Z.
111
+ * @throws {FormulaError} When the symbol is not an element.
112
+ */
113
+ export function atomicNumber(
114
+ symbol: string,
115
+ options: FormulaOptions = {},
116
+ ): number {
117
+ const element = elementBySymbol(symbol);
118
+ if (element === undefined) {
119
+ const t = catalogTranslate(
120
+ CHROME_CATALOG,
121
+ options.language ?? DEFAULT_LANGUAGE,
122
+ );
123
+ throw new FormulaError(t('formula.notElement', { symbol }));
124
+ }
125
+ return element.atomicNumber;
126
+ }
127
+
128
+ /**
129
+ * The name of an element, as the periodic table writes it.
130
+ * @param symbol - Its symbol.
131
+ * @returns The English name, or the symbol itself for an unknown one.
132
+ */
133
+ export function elementName(symbol: string): string {
134
+ return elementBySymbol(symbol)?.name ?? symbol;
135
+ }
136
+
137
+ /**
138
+ * Explain a failure of the parser in a student's terms.
139
+ * @param text - The formula as typed.
140
+ * @param error - What the parser threw.
141
+ * @param language - The language of the sentence.
142
+ * @returns The sentence.
143
+ */
144
+ export function parserMessage(
145
+ text: string,
146
+ error: unknown,
147
+ language: Language,
148
+ ): string {
149
+ const t = catalogTranslate(CHROME_CATALOG, language);
150
+ const raw = error instanceof Error ? error.message : String(error);
151
+ const unknown = /element unknown: (?<symbol>\S+)/u.exec(raw);
152
+ if (unknown?.groups?.symbol) {
153
+ return t('formula.notElement', { symbol: unknown.groups.symbol });
154
+ }
155
+ const reason = raw.split('\n', 1)[0] ?? raw;
156
+ return t('formula.unreadable', { formula: text.trim(), reason });
157
+ }
158
+
159
+ /**
160
+ * Where each element first appears in a formula as it is written, so a table
161
+ * lists Sb before O for Sb2O5 rather than in the parser's canonical order.
162
+ * @param formula - The formula as given to the parser.
163
+ * @returns Symbol → position of its first appearance.
164
+ */
165
+ function writtenOrder(formula: string): Map<string, number> {
166
+ const order = new Map<string, number>();
167
+ for (const match of formula.matchAll(/[A-Z][a-z]*/gu)) {
168
+ if (!order.has(match[0])) order.set(match[0], order.size);
169
+ }
170
+ return order;
171
+ }
172
+
173
+ function readPart(
174
+ text: string,
175
+ rawPart: ParserEntry[],
176
+ language: Language,
177
+ ): FormulaPart {
178
+ const t = catalogTranslate(CHROME_CATALOG, language);
179
+ const atoms: FormulaAtom[] = [];
180
+ let charge = 0;
181
+ for (const entry of rawPart) {
182
+ if (entry.kind === 'charge') {
183
+ charge += Number(entry.value);
184
+ continue;
185
+ }
186
+ const count = entry.multiplier ?? 1;
187
+ if (!Number.isInteger(count) || count <= 0) {
188
+ throw new FormulaError(
189
+ t('formula.wholeCounts', { formula: text.trim(), count }),
190
+ );
191
+ }
192
+ if (entry.kind === 'atom' && typeof entry.value === 'string') {
193
+ atomicNumber(entry.value, { language });
194
+ atoms.push({ symbol: entry.value, massNumber: null, count });
195
+ continue;
196
+ }
197
+ if (entry.kind === 'isotope') {
198
+ const { atom, isotope } = entry.value as {
199
+ atom: string;
200
+ isotope: number;
201
+ };
202
+ atomicNumber(atom, { language });
203
+ atoms.push({ symbol: atom, massNumber: isotope, count });
204
+ continue;
205
+ }
206
+ throw new FormulaError(
207
+ t('formula.unknownNotation', { formula: text.trim() }),
208
+ );
209
+ }
210
+ return { atoms, charge };
211
+ }
@@ -0,0 +1,51 @@
1
+ import type { ReactElement } from 'react';
2
+ import { MF } from 'react-mf';
3
+
4
+ import type { ClickToCopyElement } from '../../clipboard/ui/ClickToCopy.tsx';
5
+ import { ClickToCopy } from '../../clipboard/ui/ClickToCopy.tsx';
6
+ import { useChromeT } from '../../i18n/ui/useT.ts';
7
+ import { formulaHtml } from '../core/formulaText.ts';
8
+ import { normalizeFormula } from '../core/normalizeFormula.ts';
9
+
10
+ /** What {@link CopyableFormula} draws and copies. */
11
+ export interface CopyableFormulaProps {
12
+ /** The formula, as the data writes it: `NO3-`, `CuSO4.5H2O`, `C6H12O6`. */
13
+ formula: string;
14
+ /**
15
+ * The element drawn: a cell of its own inside a table.
16
+ * @default 'span'
17
+ */
18
+ as?: Extract<ClickToCopyElement, 'span' | 'td'>;
19
+ /**
20
+ * What the value is called in the hover title.
21
+ * @default the chrome's own word for it, in the language of the page
22
+ */
23
+ label?: string;
24
+ /**
25
+ * Class the value carries, in addition to `click-to-copy`.
26
+ * @default undefined
27
+ */
28
+ className?: string;
29
+ }
30
+
31
+ /**
32
+ * A formula drawn with its subscripts and copied by a click: as text for
33
+ * another tool, and as HTML, so a paste into a document keeps the subscripts
34
+ * and the charge.
35
+ * @param props - The formula, the element it is drawn as, and its label.
36
+ * @returns The formula.
37
+ */
38
+ export function CopyableFormula(props: CopyableFormulaProps): ReactElement {
39
+ const { formula, as = 'span', label, className } = props;
40
+ const t = useChromeT();
41
+ return (
42
+ <ClickToCopy
43
+ as={as}
44
+ className={className}
45
+ label={label ?? t('formula.copyLabel')}
46
+ value={{ text: formula, html: formulaHtml(formula) }}
47
+ >
48
+ <MF mf={normalizeFormula(formula)} />
49
+ </ClickToCopy>
50
+ );
51
+ }