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
@@ -1,5 +1,7 @@
1
+ import type { LegacyStorageKey } from './legacyEntries.ts';
1
2
  import { isPlainRecord } from './mergeStored.ts';
2
3
  import { persistBucket } from './persistBucket.ts';
4
+ import { readStorageText } from './storageEntry.ts';
3
5
 
4
6
  /**
5
7
  * Keep a tree of signals in `localStorage`, under one versioned key.
@@ -19,6 +21,12 @@ import { persistBucket } from './persistBucket.ts';
19
21
  * the tracking is always done by the copy of the library that made the
20
22
  * signals. A member that is neither a signal nor a plain object is ignored.
21
23
  * Never put a `computed()` in a bucket: it cannot be written back.
24
+ *
25
+ * With `syncAcrossTabs`, every tab follows the entry: a tab opened before
26
+ * another one stored its work would otherwise write its stale copy over it. A
27
+ * write that would store what is already there is skipped, so two tabs cannot
28
+ * echo each other, and nothing is written while another tab's copy is being
29
+ * taken on.
22
30
  * @param options - See {@link PersistSignalBucketOptions}.
23
31
  * @returns The same bucket, rehydrated, so it can be exported directly.
24
32
  */
@@ -26,26 +34,51 @@ export function persistSignalBucket<TBucket extends object>(
26
34
  options: PersistSignalBucketOptions<TBucket>,
27
35
  ): TBucket {
28
36
  const { key, version, bucket, effect, onQuotaExceeded } = options;
37
+ const { legacyKeys, forgetLegacyKeys, syncAcrossTabs = false } = options;
29
38
  const stored = persistBucket({
30
39
  key,
31
40
  version,
32
41
  defaults: snapshotBucket(bucket),
33
42
  onQuotaExceeded,
43
+ legacyKeys,
44
+ forgetLegacyKeys,
34
45
  });
35
46
 
36
47
  const { value, firstRun } = stored.read();
37
48
  if (!firstRun) hydrateBucket(bucket, value);
38
49
 
39
50
  let subscribed = false;
51
+ let adopting = false;
40
52
  effect(() => {
41
53
  const snapshot = snapshotBucket(bucket);
42
54
  if (!subscribed) {
43
55
  subscribed = true;
44
56
  return;
45
57
  }
58
+ if (adopting) return;
59
+ if (
60
+ syncAcrossTabs !== false &&
61
+ readStorageText(stored.storageKey) === JSON.stringify(snapshot)
62
+ ) {
63
+ return;
64
+ }
46
65
  stored.write(snapshot);
47
66
  });
48
67
 
68
+ if (syncAcrossTabs !== false) {
69
+ stored.subscribe((read) => {
70
+ if (typeof syncAcrossTabs === 'function' && !syncAcrossTabs(bucket)) {
71
+ return;
72
+ }
73
+ adopting = true;
74
+ try {
75
+ hydrateBucket(bucket, read.value);
76
+ } finally {
77
+ adopting = false;
78
+ }
79
+ });
80
+ }
81
+
49
82
  return bucket;
50
83
  }
51
84
 
@@ -77,6 +110,25 @@ export interface PersistSignalBucketOptions<TBucket extends object> {
77
110
  * @default undefined
78
111
  */
79
112
  onQuotaExceeded?: (error: unknown) => void;
113
+ /**
114
+ * The entries the bucket was kept under before this key, the preferred one
115
+ * first; see `persistBucket`.
116
+ * @default []
117
+ */
118
+ legacyKeys?: readonly LegacyStorageKey[];
119
+ /**
120
+ * Remove every legacy entry found once it has been read.
121
+ * @default false
122
+ */
123
+ forgetLegacyKeys?: boolean;
124
+ /**
125
+ * Take on what another tab stores, and skip a write that would store what is
126
+ * already there. A function decides each time, handed the bucket as this tab
127
+ * holds it: a bucket of work in progress keeps its own while it has some,
128
+ * rather than two tabs overwriting each other in turn.
129
+ * @default false
130
+ */
131
+ syncAcrossTabs?: boolean | ((bucket: TBucket) => boolean);
80
132
  }
81
133
 
82
134
  interface SignalLeaf {
@@ -81,6 +81,38 @@ export function removeStorageEntry(storageKey: string): void {
81
81
  }
82
82
  }
83
83
 
