react-cheminfo 0.69.0 → 0.71.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 (616) hide show
  1. package/README.md +70 -2
  2. package/lib/about/core/providers.d.ts +6 -0
  3. package/lib/about/core/providers.d.ts.map +1 -1
  4. package/lib/about/core/providers.js +7 -0
  5. package/lib/about/core/providers.js.map +1 -1
  6. package/lib/about/ui/AboutProvidedBy.js +34 -0
  7. package/lib/about/ui/AboutProvidedBy.js.map +1 -1
  8. package/lib/chart/ui/useChartPointer.d.ts +9 -1
  9. package/lib/chart/ui/useChartPointer.d.ts.map +1 -1
  10. package/lib/chart/ui/useChartPointer.js +11 -3
  11. package/lib/chart/ui/useChartPointer.js.map +1 -1
  12. package/lib/credits/core/credits.d.ts +41 -0
  13. package/lib/credits/core/credits.d.ts.map +1 -1
  14. package/lib/credits/core/credits.js +50 -0
  15. package/lib/credits/core/credits.js.map +1 -1
  16. package/lib/download/core/drawingMarkup.d.ts +8 -1
  17. package/lib/download/core/drawingMarkup.d.ts.map +1 -1
  18. package/lib/download/core/drawingMarkup.js +13 -1
  19. package/lib/download/core/drawingMarkup.js.map +1 -1
  20. package/lib/download/core/figurePng.d.ts +14 -0
  21. package/lib/download/core/figurePng.d.ts.map +1 -1
  22. package/lib/download/core/figurePng.js +21 -6
  23. package/lib/download/core/figurePng.js.map +1 -1
  24. package/lib/download/core/figureSvg.d.ts.map +1 -1
  25. package/lib/download/core/figureSvg.js +5 -3
  26. package/lib/download/core/figureSvg.js.map +1 -1
  27. package/lib/download/core/figureTarget.d.ts +13 -0
  28. package/lib/download/core/figureTarget.d.ts.map +1 -1
  29. package/lib/download/core/figureTarget.js +25 -5
  30. package/lib/download/core/figureTarget.js.map +1 -1
  31. package/lib/download/core/svgBounds.d.ts +15 -0
  32. package/lib/download/core/svgBounds.d.ts.map +1 -1
  33. package/lib/download/core/svgBounds.js +20 -0
  34. package/lib/download/core/svgBounds.js.map +1 -1
  35. package/lib/download/core/svgToPng.d.ts.map +1 -1
  36. package/lib/download/core/svgToPng.js +4 -2
  37. package/lib/download/core/svgToPng.js.map +1 -1
  38. package/lib/format/core/relativeTime.d.ts +8 -3
  39. package/lib/format/core/relativeTime.d.ts.map +1 -1
  40. package/lib/format/core/relativeTime.js +19 -7
  41. package/lib/format/core/relativeTime.js.map +1 -1
  42. package/lib/host/core/connectHost.d.ts +25 -34
  43. package/lib/host/core/connectHost.d.ts.map +1 -1
  44. package/lib/host/core/connectHost.js +43 -89
  45. package/lib/host/core/connectHost.js.map +1 -1
  46. package/lib/host/core/hostOutputs.d.ts +58 -0
  47. package/lib/host/core/hostOutputs.d.ts.map +1 -0
  48. package/lib/host/core/hostOutputs.js +151 -0
  49. package/lib/host/core/hostOutputs.js.map +1 -0
  50. package/lib/host/core/index.d.ts +9 -3
  51. package/lib/host/core/index.d.ts.map +1 -1
  52. package/lib/host/core/index.js +4 -1
  53. package/lib/host/core/index.js.map +1 -1
  54. package/lib/host/core/irKinds.d.ts +124 -0
  55. package/lib/host/core/irKinds.d.ts.map +1 -0
  56. package/lib/host/core/irKinds.js +46 -0
  57. package/lib/host/core/irKinds.js.map +1 -0
  58. package/lib/host/core/kinds.d.ts +13 -5
  59. package/lib/host/core/kinds.d.ts.map +1 -1
  60. package/lib/host/core/kinds.js +11 -5
  61. package/lib/host/core/kinds.js.map +1 -1
  62. package/lib/host/core/protocol.d.ts +50 -7
  63. package/lib/host/core/protocol.d.ts.map +1 -1
  64. package/lib/host/core/protocol.js +10 -6
  65. package/lib/host/core/protocol.js.map +1 -1
  66. package/lib/host/core/selectInputs.d.ts +37 -0
  67. package/lib/host/core/selectInputs.d.ts.map +1 -0
  68. package/lib/host/core/selectInputs.js +110 -0
  69. package/lib/host/core/selectInputs.js.map +1 -0
  70. package/lib/host/testing/sciPeaks.d.ts +31 -0
  71. package/lib/host/testing/sciPeaks.d.ts.map +1 -1
  72. package/lib/host/testing/sciPeaks.js +73 -0
  73. package/lib/host/testing/sciPeaks.js.map +1 -1
  74. package/lib/host/testing/standInHost.d.ts +9 -1
  75. package/lib/host/testing/standInHost.d.ts.map +1 -1
  76. package/lib/host/testing/standInHost.js +19 -1
  77. package/lib/host/testing/standInHost.js.map +1 -1
  78. package/lib/host/ui/useHostConnection.d.ts +13 -5
  79. package/lib/host/ui/useHostConnection.d.ts.map +1 -1
  80. package/lib/host/ui/useHostConnection.js +4 -3
  81. package/lib/host/ui/useHostConnection.js.map +1 -1
  82. package/lib/host-testing.d.ts +2 -2
  83. package/lib/host-testing.d.ts.map +1 -1
  84. package/lib/host-testing.js +2 -2
  85. package/lib/host-testing.js.map +1 -1
  86. package/lib/i18n/core/chromeCatalog.d.ts +1 -1
  87. package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
  88. package/lib/ir/core/handPicking.d.ts +58 -0
  89. package/lib/ir/core/handPicking.d.ts.map +1 -0
  90. package/lib/ir/core/handPicking.js +130 -0
  91. package/lib/ir/core/handPicking.js.map +1 -0
  92. package/lib/ir/core/index.d.ts +3 -0
  93. package/lib/ir/core/index.d.ts.map +1 -1
  94. package/lib/ir/core/index.js +3 -0
  95. package/lib/ir/core/index.js.map +1 -1
  96. package/lib/ir/core/irBandState.d.ts +57 -0
  97. package/lib/ir/core/irBandState.d.ts.map +1 -0
  98. package/lib/ir/core/irBandState.js +119 -0
  99. package/lib/ir/core/irBandState.js.map +1 -0
  100. package/lib/ir/core/irCommands.d.ts +11 -0
  101. package/lib/ir/core/irCommands.d.ts.map +1 -1
  102. package/lib/ir/core/irCommands.js +6 -0
  103. package/lib/ir/core/irCommands.js.map +1 -1
  104. package/lib/ir/core/irPeaks.d.ts +28 -0
  105. package/lib/ir/core/irPeaks.d.ts.map +1 -0
  106. package/lib/ir/core/irPeaks.js +66 -0
  107. package/lib/ir/core/irPeaks.js.map +1 -0
  108. package/lib/ir/core/irReducer.d.ts +8 -0
  109. package/lib/ir/core/irReducer.d.ts.map +1 -1
  110. package/lib/ir/core/irReducer.js +15 -32
  111. package/lib/ir/core/irReducer.js.map +1 -1
  112. package/lib/ir/core/irSpectrum.d.ts +15 -0
  113. package/lib/ir/core/irSpectrum.d.ts.map +1 -1
  114. package/lib/ir/core/irSpectrum.js.map +1 -1
  115. package/lib/ir/core/irState.d.ts +9 -6
  116. package/lib/ir/core/irState.d.ts.map +1 -1
  117. package/lib/ir/core/irState.js +10 -6
  118. package/lib/ir/core/irState.js.map +1 -1
  119. package/lib/ir/core/pickBands.d.ts +14 -0
  120. package/lib/ir/core/pickBands.d.ts.map +1 -1
  121. package/lib/ir/core/pickBands.js +39 -33
  122. package/lib/ir/core/pickBands.js.map +1 -1
  123. package/lib/ir/core/ramanBands.d.ts +9 -1
  124. package/lib/ir/core/ramanBands.d.ts.map +1 -1
  125. package/lib/ir/core/ramanBands.js +2 -2
  126. package/lib/ir/core/ramanBands.js.map +1 -1
  127. package/lib/ir/core/spectrumBands.d.ts +46 -0
  128. package/lib/ir/core/spectrumBands.d.ts.map +1 -0
  129. package/lib/ir/core/spectrumBands.js +98 -0
  130. package/lib/ir/core/spectrumBands.js.map +1 -0
  131. package/lib/ir/ui/BandRow.d.ts +5 -0
  132. package/lib/ir/ui/BandRow.d.ts.map +1 -1
  133. package/lib/ir/ui/BandRow.js +4 -3
  134. package/lib/ir/ui/BandRow.js.map +1 -1
  135. package/lib/ir/ui/BandsPanel.d.ts +5 -0
  136. package/lib/ir/ui/BandsPanel.d.ts.map +1 -1
  137. package/lib/ir/ui/BandsPanel.js +36 -16
  138. package/lib/ir/ui/BandsPanel.js.map +1 -1
  139. package/lib/ir/ui/BandsToolbar.d.ts +9 -0
  140. package/lib/ir/ui/BandsToolbar.d.ts.map +1 -0
  141. package/lib/ir/ui/BandsToolbar.js +61 -0
  142. package/lib/ir/ui/BandsToolbar.js.map +1 -0
  143. package/lib/ir/ui/IrCanvas.d.ts +2 -1
  144. package/lib/ir/ui/IrCanvas.d.ts.map +1 -1
  145. package/lib/ir/ui/IrCanvas.js +19 -5
  146. package/lib/ir/ui/IrCanvas.js.map +1 -1
  147. package/lib/ir/ui/IrChart.d.ts.map +1 -1
  148. package/lib/ir/ui/IrChart.js +8 -1
  149. package/lib/ir/ui/IrChart.js.map +1 -1
  150. package/lib/ir/ui/IrDocumentationDialog.js +1 -1
  151. package/lib/ir/ui/IrDocumentationDialog.js.map +1 -1
  152. package/lib/ir/ui/IrEditor.d.ts +4 -2
  153. package/lib/ir/ui/IrEditor.d.ts.map +1 -1
  154. package/lib/ir/ui/IrEditor.js.map +1 -1
  155. package/lib/ir/ui/IrShell.d.ts.map +1 -1
  156. package/lib/ir/ui/IrShell.js +6 -0
  157. package/lib/ir/ui/IrShell.js.map +1 -1
  158. package/lib/ir/ui/IrStatusBar.d.ts.map +1 -1
  159. package/lib/ir/ui/IrStatusBar.js +1 -1
  160. package/lib/ir/ui/IrStatusBar.js.map +1 -1
  161. package/lib/ir/ui/IrToolbar.d.ts +4 -3
  162. package/lib/ir/ui/IrToolbar.d.ts.map +1 -1
  163. package/lib/ir/ui/IrToolbar.js +5 -4
  164. package/lib/ir/ui/IrToolbar.js.map +1 -1
  165. package/lib/ir/ui/VibrationalEditor.d.ts +2 -1
  166. package/lib/ir/ui/VibrationalEditor.d.ts.map +1 -1
  167. package/lib/ir/ui/VibrationalEditor.js.map +1 -1
  168. package/lib/ir/ui/irActions.d.ts +5 -0
  169. package/lib/ir/ui/irActions.d.ts.map +1 -1
  170. package/lib/ir/ui/irActions.js +6 -0
  171. package/lib/ir/ui/irActions.js.map +1 -1
  172. package/lib/ir/ui/irChartProps.d.ts +4 -1
  173. package/lib/ir/ui/irChartProps.d.ts.map +1 -1
  174. package/lib/ir/ui/useIrEditor.d.ts +5 -11
  175. package/lib/ir/ui/useIrEditor.d.ts.map +1 -1
  176. package/lib/ir/ui/useIrEditor.js +17 -18
  177. package/lib/ir/ui/useIrEditor.js.map +1 -1
  178. package/lib/locales/de.json +63 -0
  179. package/lib/locales/en.json +63 -0
  180. package/lib/locales/es.json +63 -0
  181. package/lib/locales/fr.json +63 -0
  182. package/lib/locales/it.json +63 -0
  183. package/lib/mass/core/analyseSpectrum.d.ts +85 -0
  184. package/lib/mass/core/analyseSpectrum.d.ts.map +1 -0
  185. package/lib/mass/core/analyseSpectrum.js +101 -0
  186. package/lib/mass/core/analyseSpectrum.js.map +1 -0
  187. package/lib/mass/core/ascendingTrace.d.ts +24 -0
  188. package/lib/mass/core/ascendingTrace.d.ts.map +1 -0
  189. package/lib/mass/core/ascendingTrace.js +57 -0
  190. package/lib/mass/core/ascendingTrace.js.map +1 -0
  191. package/lib/mass/core/centroidPeaks.d.ts +77 -0
  192. package/lib/mass/core/centroidPeaks.d.ts.map +1 -0
  193. package/lib/mass/core/centroidPeaks.js +62 -0
  194. package/lib/mass/core/centroidPeaks.js.map +1 -0
  195. package/lib/mass/core/detectMassFormat.d.ts +24 -0
  196. package/lib/mass/core/detectMassFormat.d.ts.map +1 -0
  197. package/lib/mass/core/detectMassFormat.js +105 -0
  198. package/lib/mass/core/detectMassFormat.js.map +1 -0
  199. package/lib/mass/core/downloadSpectra.d.ts +24 -0
  200. package/lib/mass/core/downloadSpectra.d.ts.map +1 -0
  201. package/lib/mass/core/downloadSpectra.js +35 -0
  202. package/lib/mass/core/downloadSpectra.js.map +1 -0
  203. package/lib/mass/core/fromJcamp.d.ts +46 -0
  204. package/lib/mass/core/fromJcamp.d.ts.map +1 -0
  205. package/lib/mass/core/fromJcamp.js +123 -0
  206. package/lib/mass/core/fromJcamp.js.map +1 -0
  207. package/lib/mass/core/fromText.d.ts +30 -0
  208. package/lib/mass/core/fromText.d.ts.map +1 -0
  209. package/lib/mass/core/fromText.js +39 -0
  210. package/lib/mass/core/fromText.js.map +1 -0
  211. package/lib/mass/core/index.d.ts +70 -0
  212. package/lib/mass/core/index.d.ts.map +1 -0
  213. package/lib/mass/core/index.js +42 -0
  214. package/lib/mass/core/index.js.map +1 -0
  215. package/lib/mass/core/jcampTechnique.d.ts +38 -0
  216. package/lib/mass/core/jcampTechnique.d.ts.map +1 -0
  217. package/lib/mass/core/jcampTechnique.js +70 -0
  218. package/lib/mass/core/jcampTechnique.js.map +1 -0
  219. package/lib/mass/core/labelStackBox.d.ts +49 -0
  220. package/lib/mass/core/labelStackBox.d.ts.map +1 -0
  221. package/lib/mass/core/labelStackBox.js +44 -0
  222. package/lib/mass/core/labelStackBox.js.map +1 -0
  223. package/lib/mass/core/labelledPeaks.d.ts +79 -0
  224. package/lib/mass/core/labelledPeaks.d.ts.map +1 -0
  225. package/lib/mass/core/labelledPeaks.js +74 -0
  226. package/lib/mass/core/labelledPeaks.js.map +1 -0
  227. package/lib/mass/core/loadMassSpectra.d.ts +57 -0
  228. package/lib/mass/core/loadMassSpectra.d.ts.map +1 -0
  229. package/lib/mass/core/loadMassSpectra.js +45 -0
  230. package/lib/mass/core/loadMassSpectra.js.map +1 -0
  231. package/lib/mass/core/loadedMassSpectrum.d.ts +42 -0
  232. package/lib/mass/core/loadedMassSpectrum.d.ts.map +1 -0
  233. package/lib/mass/core/loadedMassSpectrum.js +15 -0
  234. package/lib/mass/core/loadedMassSpectrum.js.map +1 -0
  235. package/lib/mass/core/massCommands.d.ts +170 -0
  236. package/lib/mass/core/massCommands.d.ts.map +1 -0
  237. package/lib/mass/core/massCommands.js +95 -0
  238. package/lib/mass/core/massCommands.js.map +1 -0
  239. package/lib/mass/core/massDomain.d.ts +121 -0
  240. package/lib/mass/core/massDomain.d.ts.map +1 -0
  241. package/lib/mass/core/massDomain.js +179 -0
  242. package/lib/mass/core/massDomain.js.map +1 -0
  243. package/lib/mass/core/massEditorApi.d.ts +65 -0
  244. package/lib/mass/core/massEditorApi.d.ts.map +1 -0
  245. package/lib/mass/core/massEditorApi.js +10 -0
  246. package/lib/mass/core/massEditorApi.js.map +1 -0
  247. package/lib/mass/core/massFormat.d.ts +78 -0
  248. package/lib/mass/core/massFormat.d.ts.map +1 -0
  249. package/lib/mass/core/massFormat.js +168 -0
  250. package/lib/mass/core/massFormat.js.map +1 -0
  251. package/lib/mass/core/massMeta.d.ts +85 -0
  252. package/lib/mass/core/massMeta.d.ts.map +1 -0
  253. package/lib/mass/core/massMeta.js +182 -0
  254. package/lib/mass/core/massMeta.js.map +1 -0
  255. package/lib/mass/core/massReducer.d.ts +82 -0
  256. package/lib/mass/core/massReducer.d.ts.map +1 -0
  257. package/lib/mass/core/massReducer.js +163 -0
  258. package/lib/mass/core/massReducer.js.map +1 -0
  259. package/lib/mass/core/massResolution.d.ts +92 -0
  260. package/lib/mass/core/massResolution.d.ts.map +1 -0
  261. package/lib/mass/core/massResolution.js +126 -0
  262. package/lib/mass/core/massResolution.js.map +1 -0
  263. package/lib/mass/core/massSidePanels.d.ts +28 -0
  264. package/lib/mass/core/massSidePanels.d.ts.map +1 -0
  265. package/lib/mass/core/massSidePanels.js +12 -0
  266. package/lib/mass/core/massSidePanels.js.map +1 -0
  267. package/lib/mass/core/massSpectrum.d.ts +206 -0
  268. package/lib/mass/core/massSpectrum.d.ts.map +1 -0
  269. package/lib/mass/core/massSpectrum.js +41 -0
  270. package/lib/mass/core/massSpectrum.js.map +1 -0
  271. package/lib/mass/core/massState.d.ts +145 -0
  272. package/lib/mass/core/massState.d.ts.map +1 -0
  273. package/lib/mass/core/massState.js +102 -0
  274. package/lib/mass/core/massState.js.map +1 -0
  275. package/lib/mass/core/massStateEdits.d.ts +51 -0
  276. package/lib/mass/core/massStateEdits.d.ts.map +1 -0
  277. package/lib/mass/core/massStateEdits.js +90 -0
  278. package/lib/mass/core/massStateEdits.js.map +1 -0
  279. package/lib/mass/core/massSummary.d.ts +77 -0
  280. package/lib/mass/core/massSummary.d.ts.map +1 -0
  281. package/lib/mass/core/massSummary.js +174 -0
  282. package/lib/mass/core/massSummary.js.map +1 -0
  283. package/lib/mass/core/mzDecimalsSetting.d.ts +36 -0
  284. package/lib/mass/core/mzDecimalsSetting.d.ts.map +1 -0
  285. package/lib/mass/core/mzDecimalsSetting.js +42 -0
  286. package/lib/mass/core/mzDecimalsSetting.js.map +1 -0
  287. package/lib/mass/core/nearestPeak.d.ts +75 -0
  288. package/lib/mass/core/nearestPeak.d.ts.map +1 -0
  289. package/lib/mass/core/nearestPeak.js +99 -0
  290. package/lib/mass/core/nearestPeak.js.map +1 -0
  291. package/lib/mass/core/peakLabelLines.d.ts +77 -0
  292. package/lib/mass/core/peakLabelLines.d.ts.map +1 -0
  293. package/lib/mass/core/peakLabelLines.js +54 -0
  294. package/lib/mass/core/peakLabelLines.js.map +1 -0
  295. package/lib/mass/core/peakLabelPlan.d.ts +109 -0
  296. package/lib/mass/core/peakLabelPlan.d.ts.map +1 -0
  297. package/lib/mass/core/peakLabelPlan.js +143 -0
  298. package/lib/mass/core/peakLabelPlan.js.map +1 -0
  299. package/lib/mass/core/peakTable.d.ts +54 -0
  300. package/lib/mass/core/peakTable.d.ts.map +1 -0
  301. package/lib/mass/core/peakTable.js +53 -0
  302. package/lib/mass/core/peakTable.js.map +1 -0
  303. package/lib/mass/core/resolutionOf.d.ts +60 -0
  304. package/lib/mass/core/resolutionOf.d.ts.map +1 -0
  305. package/lib/mass/core/resolutionOf.js +84 -0
  306. package/lib/mass/core/resolutionOf.js.map +1 -0
  307. package/lib/mass/core/scanFilter.d.ts +25 -0
  308. package/lib/mass/core/scanFilter.d.ts.map +1 -0
  309. package/lib/mass/core/scanFilter.js +107 -0
  310. package/lib/mass/core/scanFilter.js.map +1 -0
  311. package/lib/mass/core/spectrumJcamp.d.ts +47 -0
  312. package/lib/mass/core/spectrumJcamp.d.ts.map +1 -0
  313. package/lib/mass/core/spectrumJcamp.js +167 -0
  314. package/lib/mass/core/spectrumJcamp.js.map +1 -0
  315. package/lib/mass/core/toMassSpectrum.d.ts +67 -0
  316. package/lib/mass/core/toMassSpectrum.d.ts.map +1 -0
  317. package/lib/mass/core/toMassSpectrum.js +78 -0
  318. package/lib/mass/core/toMassSpectrum.js.map +1 -0
  319. package/lib/mass/core/traceIcon.d.ts +82 -0
  320. package/lib/mass/core/traceIcon.d.ts.map +1 -0
  321. package/lib/mass/core/traceIcon.js +66 -0
  322. package/lib/mass/core/traceIcon.js.map +1 -0
  323. package/lib/mass/core/writtenResolution.d.ts +40 -0
  324. package/lib/mass/core/writtenResolution.d.ts.map +1 -0
  325. package/lib/mass/core/writtenResolution.js +77 -0
  326. package/lib/mass/core/writtenResolution.js.map +1 -0
  327. package/lib/mass/ui/MassAboutDialog.d.ts +16 -0
  328. package/lib/mass/ui/MassAboutDialog.d.ts.map +1 -0
  329. package/lib/mass/ui/MassAboutDialog.js +44 -0
  330. package/lib/mass/ui/MassAboutDialog.js.map +1 -0
  331. package/lib/mass/ui/MassCanvas.d.ts +46 -0
  332. package/lib/mass/ui/MassCanvas.d.ts.map +1 -0
  333. package/lib/mass/ui/MassCanvas.js +84 -0
  334. package/lib/mass/ui/MassCanvas.js.map +1 -0
  335. package/lib/mass/ui/MassChart.d.ts +21 -0
  336. package/lib/mass/ui/MassChart.d.ts.map +1 -0
  337. package/lib/mass/ui/MassChart.js +139 -0
  338. package/lib/mass/ui/MassChart.js.map +1 -0
  339. package/lib/mass/ui/MassCommandProvider.d.ts +15 -0
  340. package/lib/mass/ui/MassCommandProvider.d.ts.map +1 -0
  341. package/lib/mass/ui/MassCommandProvider.js +14 -0
  342. package/lib/mass/ui/MassCommandProvider.js.map +1 -0
  343. package/lib/mass/ui/MassCommandTooltip.d.ts +16 -0
  344. package/lib/mass/ui/MassCommandTooltip.d.ts.map +1 -0
  345. package/lib/mass/ui/MassCommandTooltip.js +22 -0
  346. package/lib/mass/ui/MassCommandTooltip.js.map +1 -0
  347. package/lib/mass/ui/MassDocumentationDialog.d.ts +18 -0
  348. package/lib/mass/ui/MassDocumentationDialog.d.ts.map +1 -0
  349. package/lib/mass/ui/MassDocumentationDialog.js +70 -0
  350. package/lib/mass/ui/MassDocumentationDialog.js.map +1 -0
  351. package/lib/mass/ui/MassEditor.d.ts +78 -0
  352. package/lib/mass/ui/MassEditor.d.ts.map +1 -0
  353. package/lib/mass/ui/MassEditor.js +42 -0
  354. package/lib/mass/ui/MassEditor.js.map +1 -0
  355. package/lib/mass/ui/MassHeader.d.ts +12 -0
  356. package/lib/mass/ui/MassHeader.d.ts.map +1 -0
  357. package/lib/mass/ui/MassHeader.js +20 -0
  358. package/lib/mass/ui/MassHeader.js.map +1 -0
  359. package/lib/mass/ui/MassLogo.d.ts +21 -0
  360. package/lib/mass/ui/MassLogo.d.ts.map +1 -0
  361. package/lib/mass/ui/MassLogo.js +36 -0
  362. package/lib/mass/ui/MassLogo.js.map +1 -0
  363. package/lib/mass/ui/MassPointerTracker.d.ts +44 -0
  364. package/lib/mass/ui/MassPointerTracker.d.ts.map +1 -0
  365. package/lib/mass/ui/MassPointerTracker.js +29 -0
  366. package/lib/mass/ui/MassPointerTracker.js.map +1 -0
  367. package/lib/mass/ui/MassShell.d.ts +29 -0
  368. package/lib/mass/ui/MassShell.d.ts.map +1 -0
  369. package/lib/mass/ui/MassShell.js +133 -0
  370. package/lib/mass/ui/MassShell.js.map +1 -0
  371. package/lib/mass/ui/MassSidePanel.d.ts +32 -0
  372. package/lib/mass/ui/MassSidePanel.d.ts.map +1 -0
  373. package/lib/mass/ui/MassSidePanel.js +44 -0
  374. package/lib/mass/ui/MassSidePanel.js.map +1 -0
  375. package/lib/mass/ui/MassStateProvider.d.ts +19 -0
  376. package/lib/mass/ui/MassStateProvider.d.ts.map +1 -0
  377. package/lib/mass/ui/MassStateProvider.js +16 -0
  378. package/lib/mass/ui/MassStateProvider.js.map +1 -0
  379. package/lib/mass/ui/MassStatusBar.d.ts +15 -0
  380. package/lib/mass/ui/MassStatusBar.d.ts.map +1 -0
  381. package/lib/mass/ui/MassStatusBar.js +50 -0
  382. package/lib/mass/ui/MassStatusBar.js.map +1 -0
  383. package/lib/mass/ui/MassToolbar.d.ts +24 -0
  384. package/lib/mass/ui/MassToolbar.d.ts.map +1 -0
  385. package/lib/mass/ui/MassToolbar.js +36 -0
  386. package/lib/mass/ui/MassToolbar.js.map +1 -0
  387. package/lib/mass/ui/MetadataPanel.d.ts +35 -0
  388. package/lib/mass/ui/MetadataPanel.d.ts.map +1 -0
  389. package/lib/mass/ui/MetadataPanel.js +100 -0
  390. package/lib/mass/ui/MetadataPanel.js.map +1 -0
  391. package/lib/mass/ui/MzDecimalsMenu.d.ts +28 -0
  392. package/lib/mass/ui/MzDecimalsMenu.d.ts.map +1 -0
  393. package/lib/mass/ui/MzDecimalsMenu.js +40 -0
  394. package/lib/mass/ui/MzDecimalsMenu.js.map +1 -0
  395. package/lib/mass/ui/PeakLabels.d.ts +93 -0
  396. package/lib/mass/ui/PeakLabels.d.ts.map +1 -0
  397. package/lib/mass/ui/PeakLabels.js +91 -0
  398. package/lib/mass/ui/PeakLabels.js.map +1 -0
  399. package/lib/mass/ui/PeakTable.d.ts +34 -0
  400. package/lib/mass/ui/PeakTable.d.ts.map +1 -0
  401. package/lib/mass/ui/PeakTable.js +49 -0
  402. package/lib/mass/ui/PeakTable.js.map +1 -0
  403. package/lib/mass/ui/PeaksPanel.d.ts +31 -0
  404. package/lib/mass/ui/PeaksPanel.d.ts.map +1 -0
  405. package/lib/mass/ui/PeaksPanel.js +53 -0
  406. package/lib/mass/ui/PeaksPanel.js.map +1 -0
  407. package/lib/mass/ui/SpectraPanel.d.ts +17 -0
  408. package/lib/mass/ui/SpectraPanel.d.ts.map +1 -0
  409. package/lib/mass/ui/SpectraPanel.js +76 -0
  410. package/lib/mass/ui/SpectraPanel.js.map +1 -0
  411. package/lib/mass/ui/SpectrumRow.d.ts +26 -0
  412. package/lib/mass/ui/SpectrumRow.d.ts.map +1 -0
  413. package/lib/mass/ui/SpectrumRow.js +141 -0
  414. package/lib/mass/ui/SpectrumRow.js.map +1 -0
  415. package/lib/mass/ui/SpectrumTrace.d.ts +46 -0
  416. package/lib/mass/ui/SpectrumTrace.d.ts.map +1 -0
  417. package/lib/mass/ui/SpectrumTrace.js +68 -0
  418. package/lib/mass/ui/SpectrumTrace.js.map +1 -0
  419. package/lib/mass/ui/index.d.ts +69 -0
  420. package/lib/mass/ui/index.d.ts.map +1 -0
  421. package/lib/mass/ui/index.js +42 -0
  422. package/lib/mass/ui/index.js.map +1 -0
  423. package/lib/mass/ui/massActions.d.ts +52 -0
  424. package/lib/mass/ui/massActions.d.ts.map +1 -0
  425. package/lib/mass/ui/massActions.js +62 -0
  426. package/lib/mass/ui/massActions.js.map +1 -0
  427. package/lib/mass/ui/massChartProps.d.ts +128 -0
  428. package/lib/mass/ui/massChartProps.d.ts.map +1 -0
  429. package/lib/mass/ui/massChartProps.js +14 -0
  430. package/lib/mass/ui/massChartProps.js.map +1 -0
  431. package/lib/mass/ui/massCommandContext.d.ts +36 -0
  432. package/lib/mass/ui/massCommandContext.d.ts.map +1 -0
  433. package/lib/mass/ui/massCommandContext.js +45 -0
  434. package/lib/mass/ui/massCommandContext.js.map +1 -0
  435. package/lib/mass/ui/massHostPanels.d.ts +48 -0
  436. package/lib/mass/ui/massHostPanels.d.ts.map +1 -0
  437. package/lib/mass/ui/massHostPanels.js +41 -0
  438. package/lib/mass/ui/massHostPanels.js.map +1 -0
  439. package/lib/mass/ui/massStateContext.d.ts +26 -0
  440. package/lib/mass/ui/massStateContext.d.ts.map +1 -0
  441. package/lib/mass/ui/massStateContext.js +37 -0
  442. package/lib/mass/ui/massStateContext.js.map +1 -0
  443. package/lib/mass/ui/useIntensityZoom.d.ts +20 -0
  444. package/lib/mass/ui/useIntensityZoom.d.ts.map +1 -0
  445. package/lib/mass/ui/useIntensityZoom.js +42 -0
  446. package/lib/mass/ui/useIntensityZoom.js.map +1 -0
  447. package/lib/mass/ui/useMassEditor.d.ts +52 -0
  448. package/lib/mass/ui/useMassEditor.d.ts.map +1 -0
  449. package/lib/mass/ui/useMassEditor.js +93 -0
  450. package/lib/mass/ui/useMassEditor.js.map +1 -0
  451. package/lib/mass/ui/useMassFiles.d.ts +22 -0
  452. package/lib/mass/ui/useMassFiles.d.ts.map +1 -0
  453. package/lib/mass/ui/useMassFiles.js +71 -0
  454. package/lib/mass/ui/useMassFiles.js.map +1 -0
  455. package/lib/mass/ui/useMassHandle.d.ts +15 -0
  456. package/lib/mass/ui/useMassHandle.d.ts.map +1 -0
  457. package/lib/mass/ui/useMassHandle.js +35 -0
  458. package/lib/mass/ui/useMassHandle.js.map +1 -0
  459. package/lib/mass/ui/useMassPointer.d.ts +70 -0
  460. package/lib/mass/ui/useMassPointer.d.ts.map +1 -0
  461. package/lib/mass/ui/useMassPointer.js +57 -0
  462. package/lib/mass/ui/useMassPointer.js.map +1 -0
  463. package/lib/mass/ui/useMassShortcuts.d.ts +21 -0
  464. package/lib/mass/ui/useMassShortcuts.d.ts.map +1 -0
  465. package/lib/mass/ui/useMassShortcuts.js +66 -0
  466. package/lib/mass/ui/useMassShortcuts.js.map +1 -0
  467. package/lib/mass/ui/useMassSide.d.ts +27 -0
  468. package/lib/mass/ui/useMassSide.d.ts.map +1 -0
  469. package/lib/mass/ui/useMassSide.js +45 -0
  470. package/lib/mass/ui/useMassSide.js.map +1 -0
  471. package/lib/mass/ui/useMassZoom.d.ts +92 -0
  472. package/lib/mass/ui/useMassZoom.d.ts.map +1 -0
  473. package/lib/mass/ui/useMassZoom.js +65 -0
  474. package/lib/mass/ui/useMassZoom.js.map +1 -0
  475. package/lib/mass.d.ts +16 -0
  476. package/lib/mass.d.ts.map +1 -0
  477. package/lib/mass.js +16 -0
  478. package/lib/mass.js.map +1 -0
  479. package/lib/panel/ui/PanelRail.d.ts.map +1 -1
  480. package/lib/panel/ui/PanelRail.js +6 -1
  481. package/lib/panel/ui/PanelRail.js.map +1 -1
  482. package/lib/slides/ui/slideSanitizeSchema.d.ts +3 -2
  483. package/lib/slides/ui/slideSanitizeSchema.d.ts.map +1 -1
  484. package/lib/slides/ui/slideSanitizeSchema.js +82 -2
  485. package/lib/slides/ui/slideSanitizeSchema.js.map +1 -1
  486. package/package.json +6 -1
  487. package/src/about/core/providers.ts +7 -0
  488. package/src/about/ui/AboutProvidedBy.tsx +49 -0
  489. package/src/chart/ui/useChartPointer.ts +18 -4
  490. package/src/credits/core/credits.ts +53 -0
  491. package/src/download/core/drawingMarkup.ts +18 -1
  492. package/src/download/core/figurePng.ts +21 -8
  493. package/src/download/core/figureSvg.ts +5 -2
  494. package/src/download/core/figureTarget.ts +28 -3
  495. package/src/download/core/svgBounds.ts +21 -0
  496. package/src/download/core/svgToPng.ts +3 -4
  497. package/src/format/core/relativeTime.ts +29 -7
  498. package/src/host/core/connectHost.ts +77 -120
  499. package/src/host/core/hostOutputs.ts +197 -0
  500. package/src/host/core/index.ts +13 -7
  501. package/src/host/core/irKinds.ts +92 -0
  502. package/src/host/core/kinds.ts +16 -6
  503. package/src/host/core/protocol.ts +50 -8
  504. package/src/host/core/selectInputs.ts +145 -0
  505. package/src/host/testing/sciPeaks.ts +140 -0
  506. package/src/host/testing/standInHost.ts +30 -5
  507. package/src/host/ui/useHostConnection.ts +17 -8
  508. package/src/host-testing.ts +4 -0
  509. package/src/ir/core/handPicking.ts +154 -0
  510. package/src/ir/core/index.ts +14 -0
  511. package/src/ir/core/irBandState.ts +155 -0
  512. package/src/ir/core/irCommands.ts +7 -0
  513. package/src/ir/core/irPeaks.ts +79 -0
  514. package/src/ir/core/irReducer.ts +27 -34
  515. package/src/ir/core/irSpectrum.ts +16 -0
  516. package/src/ir/core/irState.ts +15 -8
  517. package/src/ir/core/pickBands.ts +46 -39
  518. package/src/ir/core/ramanBands.ts +2 -2
  519. package/src/ir/core/spectrumBands.ts +113 -0
  520. package/src/ir/ui/BandRow.tsx +20 -2
  521. package/src/ir/ui/BandsPanel.tsx +46 -88
  522. package/src/ir/ui/BandsToolbar.tsx +152 -0
  523. package/src/ir/ui/IrCanvas.tsx +22 -11
  524. package/src/ir/ui/IrChart.tsx +10 -1
  525. package/src/ir/ui/IrDocumentationDialog.tsx +7 -0
  526. package/src/ir/ui/IrEditor.tsx +4 -2
  527. package/src/ir/ui/IrShell.tsx +5 -0
  528. package/src/ir/ui/IrStatusBar.tsx +4 -1
  529. package/src/ir/ui/IrToolbar.tsx +14 -5
  530. package/src/ir/ui/VibrationalEditor.tsx +2 -1
  531. package/src/ir/ui/irActions.ts +11 -0
  532. package/src/ir/ui/irChartProps.ts +4 -1
  533. package/src/ir/ui/useIrEditor.ts +21 -21
  534. package/src/locales/de.json +63 -0
  535. package/src/locales/en.json +63 -0
  536. package/src/locales/es.json +63 -0
  537. package/src/locales/fr.json +63 -0
  538. package/src/locales/it.json +63 -0
  539. package/src/mass/core/analyseSpectrum.ts +191 -0
  540. package/src/mass/core/ascendingTrace.ts +62 -0
  541. package/src/mass/core/centroidPeaks.ts +114 -0
  542. package/src/mass/core/detectMassFormat.ts +113 -0
  543. package/src/mass/core/downloadSpectra.ts +52 -0
  544. package/src/mass/core/fromJcamp.ts +151 -0
  545. package/src/mass/core/fromText.ts +58 -0
  546. package/src/mass/core/index.ts +194 -0
  547. package/src/mass/core/jcampTechnique.ts +87 -0
  548. package/src/mass/core/labelStackBox.ts +75 -0
  549. package/src/mass/core/labelledPeaks.ts +135 -0
  550. package/src/mass/core/loadMassSpectra.ts +86 -0
  551. package/src/mass/core/loadedMassSpectrum.ts +43 -0
  552. package/src/mass/core/massCommands.ts +156 -0
  553. package/src/mass/core/massDomain.ts +229 -0
  554. package/src/mass/core/massEditorApi.ts +68 -0
  555. package/src/mass/core/massFormat.ts +187 -0
  556. package/src/mass/core/massMeta.ts +239 -0
  557. package/src/mass/core/massReducer.ts +232 -0
  558. package/src/mass/core/massResolution.ts +169 -0
  559. package/src/mass/core/massSidePanels.ts +27 -0
  560. package/src/mass/core/massSpectrum.ts +220 -0
  561. package/src/mass/core/massState.ts +206 -0
  562. package/src/mass/core/massStateEdits.ts +104 -0
  563. package/src/mass/core/massSummary.ts +213 -0
  564. package/src/mass/core/mzDecimalsSetting.ts +52 -0
  565. package/src/mass/core/nearestPeak.ts +130 -0
  566. package/src/mass/core/peakLabelLines.ts +112 -0
  567. package/src/mass/core/peakLabelPlan.ts +246 -0
  568. package/src/mass/core/peakTable.ts +82 -0
  569. package/src/mass/core/resolutionOf.ts +100 -0
  570. package/src/mass/core/scanFilter.ts +119 -0
  571. package/src/mass/core/spectrumJcamp.ts +186 -0
  572. package/src/mass/core/toMassSpectrum.ts +126 -0
  573. package/src/mass/core/traceIcon.ts +105 -0
  574. package/src/mass/core/writtenResolution.ts +81 -0
  575. package/src/mass/ui/MassAboutDialog.tsx +89 -0
  576. package/src/mass/ui/MassCanvas.tsx +170 -0
  577. package/src/mass/ui/MassChart.tsx +266 -0
  578. package/src/mass/ui/MassCommandProvider.tsx +34 -0
  579. package/src/mass/ui/MassCommandTooltip.tsx +36 -0
  580. package/src/mass/ui/MassDocumentationDialog.tsx +136 -0
  581. package/src/mass/ui/MassEditor.tsx +130 -0
  582. package/src/mass/ui/MassHeader.tsx +38 -0
  583. package/src/mass/ui/MassLogo.tsx +84 -0
  584. package/src/mass/ui/MassPointerTracker.tsx +72 -0
  585. package/src/mass/ui/MassShell.tsx +237 -0
  586. package/src/mass/ui/MassSidePanel.tsx +107 -0
  587. package/src/mass/ui/MassStateProvider.tsx +32 -0
  588. package/src/mass/ui/MassStatusBar.tsx +108 -0
  589. package/src/mass/ui/MassToolbar.tsx +120 -0
  590. package/src/mass/ui/MetadataPanel.tsx +201 -0
  591. package/src/mass/ui/MzDecimalsMenu.tsx +86 -0
  592. package/src/mass/ui/PeakLabels.tsx +242 -0
  593. package/src/mass/ui/PeakTable.tsx +199 -0
  594. package/src/mass/ui/PeaksPanel.tsx +182 -0
  595. package/src/mass/ui/SpectraPanel.tsx +153 -0
  596. package/src/mass/ui/SpectrumRow.tsx +246 -0
  597. package/src/mass/ui/SpectrumTrace.tsx +119 -0
  598. package/src/mass/ui/index.ts +69 -0
  599. package/src/mass/ui/massActions.ts +109 -0
  600. package/src/mass/ui/massChartProps.ts +130 -0
  601. package/src/mass/ui/massCommandContext.tsx +65 -0
  602. package/src/mass/ui/massHostPanels.ts +71 -0
  603. package/src/mass/ui/massStateContext.ts +48 -0
  604. package/src/mass/ui/useIntensityZoom.ts +55 -0
  605. package/src/mass/ui/useMassEditor.ts +156 -0
  606. package/src/mass/ui/useMassFiles.ts +91 -0
  607. package/src/mass/ui/useMassHandle.ts +58 -0
  608. package/src/mass/ui/useMassPointer.ts +120 -0
  609. package/src/mass/ui/useMassShortcuts.ts +85 -0
  610. package/src/mass/ui/useMassSide.ts +63 -0
  611. package/src/mass/ui/useMassZoom.ts +157 -0
  612. package/src/mass.ts +16 -0
  613. package/src/panel/ui/PanelRail.tsx +5 -1
  614. package/src/slides/ui/slideSanitizeSchema.ts +86 -2
  615. package/styles/chrome.css +7 -0
  616. package/styles/slides.css +47 -0
