@morscherlab/mint-sdk 1.0.0-alpha.2

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 (491) hide show
  1. package/README.md +326 -0
  2. package/dist/__stories__/experiment-helpers.d.ts +25 -0
  3. package/dist/__tests__/components/AppLayout.test.d.ts +1 -0
  4. package/dist/__tests__/components/AppSidebar.test.d.ts +1 -0
  5. package/dist/__tests__/components/AppTopBar.test.d.ts +1 -0
  6. package/dist/__tests__/components/BaseInput.test.d.ts +1 -0
  7. package/dist/__tests__/components/BasePill.test.d.ts +1 -0
  8. package/dist/__tests__/components/Calendar.test.d.ts +1 -0
  9. package/dist/__tests__/components/CollapsibleCard.test.d.ts +1 -0
  10. package/dist/__tests__/components/DataFrame.test.d.ts +1 -0
  11. package/dist/__tests__/components/DropdownButton.test.d.ts +1 -0
  12. package/dist/__tests__/composables/formBuilderRegistry.test.d.ts +1 -0
  13. package/dist/__tests__/composables/useAppExperiment.test.d.ts +1 -0
  14. package/dist/__tests__/composables/useAuth.test.d.ts +1 -0
  15. package/dist/__tests__/composables/useAutoGroup.test.d.ts +1 -0
  16. package/dist/__tests__/composables/useExperimentData.test.d.ts +13 -0
  17. package/dist/__tests__/composables/useExperimentSave.test.d.ts +1 -0
  18. package/dist/__tests__/composables/useForm.test.d.ts +1 -0
  19. package/dist/__tests__/composables/useFormBuilder.test.d.ts +1 -0
  20. package/dist/__tests__/composables/usePlatformContext.test.d.ts +1 -0
  21. package/dist/__tests__/composables/usePluginApi.test.d.ts +13 -0
  22. package/dist/__tests__/composables/usePluginConfig.test.d.ts +14 -0
  23. package/dist/__tests__/utils/color.test.d.ts +1 -0
  24. package/dist/auth-BYmxZdJl.js +297 -0
  25. package/dist/auth-BYmxZdJl.js.map +1 -0
  26. package/dist/components/AlertBox.vue.d.ts +34 -0
  27. package/dist/components/AppAvatarMenu.vue.d.ts +58 -0
  28. package/dist/components/AppContainer.vue.d.ts +28 -0
  29. package/dist/components/AppLayout.vue.d.ts +31 -0
  30. package/dist/components/AppPageSelector.vue.d.ts +43 -0
  31. package/dist/components/AppPillNav.vue.d.ts +11 -0
  32. package/dist/components/AppPluginSwitcher.vue.d.ts +38 -0
  33. package/dist/components/AppSidebar.vue.d.ts +47 -0
  34. package/dist/components/AppTopBar.vue.d.ts +111 -0
  35. package/dist/components/AuditTrail.vue.d.ts +38 -0
  36. package/dist/components/AutoGroupModal.vue.d.ts +124 -0
  37. package/dist/components/Avatar.vue.d.ts +14 -0
  38. package/dist/components/BaseButton.vue.d.ts +37 -0
  39. package/dist/components/BaseCheckbox.vue.d.ts +17 -0
  40. package/dist/components/BaseInput.vue.d.ts +34 -0
  41. package/dist/components/BaseModal.vue.d.ts +46 -0
  42. package/dist/components/BasePill.vue.d.ts +57 -0
  43. package/dist/components/BaseRadioGroup.vue.d.ts +21 -0
  44. package/dist/components/BaseSelect.vue.d.ts +20 -0
  45. package/dist/components/BaseSlider.vue.d.ts +22 -0
  46. package/dist/components/BaseTabs.vue.d.ts +14 -0
  47. package/dist/components/BaseTextarea.vue.d.ts +30 -0
  48. package/dist/components/BaseToggle.vue.d.ts +19 -0
  49. package/dist/components/BatchProgressList.vue.d.ts +43 -0
  50. package/dist/components/Breadcrumb.vue.d.ts +33 -0
  51. package/dist/components/Calendar.vue.d.ts +107 -0
  52. package/dist/components/ChartContainer.vue.d.ts +31 -0
  53. package/dist/components/ChemicalFormula.vue.d.ts +8 -0
  54. package/dist/components/CollapsibleCard.vue.d.ts +41 -0
  55. package/dist/components/ColorSlider.vue.d.ts +34 -0
  56. package/dist/components/ConcentrationInput.vue.d.ts +25 -0
  57. package/dist/components/ConfirmDialog.vue.d.ts +42 -0
  58. package/dist/components/DataFrame.vue.d.ts +107 -0
  59. package/dist/components/DatePicker.vue.d.ts +25 -0
  60. package/dist/components/DateTimePicker.vue.d.ts +30 -0
  61. package/dist/components/Divider.vue.d.ts +14 -0
  62. package/dist/components/DoseCalculator.vue.d.ts +19 -0
  63. package/dist/components/DropdownButton.vue.d.ts +47 -0
  64. package/dist/components/EmptyState.vue.d.ts +36 -0
  65. package/dist/components/ExperimentCodeBadge.vue.d.ts +14 -0
  66. package/dist/components/ExperimentDataViewer.vue.d.ts +29 -0
  67. package/dist/components/ExperimentPopover.vue.d.ts +32 -0
  68. package/dist/components/ExperimentSelectorModal.vue.d.ts +28 -0
  69. package/dist/components/ExperimentTimeline.vue.d.ts +44 -0
  70. package/dist/components/FileUploader.vue.d.ts +40 -0
  71. package/dist/components/FitPanel.vue.d.ts +46 -0
  72. package/dist/components/FormActions.vue.d.ts +33 -0
  73. package/dist/components/FormBuilder.vue.d.ts +287 -0
  74. package/dist/components/FormField.vue.d.ts +28 -0
  75. package/dist/components/FormFieldRenderer.vue.d.ts +31 -0
  76. package/dist/components/FormSection.vue.d.ts +43 -0
  77. package/dist/components/FormulaInput.vue.d.ts +25 -0
  78. package/dist/components/GroupAssigner.vue.d.ts +25 -0
  79. package/dist/components/GroupingModal.vue.d.ts +12 -0
  80. package/dist/components/IconButton.vue.d.ts +34 -0
  81. package/dist/components/LoadingSpinner.vue.d.ts +12 -0
  82. package/dist/components/MoleculeInput.vue.d.ts +27 -0
  83. package/dist/components/MultiSelect.vue.d.ts +19 -0
  84. package/dist/components/NumberInput.vue.d.ts +22 -0
  85. package/dist/components/PlateMapEditor.vue.d.ts +50 -0
  86. package/dist/components/ProgressBar.vue.d.ts +23 -0
  87. package/dist/components/ProtocolStepEditor.vue.d.ts +24 -0
  88. package/dist/components/RackEditor.vue.d.ts +40 -0
  89. package/dist/components/ReagentEditor.vue.d.ts +30 -0
  90. package/dist/components/ReagentList.vue.d.ts +32 -0
  91. package/dist/components/ResourceCard.vue.d.ts +50 -0
  92. package/dist/components/SampleHierarchyTree.vue.d.ts +26 -0
  93. package/dist/components/SampleLegend.vue.d.ts +32 -0
  94. package/dist/components/SampleSelector.vue.d.ts +29 -0
  95. package/dist/components/ScheduleCalendar.vue.d.ts +110 -0
  96. package/dist/components/ScientificNumber.vue.d.ts +14 -0
  97. package/dist/components/SegmentedControl.vue.d.ts +20 -0
  98. package/dist/components/SequenceInput.vue.d.ts +54 -0
  99. package/dist/components/SettingsButton.vue.d.ts +30 -0
  100. package/dist/components/SettingsModal.vue.d.ts +36 -0
  101. package/dist/components/Skeleton.vue.d.ts +11 -0
  102. package/dist/components/StatusIndicator.vue.d.ts +13 -0
  103. package/dist/components/StepWizard.vue.d.ts +65 -0
  104. package/dist/components/TagsInput.vue.d.ts +39 -0
  105. package/dist/components/ThemeToggle.vue.d.ts +7 -0
  106. package/dist/components/TimePicker.vue.d.ts +29 -0
  107. package/dist/components/TimeRangeInput.vue.d.ts +27 -0
  108. package/dist/components/ToastNotification.vue.d.ts +2 -0
  109. package/dist/components/Tooltip.vue.d.ts +35 -0
  110. package/dist/components/UnitInput.vue.d.ts +39 -0
  111. package/dist/components/WellEditPopup.vue.d.ts +25 -0
  112. package/dist/components/WellPlate.vue.d.ts +73 -0
  113. package/dist/components/index.d.ts +87 -0
  114. package/dist/components/index.js +3 -0
  115. package/dist/components-CKf-UpGi.js +15089 -0
  116. package/dist/components-CKf-UpGi.js.map +1 -0
  117. package/dist/composables/experiment-utils.d.ts +8 -0
  118. package/dist/composables/formBuilderRegistry.d.ts +13 -0
  119. package/dist/composables/index.d.ts +28 -0
  120. package/dist/composables/index.js +3 -0
  121. package/dist/composables/useApi.d.ts +20 -0
  122. package/dist/composables/useAppExperiment.d.ts +37 -0
  123. package/dist/composables/useAsync.d.ts +128 -0
  124. package/dist/composables/useAuth.d.ts +47 -0
  125. package/dist/composables/useAutoGroup.d.ts +106 -0
  126. package/dist/composables/useChemicalFormula.d.ts +21 -0
  127. package/dist/composables/useConcentrationUnits.d.ts +29 -0
  128. package/dist/composables/useDoseCalculator.d.ts +58 -0
  129. package/dist/composables/useExperimentData.d.ts +18 -0
  130. package/dist/composables/useExperimentSave.d.ts +36 -0
  131. package/dist/composables/useExperimentSelector.d.ts +30 -0
  132. package/dist/composables/useForm.d.ts +92 -0
  133. package/dist/composables/useFormBuilder.d.ts +24 -0
  134. package/dist/composables/usePasskey.d.ts +10 -0
  135. package/dist/composables/usePlatformContext.d.ts +131 -0
  136. package/dist/composables/usePluginApi.d.ts +29 -0
  137. package/dist/composables/usePluginConfig.d.ts +13 -0
  138. package/dist/composables/useProtocolTemplates.d.ts +44 -0
  139. package/dist/composables/useRackEditor.d.ts +31 -0
  140. package/dist/composables/useReagentSeries.d.ts +23 -0
  141. package/dist/composables/useScheduleDrag.d.ts +78 -0
  142. package/dist/composables/useSequenceUtils.d.ts +14 -0
  143. package/dist/composables/useTheme.d.ts +8 -0
  144. package/dist/composables/useTimeUtils.d.ts +29 -0
  145. package/dist/composables/useToast.d.ts +22 -0
  146. package/dist/composables/useWellPlateEditor.d.ts +33 -0
  147. package/dist/composables-D0QfFzq1.js +805 -0
  148. package/dist/composables-D0QfFzq1.js.map +1 -0
  149. package/dist/histoire.setup.d.ts +1 -0
  150. package/dist/index.d.ts +6 -0
  151. package/dist/index.js +7 -0
  152. package/dist/install.d.ts +16 -0
  153. package/dist/install.js +23 -0
  154. package/dist/install.js.map +1 -0
  155. package/dist/stores/auth.d.ts +146 -0
  156. package/dist/stores/index.d.ts +2 -0
  157. package/dist/stores/index.js +2 -0
  158. package/dist/stores/settings.d.ts +75 -0
  159. package/dist/styles.css +29728 -0
  160. package/dist/tailwind.preset.d.ts +58 -0
  161. package/dist/tailwind.preset.js +66 -0
  162. package/dist/tailwind.preset.js.map +1 -0
  163. package/dist/types/auth.d.ts +42 -0
  164. package/dist/types/auto-group.d.ts +34 -0
  165. package/dist/types/components.d.ts +528 -0
  166. package/dist/types/form-builder.d.ts +167 -0
  167. package/dist/types/index.d.ts +5 -0
  168. package/dist/types/index.js +0 -0
  169. package/dist/types/platform.d.ts +75 -0
  170. package/dist/useScheduleDrag-DAJueTbK.js +7181 -0
  171. package/dist/useScheduleDrag-DAJueTbK.js.map +1 -0
  172. package/dist/utils/color.d.ts +24 -0
  173. package/package.json +114 -0
  174. package/src/__stories__/experiment-helpers.ts +83 -0
  175. package/src/__tests__/components/AppLayout.test.ts +163 -0
  176. package/src/__tests__/components/AppSidebar.test.ts +292 -0
  177. package/src/__tests__/components/AppTopBar.test.ts +683 -0
  178. package/src/__tests__/components/BaseInput.test.ts +99 -0
  179. package/src/__tests__/components/BasePill.test.ts +291 -0
  180. package/src/__tests__/components/Calendar.test.ts +566 -0
  181. package/src/__tests__/components/CollapsibleCard.test.ts +524 -0
  182. package/src/__tests__/components/DataFrame.test.ts +767 -0
  183. package/src/__tests__/components/DropdownButton.test.ts +471 -0
  184. package/src/__tests__/composables/formBuilderRegistry.test.ts +187 -0
  185. package/src/__tests__/composables/useAppExperiment.test.ts +560 -0
  186. package/src/__tests__/composables/useAuth.test.ts +188 -0
  187. package/src/__tests__/composables/useAutoGroup.test.ts +860 -0
  188. package/src/__tests__/composables/useExperimentData.test.ts +127 -0
  189. package/src/__tests__/composables/useExperimentSave.test.ts +347 -0
  190. package/src/__tests__/composables/useForm.test.ts +205 -0
  191. package/src/__tests__/composables/useFormBuilder.test.ts +917 -0
  192. package/src/__tests__/composables/usePlatformContext.test.ts +116 -0
  193. package/src/__tests__/composables/usePluginApi.test.ts +81 -0
  194. package/src/__tests__/composables/usePluginConfig.test.ts +176 -0
  195. package/src/__tests__/utils/color.test.ts +96 -0
  196. package/src/components/AlertBox.story.vue +204 -0
  197. package/src/components/AlertBox.vue +88 -0
  198. package/src/components/AppAvatarMenu.story.vue +155 -0
  199. package/src/components/AppAvatarMenu.vue +184 -0
  200. package/src/components/AppContainer.story.vue +104 -0
  201. package/src/components/AppContainer.vue +34 -0
  202. package/src/components/AppLayout.story.vue +292 -0
  203. package/src/components/AppLayout.vue +75 -0
  204. package/src/components/AppPageSelector.vue +159 -0
  205. package/src/components/AppPillNav.vue +66 -0
  206. package/src/components/AppPluginSwitcher.vue +241 -0
  207. package/src/components/AppSidebar.story.vue +309 -0
  208. package/src/components/AppSidebar.vue +119 -0
  209. package/src/components/AppTopBar.story.vue +304 -0
  210. package/src/components/AppTopBar.vue +661 -0
  211. package/src/components/AuditTrail.story.vue +163 -0
  212. package/src/components/AuditTrail.vue +151 -0
  213. package/src/components/AutoGroupModal.story.vue +273 -0
  214. package/src/components/AutoGroupModal.vue +566 -0
  215. package/src/components/Avatar.story.vue +115 -0
  216. package/src/components/Avatar.vue +79 -0
  217. package/src/components/BaseButton.story.vue +96 -0
  218. package/src/components/BaseButton.vue +73 -0
  219. package/src/components/BaseCheckbox.story.vue +73 -0
  220. package/src/components/BaseCheckbox.vue +69 -0
  221. package/src/components/BaseInput.story.vue +98 -0
  222. package/src/components/BaseInput.vue +74 -0
  223. package/src/components/BaseModal.story.vue +237 -0
  224. package/src/components/BaseModal.vue +182 -0
  225. package/src/components/BasePill.story.vue +142 -0
  226. package/src/components/BasePill.vue +89 -0
  227. package/src/components/BaseRadioGroup.story.vue +145 -0
  228. package/src/components/BaseRadioGroup.vue +124 -0
  229. package/src/components/BaseSelect.story.vue +120 -0
  230. package/src/components/BaseSelect.vue +71 -0
  231. package/src/components/BaseSlider.story.vue +122 -0
  232. package/src/components/BaseSlider.vue +126 -0
  233. package/src/components/BaseTabs.story.vue +127 -0
  234. package/src/components/BaseTabs.vue +59 -0
  235. package/src/components/BaseTextarea.story.vue +91 -0
  236. package/src/components/BaseTextarea.vue +62 -0
  237. package/src/components/BaseToggle.story.vue +81 -0
  238. package/src/components/BaseToggle.vue +76 -0
  239. package/src/components/BatchProgressList.story.vue +92 -0
  240. package/src/components/BatchProgressList.vue +184 -0
  241. package/src/components/Breadcrumb.story.vue +106 -0
  242. package/src/components/Breadcrumb.vue +75 -0
  243. package/src/components/Calendar.story.vue +106 -0
  244. package/src/components/Calendar.vue +363 -0
  245. package/src/components/ChartContainer.story.vue +113 -0
  246. package/src/components/ChartContainer.vue +64 -0
  247. package/src/components/ChemicalFormula.story.vue +102 -0
  248. package/src/components/ChemicalFormula.vue +39 -0
  249. package/src/components/CollapsibleCard.story.vue +135 -0
  250. package/src/components/CollapsibleCard.vue +167 -0
  251. package/src/components/ColorSlider.story.vue +120 -0
  252. package/src/components/ColorSlider.vue +164 -0
  253. package/src/components/ConcentrationInput.story.vue +77 -0
  254. package/src/components/ConcentrationInput.vue +185 -0
  255. package/src/components/ConfirmDialog.story.vue +248 -0
  256. package/src/components/ConfirmDialog.vue +93 -0
  257. package/src/components/DataFrame.story.vue +148 -0
  258. package/src/components/DataFrame.vue +419 -0
  259. package/src/components/DatePicker.story.vue +119 -0
  260. package/src/components/DatePicker.vue +330 -0
  261. package/src/components/DateTimePicker.story.vue +112 -0
  262. package/src/components/DateTimePicker.vue +392 -0
  263. package/src/components/Divider.story.vue +80 -0
  264. package/src/components/Divider.vue +49 -0
  265. package/src/components/DoseCalculator.story.vue +68 -0
  266. package/src/components/DoseCalculator.vue +476 -0
  267. package/src/components/DropdownButton.story.vue +102 -0
  268. package/src/components/DropdownButton.vue +181 -0
  269. package/src/components/EmptyState.story.vue +135 -0
  270. package/src/components/EmptyState.vue +69 -0
  271. package/src/components/ExperimentCodeBadge.story.vue +77 -0
  272. package/src/components/ExperimentCodeBadge.vue +64 -0
  273. package/src/components/ExperimentDataViewer.story.vue +174 -0
  274. package/src/components/ExperimentDataViewer.vue +288 -0
  275. package/src/components/ExperimentPopover.story.vue +384 -0
  276. package/src/components/ExperimentPopover.vue +241 -0
  277. package/src/components/ExperimentSelectorModal.story.vue +391 -0
  278. package/src/components/ExperimentSelectorModal.vue +387 -0
  279. package/src/components/ExperimentTimeline.story.vue +161 -0
  280. package/src/components/ExperimentTimeline.vue +382 -0
  281. package/src/components/FileUploader.story.vue +107 -0
  282. package/src/components/FileUploader.vue +386 -0
  283. package/src/components/FitPanel.story.vue +125 -0
  284. package/src/components/FitPanel.vue +120 -0
  285. package/src/components/FormActions.vue +92 -0
  286. package/src/components/FormBuilder.vue +214 -0
  287. package/src/components/FormField.story.vue +132 -0
  288. package/src/components/FormField.vue +59 -0
  289. package/src/components/FormFieldRenderer.vue +58 -0
  290. package/src/components/FormSection.vue +90 -0
  291. package/src/components/FormulaInput.story.vue +96 -0
  292. package/src/components/FormulaInput.vue +125 -0
  293. package/src/components/GroupAssigner.story.vue +83 -0
  294. package/src/components/GroupAssigner.vue +284 -0
  295. package/src/components/GroupingModal.story.vue +52 -0
  296. package/src/components/GroupingModal.vue +422 -0
  297. package/src/components/IconButton.story.vue +135 -0
  298. package/src/components/IconButton.vue +73 -0
  299. package/src/components/LoadingSpinner.story.vue +70 -0
  300. package/src/components/LoadingSpinner.vue +50 -0
  301. package/src/components/MoleculeInput.story.vue +66 -0
  302. package/src/components/MoleculeInput.vue +426 -0
  303. package/src/components/MultiSelect.story.vue +132 -0
  304. package/src/components/MultiSelect.vue +118 -0
  305. package/src/components/NumberInput.story.vue +122 -0
  306. package/src/components/NumberInput.vue +160 -0
  307. package/src/components/PlateMapEditor.story.vue +92 -0
  308. package/src/components/PlateMapEditor.vue +513 -0
  309. package/src/components/ProgressBar.story.vue +148 -0
  310. package/src/components/ProgressBar.vue +114 -0
  311. package/src/components/ProtocolStepEditor.story.vue +69 -0
  312. package/src/components/ProtocolStepEditor.vue +522 -0
  313. package/src/components/RackEditor.story.vue +100 -0
  314. package/src/components/RackEditor.vue +371 -0
  315. package/src/components/ReagentEditor.story.vue +153 -0
  316. package/src/components/ReagentEditor.vue +418 -0
  317. package/src/components/ReagentList.story.vue +137 -0
  318. package/src/components/ReagentList.vue +463 -0
  319. package/src/components/ResourceCard.story.vue +150 -0
  320. package/src/components/ResourceCard.vue +161 -0
  321. package/src/components/SampleHierarchyTree.story.vue +161 -0
  322. package/src/components/SampleHierarchyTree.vue +256 -0
  323. package/src/components/SampleLegend.story.vue +91 -0
  324. package/src/components/SampleLegend.vue +119 -0
  325. package/src/components/SampleSelector.story.vue +111 -0
  326. package/src/components/SampleSelector.vue +1033 -0
  327. package/src/components/ScheduleCalendar.story.vue +195 -0
  328. package/src/components/ScheduleCalendar.vue +569 -0
  329. package/src/components/ScientificNumber.story.vue +127 -0
  330. package/src/components/ScientificNumber.vue +197 -0
  331. package/src/components/SegmentedControl.story.vue +132 -0
  332. package/src/components/SegmentedControl.vue +79 -0
  333. package/src/components/SequenceInput.story.vue +119 -0
  334. package/src/components/SequenceInput.vue +209 -0
  335. package/src/components/SettingsButton.story.vue +58 -0
  336. package/src/components/SettingsButton.vue +76 -0
  337. package/src/components/SettingsModal.story.vue +145 -0
  338. package/src/components/SettingsModal.vue +146 -0
  339. package/src/components/Skeleton.story.vue +141 -0
  340. package/src/components/Skeleton.vue +74 -0
  341. package/src/components/StatusIndicator.story.vue +99 -0
  342. package/src/components/StatusIndicator.vue +40 -0
  343. package/src/components/StepWizard.story.vue +155 -0
  344. package/src/components/StepWizard.vue +223 -0
  345. package/src/components/TagsInput.story.vue +155 -0
  346. package/src/components/TagsInput.vue +265 -0
  347. package/src/components/ThemeToggle.story.vue +36 -0
  348. package/src/components/ThemeToggle.vue +54 -0
  349. package/src/components/TimePicker.story.vue +96 -0
  350. package/src/components/TimePicker.vue +273 -0
  351. package/src/components/TimeRangeInput.story.vue +104 -0
  352. package/src/components/TimeRangeInput.vue +122 -0
  353. package/src/components/ToastNotification.story.vue +157 -0
  354. package/src/components/ToastNotification.vue +62 -0
  355. package/src/components/Tooltip.story.vue +138 -0
  356. package/src/components/Tooltip.vue +119 -0
  357. package/src/components/UnitInput.story.vue +194 -0
  358. package/src/components/UnitInput.vue +213 -0
  359. package/src/components/WellEditPopup.vue +234 -0
  360. package/src/components/WellPlate.story.vue +282 -0
  361. package/src/components/WellPlate.vue +830 -0
  362. package/src/components/index.ts +118 -0
  363. package/src/composables/experiment-utils.ts +57 -0
  364. package/src/composables/formBuilderRegistry.ts +79 -0
  365. package/src/composables/index.ts +140 -0
  366. package/src/composables/useApi.ts +167 -0
  367. package/src/composables/useAppExperiment.ts +159 -0
  368. package/src/composables/useAsync.ts +323 -0
  369. package/src/composables/useAuth.ts +445 -0
  370. package/src/composables/useAutoGroup.ts +641 -0
  371. package/src/composables/useChemicalFormula.ts +275 -0
  372. package/src/composables/useConcentrationUnits.ts +246 -0
  373. package/src/composables/useDoseCalculator.ts +370 -0
  374. package/src/composables/useExperimentData.ts +86 -0
  375. package/src/composables/useExperimentSave.ts +192 -0
  376. package/src/composables/useExperimentSelector.ts +292 -0
  377. package/src/composables/useForm.ts +416 -0
  378. package/src/composables/useFormBuilder.ts +383 -0
  379. package/src/composables/usePasskey.ts +216 -0
  380. package/src/composables/usePlatformContext.ts +299 -0
  381. package/src/composables/usePluginApi.ts +39 -0
  382. package/src/composables/usePluginConfig.ts +93 -0
  383. package/src/composables/useProtocolTemplates.ts +518 -0
  384. package/src/composables/useRackEditor.ts +222 -0
  385. package/src/composables/useReagentSeries.ts +91 -0
  386. package/src/composables/useScheduleDrag.ts +245 -0
  387. package/src/composables/useSequenceUtils.ts +105 -0
  388. package/src/composables/useTheme.ts +58 -0
  389. package/src/composables/useTimeUtils.ts +131 -0
  390. package/src/composables/useToast.ts +40 -0
  391. package/src/composables/useWellPlateEditor.ts +421 -0
  392. package/src/histoire.setup.ts +17 -0
  393. package/src/index.ts +367 -0
  394. package/src/install.ts +32 -0
  395. package/src/stores/auth.ts +152 -0
  396. package/src/stores/index.ts +2 -0
  397. package/src/stores/settings.ts +218 -0
  398. package/src/styles/components/alert-box.css +150 -0
  399. package/src/styles/components/app-avatar-menu.css +155 -0
  400. package/src/styles/components/app-container.css +33 -0
  401. package/src/styles/components/app-layout.css +98 -0
  402. package/src/styles/components/app-page-selector.css +191 -0
  403. package/src/styles/components/app-pill-nav.css +57 -0
  404. package/src/styles/components/app-plugin-switcher.css +209 -0
  405. package/src/styles/components/app-sidebar.css +145 -0
  406. package/src/styles/components/app-top-bar.css +492 -0
  407. package/src/styles/components/audit-trail.css +143 -0
  408. package/src/styles/components/auto-group-modal.css +644 -0
  409. package/src/styles/components/avatar.css +73 -0
  410. package/src/styles/components/batch-progress-list.css +196 -0
  411. package/src/styles/components/breadcrumb.css +64 -0
  412. package/src/styles/components/button.css +188 -0
  413. package/src/styles/components/calendar.css +192 -0
  414. package/src/styles/components/chart-container.css +69 -0
  415. package/src/styles/components/checkbox.css +123 -0
  416. package/src/styles/components/chemical-formula.css +46 -0
  417. package/src/styles/components/collapsible-card.css +253 -0
  418. package/src/styles/components/color-slider.css +110 -0
  419. package/src/styles/components/concentration-input.css +156 -0
  420. package/src/styles/components/confirm-dialog.css +183 -0
  421. package/src/styles/components/dataframe.css +382 -0
  422. package/src/styles/components/date-picker.css +243 -0
  423. package/src/styles/components/datetime-picker.css +229 -0
  424. package/src/styles/components/divider.css +63 -0
  425. package/src/styles/components/dose-calculator.css +301 -0
  426. package/src/styles/components/dropdown-button.css +280 -0
  427. package/src/styles/components/empty-state.css +151 -0
  428. package/src/styles/components/experiment-code-badge.css +33 -0
  429. package/src/styles/components/experiment-data-viewer.css +138 -0
  430. package/src/styles/components/experiment-popover.css +562 -0
  431. package/src/styles/components/experiment-selector-modal.css +285 -0
  432. package/src/styles/components/experiment-timeline.css +529 -0
  433. package/src/styles/components/file-uploader.css +310 -0
  434. package/src/styles/components/fit-panel.css +67 -0
  435. package/src/styles/components/form-builder.css +69 -0
  436. package/src/styles/components/form-field.css +48 -0
  437. package/src/styles/components/formula-input.css +103 -0
  438. package/src/styles/components/group-assigner.css +200 -0
  439. package/src/styles/components/grouping-modal.css +323 -0
  440. package/src/styles/components/icon-button.css +192 -0
  441. package/src/styles/components/input.css +66 -0
  442. package/src/styles/components/loading-spinner.css +67 -0
  443. package/src/styles/components/modal.css +350 -0
  444. package/src/styles/components/molecule-input.css +186 -0
  445. package/src/styles/components/multi-select.css +131 -0
  446. package/src/styles/components/number-input.css +199 -0
  447. package/src/styles/components/pill.css +188 -0
  448. package/src/styles/components/plate-map-editor.css +464 -0
  449. package/src/styles/components/progress-bar.css +133 -0
  450. package/src/styles/components/protocol-step-editor.css +449 -0
  451. package/src/styles/components/rack-editor.css +265 -0
  452. package/src/styles/components/radio-group.css +240 -0
  453. package/src/styles/components/reagent-editor.css +510 -0
  454. package/src/styles/components/reagent-list.css +407 -0
  455. package/src/styles/components/resource-card.css +360 -0
  456. package/src/styles/components/sample-hierarchy-tree.css +314 -0
  457. package/src/styles/components/sample-legend.css +201 -0
  458. package/src/styles/components/sample-selector.css +751 -0
  459. package/src/styles/components/schedule-calendar.css +478 -0
  460. package/src/styles/components/scientific-number.css +63 -0
  461. package/src/styles/components/segmented-control.css +197 -0
  462. package/src/styles/components/select.css +77 -0
  463. package/src/styles/components/sequence-input.css +184 -0
  464. package/src/styles/components/settings-button.css +94 -0
  465. package/src/styles/components/settings-modal.css +95 -0
  466. package/src/styles/components/skeleton.css +49 -0
  467. package/src/styles/components/slider.css +74 -0
  468. package/src/styles/components/status-indicator.css +66 -0
  469. package/src/styles/components/step-wizard.css +192 -0
  470. package/src/styles/components/tabs.css +95 -0
  471. package/src/styles/components/tags-input.css +195 -0
  472. package/src/styles/components/textarea.css +82 -0
  473. package/src/styles/components/theme-toggle.css +69 -0
  474. package/src/styles/components/time-picker.css +171 -0
  475. package/src/styles/components/time-range-input.css +42 -0
  476. package/src/styles/components/toast.css +91 -0
  477. package/src/styles/components/toggle.css +146 -0
  478. package/src/styles/components/tooltip.css +91 -0
  479. package/src/styles/components/unit-input.css +123 -0
  480. package/src/styles/components/well-edit-popup.css +252 -0
  481. package/src/styles/components/well-plate.css +307 -0
  482. package/src/styles/index.css +87 -0
  483. package/src/styles/variables.css +1117 -0
  484. package/src/tailwind.preset.ts +61 -0
  485. package/src/types/auth.ts +55 -0
  486. package/src/types/auto-group.ts +40 -0
  487. package/src/types/components.ts +710 -0
  488. package/src/types/form-builder.ts +197 -0
  489. package/src/types/index.ts +207 -0
  490. package/src/types/platform.ts +116 -0
  491. package/src/utils/color.ts +96 -0