84
+ /**
85
+ * Read one `localStorage` entry as the text it was stored as, so two copies of
86
+ * a payload can be compared without parsing either.
87
+ * @param storageKey - The full key.
88
+ * @returns The stored text, or `null` when there is none or the store cannot
89
+ * be reached.
90
+ */
91
+ export function readStorageText(storageKey: string): string | null {
92
+ try {
93
+ return globalThis.localStorage?.getItem(storageKey) ?? null;
94
+ } catch {
95
+ return null;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Write one `localStorage` entry as text, exactly as given. Best effort.
101
+ * @param storageKey - The full key.
102
+ * @param text - What to store.
103
+ * @returns Whether the store took it.
104
+ */
105
+ export function writeStorageText(storageKey: string, text: string): boolean {
106
+ try {
107
+ const storage = globalThis.localStorage as Storage | undefined;
108
+ if (storage === undefined) return false;
109
+ storage.setItem(storageKey, text);
110
+ return true;
111
+ } catch {
112
+ return false;
113
+ }
114
+ }
115
+
84
116
  function isQuotaExceeded(error: unknown): boolean {
85
117
  if (typeof error !== 'object' || error === null) return false;
86
118
  const { name, code } = error as { name?: unknown; code?: unknown };
@@ -0,0 +1,57 @@
1
+ import type { ReactElement } from 'react';
2
+ import { useState } from 'react';
3
+
4
+ import { useChromeT } from '../../i18n/ui/useT.ts';
5
+ import { OverlayIconButton } from '../../overlay/ui/OverlayIconButton.tsx';
6
+
7
+ import { StructureExportDialog } from './StructureExportDialog.tsx';
8
+ import type { StructureExportPanelProps } from './StructureExportPanel.tsx';
9
+
10
+ /** What {@link StructureExportButton} hands over. */
11
+ export interface StructureExportButtonProps extends StructureExportPanelProps {
12
+ /**
13
+ * What the glyph is called, for the pointer and for a screen reader.
14
+ * @default the chrome's own line, `Copy or save the structure: SMILES,
15
+ * molfile`
16
+ */
17
+ label?: string;
18
+ /**
19
+ * Value of the `data-testid` attribute of the glyph.
20
+ * @default 'card-structure'
21
+ */
22
+ testId?: string;
23
+ }
24
+
25
+ /**
26
+ * The glyph beside a figure that hands its one structure over: as a SMILES,
27
+ * a molfile or an idCode, each copied with a click, or as a picture — the
28
+ * export dialog, the one the chat opens on its own molecule cards. It is drawn
29
+ * like the figure's other glyphs, so it sits in the `tools` of a `CardFigure`
30
+ * or of any overlay bar.
31
+ * @param props - The structure, the name of its files, and the glyph's name.
32
+ * @returns The glyph; the dialog is drawn over the page while it is open.
33
+ */
34
+ export function StructureExportButton(
35
+ props: StructureExportButtonProps,
36
+ ): ReactElement {
37
+ const { label, testId = 'card-structure', ...structure } = props;
38
+ const t = useChromeT();
39
+ const [isOpen, setIsOpen] = useState(false);
40
+ return (
41
+ <>
42
+ <OverlayIconButton
43
+ icon="export"
44
+ label={label ?? t('structure.export.glyph')}
45
+ active={isOpen}
46
+ opensMenu
47
+ testId={testId}
48
+ onClick={() => setIsOpen(true)}
49
+ />
50
+ <StructureExportDialog
51
+ {...structure}
52
+ isOpen={isOpen}
53
+ onClose={() => setIsOpen(false)}
54
+ />
55
+ </>
56
+ );
57
+ }
@@ -25,6 +25,10 @@ export type { StructureEditorHelpProps } from './StructureEditorHelp.tsx';
25
25
  export { StructureEditorHelp } from './StructureEditorHelp.tsx';
26
26
  export type { StructureExportDialogProps } from './StructureExportDialog.tsx';
27
27
  export { StructureExportDialog } from './StructureExportDialog.tsx';
28
+ export type { StructureExportButtonProps } from './StructureExportButton.tsx';
29
+ export { StructureExportButton } from './StructureExportButton.tsx';
28
30
  export type { StructureExportPanelProps } from './StructureExportPanel.tsx';
29
31
  export type { ToolbarFloorOptions } from './useToolbarFloor.ts';
30
32
  export { useToolbarFloor } from './useToolbarFloor.ts';
33
+ export type { StructureDraft } from './useStructureDraft.ts';
34
+ export { useStructureDraft } from './useStructureDraft.ts';
@@ -0,0 +1,47 @@
1
+ import { useCallback, useState } from 'react';
2
+
3
+ /** What a structure editor is drawing, and how a page reloads it. */
4
+ export interface StructureDraft<TValue> {
5
+ /** What the canvas holds: the structure last loaded or last drawn. */
6
+ value: TValue;
7
+ /**
8
+ * What `StructureEditor` takes as `revision`: it reloads `value` into the
9
+ * canvas each time this changes, and only then.
10
+ */
11
+ revision: number;
12
+ /**
13
+ * Put a structure in the canvas, replacing what is drawn: an example, the
14
+ * query a dialog is opened on, a Clear button loading the empty string.
15
+ * @param value - The structure, as the editor's `inputFormat` reads it.
16
+ */
17
+ load: (value: TValue) => void;
18
+ /**
19
+ * Keep what the editor drew, without reloading the canvas: what its
20
+ * `onChange` hands over. The same value twice changes nothing.
21
+ * @param value - The structure the editor reports.
22
+ */
23
+ update: (value: TValue) => void;
24
+ }
25
+
26
+ /**
27
+ * The draft a `StructureEditor` is fed: what it draws, and the revision that
28
+ * makes it reload. The editor is uncontrolled, so feeding every stroke back
29
+ * into it would replace the structure under the pen; this keeps the drawing
30
+ * out of the canvas until the page means to load one.
31
+ * @param initial - What the canvas opens on.
32
+ * @returns The draft, and the two ways to change it.
33
+ */
34
+ export function useStructureDraft<TValue = string>(
35
+ initial: TValue,
36
+ ): StructureDraft<TValue> {
37
+ const [draft, setDraft] = useState({ value: initial, revision: 0 });
38
+ const load = useCallback((value: TValue) => {
39
+ setDraft((previous) => ({ value, revision: previous.revision + 1 }));
40
+ }, []);
41
+ const update = useCallback((value: TValue) => {
42
+ setDraft((previous) =>
43
+ Object.is(previous.value, value) ? previous : { ...previous, value },
44
+ );
45
+ }, []);
46
+ return { value: draft.value, revision: draft.revision, load, update };
47
+ }
package/src/ui.ts CHANGED
@@ -27,12 +27,14 @@ export * from './parallel/ui/index.ts';
27
27
  export * from './pedagogy/ui/index.ts';
28
28
  export * from './periodic/ui/index.ts';
29
29
  export * from './projection/ui/index.ts';
30
+ export * from './property/ui/index.ts';
30
31
  export * from './range/ui/index.ts';
31
32
  export * from './router/ui/index.ts';
32
33
  export * from './scatter/ui/index.ts';
33
34
  export * from './scatter3d/ui/index.ts';
34
35
  export * from './share/ui/index.ts';
35
36
  export * from './shared/ui/index.ts';
37
+ export * from './shell/ui/index.ts';
36
38
  export * from './split/ui/index.ts';
37
39
  export * from './taxonomy/ui/index.ts';
38
40
  // The conformer table, from the structure module's file rather than its
@@ -18,6 +18,7 @@ export { createWorkerChannel } from './workerChannel.ts';
18
18
  export type {
19
19
  WorkerChannel,
20
20
  WorkerChannelOptions,
21
+ WorkerFailure,
21
22
  WorkerJobContext,
22
23
  WorkerRequestOptions,
23
24
  WorkerSchedule,
@@ -27,12 +27,13 @@ export interface WorkerProgressMessage<TProgress> {
27
27
 
28
28
  /**
29
29
  * What a worker posts once a job is over. An `Error` does not survive the
30
- * boundary, so a failure travels as its message and becomes an `Error` again
31
- * on this side.
30
+ * boundary, so a failure travels as its message — plus, when the worker was
31
+ * told how, a `detail` the page reads to say it in its own words — and becomes
32
+ * an `Error` again on this side.
32
33
  */
33
34
  export type WorkerResponseMessage<TResponse> =
34
35
  | { id: number; ok: true; response: TResponse }
35
- | { id: number; ok: false; message: string };
36
+ | { id: number; ok: false; message: string; detail?: unknown };
36
37
 
37
38
  /**
38
39
  * As much of a `Worker` as a channel uses. A real `Worker` satisfies it, and so
@@ -0,0 +1,21 @@
1
+ /** The file OpenChemLib's static resources are served as, at the site's root. */
2
+ export const OCL_RESOURCES_FILE = 'openchemlib-resources.json';
3
+
4
+ /**
5
+ * Where OpenChemLib's static resources sit, as this page is mounted.
6
+ *
7
+ * A worker fetches them, but a worker has no `document` and so cannot read the
8
+ * `<base>` the page carries, and its own script address is no help either: it
9
+ * sits under `assets/` in the build and under `src/` in development. So the
10
+ * page resolves the address and posts it with the job — the argument
11
+ * `registerResources` needs in a worker.
12
+ * @param file - The file, relative to the site's own root.
13
+ * @returns The absolute address, or `undefined` outside a page, where
14
+ * `registerResources` reads the file from the installed package instead.
15
+ */
16
+ export function oclResourcesUrl(
17
+ file: string = OCL_RESOURCES_FILE,
18
+ ): string | undefined {
19
+ const baseUri = globalThis.document?.baseURI;
20
+ return baseUri ? new URL(file, baseUri).href : undefined;
21
+ }
@@ -37,6 +37,14 @@ export interface ServeWorkerRequestsOptions<TRequest, TResponse> {
37
37
  * @default undefined — every answer is copied
38
38
  */
39
39
  transfer?: (response: TResponse) => Transferable[];
40
+ /**
41
+ * What a failed answer carries besides its message, for the page's channel
42
+ * to word the failure in its own language — the key of a translatable
43
+ * message and its values, say. It must survive being posted; answering
44
+ * `undefined` adds nothing.
45
+ * @default undefined — a failure carries its message alone
46
+ */
47
+ detail?: (error: unknown) => unknown;
40
48
  }
41
49
 
42
50
  /**
@@ -59,7 +67,7 @@ export function serveWorkerRequests<TRequest, TResponse, TProgress = never>(
59
67
  ) => TResponse | Promise<TResponse>,
60
68
  options: ServeWorkerRequestsOptions<TRequest, TResponse> = {},
61
69
  ): boolean {
62
- const { accepts, transfer } = options;
70
+ const { accepts, transfer, detail } = options;
63
71
  const scope = options.scope ?? dedicatedWorkerScope();
64
72
  if (scope === undefined) return false;
65
73
  const running = new Map<number, AbortController>();
@@ -99,7 +107,15 @@ export function serveWorkerRequests<TRequest, TResponse, TProgress = never>(
99
107
  answer(scope, { id, ok: true, response }, transfer);
100
108
  },
101
109
  (error: unknown) => {
102
- answer(scope, { id, ok: false, message: errorMessage(error) });
110
+ const message = errorMessage(error);
111
+ const extra =
112
+ detail === undefined ? undefined : describe(detail, error);
113
+ answer(
114
+ scope,
115
+ extra === undefined
116
+ ? { id, ok: false, message }
117
+ : { id, ok: false, message, detail: extra },
118
+ );
103
119
  },
104
120
  )
105
121
  .finally(() => {
@@ -127,6 +143,18 @@ function answer<TResponse>(
127
143
  }
128
144
  }
129
145
 
146
+ function describe(
147
+ detail: (error: unknown) => unknown,
148
+ error: unknown,
149
+ ): unknown {
150
+ try {
151
+ return detail(error);
152
+ } catch {
153
+ // A detail that cannot be worked out leaves the message to say it all.
154
+ return undefined;
155
+ }
156
+ }
157
+
130
158
  function dedicatedWorkerScope(): WorkerScopeLike | undefined {
131
159
  const candidate = globalThis as {
132
160
  postMessage?: unknown;
@@ -1,7 +1,7 @@
1
1
  import { toError } from '../../error/core/toError.ts';
2
2
 
3
3
  import { CancelledRequestError, RequestTimeoutError } from './errors.ts';
4
- import type { WorkerLike, WorkerRequestMessage } from './messages.ts';
4
+ import type { WorkerRequestMessage } from './messages.ts';
5
5
  import type {
6
6
  Outcome,
7
7
  PendingRequest,
@@ -12,7 +12,6 @@ import {
12
12
  positiveMilliseconds,
13
13
  postCancel,
14
14
  runJobInProcess,
15
- subscribeToWorker,
16
15
  watchRequest,
17
16
  } from './workerChannelParts.ts';
18
17
  import type {
@@ -21,6 +20,7 @@ import type {
21
20
  WorkerRequestOptions,
22
21
  WorkerSource,
23
22
  } from './workerChannelTypes.ts';
23
+ import { createWorkerSlot } from './workerSlot.ts';
24
24
 
25
25
  /**
26
26
  * Turn a worker into something a page can await.
@@ -30,10 +30,9 @@ import type {
30
30
  * — cancelled, superseded, timed out, or failed — drops out of the pending map
31
31
  * at that moment. The worker may post progress about a job while it runs.
32
32
  *
33
- * Handed a worker, the channel posts to it and terminates it when asked, but
34
- * never creates one. Handed a function, it creates the worker on the first
35
- * request, replaces one that failed or was terminated, and can fall back to an
36
- * in-process run where there is no `Worker`.
33
+ * Handed a worker, the channel posts to it and terminates it when asked. Handed
34
+ * a function, it creates the worker when needed and can run the job in-process
35
+ * instead; see `createWorkerSlot`.
37
36
  * @param source - The worker, or the function that creates it.
38
37
  * @param options - See {@link WorkerChannelOptions}.
39
38
  * @returns The channel.
@@ -43,38 +42,44 @@ export function createWorkerChannel<TRequest, TResponse, TProgress = never>(
43
42
  source: WorkerSource,
44
43
  options: WorkerChannelOptions<TRequest, TResponse, TProgress> = {},
45
44
  ): WorkerChannel<TRequest, TResponse, TProgress> {
46
- const {
47
- name = 'worker',
48
- schedule = 'parallel',
49
- cooperativeCancel = false,
50
- defaultTimeoutMs,
51
- runInProcess,
52
- } = options;
45
+ const { name = 'worker', schedule = 'parallel' } = options;
46
+ const { cooperativeCancel = false, retryAfterWorkerFailure } = options;
47
+ const { defaultTimeoutMs, runInProcess, failure } = options;
53
48
  const fallbackTimeoutMs = positiveMilliseconds(
54
49
  defaultTimeoutMs,
55
50
  DEFAULT_TIMEOUT_MS,
56
51
  );
57
- const factory = typeof source === 'function' ? source : undefined;
58
- if (schedule === 'restart' && factory === undefined) {
52
+ if (schedule === 'restart' && typeof source !== 'function') {
59
53
  throw new Error(`${name}: the restart schedule needs a worker factory`);
60
54
  }
61
- const hasNoWorker = !('Worker' in globalThis);
62
- const runLocally = factory && hasNoWorker ? runInProcess : undefined;
55
+ const retries = typeof source === 'function' && retryAfterWorkerFailure;
63
56
 
64
57
  const pending = new Map<number, PendingRequest<TResponse, TProgress>>();
65
- let worker: WorkerLike | null = null;
58
+ const posted = new Map<number, QueuedRequest<TRequest>>();
59
+ const retried = new Set<number>();
66
60
  let closed = false;
67
61
  let nextId = 0;
68
62
  let activeId: number | null = null;
69
63
  let queued: QueuedRequest<TRequest> | null = null;
70
- if (typeof source !== 'function') {
71
- worker = source;
72
- listen(source);
73
- }
64
+ const slot = createWorkerSlot<TRequest, TResponse, TProgress>(source, {
65
+ name,
66
+ runInProcess,
67
+ failure,
68
+ isWaited: (id) => pending.has(id),
69
+ report: {
70
+ progress: (id, progress) => {
71
+ pending.get(id)?.onProgress?.(progress as TProgress);
72
+ },
73
+ answer: (id, outcome) => complete(id, outcome),
74
+ failure: (error) => workerFailed(error),
75
+ },
76
+ });
74
77
 
75
78
  function take(id: number): PendingRequest<TResponse, TProgress> | undefined {
76
79
  const entry = pending.get(id);
77
80
  pending.delete(id);
81
+ posted.delete(id);
82
+ retried.delete(id);
78
83
  entry?.dispose();
79
84
  return entry;
80
85
  }
@@ -93,7 +98,7 @@ export function createWorkerChannel<TRequest, TResponse, TProgress = never>(
93
98
  const entry = take(id);
94
99
  if (entry === undefined) return false;
95
100
  entry.controller?.abort();
96
- if (cooperativeCancel && entry.dispatched) postCancel(worker, id);
101
+ if (cooperativeCancel && entry.dispatched) postCancel(slot.current, id);
97
102
  entry.reject(error);
98
103
  return true;
99
104
  }
@@ -106,22 +111,18 @@ export function createWorkerChannel<TRequest, TResponse, TProgress = never>(
106
111
  }
107
112
 
108
113
  function rejectAll(error: Error): void {
109
- for (const entry of pending.values()) {
110
- entry.dispose();
114
+ for (const [id, entry] of pending) {
115
+ take(id);
111
116
  entry.controller?.abort();
112
117
  entry.reject(error);
113
118
  }
114
- pending.clear();
115
119
  activeId = null;
116
120
  queued = null;
117
121
  }
118
122
 
119
123
  function abandonActive(): void {
120
124
  activeId = null;
121
- if (factory !== undefined && worker !== null) {
122
- worker.terminate();
123
- worker = null;
124
- }
125
+ slot.abandon();
125
126
  }
126
127
 
127
128
  function startQueued(): void {
@@ -135,55 +136,55 @@ export function createWorkerChannel<TRequest, TResponse, TProgress = never>(
135
136
  if (entry === undefined) return;
136
137
  entry.dispatched = true;
137
138
  if (schedule !== 'parallel') activeId = id;
138
- if (runLocally !== undefined) {
139
- entry.controller = runJobInProcess(
140
- runLocally,
141
- payload,
142
- (progress) => pending.get(id)?.onProgress?.(progress),
143
- (outcome) => complete(id, outcome),
144
- );
145
- return;
146
- }
147
- const message: WorkerRequestMessage<TRequest> = { id, request: payload };
148
139
  try {
149
- const target = currentWorker();
150
- if (transfer === undefined) target.postMessage(message);
151
- else target.postMessage(message, transfer);
140
+ const target = slot.acquire();
141
+ if (target !== null) {
142
+ if (retries && transfer === undefined) {
143
+ posted.set(id, { id, payload, transfer });
144
+ }
145
+ const message: WorkerRequestMessage<TRequest> = {
146
+ id,
147
+ request: payload,
148
+ };
149
+ if (transfer === undefined) target.postMessage(message);
150
+ else target.postMessage(message, transfer);
151
+ return;
152
+ }
152
153
  } catch (error) {
153
154
  complete(id, { ok: false, error: toError(error) });
155
+ return;
154
156
  }
157
+ entry.controller = runJobInProcess(
158
+ slot.runLocally,
159
+ payload,
160
+ (progress) => pending.get(id)?.onProgress?.(progress),
161
+ (outcome) => complete(id, outcomeOf(outcome)),
162
+ );
155
163
  }
156
164
 
157
- function currentWorker(): WorkerLike {
158
- if (worker === null) {
159
- if (factory === undefined) {
160
- throw new CancelledRequestError(`${name} was terminated`);
161
- }
162
- worker = factory();
163
- listen(worker);
164
- }
165
- return worker;
165
+ function outcomeOf(outcome: Outcome<TResponse>): Outcome<TResponse> {
166
+ return outcome.ok
167
+ ? outcome
168
+ : { ok: false, error: slot.jobFailed(outcome.error) };
166
169
  }
167
170
 
168
- function listen(target: WorkerLike): void {
169
- subscribeToWorker(target, {
170
- progress: (id, progress) => {
171
- if (target !== worker) return;
172
- pending.get(id)?.onProgress?.(progress as TProgress);
173
- },
174
- answer: (id, outcome) => {
175
- if (target === worker) complete(id, outcome as Outcome<TResponse>);
176
- },
177
- failure: (detail) => {
178
- if (target !== worker) return;
179
- if (factory !== undefined) {
180
- target.terminate();
181
- worker = null;
182
- }
183
- const suffix = detail === '' ? '' : `: ${detail}`;
184
- rejectAll(new Error(`${name} failed${suffix}`));
185
- },
186
- });
171
+ function workerFailed(error: Error): void {
172
+ if (!retries) {
173
+ rejectAll(error);
174
+ return;
175
+ }
176
+ activeId = null;
177
+ for (const [id, entry] of pending) {
178
+ if (!entry.dispatched) continue;
179
+ const job = posted.get(id);
180
+ if (job === undefined || retried.has(id)) {
181
+ drop(id, error);
182
+ continue;
183
+ }
184
+ retried.add(id);
185
+ dispatch(id, job.payload);
186
+ }
187
+ if (activeId === null) startQueued();
187
188
  }
188
189
 
189
190
  function expire(id: number, budget: number): void {
@@ -242,9 +243,8 @@ export function createWorkerChannel<TRequest, TResponse, TProgress = never>(
242
243
  cancel: () => dropAll('was cancelled'),
243
244
  terminate: () => {
244
245
  rejectAll(new CancelledRequestError(`${name} was terminated`));
245
- worker?.terminate();
246
- if (factory === undefined) closed = true;
247
- else worker = null;
246
+ slot.terminate();
247
+ if (!slot.hasFactory) closed = true;
248
248
  },
249
249
  };
250
250
  }