@@ -42,18 +42,31 @@ export async function figurePng(
42
42
  }
43
43
 
44
44
  /**
45
- * The SVG document, decoded and ready to be painted.
45
+ * Where the browser reads an SVG document back from, to paint it.
46
46
  *
47
- * It goes through an object URL rather than a `data:` one because a figure of
48
- * a few thousand dots runs to hundreds of kilobytes, and a `data:` URL that
49
- * long is refused outright by some browsers.
47
+ * An object URL rather than a `data:` one, because a figure of a few thousand
48
+ * dots runs to hundreds of kilobytes and a `data:` URL that long is refused
49
+ * outright by some browsers — except for a document holding a
50
+ * `<foreignObject>`, which is how native MathML sits in a chart. Chromium
51
+ * taints any canvas that paints such a document read from an object URL, and
52
+ * the figure then cannot be saved at all; read from a `data:` URL it paints
53
+ * clean. Such a figure carries a handful of labels, far short of that length.
54
+ * @param markup - The SVG document.
55
+ * @returns The URL; one starting with `blob:` is revoked once read.
56
+ */
57
+ export function svgImageUrl(markup: string): string {
58
+ return markup.includes('<foreignObject')
59
+ ? `data:${FIGURE_SVG_TYPE},${encodeURIComponent(markup)}`
60
+ : URL.createObjectURL(new Blob([markup], { type: FIGURE_SVG_TYPE }));
61
+ }
62
+
63
+ /**
64
+ * The SVG document, decoded and ready to be painted.
50
65
  * @param figure - The figure, already copied off the page.
51
66
  * @returns The decoded image.
52
67
  */