@@ -0,0 +1,70 @@
1
+ <script setup lang="ts">
2
+ import LoadingSpinner from './LoadingSpinner.vue'
3
+ import type { SpinnerSize, SpinnerVariant } from '../types'
4
+
5
+ const sizes: SpinnerSize[] = ['xs', 'sm', 'md', 'lg']
6
+ const variants: SpinnerVariant[] = ['primary', 'cta', 'muted']
7
+ </script>
8
+
9
+ <template>
10
+ <Story title="Feedback/LoadingSpinner">
11
+ <Variant title="Playground">
12
+ <template #default="{ state }">
13
+ <div style="padding: 2rem; display: flex; align-items: center; justify-content: center;">
14
+ <LoadingSpinner
15
+ :size="state.size"
16
+ :variant="state.variant"
17
+ :label="state.label"
18
+ />
19
+ </div>
20
+ </template>
21
+
22
+ <template #controls="{ state }">
23
+ <HstSelect
24
+ v-model="state.size"
25
+ title="Size"
26
+ :options="sizes.map(s => ({ label: s, value: s }))"
27
+ />
28
+ <HstSelect
29
+ v-model="state.variant"
30
+ title="Variant"
31
+ :options="variants.map(v => ({ label: v, value: v }))"
32
+ />
33
+ <HstText v-model="state.label" title="Label" />
34
+ </template>
35
+ </Variant>
36
+
37
+ <Variant title="All Sizes">
38
+ <div style="padding: 2rem; display: flex; flex-wrap: wrap; gap: 2rem; align-items: flex-end; justify-content: center;">
39
+ <div v-for="size in sizes" :key="size" style="display: flex; flex-direction: column; align-items: center; gap: 0.5rem;">
40
+ <LoadingSpinner :size="size" />
41
+ <span style="font-size: 0.75rem; color: var(--text-muted, #94a3b8); text-transform: uppercase; letter-spacing: 0.05em;">{{ size }}</span>
42
+ </div>
43
+ </div>
44
+ </Variant>
45
+
46
+ <Variant title="All Variants">
47
+ <div style="padding: 2rem; display: flex; flex-wrap: wrap; gap: 2rem; align-items: center; justify-content: center;">
48
+ <div v-for="variant in variants" :key="variant" style="display: flex; flex-direction: column; align-items: center; gap: 0.5rem;">
49
+ <LoadingSpinner :variant="variant" size="md" />
50
+ <span style="font-size: 0.75rem; color: var(--text-muted, #94a3b8); text-transform: uppercase; letter-spacing: 0.05em;">{{ variant }}</span>
51
+ </div>
52
+ </div>
53
+ </Variant>
54
+
55
+ <Variant title="Custom Labels">
56
+ <div style="padding: 2rem; display: flex; flex-direction: column; gap: 2rem; align-items: center;">
57
+ <LoadingSpinner size="md" label="Loading experiments..." />
58
+ <LoadingSpinner size="sm" label="Processing plates..." variant="cta" />
59
+ <LoadingSpinner size="lg" label="Analyzing data..." />
60
+ </div>
61
+ </Variant>
62
+
63
+ <Variant title="Inline Usage">
64
+ <div style="padding: 2rem; display: flex; align-items: center; gap: 0.5rem; justify-content: center;">
65
+ <LoadingSpinner size="xs" label="" />
66
+ <span style="color: var(--text-muted, #94a3b8); font-size: 0.875rem;">Fetching results...</span>
67
+ </div>
68
+ </Variant>
69
+ </Story>
70
+ </template>
@@ -0,0 +1,50 @@
1
+ <script setup lang="ts">
2
+ /** Animated SVG spinner with size and color variants, and a visually hidden status label for screen readers. */
3
+ interface Props {
4
+ size?: 'xs' | 'sm' | 'md' | 'lg'
5
+ variant?: 'primary' | 'cta' | 'muted'
6
+ label?: string
7
+ }
8
+
9
+ withDefaults(defineProps<Props>(), {
10
+ size: 'md',
11
+ variant: 'primary',
12
+ label: 'Loading',
13
+ })
14
+ </script>
15
+
16
+ <template>
17
+ <div
18
+ :class="[
19
+ 'mld-spinner',
20
+ `mld-spinner--${size}`,
21
+ `mld-spinner--${variant}`,
22
+ ]"
23
+ role="status"
24
+ >
25
+ <svg
26
+ class="mld-spinner__svg"
27
+ fill="none"
28
+ viewBox="0 0 24 24"
29
+ >
30
+ <circle
31
+ style="opacity: 0.25"
32
+ cx="12"
33
+ cy="12"
34
+ r="10"
35
+ stroke="currentColor"
36
+ stroke-width="4"
37
+ />
38
+ <path
39
+ style="opacity: 0.75"
40
+ fill="currentColor"
41
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
42
+ />
43
+ </svg>
44
+ <span class="mld-spinner__label">{{ label }}</span>
45
+ </div>
46
+ </template>
47
+
48
+ <style>
49
+ @import '../styles/components/loading-spinner.css';
50
+ </style>
@@ -0,0 +1,66 @@
1
+ <script setup lang="ts">
2
+ import MoleculeInput from './MoleculeInput.vue'
3
+ import type { MoleculeData } from '../types'
4
+
5
+ function initState() {
6
+ return {
7
+ modelValue: undefined as MoleculeData | undefined,
8
+ disabled: false,
9
+ readonly: false,
10
+ height: 300,
11
+ showSmiles: true,
12
+ placeholder: 'Draw a chemical structure',
13
+ error: false,
14
+ }
15
+ }
16
+ </script>
17
+
18
+ <template>
19
+ <Story title="Lab/MoleculeInput">
20
+ <Variant title="Playground" :init-state="initState">
21
+ <template #default="{ state }">
22
+ <div style="padding: 2rem; max-width: 600px;">
23
+ <MoleculeInput
24
+ v-model="state.modelValue"
25
+ :disabled="state.disabled"
26
+ :readonly="state.readonly"
27
+ :height="state.height"
28
+ :show-smiles="state.showSmiles"
29
+ :placeholder="state.placeholder"
30
+ :error="state.error"
31
+ />
32
+ </div>
33
+ </template>
34
+ <template #controls="{ state }">
35
+ <HstCheckbox v-model="state.disabled" title="Disabled" />
36
+ <HstCheckbox v-model="state.readonly" title="Readonly" />
37
+ <HstSlider v-model="state.height" title="Height (px)" :min="200" :max="500" />
38
+ <HstCheckbox v-model="state.showSmiles" title="Show SMILES" />
39
+ <HstText v-model="state.placeholder" title="Placeholder" />
40
+ <HstCheckbox v-model="state.error" title="Error" />
41
+ </template>
42
+ </Variant>
43
+
44
+ <Variant title="Readonly (No Structure)">
45
+ <div style="padding: 2rem; max-width: 600px;">
46
+ <MoleculeInput readonly :height="250" />
47
+ </div>
48
+ </Variant>
49
+
50
+ <Variant title="Readonly (With Structure)">
51
+ <div style="padding: 2rem; max-width: 600px;">
52
+ <MoleculeInput
53
+ readonly
54
+ :height="250"
55
+ :model-value="{ smiles: 'CCO', molfile: '' }"
56
+ />
57
+ </div>
58
+ </Variant>
59
+
60
+ <Variant title="Error State">
61
+ <div style="padding: 2rem; max-width: 600px;">
62
+ <MoleculeInput error :height="250" />
63
+ </div>
64
+ </Variant>
65
+ </Story>
66
+ </template>
@@ -0,0 +1,426 @@
1
+ <script setup lang="ts">
2
+ /** Interactive chemical structure editor powered by JSME, emitting SMILES strings for compound registration in experiment design. */
3
+ import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
4
+ import type { MoleculeData } from '../types'
5
+
6
+ interface Props {
7
+ modelValue?: MoleculeData
8
+ disabled?: boolean
9
+ readonly?: boolean
10
+ height?: number
11
+ showSmiles?: boolean
12
+ placeholder?: string
13
+ error?: boolean
14
+ }
15
+
16
+ const props = withDefaults(defineProps<Props>(), {
17
+ disabled: false,
18
+ readonly: false,
19
+ height: 300,
20
+ showSmiles: true,
21
+ placeholder: 'Draw a chemical structure',
22
+ error: false,
23
+ })
24
+
25
+ const emit = defineEmits<{
26
+ 'update:modelValue': [data: MoleculeData | undefined]
27
+ 'error': [message: string]
28
+ }>()
29
+
30
+ // State
31
+ const containerRef = ref<HTMLDivElement | null>(null)
32
+ const jsmeInstance = ref<unknown>(null)
33
+ const isLoading = ref(true)
34
+ const loadError = ref<string | null>(null)
35
+ const debounceTimer = ref<ReturnType<typeof setTimeout> | null>(null)
36
+
37
+ // Computed
38
+ const hasStructure = computed(() => {
39
+ return props.modelValue?.smiles && props.modelValue.smiles.length > 0
40
+ })
41
+
42
+ // Use window-level state to persist across component instances and hot-reloads
43
+ interface JSMEGlobalState {
44
+ __jsmeCallbacks__?: Array<() => void>
45
+ __jsmeLoading__?: boolean
46
+ __jsmeLoaded__?: boolean
47
+ __jsmeLoadPromise__?: Promise<void>
48
+ JSApplet?: { JSME?: new (id: string, width: string, height: string, options?: object) => unknown }
49
+ jsmeOnLoad?: () => void
50
+ }
51
+
52
+ function getJSMEState(): JSMEGlobalState {
53
+ const win = window as unknown as JSMEGlobalState
54
+ if (!win.__jsmeCallbacks__) {
55
+ win.__jsmeCallbacks__ = []
56
+ }
57
+ return win
58
+ }
59
+
60
+ function waitForJSME(): Promise<void> {
61
+ const win = getJSMEState()
62
+
63
+ // Already loaded
64
+ if (win.JSApplet?.JSME) {
65
+ return Promise.resolve()
66
+ }
67
+
68
+ // Reuse existing promise if loading
69
+ if (win.__jsmeLoadPromise__) {
70
+ return win.__jsmeLoadPromise__
71
+ }
72
+
73
+ win.__jsmeLoadPromise__ = new Promise((resolve, reject) => {
74
+ // Double-check after promise creation
75
+ if (win.JSApplet?.JSME) {
76
+ resolve()
77
+ return
78
+ }
79
+
80
+ // Set up global callback FIRST (before checking for existing script)
81
+ const originalOnLoad = win.jsmeOnLoad
82
+ win.jsmeOnLoad = () => {
83
+ win.__jsmeLoaded__ = true
84
+ originalOnLoad?.()
85
+ win.__jsmeCallbacks__?.forEach(cb => cb())
86
+ win.__jsmeCallbacks__ = []
87
+ resolve()
88
+ }
89
+
90
+ // Add to callback queue
91
+ win.__jsmeCallbacks__?.push(resolve)
92
+
93
+ // Check if script already exists
94
+ const existingScript = document.querySelector('script[data-jsme]')
95
+ if (existingScript) {
96
+ // Script exists, poll for JSApplet.JSME (in case jsmeOnLoad already fired)
97
+ const checkReady = setInterval(() => {
98
+ if (win.JSApplet?.JSME) {
99
+ clearInterval(checkReady)
100
+ win.__jsmeLoaded__ = true
101
+ win.__jsmeCallbacks__?.forEach(cb => cb())
102
+ win.__jsmeCallbacks__ = []
103
+ resolve()
104
+ }
105
+ }, 100)
106
+
107
+ setTimeout(() => {
108
+ clearInterval(checkReady)
109
+ if (!win.__jsmeLoaded__ && !win.JSApplet?.JSME) {
110
+ reject(new Error('JSME initialization timeout'))
111
+ }
112
+ }, 15000)
113
+ return
114
+ }
115
+
116
+ // Load the script
117
+ win.__jsmeLoading__ = true
118
+ const script = document.createElement('script')
119
+ script.src = 'https://jsme-editor.github.io/dist/jsme/jsme.nocache.js'
120
+ script.integrity = 'sha384-l6tNzsc/eAJ7uql0dGAcHYI5ANVEV7DrJYjzXp3t13L+3OzLnfpzJO0Uio7mUSjY'
121
+ script.crossOrigin = 'anonymous'
122
+ script.async = true
123
+ script.setAttribute('data-jsme', 'true')
124
+
125
+ script.onerror = () => {
126
+ win.__jsmeLoading__ = false
127
+ win.__jsmeLoadPromise__ = undefined
128
+ reject(new Error('Failed to load JSME script'))
129
+ }
130
+
131
+ // Timeout
132
+ setTimeout(() => {
133
+ if (!win.__jsmeLoaded__ && !win.JSApplet?.JSME) {
134
+ reject(new Error('JSME initialization timeout'))
135
+ }
136
+ }, 15000)
137
+
138
+ document.head.appendChild(script)
139
+ })
140
+
141
+ return win.__jsmeLoadPromise__
142
+ }
143
+
144
+ // Wait for the browser to complete a paint cycle
145
+ function waitForPaint(): Promise<void> {
146
+ return new Promise(resolve => {
147
+ requestAnimationFrame(() => requestAnimationFrame(() => resolve()))
148
+ })
149
+ }
150
+
151
+ // JSME initialization — two phases:
152
+ // 1. Load the script (no DOM needed)
153
+ // 2. Reveal the editor div, wait for paint, then mount JSME with explicit px dimensions
154
+ async function initJSME() {
155
+ if (props.readonly) return
156
+
157
+ try {
158
+ isLoading.value = true
159
+ loadError.value = null
160
+
161
+ // Phase 1: load the JSME script (independent of DOM)
162
+ await waitForJSME()
163
+
164
+ const win = getJSMEState()
165
+ if (!win.JSApplet?.JSME) {
166
+ throw new Error('JSME library not available after loading')
167
+ }
168
+
169
+ // Phase 2: reveal the editor div by clearing the loading state
170
+ isLoading.value = false
171
+ await nextTick()
172
+
173
+ if (!containerRef.value) return
174
+
175
+ // Wait for a full paint cycle so GWT can measure the container
176
+ await waitForPaint()
177
+
178
+ if (!containerRef.value) return
179
+
180
+ // Use explicit pixel width — GWT cannot resolve percentage widths reliably
181
+ const rect = containerRef.value.getBoundingClientRect()
182
+ const width = Math.floor(rect.width) || 400
183
+
184
+ // Mount JSME into the now-painted container
185
+ const editorId = `jsme-${Date.now()}`
186
+ containerRef.value.id = editorId
187
+
188
+ const instance = new win.JSApplet.JSME(
189
+ editorId,
190
+ `${width}px`,
191
+ `${props.height}px`,
192
+ {
193
+ options: 'query,hydrogens,paste',
194
+ }
195
+ )
196
+
197
+ jsmeInstance.value = instance
198
+
199
+ // Set initial value if provided
200
+ if (props.modelValue?.molfile) {
201
+ (instance as { readMolFile: (mol: string) => void }).readMolFile(props.modelValue.molfile)
202
+ }
203
+
204
+ // Set up change callback
205
+ (instance as { setCallBack: (event: string, callback: () => void) => void }).setCallBack('AfterStructureModified', handleStructureChange)
206
+ } catch (err) {
207
+ const message = err instanceof Error ? err.message : 'Failed to load molecule editor'
208
+ loadError.value = message
209
+ emit('error', message)
210
+ isLoading.value = false
211
+ }
212
+ }
213
+
214
+ function handleStructureChange() {
215
+ // Debounce the change event
216
+ if (debounceTimer.value) {
217
+ clearTimeout(debounceTimer.value)
218
+ }
219
+
220
+ debounceTimer.value = setTimeout(() => {
221
+ if (!jsmeInstance.value) return
222
+
223
+ const instance = jsmeInstance.value as {
224
+ smiles: () => string
225
+ molFile: () => string
226
+ }
227
+
228
+ const smiles = instance.smiles()
229
+ const molfile = instance.molFile()
230
+
231
+ if (!smiles || smiles.length === 0) {
232
+ emit('update:modelValue', undefined)
233
+ } else {
234
+ emit('update:modelValue', { smiles, molfile })
235
+ }
236
+ }, 300)
237
+ }
238
+
239
+ function clearStructure() {
240
+ if (jsmeInstance.value) {
241
+ (jsmeInstance.value as { reset: () => void }).reset()
242
+ }
243
+ emit('update:modelValue', undefined)
244
+ }
245
+
246
+ // Watch for external value changes
247
+ watch(() => props.modelValue, (newValue) => {
248
+ if (!jsmeInstance.value) return
249
+
250
+ const instance = jsmeInstance.value as {
251
+ smiles: () => string
252
+ readMolFile: (mol: string) => void
253
+ reset: () => void
254
+ }
255
+
256
+ // Only update if the external value differs from current
257
+ const currentSmiles = instance.smiles()
258
+ if (newValue?.smiles !== currentSmiles) {
259
+ if (newValue?.molfile) {
260
+ instance.readMolFile(newValue.molfile)
261
+ } else {
262
+ instance.reset()
263
+ }
264
+ }
265
+ })
266
+
267
+ // Lifecycle
268
+ onMounted(() => {
269
+ if (!props.readonly) {
270
+ initJSME()
271
+ } else {
272
+ isLoading.value = false
273
+ }
274
+ })
275
+
276
+ onUnmounted(() => {
277
+ if (debounceTimer.value) {
278
+ clearTimeout(debounceTimer.value)
279
+ }
280
+ jsmeInstance.value = null
281
+ })
282
+ </script>
283
+
284
+ <template>
285
+ <div
286
+ :class="[
287
+ 'mld-molecule-input',
288
+ disabled ? 'mld-molecule-input--disabled' : '',
289
+ readonly ? 'mld-molecule-input--readonly' : '',
290
+ error ? 'mld-molecule-input--error' : '',
291
+ ]"
292
+ >
293
+ <!-- Loading state -->
294
+ <div
295
+ v-if="isLoading && !readonly"
296
+ class="mld-molecule-input__skeleton"
297
+ :style="{ height: `${height}px` }"
298
+ >
299
+ <svg
300
+ class="mld-molecule-input__skeleton-icon"
301
+ fill="none"
302
+ stroke="currentColor"
303
+ viewBox="0 0 24 24"
304
+ aria-hidden="true"
305
+ >
306
+ <path
307
+ stroke-linecap="round"
308
+ stroke-linejoin="round"
309
+ stroke-width="2"
310
+ d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"
311
+ />
312
+ </svg>
313
+ <span class="mld-molecule-input__skeleton-text">Loading molecule editor...</span>
314
+ </div>
315
+
316
+ <!-- Error state -->
317
+ <div
318
+ v-else-if="loadError"
319
+ class="mld-molecule-input__error"
320
+ >
321
+ <svg
322
+ class="mld-molecule-input__error-icon"
323
+ fill="none"
324
+ stroke="currentColor"
325
+ viewBox="0 0 24 24"
326
+ aria-hidden="true"
327
+ >
328
+ <path
329
+ stroke-linecap="round"
330
+ stroke-linejoin="round"
331
+ stroke-width="2"
332
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
333
+ />
334
+ </svg>
335
+ {{ loadError }}
336
+ </div>
337
+
338
+ <!-- Readonly mode - show empty or placeholder -->
339
+ <template v-else-if="readonly">
340
+ <div
341
+ v-if="hasStructure"
342
+ class="mld-molecule-input__readonly"
343
+ :style="{ height: `${height}px` }"
344
+ >
345
+ <!-- In readonly mode, we just display a placeholder since we don't have SVG rendering -->
346
+ <div class="mld-molecule-input__empty">
347
+ <svg
348
+ class="mld-molecule-input__empty-icon"
349
+ fill="none"
350
+ stroke="currentColor"
351
+ viewBox="0 0 24 24"
352
+ aria-hidden="true"
353
+ >
354
+ <path
355
+ stroke-linecap="round"
356
+ stroke-linejoin="round"
357
+ stroke-width="2"
358
+ d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"
359
+ />
360
+ </svg>
361
+ <span class="mld-molecule-input__empty-text">Structure defined (readonly)</span>
362
+ </div>
363
+ </div>
364
+ <div
365
+ v-else
366
+ class="mld-molecule-input__empty"
367
+ :style="{ height: `${height}px` }"
368
+ >
369
+ <svg
370
+ class="mld-molecule-input__empty-icon"
371
+ fill="none"
372
+ stroke="currentColor"
373
+ viewBox="0 0 24 24"
374
+ aria-hidden="true"
375
+ >
376
+ <path
377
+ stroke-linecap="round"
378
+ stroke-linejoin="round"
379
+ stroke-width="2"
380
+ d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"
381
+ />
382
+ </svg>
383
+ <span class="mld-molecule-input__empty-text">No structure</span>
384
+ </div>
385
+ </template>
386
+
387
+ <!-- Editor mode -->
388
+ <template v-else>
389
+ <div
390
+ ref="containerRef"
391
+ class="mld-molecule-input__editor"
392
+ :style="{ height: `${height}px` }"
393
+ role="application"
394
+ aria-label="Molecule structure editor"
395
+ />
396
+
397
+ <!-- Actions toolbar -->
398
+ <div class="mld-molecule-input__actions">
399
+ <button
400
+ type="button"
401
+ class="mld-molecule-input__action-btn"
402
+ :disabled="!hasStructure || disabled"
403
+ aria-label="Clear structure"
404
+ @click="clearStructure"
405
+ >
406
+ <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
407
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
408
+ </svg>
409
+ </button>
410
+ </div>
411
+ </template>
412
+
413
+ <!-- SMILES display -->
414
+ <div
415
+ v-if="showSmiles && hasStructure && !loadError"
416
+ class="mld-molecule-input__smiles"
417
+ >
418
+ <span class="mld-molecule-input__smiles-label">SMILES:</span>
419
+ {{ modelValue?.smiles }}
420
+ </div>
421
+ </div>
422
+ </template>
423
+
424
+ <style>
425
+ @import '../styles/components/molecule-input.css';
426
+ </style>