53
68
  async function loadFigure(figure: FigureSvg): Promise<HTMLImageElement> {
54
- const url = URL.createObjectURL(
55
- new Blob([figure.markup], { type: FIGURE_SVG_TYPE }),
56
- );
69
+ const url = svgImageUrl(figure.markup);
57
70
  try {
58
71
  const image = new Image(figure.width, figure.height);
59
72
  await new Promise<void>((resolve, reject) => {
@@ -65,7 +78,7 @@ async function loadFigure(figure: FigureSvg): Promise<HTMLImageElement> {
65
78
  });
66
79
  return image;
67
80
  } finally {
68
- URL.revokeObjectURL(url);
81
+ if (url.startsWith('blob:')) URL.revokeObjectURL(url);
69
82
  }
70
83
  }
71
84
 
@@ -19,7 +19,9 @@ import {
19
19
  figureElement,
20
20
  figureLegends,
21
21
  figurePaintings,
22
+ pieceBounds,
22
23
  } from './figureTarget.ts';
24
+ import { drawingCrop } from './svgBounds.ts';
23
25
 
24
26
  /** How the figure is copied. */
25
27
  export interface FigureSvgOptions {
@@ -85,9 +87,10 @@ export function figureSvg(
85
87
  const styles = window.getComputedStyle(element);
86
88
  const pieces: FigurePiece[] = [];
87
89
  for (const drawing of drawings) {
88
- const box = drawing.getBoundingClientRect();
90
+ const crop = drawingCrop(drawing);
91
+ const box = pieceBounds(drawing, crop);
89
92
  pieces.push({
90
- markup: drawingMarkup(drawing),
93
+ markup: drawingMarkup(drawing, crop),
91
94
  x: box.left - bounds.left,
92
95
  y: box.top - bounds.top,
93
96
  });
@@ -9,6 +9,8 @@
9
9
  */
10
10
 
11
11
  import type { FigurePixels } from './figureScale.ts';
12
+ import type { SvgBox } from './svgBounds.ts';
13
+ import { drawingCrop } from './svgBounds.ts';
12
14
 
13
15
  /** Chrome floating over a figure, which is not part of the picture. */
14
16
  const CHROME = '[data-figure="chrome"]';
@@ -129,11 +131,11 @@ export function figureBounds(drawings: readonly Element[]): FigureBounds {
129
131
  let right = Number.NEGATIVE_INFINITY;
130
132
  let bottom = Number.NEGATIVE_INFINITY;
131
133
  for (const drawing of drawings) {
132
- const box = drawing.getBoundingClientRect();
134
+ const box = pieceBounds(drawing);
133
135
  if (box.left < left) left = box.left;
134
136
  if (box.top < top) top = box.top;
135
- if (box.right > right) right = box.right;
136
- if (box.bottom > bottom) bottom = box.bottom;
137
+ if (box.left + box.width > right) right = box.left + box.width;
138
+ if (box.top + box.height > bottom) bottom = box.top + box.height;
137
139
  }
138
140
  if (!Number.isFinite(left) || !Number.isFinite(top)) {
139
141
  return { left: 0, top: 0, width: 0, height: 0 };
@@ -141,6 +143,29 @@ export function figureBounds(drawings: readonly Element[]): FigureBounds {
141
143
  return { left, top, width: right - left, height: bottom - top };
142
144
  }
143
145
 
146
+ /**
147
+ * Where one piece of a figure goes in the file, and how much room it takes.
148
+ *
149
+ * A piece is its box on the page, except for a drawing cropped to what it is
150
+ * of: that one keeps the corner it stands at and takes the size of its marks,
151
+ * since its window on screen is only wherever the canvas was last left.
152
+ * @param piece - A drawing, or a part drawn in HTML, as it sits on the page.
153
+ * @param crop - The rectangle of it that is saved, in drawing units. Defaults
154
+ * to the one `drawingCrop` finds in a drawing, and to none for anything else.
155
+ * @returns Its box.
156
+ */
157
+ export function pieceBounds(
158
+ piece: Element,
159
+ crop: SvgBox | null = piece instanceof SVGSVGElement
160
+ ? drawingCrop(piece)
161
+ : null,
162
+ ): FigureBounds {
163
+ const { left, top, width, height } = piece.getBoundingClientRect();
164
+ return crop === null
165
+ ? { left, top, width, height }
166
+ : { left, top, width: crop.width, height: crop.height };
167
+ }
168
+
144
169
  /**
145
170
  * How big the saved figure would be at its own size, for a control that has to
146
171
  * write the resulting file size before anybody presses save.
@@ -43,6 +43,27 @@ export function contentBox(svg: SVGSVGElement, padding = 0): SvgBox | null {
43
43
  };
44
44
  }
45
45
 
46
+ /** Room left around the marks of a drawing saved from what it is of. */
47
+ export const CONTENT_PADDING = 12;
48
+
49
+ /**
50
+ * The part of a drawing a saved figure is made of, when the drawing says.
51
+ *
52
+ * Only a drawing that marks the group it is *of* is cropped: a canvas the
53
+ * reader pans and zooms, whose window on screen is wherever it was last left.
54
+ * Every other drawing is saved at the box it is drawn to, which is what a
55
+ * chart is — its axes reach the edges of that box, and a crop to where its
56
+ * trace happens to reach would leave them hanging.
57
+ * @param svg - The drawing, as it is rendered.
58
+ * @returns The rectangle to save, in drawing units, or `null` when the drawing
59
+ * marks no such group or nothing is drawn in it.
60
+ */
61
+ export function drawingCrop(svg: SVGSVGElement): SvgBox | null {
62
+ const marked = `[${CSS.escape(CONTENT_ATTRIBUTE)}]`;
63
+ if (svg.querySelector(marked) === null) return null;
64
+ return contentBox(svg, CONTENT_PADDING);
65
+ }
66
+
46
67
  /**
47
68
  * The rectangle the element itself covers, rather than the marks inside it.
48
69
  *
@@ -1,3 +1,4 @@
1
+ import { svgImageUrl } from './figurePng.ts';
1
2
  import type { SvgBox } from './svgBounds.ts';
2
3
  import { svgToString } from './svgToString.ts';
3
4
 
@@ -92,9 +93,7 @@ function side(value: number): number {
92
93
  * @returns The image, already decoded, so it can be drawn straight away.
93
94
  */
94
95
  async function loadImage(source: string): Promise<HTMLImageElement> {
95
- const url = URL.createObjectURL(
96
- new Blob([source], { type: 'image/svg+xml' }),
97
- );
96
+ const url = svgImageUrl(source);
98
97
  try {
99
98
  const image = new Image();
100
99
  image.src = url;
@@ -102,7 +101,7 @@ async function loadImage(source: string): Promise<HTMLImageElement> {
102
101
  return image;
103
102
  } finally {
104
103
  // The decoded image no longer needs the URL it came from.
105
- URL.revokeObjectURL(url);
104
+ if (url.startsWith('blob:')) URL.revokeObjectURL(url);
106
105
  }
107
106
  }
108
107
 
@@ -16,7 +16,8 @@ const UNITS: ReadonlyArray<{
16
16
  { unit: 'minute', milliseconds: MINUTE },
17
17
  ];
18
18
 
19
- const FORMATTER = new Intl.RelativeTimeFormat('en-US', { numeric: 'auto' });
19
+ /** One formatter per locale, made the first time that locale is asked for. */
20
+ const FORMATTERS = new Map<string, Intl.RelativeTimeFormat>();
20
21
 
21
22
  /** How {@link formatRelativeTime} measures and what it writes for nothing. */
22
23
  export interface FormatRelativeTimeOptions {
@@ -32,16 +33,21 @@ export interface FormatRelativeTimeOptions {
32
33
  * @default MISSING_VALUE
33
34
  */
34
35
  missing?: string;
36
+ /**
37
+ * The language the phrase is written in: the page's, from `useLanguage()`.
38
+ * @default 'en-US'
39
+ */
40
+ locale?: string;
35
41
  }
36
42
 
37
43
  /**
38
44
  * How far an instant is from now, in words: `just now`, `5 minutes ago`,
39
- * `yesterday`, `in 3 weeks`.
45
+ * `yesterday`, `in 3 weeks` — or `il y a 5 minutes` in French.
40
46
  *
41
47
  * The largest unit that fits is used and the count is rounded towards zero, so
42
48
  * ninety minutes read `1 hour ago` rather than claiming two. Under a minute
43
- * either way is `just now`, which also absorbs a clock that is a few seconds
44
- * ahead. The locale is fixed to `en-US`, like every formatter of this module.
49
+ * either way is `just now` (the language's own word for now, outside
50
+ * English), which also absorbs a clock that is a few seconds ahead.
45
51
  * @param instant - Epoch milliseconds, an ISO timestamp or a date.
46
52
  * @param options - See {@link FormatRelativeTimeOptions}.
47
53
  * @returns The phrase, or the missing text when the instant is absent or unreadable.
@@ -50,20 +56,36 @@ export function formatRelativeTime(
50
56
  instant: number | string | Date | undefined,
51
57
  options: FormatRelativeTimeOptions = {},
52
58
  ): string {
53
- const { now = Date.now(), missing = MISSING_VALUE } = options;
59
+ const {
60
+ now = Date.now(),
61
+ missing = MISSING_VALUE,
62
+ locale = 'en-US',
63
+ } = options;
54
64
  const time = toTime(instant);
55
65
  const reference = toTime(now);
56
66
  if (time === undefined || reference === undefined) return missing;
57
67
 
68
+ const formatter = formatterOf(locale);
58
69
  const elapsed = time - reference;
59
70
  const distance = Math.abs(elapsed);
60
71
  for (const { unit, milliseconds } of UNITS) {
61
72
  if (distance >= milliseconds) {
62
73
  const count = Math.floor(distance / milliseconds);
63
- return FORMATTER.format(elapsed < 0 ? -count : count, unit);
74
+ return formatter.format(elapsed < 0 ? -count : count, unit);
64
75
  }
65
76
  }
66
- return 'just now';
77
+ return locale.toLowerCase().startsWith('en')
78
+ ? 'just now'
79
+ : formatter.format(0, 'second');
80
+ }
81
+
82
+ function formatterOf(locale: string): Intl.RelativeTimeFormat {
83
+ let formatter = FORMATTERS.get(locale);
84
+ if (formatter === undefined) {
85
+ formatter = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
86
+ FORMATTERS.set(locale, formatter);
87
+ }
88
+ return formatter;
67
89
  }
68
90
 
69
91
  function toTime(
@@ -1,22 +1,22 @@
1
1
  /**
2
2
  * A tool's side of the host protocol: introduce itself to the page that
3
- * frames it, take the private port that page answers with, and serve what
4
- * arrives through it.
3
+ * frames it, take the private port that page answers with, serve what arrives
4
+ * through it, and send back what the host takes back.
5
5
  *
6
6
  * Free of React and of the DOM — the window is handed in — so a test drives the
7
7
  * whole exchange with a fake host and a real `MessageChannel`.
8
8
  */
9
9
 
10
- import type { CheckedValue, KnownKind } from './kinds.ts';
11
- import { checkValue, isKnownKind, kindAccepts } from './kinds.ts';
10
+ import type { HostOutputs } from './hostOutputs.ts';
11
+ import {
12
+ OUTPUT_DELAY,
13
+ answerGet,
14
+ createOutputPublisher,
15
+ } from './hostOutputs.ts';
12
16
  import type {
13
- GetRequest,
14
17
  HelloMessage,
15
- HostError,
16
- HostItem,
17
18
  LoadRequest,
18
19
  ResultMessage,
19
- ToolInput,
20
20
  ToolInterface,
21
21
  UsedItem,
22
22
  } from './protocol.ts';
@@ -26,13 +26,11 @@ import {
26
26
  isConnect,
27
27
  parseRequest,
28
28
  } from './protocol.ts';
29
+ import type { LoadedInput } from './selectInputs.ts';
30
+ import { selectInputs } from './selectInputs.ts';
29
31
 
30
- /** The item an input took, and its value as the input's kind reads it. */
31
- export interface LoadedInput {
32
- key: string;
33
- item: HostItem;
34
- checked: CheckedValue;
35
- }
32
+ export type { InputSelection, LoadedInput } from './selectInputs.ts';
33
+ export { selectInputs } from './selectInputs.ts';
36
34
 
37
35
  /** What a window `message` event carries, as far as the protocol reads it. */
38
36
  export interface HostMessageEvent {
@@ -54,17 +52,25 @@ export interface HostConnectionOptions {
54
52
  /** The build that is running, as the About states it, or `null`. */
55
53
  build: unknown;
56
54
  /**
57
- * Show the inputs of a new document. Throwing means they could not be read:
58
- * the error's message goes back to the host, and the document is not marked
59
- * as applied.
55
+ * Show the inputs of a new document. Throwing, or rejecting, means they
56
+ * could not be read: the error's message goes back to the host, and the
57
+ * document is not marked as applied.
58
+ */
59
+ load: (inputs: readonly LoadedInput[]) => void | Promise<void>;
60
+ /**
61
+ * Where the values of the tool's outputs are read, for `get` and for the
62
+ * `output` sent when one the host accepted changes.
63
+ * @default undefined — nothing goes back
60
64
  */
61
- load: (inputs: readonly LoadedInput[]) => void;
65
+ outputs?: HostOutputs;
66
+ /**
67
+ * How long the outputs must stay still before a change is sent, in
68
+ * milliseconds.
69
+ * @default OUTPUT_DELAY
70
+ */
71
+ outputDelay?: number;
62
72
  }
63
73
 
64
- /** What choosing the items of a request gave. */
65
- export type InputSelection =
66
- { ok: true; inputs: LoadedInput[] } | { ok: false; error: HostError };
67
-
68
74
  /**
69
75
  * Say `hello` to the host, and serve its requests once it connects.
70
76
  *
@@ -72,24 +78,50 @@ export type InputSelection =
72
78
  * one replaces the first — a host that reloads its own script connects again.
73
79
  * A document is applied once: the same one again is answered as before and
74
80
  * changes nothing, so a host re-sending it never overwrites the user's work.
81
+ * Requests are served one at a time, in order, so a `get` sent after a `load`
82
+ * reads what that load showed, however long it took.
75
83
  * @param options - The window, the tool, and its load function.
76
84
  * @returns The function that stops listening and closes the port.
77
85
  */
78
86
  export function connectHost(options: HostConnectionOptions): () => void {
79
- const { host, listen, tool, origin, build, load } = options;
87
+ const { host, listen, tool, origin, build, load, outputs } = options;
88
+ const { outputDelay = OUTPUT_DELAY } = options;
80
89
  let port: MessagePort | null = null;
81
90
  let appliedDocument: string | null = null;
82
91
  let appliedUsed: UsedItem[] = [];
92
+ let served: Promise<void> = Promise.resolve();
93
+
94
+ const publisher =
95
+ outputs === undefined
96
+ ? null
97
+ : createOutputPublisher(
98
+ outputs,
99
+ (message) => port?.postMessage(message),
100
+ outputDelay,
101
+ );
102
+ const stopHearing = outputs?.subscribe(() => publisher?.changed());
83
103
 
84
104
  function serve(data: unknown): void {
85
105
  const request = parseRequest(data);
86
106
  if (request === null) return;
87
- const result =
88
- request.type === 'load' ? applyLoad(request) : answerGet(request, tool);
89
- port?.postMessage(result);
107
+ served = served
108
+ .then(async () => {
109
+ const result =
110
+ request.type === 'load'
111
+ ? await applyLoad(request)
112
+ : answerGet(request, tool, outputs);
113
+ port?.postMessage(result);
114
+ })
115
+ // A request that failed must not stop the ones after it being served,
116
+ // and must still be seen: it is thrown again, on its own.
117
+ .catch((error: unknown) => {
118
+ queueMicrotask(() => {
119
+ throw error;
120
+ });
121
+ });
90
122
  }
91
123
 
92
- function applyLoad(request: LoadRequest): ResultMessage {
124
+ async function applyLoad(request: LoadRequest): Promise<ResultMessage> {
93
125
  const { id, document, items } = request;
94
126
  if (document === appliedDocument) {
95
127
  return { type: 'result', id, ok: true, used: appliedUsed };
@@ -98,8 +130,11 @@ export function connectHost(options: HostConnectionOptions): () => void {
98
130
  if (!selection.ok) {
99
131
  return { type: 'result', id, ok: false, error: selection.error };
100
132
  }
133
+ // An edit still settling belongs to the document on screen, which the
134
+ // outputs describe only until this one replaces it.
135
+ publisher?.flush();
101
136
  try {
102
- load(selection.inputs);
137
+ await load(selection.inputs);
103
138
  } catch (error) {
104
139
  const message = error instanceof Error ? error.message : String(error);
105
140
  return {
@@ -109,11 +144,19 @@ export function connectHost(options: HostConnectionOptions): () => void {
109
144
  error: { code: 'unreadable', message },
110
145
  };
111
146
  }
147
+ return applied(request, selection.inputs);
148
+ }
149
+
150
+ // Nothing else runs between a load's check and this: requests are served
151
+ // one at a time.
152
+ function applied(
153
+ request: LoadRequest,
154
+ inputs: readonly LoadedInput[],
155
+ ): ResultMessage {
156
+ const { id, document, accept = [] } = request;
112
157
  appliedDocument = document;
113
- appliedUsed = selection.inputs.map(({ key, item }) => ({
114
- key,
115
- kind: item.kind,
116
- }));
158
+ appliedUsed = inputs.map(({ key, item }) => ({ key, kind: item.kind }));
159
+ publisher?.baseline(document, accept);
117
160
  return { type: 'result', id, ok: true, used: appliedUsed };
118
161
  }
119
162
 
@@ -146,95 +189,9 @@ export function connectHost(options: HostConnectionOptions): () => void {
146
189
 
147
190
  return () => {
148
191
  stopListening();
192
+ stopHearing?.();
193
+ publisher?.stop();
149
194
  port?.close();
150
195
  port = null;
151
196
  };
152
197
  }
153
-
154
- /**
155
- * Give each input of the tool one item of a request.
156
- *
157
- * Each input tries its kinds in its own order of preference, and takes the
158
- * first item that falls under one and passes that kind's schema: the tool
159
- * knows which notation serves it best, whatever order the host listed them in.
160
- * An item naming another input's `key` is left to that input.
161
- * @param tool - What the tool takes.
162
- * @param items - What the host sent.
163
- * @returns One item per input, or why an input got none.
164
- */
165
- export function selectInputs(
166
- tool: ToolInterface,
167
- items: readonly HostItem[],
168
- ): InputSelection {
169
- const inputs: LoadedInput[] = [];
170
- for (const input of tool.inputs) {
171
- const errors: string[] = [];
172
- const loaded = selectInput(input, items, errors);
173
- if (loaded !== null) {
174
- inputs.push(loaded);
175
- continue;
176
- }
177
- if (errors.length > 0) {
178
- return {
179
- ok: false,
180
- error: {
181
- code: 'invalid',
182
- message: `No item for ${input.key} has the shape its kind requires, so ${tool.name} loaded nothing.`,
183
- errors,
184
- },
185
- };
186
- }
187
- return {
188
- ok: false,
189
- error: {
190
- code: 'no-input',
191
- message: `No item is a ${KIND_LIST.format(input.kinds)}, so ${tool.name} loaded nothing.`,
192
- },
193
- };
194
- }
195
- return { ok: true, inputs };
196
- }
197
-
198
- function selectInput(
199
- input: ToolInput,
200
- items: readonly HostItem[],
201
- errors: string[],
202
- ): LoadedInput | null {
203
- for (const accepted of input.kinds) {
204
- if (!isKnownKind(accepted)) continue;
205
- for (const item of items) {
206
- if (item.key !== undefined && item.key !== input.key) continue;
207
- if (!kindAccepts(accepted, item.kind)) continue;
208
- const loaded = checkItem(input.key, accepted, item, errors);
209
- if (loaded !== null) return loaded;
210
- }
211
- }
212
- return null;
213
- }
214
-
215
- function checkItem(
216
- key: string,
217
- accepted: KnownKind,
218
- item: HostItem,
219
- errors: string[],
220
- ): LoadedInput | null {
221
- const outcome = checkValue(accepted, item.value);
222
- if (outcome.valid) return { key, item, checked: outcome.checked };
223
- for (const error of outcome.errors) errors.push(`${item.kind} ${error}`);
224
- return null;
225
- }
226
-
227
- function answerGet(request: GetRequest, tool: ToolInterface): ResultMessage {
228
- // Nothing goes back yet: no tool declares an output.
229
- return {
230
- type: 'result',
231
- id: request.id,
232
- ok: false,
233
- error: {
234
- code: 'no-output',
235
- message: `${tool.name} has nothing to give back yet.`,
236
- },
237
- };
238
- }
239
-
240
- const KIND_LIST = new Intl.ListFormat('en', { type: 'disjunction' });