@morscherlab/mld-sdk 0.6.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 (714) hide show
  1. package/README.md +326 -0
  2. package/dist/__tests__/components/AppLayout.test.d.ts +1 -0
  3. package/dist/__tests__/components/AppSidebar.test.d.ts +1 -0
  4. package/dist/__tests__/components/AppTopBar.test.d.ts +1 -0
  5. package/dist/__tests__/components/BaseInput.test.d.ts +1 -0
  6. package/dist/__tests__/components/BasePill.test.d.ts +1 -0
  7. package/dist/__tests__/components/Calendar.test.d.ts +1 -0
  8. package/dist/__tests__/components/CollapsibleCard.test.d.ts +1 -0
  9. package/dist/__tests__/components/DataFrame.test.d.ts +1 -0
  10. package/dist/__tests__/components/DropdownButton.test.d.ts +1 -0
  11. package/dist/__tests__/composables/useAuth.test.d.ts +1 -0
  12. package/dist/__tests__/composables/useForm.test.d.ts +1 -0
  13. package/dist/components/AlertBox.vue.d.ts +30 -0
  14. package/dist/components/AlertBox.vue.js +125 -0
  15. package/dist/components/AlertBox.vue.js.map +1 -0
  16. package/dist/components/AlertBox.vue3.js +6 -0
  17. package/dist/components/AlertBox.vue3.js.map +1 -0
  18. package/dist/components/AppContainer.vue.d.ts +28 -0
  19. package/dist/components/AppContainer.vue.js +28 -0
  20. package/dist/components/AppContainer.vue.js.map +1 -0
  21. package/dist/components/AppContainer.vue2.js +5 -0
  22. package/dist/components/AppContainer.vue2.js.map +1 -0
  23. package/dist/components/AppLayout.vue.d.ts +31 -0
  24. package/dist/components/AppLayout.vue.js +51 -0
  25. package/dist/components/AppLayout.vue.js.map +1 -0
  26. package/dist/components/AppLayout.vue3.js +6 -0
  27. package/dist/components/AppLayout.vue3.js.map +1 -0
  28. package/dist/components/AppSidebar.vue.d.ts +44 -0
  29. package/dist/components/AppSidebar.vue.js +79 -0
  30. package/dist/components/AppSidebar.vue.js.map +1 -0
  31. package/dist/components/AppSidebar.vue3.js +6 -0
  32. package/dist/components/AppSidebar.vue3.js.map +1 -0
  33. package/dist/components/AppTopBar.vue.d.ts +77 -0
  34. package/dist/components/AppTopBar.vue.js +564 -0
  35. package/dist/components/AppTopBar.vue.js.map +1 -0
  36. package/dist/components/AppTopBar.vue3.js +6 -0
  37. package/dist/components/AppTopBar.vue3.js.map +1 -0
  38. package/dist/components/AuditTrail.vue.d.ts +47 -0
  39. package/dist/components/AuditTrail.vue.js +189 -0
  40. package/dist/components/AuditTrail.vue.js.map +1 -0
  41. package/dist/components/AuditTrail.vue3.js +6 -0
  42. package/dist/components/AuditTrail.vue3.js.map +1 -0
  43. package/dist/components/Avatar.vue.d.ts +13 -0
  44. package/dist/components/Avatar.vue.js +64 -0
  45. package/dist/components/Avatar.vue.js.map +1 -0
  46. package/dist/components/Avatar.vue3.js +6 -0
  47. package/dist/components/Avatar.vue3.js.map +1 -0
  48. package/dist/components/BaseButton.vue.d.ts +37 -0
  49. package/dist/components/BaseButton.vue.js +64 -0
  50. package/dist/components/BaseButton.vue.js.map +1 -0
  51. package/dist/components/BaseButton.vue3.js +6 -0
  52. package/dist/components/BaseButton.vue3.js.map +1 -0
  53. package/dist/components/BaseCheckbox.vue.d.ts +16 -0
  54. package/dist/components/BaseCheckbox.vue.js +71 -0
  55. package/dist/components/BaseCheckbox.vue.js.map +1 -0
  56. package/dist/components/BaseCheckbox.vue3.js +6 -0
  57. package/dist/components/BaseCheckbox.vue3.js.map +1 -0
  58. package/dist/components/BaseInput.vue.d.ts +33 -0
  59. package/dist/components/BaseInput.vue.js +57 -0
  60. package/dist/components/BaseInput.vue.js.map +1 -0
  61. package/dist/components/BaseInput.vue3.js +6 -0
  62. package/dist/components/BaseInput.vue3.js.map +1 -0
  63. package/dist/components/BaseModal.vue.d.ts +38 -0
  64. package/dist/components/BaseModal.vue.js +118 -0
  65. package/dist/components/BaseModal.vue.js.map +1 -0
  66. package/dist/components/BaseModal.vue3.js +6 -0
  67. package/dist/components/BaseModal.vue3.js.map +1 -0
  68. package/dist/components/BasePill.vue.d.ts +54 -0
  69. package/dist/components/BasePill.vue.js +68 -0
  70. package/dist/components/BasePill.vue.js.map +1 -0
  71. package/dist/components/BasePill.vue3.js +6 -0
  72. package/dist/components/BasePill.vue3.js.map +1 -0
  73. package/dist/components/BaseRadioGroup.vue.d.ts +19 -0
  74. package/dist/components/BaseRadioGroup.vue.js +80 -0
  75. package/dist/components/BaseRadioGroup.vue.js.map +1 -0
  76. package/dist/components/BaseRadioGroup.vue3.js +6 -0
  77. package/dist/components/BaseRadioGroup.vue3.js.map +1 -0
  78. package/dist/components/BaseSelect.vue.d.ts +19 -0
  79. package/dist/components/BaseSelect.vue.js +70 -0
  80. package/dist/components/BaseSelect.vue.js.map +1 -0
  81. package/dist/components/BaseSelect.vue3.js +6 -0
  82. package/dist/components/BaseSelect.vue3.js.map +1 -0
  83. package/dist/components/BaseSlider.vue.d.ts +22 -0
  84. package/dist/components/BaseSlider.vue.js +97 -0
  85. package/dist/components/BaseSlider.vue.js.map +1 -0
  86. package/dist/components/BaseSlider.vue3.js +6 -0
  87. package/dist/components/BaseSlider.vue3.js.map +1 -0
  88. package/dist/components/BaseTabs.vue.d.ts +14 -0
  89. package/dist/components/BaseTabs.vue.js +63 -0
  90. package/dist/components/BaseTabs.vue.js.map +1 -0
  91. package/dist/components/BaseTabs.vue3.js +6 -0
  92. package/dist/components/BaseTabs.vue3.js.map +1 -0
  93. package/dist/components/BaseTextarea.vue.d.ts +28 -0
  94. package/dist/components/BaseTextarea.vue.js +49 -0
  95. package/dist/components/BaseTextarea.vue.js.map +1 -0
  96. package/dist/components/BaseTextarea.vue3.js +6 -0
  97. package/dist/components/BaseTextarea.vue3.js.map +1 -0
  98. package/dist/components/BaseToggle.vue.d.ts +16 -0
  99. package/dist/components/BaseToggle.vue.js +65 -0
  100. package/dist/components/BaseToggle.vue.js.map +1 -0
  101. package/dist/components/BaseToggle.vue3.js +6 -0
  102. package/dist/components/BaseToggle.vue3.js.map +1 -0
  103. package/dist/components/BatchProgressList.vue.d.ts +59 -0
  104. package/dist/components/BatchProgressList.vue.js +250 -0
  105. package/dist/components/BatchProgressList.vue.js.map +1 -0
  106. package/dist/components/BatchProgressList.vue3.js +6 -0
  107. package/dist/components/BatchProgressList.vue3.js.map +1 -0
  108. package/dist/components/Breadcrumb.vue.d.ts +37 -0
  109. package/dist/components/Breadcrumb.vue.js +71 -0
  110. package/dist/components/Breadcrumb.vue.js.map +1 -0
  111. package/dist/components/Breadcrumb.vue3.js +6 -0
  112. package/dist/components/Breadcrumb.vue3.js.map +1 -0
  113. package/dist/components/Calendar.vue.d.ts +107 -0
  114. package/dist/components/Calendar.vue.js +328 -0
  115. package/dist/components/Calendar.vue.js.map +1 -0
  116. package/dist/components/Calendar.vue3.js +6 -0
  117. package/dist/components/Calendar.vue3.js.map +1 -0
  118. package/dist/components/ChartContainer.vue.d.ts +31 -0
  119. package/dist/components/ChartContainer.vue.js +78 -0
  120. package/dist/components/ChartContainer.vue.js.map +1 -0
  121. package/dist/components/ChartContainer.vue3.js +6 -0
  122. package/dist/components/ChartContainer.vue3.js.map +1 -0
  123. package/dist/components/ChemicalFormula.vue.d.ts +8 -0
  124. package/dist/components/ChemicalFormula.vue.js +57 -0
  125. package/dist/components/ChemicalFormula.vue.js.map +1 -0
  126. package/dist/components/ChemicalFormula.vue3.js +6 -0
  127. package/dist/components/ChemicalFormula.vue3.js.map +1 -0
  128. package/dist/components/CollapsibleCard.vue.d.ts +39 -0
  129. package/dist/components/CollapsibleCard.vue.js +175 -0
  130. package/dist/components/CollapsibleCard.vue.js.map +1 -0
  131. package/dist/components/CollapsibleCard.vue3.js +6 -0
  132. package/dist/components/CollapsibleCard.vue3.js.map +1 -0
  133. package/dist/components/ColorSlider.vue.d.ts +34 -0
  134. package/dist/components/ColorSlider.vue.js +131 -0
  135. package/dist/components/ColorSlider.vue.js.map +1 -0
  136. package/dist/components/ColorSlider.vue3.js +6 -0
  137. package/dist/components/ColorSlider.vue3.js.map +1 -0
  138. package/dist/components/ConcentrationInput.vue.d.ts +25 -0
  139. package/dist/components/ConcentrationInput.vue.js +161 -0
  140. package/dist/components/ConcentrationInput.vue.js.map +1 -0
  141. package/dist/components/ConcentrationInput.vue3.js +6 -0
  142. package/dist/components/ConcentrationInput.vue3.js.map +1 -0
  143. package/dist/components/ConfirmDialog.vue.d.ts +41 -0
  144. package/dist/components/ConfirmDialog.vue.js +140 -0
  145. package/dist/components/ConfirmDialog.vue.js.map +1 -0
  146. package/dist/components/ConfirmDialog.vue3.js +6 -0
  147. package/dist/components/ConfirmDialog.vue3.js.map +1 -0
  148. package/dist/components/DataFrame.vue.d.ts +107 -0
  149. package/dist/components/DataFrame.vue.js +430 -0
  150. package/dist/components/DataFrame.vue.js.map +1 -0
  151. package/dist/components/DataFrame.vue3.js +6 -0
  152. package/dist/components/DataFrame.vue3.js.map +1 -0
  153. package/dist/components/DatePicker.vue.d.ts +23 -0
  154. package/dist/components/DatePicker.vue.js +278 -0
  155. package/dist/components/DatePicker.vue.js.map +1 -0
  156. package/dist/components/DatePicker.vue3.js +6 -0
  157. package/dist/components/DatePicker.vue3.js.map +1 -0
  158. package/dist/components/DateTimePicker.vue.d.ts +30 -0
  159. package/dist/components/DateTimePicker.vue.js +413 -0
  160. package/dist/components/DateTimePicker.vue.js.map +1 -0
  161. package/dist/components/DateTimePicker.vue3.js +6 -0
  162. package/dist/components/DateTimePicker.vue3.js.map +1 -0
  163. package/dist/components/Divider.vue.d.ts +11 -0
  164. package/dist/components/Divider.vue.js +42 -0
  165. package/dist/components/Divider.vue.js.map +1 -0
  166. package/dist/components/Divider.vue3.js +6 -0
  167. package/dist/components/Divider.vue3.js.map +1 -0
  168. package/dist/components/DoseCalculator.vue.d.ts +19 -0
  169. package/dist/components/DoseCalculator.vue.js +470 -0
  170. package/dist/components/DoseCalculator.vue.js.map +1 -0
  171. package/dist/components/DoseCalculator.vue3.js +6 -0
  172. package/dist/components/DoseCalculator.vue3.js.map +1 -0
  173. package/dist/components/DropdownButton.vue.d.ts +47 -0
  174. package/dist/components/DropdownButton.vue.js +171 -0
  175. package/dist/components/DropdownButton.vue.js.map +1 -0
  176. package/dist/components/DropdownButton.vue3.js +6 -0
  177. package/dist/components/DropdownButton.vue3.js.map +1 -0
  178. package/dist/components/EmptyState.vue.d.ts +33 -0
  179. package/dist/components/EmptyState.vue.js +80 -0
  180. package/dist/components/EmptyState.vue.js.map +1 -0
  181. package/dist/components/EmptyState.vue3.js +6 -0
  182. package/dist/components/EmptyState.vue3.js.map +1 -0
  183. package/dist/components/ExperimentTimeline.vue.d.ts +44 -0
  184. package/dist/components/ExperimentTimeline.vue.js +395 -0
  185. package/dist/components/ExperimentTimeline.vue.js.map +1 -0
  186. package/dist/components/ExperimentTimeline.vue3.js +6 -0
  187. package/dist/components/ExperimentTimeline.vue3.js.map +1 -0
  188. package/dist/components/FileUploader.vue.d.ts +23 -0
  189. package/dist/components/FileUploader.vue.js +416 -0
  190. package/dist/components/FileUploader.vue.js.map +1 -0
  191. package/dist/components/FileUploader.vue3.js +6 -0
  192. package/dist/components/FileUploader.vue3.js.map +1 -0
  193. package/dist/components/FormField.vue.d.ts +24 -0
  194. package/dist/components/FormField.vue.js +45 -0
  195. package/dist/components/FormField.vue.js.map +1 -0
  196. package/dist/components/FormField.vue3.js +6 -0
  197. package/dist/components/FormField.vue3.js.map +1 -0
  198. package/dist/components/FormulaInput.vue.d.ts +25 -0
  199. package/dist/components/FormulaInput.vue.js +121 -0
  200. package/dist/components/FormulaInput.vue.js.map +1 -0
  201. package/dist/components/FormulaInput.vue3.js +6 -0
  202. package/dist/components/FormulaInput.vue3.js.map +1 -0
  203. package/dist/components/GroupAssigner.vue.d.ts +25 -0
  204. package/dist/components/GroupAssigner.vue.js +311 -0
  205. package/dist/components/GroupAssigner.vue.js.map +1 -0
  206. package/dist/components/GroupAssigner.vue3.js +6 -0
  207. package/dist/components/GroupAssigner.vue3.js.map +1 -0
  208. package/dist/components/GroupingModal.vue.d.ts +12 -0
  209. package/dist/components/GroupingModal.vue.js +446 -0
  210. package/dist/components/GroupingModal.vue.js.map +1 -0
  211. package/dist/components/GroupingModal.vue3.js +6 -0
  212. package/dist/components/GroupingModal.vue3.js.map +1 -0
  213. package/dist/components/IconButton.vue.d.ts +34 -0
  214. package/dist/components/IconButton.vue.js +67 -0
  215. package/dist/components/IconButton.vue.js.map +1 -0
  216. package/dist/components/IconButton.vue3.js +6 -0
  217. package/dist/components/IconButton.vue3.js.map +1 -0
  218. package/dist/components/LoadingSpinner.vue.d.ts +11 -0
  219. package/dist/components/LoadingSpinner.vue.js +47 -0
  220. package/dist/components/LoadingSpinner.vue.js.map +1 -0
  221. package/dist/components/LoadingSpinner.vue3.js +6 -0
  222. package/dist/components/LoadingSpinner.vue3.js.map +1 -0
  223. package/dist/components/MoleculeInput.vue.d.ts +30 -0
  224. package/dist/components/MoleculeInput.vue.js +327 -0
  225. package/dist/components/MoleculeInput.vue.js.map +1 -0
  226. package/dist/components/MoleculeInput.vue3.js +6 -0
  227. package/dist/components/MoleculeInput.vue3.js.map +1 -0
  228. package/dist/components/MultiSelect.vue.d.ts +19 -0
  229. package/dist/components/MultiSelect.vue.js +109 -0
  230. package/dist/components/MultiSelect.vue.js.map +1 -0
  231. package/dist/components/MultiSelect.vue3.js +6 -0
  232. package/dist/components/MultiSelect.vue3.js.map +1 -0
  233. package/dist/components/NumberInput.vue.d.ts +21 -0
  234. package/dist/components/NumberInput.vue.js +133 -0
  235. package/dist/components/NumberInput.vue.js.map +1 -0
  236. package/dist/components/NumberInput.vue3.js +6 -0
  237. package/dist/components/NumberInput.vue3.js.map +1 -0
  238. package/dist/components/PlateMapEditor.vue.d.ts +50 -0
  239. package/dist/components/PlateMapEditor.vue.js +480 -0
  240. package/dist/components/PlateMapEditor.vue.js.map +1 -0
  241. package/dist/components/PlateMapEditor.vue3.js +6 -0
  242. package/dist/components/PlateMapEditor.vue3.js.map +1 -0
  243. package/dist/components/ProgressBar.vue.d.ts +17 -0
  244. package/dist/components/ProgressBar.vue.js +65 -0
  245. package/dist/components/ProgressBar.vue.js.map +1 -0
  246. package/dist/components/ProgressBar.vue3.js +6 -0
  247. package/dist/components/ProgressBar.vue3.js.map +1 -0
  248. package/dist/components/ProtocolStepEditor.vue.d.ts +24 -0
  249. package/dist/components/ProtocolStepEditor.vue.js +491 -0
  250. package/dist/components/ProtocolStepEditor.vue.js.map +1 -0
  251. package/dist/components/ProtocolStepEditor.vue3.js +6 -0
  252. package/dist/components/ProtocolStepEditor.vue3.js.map +1 -0
  253. package/dist/components/RackEditor.vue.d.ts +40 -0
  254. package/dist/components/RackEditor.vue.js +325 -0
  255. package/dist/components/RackEditor.vue.js.map +1 -0
  256. package/dist/components/RackEditor.vue3.js +6 -0
  257. package/dist/components/RackEditor.vue3.js.map +1 -0
  258. package/dist/components/ReagentList.vue.d.ts +46 -0
  259. package/dist/components/ReagentList.vue.js +526 -0
  260. package/dist/components/ReagentList.vue.js.map +1 -0
  261. package/dist/components/ReagentList.vue3.js +6 -0
  262. package/dist/components/ReagentList.vue3.js.map +1 -0
  263. package/dist/components/ResourceCard.vue.d.ts +50 -0
  264. package/dist/components/ResourceCard.vue.js +212 -0
  265. package/dist/components/ResourceCard.vue.js.map +1 -0
  266. package/dist/components/ResourceCard.vue3.js +6 -0
  267. package/dist/components/ResourceCard.vue3.js.map +1 -0
  268. package/dist/components/SampleHierarchyTree.vue.d.ts +37 -0
  269. package/dist/components/SampleHierarchyTree.vue.js +217 -0
  270. package/dist/components/SampleHierarchyTree.vue.js.map +1 -0
  271. package/dist/components/SampleHierarchyTree.vue3.js +6 -0
  272. package/dist/components/SampleHierarchyTree.vue3.js.map +1 -0
  273. package/dist/components/SampleLegend.vue.d.ts +32 -0
  274. package/dist/components/SampleLegend.vue.js +129 -0
  275. package/dist/components/SampleLegend.vue.js.map +1 -0
  276. package/dist/components/SampleLegend.vue3.js +6 -0
  277. package/dist/components/SampleLegend.vue3.js.map +1 -0
  278. package/dist/components/SampleSelector.vue.d.ts +29 -0
  279. package/dist/components/SampleSelector.vue.js +1076 -0
  280. package/dist/components/SampleSelector.vue.js.map +1 -0
  281. package/dist/components/SampleSelector.vue3.js +6 -0
  282. package/dist/components/SampleSelector.vue3.js.map +1 -0
  283. package/dist/components/ScheduleCalendar.vue.d.ts +110 -0
  284. package/dist/components/ScheduleCalendar.vue.js +535 -0
  285. package/dist/components/ScheduleCalendar.vue.js.map +1 -0
  286. package/dist/components/ScheduleCalendar.vue3.js +6 -0
  287. package/dist/components/ScheduleCalendar.vue3.js.map +1 -0
  288. package/dist/components/ScientificNumber.vue.d.ts +14 -0
  289. package/dist/components/ScientificNumber.vue.js +190 -0
  290. package/dist/components/ScientificNumber.vue.js.map +1 -0
  291. package/dist/components/ScientificNumber.vue3.js +6 -0
  292. package/dist/components/ScientificNumber.vue3.js.map +1 -0
  293. package/dist/components/SegmentedControl.vue.d.ts +20 -0
  294. package/dist/components/SegmentedControl.vue.js +71 -0
  295. package/dist/components/SegmentedControl.vue.js.map +1 -0
  296. package/dist/components/SegmentedControl.vue3.js +6 -0
  297. package/dist/components/SegmentedControl.vue3.js.map +1 -0
  298. package/dist/components/SequenceInput.vue.d.ts +54 -0
  299. package/dist/components/SequenceInput.vue.js +204 -0
  300. package/dist/components/SequenceInput.vue.js.map +1 -0
  301. package/dist/components/SequenceInput.vue3.js +6 -0
  302. package/dist/components/SequenceInput.vue3.js.map +1 -0
  303. package/dist/components/SettingsButton.vue.d.ts +30 -0
  304. package/dist/components/SettingsButton.vue.js +72 -0
  305. package/dist/components/SettingsButton.vue.js.map +1 -0
  306. package/dist/components/SettingsButton.vue3.js +6 -0
  307. package/dist/components/SettingsButton.vue3.js.map +1 -0
  308. package/dist/components/SettingsModal.vue.d.ts +40 -0
  309. package/dist/components/SettingsModal.vue.js +141 -0
  310. package/dist/components/SettingsModal.vue.js.map +1 -0
  311. package/dist/components/SettingsModal.vue3.js +6 -0
  312. package/dist/components/SettingsModal.vue3.js.map +1 -0
  313. package/dist/components/Skeleton.vue.d.ts +11 -0
  314. package/dist/components/Skeleton.vue.js +65 -0
  315. package/dist/components/Skeleton.vue.js.map +1 -0
  316. package/dist/components/Skeleton.vue3.js +6 -0
  317. package/dist/components/Skeleton.vue3.js.map +1 -0
  318. package/dist/components/StatusIndicator.vue.d.ts +13 -0
  319. package/dist/components/StatusIndicator.vue.js +38 -0
  320. package/dist/components/StatusIndicator.vue.js.map +1 -0
  321. package/dist/components/StatusIndicator.vue3.js +6 -0
  322. package/dist/components/StatusIndicator.vue3.js.map +1 -0
  323. package/dist/components/StepWizard.vue.d.ts +72 -0
  324. package/dist/components/StepWizard.vue.js +213 -0
  325. package/dist/components/StepWizard.vue.js.map +1 -0
  326. package/dist/components/StepWizard.vue3.js +6 -0
  327. package/dist/components/StepWizard.vue3.js.map +1 -0
  328. package/dist/components/TagsInput.vue.d.ts +23 -0
  329. package/dist/components/TagsInput.vue.js +124 -0
  330. package/dist/components/TagsInput.vue.js.map +1 -0
  331. package/dist/components/TagsInput.vue3.js +6 -0
  332. package/dist/components/TagsInput.vue3.js.map +1 -0
  333. package/dist/components/ThemeToggle.vue.d.ts +7 -0
  334. package/dist/components/ThemeToggle.vue.js +49 -0
  335. package/dist/components/ThemeToggle.vue.js.map +1 -0
  336. package/dist/components/ThemeToggle.vue3.js +6 -0
  337. package/dist/components/ThemeToggle.vue3.js.map +1 -0
  338. package/dist/components/TimePicker.vue.d.ts +29 -0
  339. package/dist/components/TimePicker.vue.js +275 -0
  340. package/dist/components/TimePicker.vue.js.map +1 -0
  341. package/dist/components/TimePicker.vue3.js +6 -0
  342. package/dist/components/TimePicker.vue3.js.map +1 -0
  343. package/dist/components/TimeRangeInput.vue.d.ts +27 -0
  344. package/dist/components/TimeRangeInput.vue.js +115 -0
  345. package/dist/components/TimeRangeInput.vue.js.map +1 -0
  346. package/dist/components/TimeRangeInput.vue3.js +6 -0
  347. package/dist/components/TimeRangeInput.vue3.js.map +1 -0
  348. package/dist/components/ToastNotification.vue.d.ts +2 -0
  349. package/dist/components/ToastNotification.vue.js +113 -0
  350. package/dist/components/ToastNotification.vue.js.map +1 -0
  351. package/dist/components/ToastNotification.vue3.js +6 -0
  352. package/dist/components/ToastNotification.vue3.js.map +1 -0
  353. package/dist/components/Tooltip.vue.d.ts +25 -0
  354. package/dist/components/Tooltip.vue.js +52 -0
  355. package/dist/components/Tooltip.vue.js.map +1 -0
  356. package/dist/components/Tooltip.vue3.js +6 -0
  357. package/dist/components/Tooltip.vue3.js.map +1 -0
  358. package/dist/components/UnitInput.vue.d.ts +44 -0
  359. package/dist/components/UnitInput.vue.js +178 -0
  360. package/dist/components/UnitInput.vue.js.map +1 -0
  361. package/dist/components/UnitInput.vue3.js +6 -0
  362. package/dist/components/UnitInput.vue3.js.map +1 -0
  363. package/dist/components/WellEditPopup.vue.d.ts +25 -0
  364. package/dist/components/WellEditPopup.vue.js +250 -0
  365. package/dist/components/WellEditPopup.vue.js.map +1 -0
  366. package/dist/components/WellEditPopup.vue3.js +6 -0
  367. package/dist/components/WellEditPopup.vue3.js.map +1 -0
  368. package/dist/components/WellPlate.vue.d.ts +69 -0
  369. package/dist/components/WellPlate.vue.js +531 -0
  370. package/dist/components/WellPlate.vue.js.map +1 -0
  371. package/dist/components/WellPlate.vue3.js +6 -0
  372. package/dist/components/WellPlate.vue3.js.map +1 -0
  373. package/dist/components/index.d.ts +71 -0
  374. package/dist/components/index.js +215 -0
  375. package/dist/components/index.js.map +1 -0
  376. package/dist/composables/index.d.ts +17 -0
  377. package/dist/composables/index.js +49 -0
  378. package/dist/composables/index.js.map +1 -0
  379. package/dist/composables/useApi.d.ts +19 -0
  380. package/dist/composables/useApi.js +101 -0
  381. package/dist/composables/useApi.js.map +1 -0
  382. package/dist/composables/useAsync.d.ts +127 -0
  383. package/dist/composables/useAsync.js +141 -0
  384. package/dist/composables/useAsync.js.map +1 -0
  385. package/dist/composables/useAuth.d.ts +44 -0
  386. package/dist/composables/useAuth.js +286 -0
  387. package/dist/composables/useAuth.js.map +1 -0
  388. package/dist/composables/useChemicalFormula.d.ts +20 -0
  389. package/dist/composables/useChemicalFormula.js +228 -0
  390. package/dist/composables/useChemicalFormula.js.map +1 -0
  391. package/dist/composables/useConcentrationUnits.d.ts +28 -0
  392. package/dist/composables/useConcentrationUnits.js +154 -0
  393. package/dist/composables/useConcentrationUnits.js.map +1 -0
  394. package/dist/composables/useDoseCalculator.d.ts +57 -0
  395. package/dist/composables/useDoseCalculator.js +228 -0
  396. package/dist/composables/useDoseCalculator.js.map +1 -0
  397. package/dist/composables/useForm.d.ts +91 -0
  398. package/dist/composables/useForm.js +253 -0
  399. package/dist/composables/useForm.js.map +1 -0
  400. package/dist/composables/usePasskey.d.ts +9 -0
  401. package/dist/composables/usePasskey.js +171 -0
  402. package/dist/composables/usePasskey.js.map +1 -0
  403. package/dist/composables/usePlatformContext.d.ts +125 -0
  404. package/dist/composables/usePlatformContext.js +149 -0
  405. package/dist/composables/usePlatformContext.js.map +1 -0
  406. package/dist/composables/useProtocolTemplates.d.ts +43 -0
  407. package/dist/composables/useProtocolTemplates.js +437 -0
  408. package/dist/composables/useProtocolTemplates.js.map +1 -0
  409. package/dist/composables/useRackEditor.d.ts +30 -0
  410. package/dist/composables/useRackEditor.js +168 -0
  411. package/dist/composables/useRackEditor.js.map +1 -0
  412. package/dist/composables/useScheduleDrag.d.ts +77 -0
  413. package/dist/composables/useScheduleDrag.js +183 -0
  414. package/dist/composables/useScheduleDrag.js.map +1 -0
  415. package/dist/composables/useSequenceUtils.d.ts +13 -0
  416. package/dist/composables/useSequenceUtils.js +93 -0
  417. package/dist/composables/useSequenceUtils.js.map +1 -0
  418. package/dist/composables/useTheme.d.ts +7 -0
  419. package/dist/composables/useTheme.js +40 -0
  420. package/dist/composables/useTheme.js.map +1 -0
  421. package/dist/composables/useTimeUtils.d.ts +15 -0
  422. package/dist/composables/useTimeUtils.js +105 -0
  423. package/dist/composables/useTimeUtils.js.map +1 -0
  424. package/dist/composables/useToast.d.ts +19 -0
  425. package/dist/composables/useToast.js +33 -0
  426. package/dist/composables/useToast.js.map +1 -0
  427. package/dist/composables/useWellPlateEditor.d.ts +31 -0
  428. package/dist/composables/useWellPlateEditor.js +315 -0
  429. package/dist/composables/useWellPlateEditor.js.map +1 -0
  430. package/dist/histoire.setup.d.ts +1 -0
  431. package/dist/index.d.ts +5 -0
  432. package/dist/index.js +265 -0
  433. package/dist/index.js.map +1 -0
  434. package/dist/install.d.ts +16 -0
  435. package/dist/install.js +15 -0
  436. package/dist/install.js.map +1 -0
  437. package/dist/stores/auth.d.ts +146 -0
  438. package/dist/stores/auth.js +122 -0
  439. package/dist/stores/auth.js.map +1 -0
  440. package/dist/stores/index.d.ts +2 -0
  441. package/dist/stores/index.js +8 -0
  442. package/dist/stores/index.js.map +1 -0
  443. package/dist/stores/settings.d.ts +75 -0
  444. package/dist/stores/settings.js +180 -0
  445. package/dist/stores/settings.js.map +1 -0
  446. package/dist/styles.css +23264 -0
  447. package/dist/tailwind.preset.d.ts +56 -0
  448. package/dist/tailwind.preset.js +59 -0
  449. package/dist/tailwind.preset.js.map +1 -0
  450. package/dist/types/auth.d.ts +42 -0
  451. package/dist/types/components.d.ts +405 -0
  452. package/dist/types/index.d.ts +3 -0
  453. package/dist/types/index.js +2 -0
  454. package/dist/types/index.js.map +1 -0
  455. package/dist/types/platform.d.ts +66 -0
  456. package/package.json +111 -0
  457. package/src/__tests__/components/AppLayout.test.ts +163 -0
  458. package/src/__tests__/components/AppSidebar.test.ts +292 -0
  459. package/src/__tests__/components/AppTopBar.test.ts +590 -0
  460. package/src/__tests__/components/BaseInput.test.ts +99 -0
  461. package/src/__tests__/components/BasePill.test.ts +291 -0
  462. package/src/__tests__/components/Calendar.test.ts +566 -0
  463. package/src/__tests__/components/CollapsibleCard.test.ts +524 -0
  464. package/src/__tests__/components/DataFrame.test.ts +767 -0
  465. package/src/__tests__/components/DropdownButton.test.ts +471 -0
  466. package/src/__tests__/composables/useAuth.test.ts +141 -0
  467. package/src/__tests__/composables/useForm.test.ts +205 -0
  468. package/src/components/AlertBox.story.vue +100 -0
  469. package/src/components/AlertBox.vue +68 -0
  470. package/src/components/AppContainer.story.vue +104 -0
  471. package/src/components/AppContainer.vue +33 -0
  472. package/src/components/AppLayout.story.vue +218 -0
  473. package/src/components/AppLayout.vue +75 -0
  474. package/src/components/AppSidebar.story.vue +300 -0
  475. package/src/components/AppSidebar.vue +114 -0
  476. package/src/components/AppTopBar.story.vue +112 -0
  477. package/src/components/AppTopBar.vue +471 -0
  478. package/src/components/AuditTrail.story.vue +163 -0
  479. package/src/components/AuditTrail.vue +161 -0
  480. package/src/components/Avatar.story.vue +92 -0
  481. package/src/components/Avatar.vue +65 -0
  482. package/src/components/BaseButton.story.vue +96 -0
  483. package/src/components/BaseButton.vue +72 -0
  484. package/src/components/BaseCheckbox.story.vue +73 -0
  485. package/src/components/BaseCheckbox.vue +68 -0
  486. package/src/components/BaseInput.story.vue +98 -0
  487. package/src/components/BaseInput.vue +70 -0
  488. package/src/components/BaseModal.story.vue +126 -0
  489. package/src/components/BaseModal.vue +118 -0
  490. package/src/components/BasePill.story.vue +131 -0
  491. package/src/components/BasePill.vue +84 -0
  492. package/src/components/BaseRadioGroup.story.vue +116 -0
  493. package/src/components/BaseRadioGroup.vue +83 -0
  494. package/src/components/BaseSelect.story.vue +110 -0
  495. package/src/components/BaseSelect.vue +67 -0
  496. package/src/components/BaseSlider.story.vue +82 -0
  497. package/src/components/BaseSlider.vue +112 -0
  498. package/src/components/BaseTabs.story.vue +106 -0
  499. package/src/components/BaseTabs.vue +58 -0
  500. package/src/components/BaseTextarea.story.vue +91 -0
  501. package/src/components/BaseTextarea.vue +58 -0
  502. package/src/components/BaseToggle.story.vue +81 -0
  503. package/src/components/BaseToggle.vue +72 -0
  504. package/src/components/BatchProgressList.story.vue +92 -0
  505. package/src/components/BatchProgressList.vue +202 -0
  506. package/src/components/Breadcrumb.story.vue +93 -0
  507. package/src/components/Breadcrumb.vue +73 -0
  508. package/src/components/Calendar.story.vue +106 -0
  509. package/src/components/Calendar.vue +362 -0
  510. package/src/components/ChartContainer.story.vue +113 -0
  511. package/src/components/ChartContainer.vue +63 -0
  512. package/src/components/ChemicalFormula.story.vue +102 -0
  513. package/src/components/ChemicalFormula.vue +38 -0
  514. package/src/components/CollapsibleCard.story.vue +124 -0
  515. package/src/components/CollapsibleCard.vue +164 -0
  516. package/src/components/ColorSlider.story.vue +120 -0
  517. package/src/components/ColorSlider.vue +163 -0
  518. package/src/components/ConcentrationInput.story.vue +77 -0
  519. package/src/components/ConcentrationInput.vue +184 -0
  520. package/src/components/ConfirmDialog.story.vue +145 -0
  521. package/src/components/ConfirmDialog.vue +96 -0
  522. package/src/components/DataFrame.story.vue +148 -0
  523. package/src/components/DataFrame.vue +418 -0
  524. package/src/components/DatePicker.story.vue +104 -0
  525. package/src/components/DatePicker.vue +298 -0
  526. package/src/components/DateTimePicker.story.vue +112 -0
  527. package/src/components/DateTimePicker.vue +391 -0
  528. package/src/components/Divider.story.vue +72 -0
  529. package/src/components/Divider.vue +45 -0
  530. package/src/components/DoseCalculator.story.vue +68 -0
  531. package/src/components/DoseCalculator.vue +475 -0
  532. package/src/components/DropdownButton.story.vue +102 -0
  533. package/src/components/DropdownButton.vue +180 -0
  534. package/src/components/EmptyState.story.vue +124 -0
  535. package/src/components/EmptyState.vue +64 -0
  536. package/src/components/ExperimentTimeline.story.vue +161 -0
  537. package/src/components/ExperimentTimeline.vue +381 -0
  538. package/src/components/FileUploader.story.vue +106 -0
  539. package/src/components/FileUploader.vue +397 -0
  540. package/src/components/FormField.story.vue +119 -0
  541. package/src/components/FormField.vue +37 -0
  542. package/src/components/FormulaInput.story.vue +96 -0
  543. package/src/components/FormulaInput.vue +123 -0
  544. package/src/components/GroupAssigner.story.vue +83 -0
  545. package/src/components/GroupAssigner.vue +283 -0
  546. package/src/components/GroupingModal.story.vue +52 -0
  547. package/src/components/GroupingModal.vue +420 -0
  548. package/src/components/IconButton.story.vue +135 -0
  549. package/src/components/IconButton.vue +72 -0
  550. package/src/components/LoadingSpinner.story.vue +70 -0
  551. package/src/components/LoadingSpinner.vue +49 -0
  552. package/src/components/MoleculeInput.story.vue +66 -0
  553. package/src/components/MoleculeInput.vue +414 -0
  554. package/src/components/MultiSelect.story.vue +121 -0
  555. package/src/components/MultiSelect.vue +117 -0
  556. package/src/components/NumberInput.story.vue +90 -0
  557. package/src/components/NumberInput.vue +128 -0
  558. package/src/components/PlateMapEditor.story.vue +92 -0
  559. package/src/components/PlateMapEditor.vue +512 -0
  560. package/src/components/ProgressBar.story.vue +117 -0
  561. package/src/components/ProgressBar.vue +63 -0
  562. package/src/components/ProtocolStepEditor.story.vue +69 -0
  563. package/src/components/ProtocolStepEditor.vue +521 -0
  564. package/src/components/RackEditor.story.vue +100 -0
  565. package/src/components/RackEditor.vue +370 -0
  566. package/src/components/ReagentList.story.vue +137 -0
  567. package/src/components/ReagentList.vue +487 -0
  568. package/src/components/ResourceCard.story.vue +150 -0
  569. package/src/components/ResourceCard.vue +160 -0
  570. package/src/components/SampleHierarchyTree.story.vue +161 -0
  571. package/src/components/SampleHierarchyTree.vue +279 -0
  572. package/src/components/SampleLegend.story.vue +91 -0
  573. package/src/components/SampleLegend.vue +118 -0
  574. package/src/components/SampleSelector.story.vue +100 -0
  575. package/src/components/SampleSelector.vue +955 -0
  576. package/src/components/ScheduleCalendar.story.vue +195 -0
  577. package/src/components/ScheduleCalendar.vue +568 -0
  578. package/src/components/ScientificNumber.story.vue +128 -0
  579. package/src/components/ScientificNumber.vue +197 -0
  580. package/src/components/SegmentedControl.story.vue +112 -0
  581. package/src/components/SegmentedControl.vue +78 -0
  582. package/src/components/SequenceInput.story.vue +119 -0
  583. package/src/components/SequenceInput.vue +208 -0
  584. package/src/components/SettingsButton.story.vue +58 -0
  585. package/src/components/SettingsButton.vue +75 -0
  586. package/src/components/SettingsModal.story.vue +145 -0
  587. package/src/components/SettingsModal.vue +151 -0
  588. package/src/components/Skeleton.story.vue +120 -0
  589. package/src/components/Skeleton.vue +73 -0
  590. package/src/components/StatusIndicator.story.vue +99 -0
  591. package/src/components/StatusIndicator.vue +39 -0
  592. package/src/components/StepWizard.story.vue +155 -0
  593. package/src/components/StepWizard.vue +231 -0
  594. package/src/components/TagsInput.story.vue +104 -0
  595. package/src/components/TagsInput.vue +127 -0
  596. package/src/components/ThemeToggle.story.vue +36 -0
  597. package/src/components/ThemeToggle.vue +53 -0
  598. package/src/components/TimePicker.story.vue +96 -0
  599. package/src/components/TimePicker.vue +272 -0
  600. package/src/components/TimeRangeInput.story.vue +104 -0
  601. package/src/components/TimeRangeInput.vue +121 -0
  602. package/src/components/ToastNotification.story.vue +130 -0
  603. package/src/components/ToastNotification.vue +54 -0
  604. package/src/components/Tooltip.story.vue +102 -0
  605. package/src/components/Tooltip.vue +61 -0
  606. package/src/components/UnitInput.story.vue +170 -0
  607. package/src/components/UnitInput.vue +218 -0
  608. package/src/components/WellEditPopup.vue +234 -0
  609. package/src/components/WellPlate.story.vue +198 -0
  610. package/src/components/WellPlate.vue +637 -0
  611. package/src/components/index.ts +96 -0
  612. package/src/composables/index.ts +87 -0
  613. package/src/composables/useApi.ts +145 -0
  614. package/src/composables/useAsync.ts +322 -0
  615. package/src/composables/useAuth.ts +427 -0
  616. package/src/composables/useChemicalFormula.ts +274 -0
  617. package/src/composables/useConcentrationUnits.ts +245 -0
  618. package/src/composables/useDoseCalculator.ts +369 -0
  619. package/src/composables/useForm.ts +415 -0
  620. package/src/composables/usePasskey.ts +209 -0
  621. package/src/composables/usePlatformContext.ts +232 -0
  622. package/src/composables/useProtocolTemplates.ts +517 -0
  623. package/src/composables/useRackEditor.ts +221 -0
  624. package/src/composables/useScheduleDrag.ts +244 -0
  625. package/src/composables/useSequenceUtils.ts +104 -0
  626. package/src/composables/useTheme.ts +49 -0
  627. package/src/composables/useTimeUtils.ts +114 -0
  628. package/src/composables/useToast.ts +39 -0
  629. package/src/composables/useWellPlateEditor.ts +408 -0
  630. package/src/histoire.setup.ts +17 -0
  631. package/src/index.ts +283 -0
  632. package/src/install.ts +32 -0
  633. package/src/stores/auth.ts +144 -0
  634. package/src/stores/index.ts +2 -0
  635. package/src/stores/settings.ts +229 -0
  636. package/src/styles/components/alert-box.css +94 -0
  637. package/src/styles/components/app-container.css +32 -0
  638. package/src/styles/components/app-layout.css +99 -0
  639. package/src/styles/components/app-sidebar.css +133 -0
  640. package/src/styles/components/app-top-bar.css +396 -0
  641. package/src/styles/components/audit-trail.css +143 -0
  642. package/src/styles/components/avatar.css +64 -0
  643. package/src/styles/components/batch-progress-list.css +196 -0
  644. package/src/styles/components/breadcrumb.css +45 -0
  645. package/src/styles/components/button.css +119 -0
  646. package/src/styles/components/calendar.css +192 -0
  647. package/src/styles/components/chart-container.css +69 -0
  648. package/src/styles/components/checkbox.css +112 -0
  649. package/src/styles/components/chemical-formula.css +46 -0
  650. package/src/styles/components/collapsible-card.css +184 -0
  651. package/src/styles/components/color-slider.css +110 -0
  652. package/src/styles/components/concentration-input.css +142 -0
  653. package/src/styles/components/confirm-dialog.css +137 -0
  654. package/src/styles/components/dataframe.css +382 -0
  655. package/src/styles/components/date-picker.css +239 -0
  656. package/src/styles/components/datetime-picker.css +225 -0
  657. package/src/styles/components/divider.css +62 -0
  658. package/src/styles/components/dose-calculator.css +301 -0
  659. package/src/styles/components/dropdown-button.css +236 -0
  660. package/src/styles/components/empty-state.css +148 -0
  661. package/src/styles/components/experiment-timeline.css +529 -0
  662. package/src/styles/components/file-uploader.css +517 -0
  663. package/src/styles/components/form-field.css +35 -0
  664. package/src/styles/components/formula-input.css +96 -0
  665. package/src/styles/components/group-assigner.css +200 -0
  666. package/src/styles/components/grouping-modal.css +323 -0
  667. package/src/styles/components/icon-button.css +128 -0
  668. package/src/styles/components/input.css +53 -0
  669. package/src/styles/components/loading-spinner.css +67 -0
  670. package/src/styles/components/modal.css +122 -0
  671. package/src/styles/components/molecule-input.css +186 -0
  672. package/src/styles/components/multi-select.css +131 -0
  673. package/src/styles/components/number-input.css +110 -0
  674. package/src/styles/components/pill.css +172 -0
  675. package/src/styles/components/plate-map-editor.css +464 -0
  676. package/src/styles/components/progress-bar.css +88 -0
  677. package/src/styles/components/protocol-step-editor.css +449 -0
  678. package/src/styles/components/rack-editor.css +259 -0
  679. package/src/styles/components/radio-group.css +158 -0
  680. package/src/styles/components/reagent-list.css +407 -0
  681. package/src/styles/components/resource-card.css +360 -0
  682. package/src/styles/components/sample-hierarchy-tree.css +314 -0
  683. package/src/styles/components/sample-legend.css +201 -0
  684. package/src/styles/components/sample-selector.css +701 -0
  685. package/src/styles/components/schedule-calendar.css +478 -0
  686. package/src/styles/components/scientific-number.css +63 -0
  687. package/src/styles/components/segmented-control.css +190 -0
  688. package/src/styles/components/select.css +72 -0
  689. package/src/styles/components/sequence-input.css +184 -0
  690. package/src/styles/components/settings-button.css +75 -0
  691. package/src/styles/components/settings-modal.css +95 -0
  692. package/src/styles/components/skeleton.css +36 -0
  693. package/src/styles/components/slider.css +78 -0
  694. package/src/styles/components/status-indicator.css +59 -0
  695. package/src/styles/components/step-wizard.css +187 -0
  696. package/src/styles/components/tabs.css +108 -0
  697. package/src/styles/components/tags-input.css +118 -0
  698. package/src/styles/components/textarea.css +65 -0
  699. package/src/styles/components/theme-toggle.css +50 -0
  700. package/src/styles/components/time-picker.css +171 -0
  701. package/src/styles/components/time-range-input.css +42 -0
  702. package/src/styles/components/toast.css +72 -0
  703. package/src/styles/components/toggle.css +115 -0
  704. package/src/styles/components/tooltip.css +98 -0
  705. package/src/styles/components/unit-input.css +138 -0
  706. package/src/styles/components/well-edit-popup.css +252 -0
  707. package/src/styles/components/well-plate.css +310 -0
  708. package/src/styles/index.css +80 -0
  709. package/src/styles/variables.css +1078 -0
  710. package/src/tailwind.preset.ts +59 -0
  711. package/src/types/auth.ts +55 -0
  712. package/src/types/components.ts +560 -0
  713. package/src/types/index.ts +146 -0
  714. package/src/types/platform.ts +101 -0
@@ -0,0 +1,427 @@
1
+ import axios from 'axios'
2
+ import { ref, onMounted, onUnmounted, watch, type Ref } from 'vue'
3
+ import { useAuthStore } from '../stores/auth'
4
+ import { useSettingsStore } from '../stores/settings'
5
+ import type { AuthConfig, UserInfo, LoginResponse, TokenVerifyResponse, UpdateProfileRequest } from '../types'
6
+
7
+ interface UserResponse {
8
+ id: string
9
+ username: string
10
+ shortname: string | null
11
+ email: string | null
12
+ role: string
13
+ is_active: boolean
14
+ }
15
+
16
+ interface RefreshResponse {
17
+ access_token: string
18
+ expires_in: number
19
+ token_type: string
20
+ }
21
+
22
+ // Token refresh configuration
23
+ const TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000 // Refresh 5 minutes before expiry
24
+ const TOKEN_REFRESH_CHECK_INTERVAL_MS = 60 * 1000 // Check every minute
25
+
26
+ /**
27
+ * Authentication composable with automatic token refresh.
28
+ *
29
+ * Features:
30
+ * - Automatic token refresh before expiration
31
+ * - Login/logout/register functionality
32
+ * - Token verification on startup
33
+ * - User profile management
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const { login, logout, isAuthenticated, user } = useAuth()
38
+ *
39
+ * // Login
40
+ * const success = await login('username', 'password')
41
+ *
42
+ * // Automatic refresh is enabled by default
43
+ * // Tokens are refreshed 5 minutes before expiration
44
+ * ```
45
+ */
46
+ export interface UseAuthReturn {
47
+ login: (username: string, password: string) => Promise<boolean>
48
+ logout: () => void
49
+ register: (username: string, password: string, email?: string) => Promise<boolean>
50
+ verifyToken: () => Promise<boolean>
51
+ fetchAuthConfig: () => Promise<AuthConfig>
52
+ initializeAuth: () => Promise<void>
53
+ getCurrentUser: () => Promise<UserInfo | null>
54
+ getAuthHeader: () => Record<string, string>
55
+ updateProfile: (data: { email?: string; shortname?: string; currentPassword?: string; newPassword?: string }) => Promise<{ success: boolean; error?: string }>
56
+ refreshToken: () => Promise<boolean>
57
+ isRefreshing: Ref<boolean>
58
+ }
59
+
60
+ export function useAuth(): UseAuthReturn {
61
+ const authStore = useAuthStore()
62
+ const settingsStore = useSettingsStore()
63
+
64
+ // Track refresh timer
65
+ const refreshTimerId = ref<number | null>(null)
66
+ const isRefreshing = ref(false)
67
+
68
+ function getApiBaseUrl(): string {
69
+ return settingsStore.getApiBaseUrl()
70
+ }
71
+
72
+ async function fetchAuthConfig(): Promise<AuthConfig> {
73
+ try {
74
+ const response = await axios.get<{
75
+ auth_required: boolean
76
+ passkey_enabled: boolean
77
+ passkey_registered?: boolean
78
+ registration_enabled?: boolean
79
+ database_mode?: string
80
+ }>(`${getApiBaseUrl()}/setup/config/public`)
81
+
82
+ const config: AuthConfig = {
83
+ authRequired: response.data.auth_required,
84
+ passkeyEnabled: response.data.passkey_enabled,
85
+ passkeyRegistered: response.data.passkey_registered ?? false,
86
+ registrationEnabled: response.data.registration_enabled ?? false,
87
+ databaseMode: response.data.database_mode ?? 'none',
88
+ }
89
+
90
+ authStore.setAuthConfig(config)
91
+ return config
92
+ } catch (error) {
93
+ console.error('Failed to fetch auth config:', error)
94
+ return {
95
+ authRequired: false,
96
+ passkeyEnabled: false,
97
+ passkeyRegistered: false,
98
+ registrationEnabled: false,
99
+ databaseMode: 'none',
100
+ }
101
+ }
102
+ }
103
+
104
+ async function login(username: string, password: string): Promise<boolean> {
105
+ authStore.setLoading(true)
106
+ authStore.setError(null)
107
+
108
+ try {
109
+ const response = await axios.post<LoginResponse>(
110
+ `${getApiBaseUrl()}/auth/login`,
111
+ { username, password }
112
+ )
113
+
114
+ authStore.setToken(response.data.access_token, response.data.expires_in)
115
+ authStore.setUsername(username)
116
+
117
+ await getCurrentUser()
118
+
119
+ // Start auto-refresh after successful login
120
+ scheduleTokenRefresh()
121
+
122
+ return true
123
+ } catch (error) {
124
+ if (axios.isAxiosError(error) && error.response) {
125
+ authStore.setError(error.response.data.detail || 'Login failed')
126
+ } else {
127
+ authStore.setError('Network error. Please try again.')
128
+ }
129
+ return false
130
+ } finally {
131
+ authStore.setLoading(false)
132
+ }
133
+ }
134
+
135
+ async function register(username: string, password: string, email?: string): Promise<boolean> {
136
+ authStore.setLoading(true)
137
+ authStore.setError(null)
138
+
139
+ try {
140
+ await axios.post<UserResponse>(
141
+ `${getApiBaseUrl()}/users/register`,
142
+ { username, password, email }
143
+ )
144
+
145
+ return await login(username, password)
146
+ } catch (error) {
147
+ if (axios.isAxiosError(error) && error.response) {
148
+ authStore.setError(error.response.data.detail || 'Registration failed')
149
+ } else {
150
+ authStore.setError('Network error. Please try again.')
151
+ }
152
+ return false
153
+ } finally {
154
+ authStore.setLoading(false)
155
+ }
156
+ }
157
+
158
+ async function getCurrentUser(): Promise<UserInfo | null> {
159
+ if (!authStore.token) {
160
+ return null
161
+ }
162
+
163
+ try {
164
+ const response = await axios.get<UserResponse>(
165
+ `${getApiBaseUrl()}/users/me`,
166
+ { headers: getAuthHeader() }
167
+ )
168
+
169
+ const userInfo: UserInfo = {
170
+ id: response.data.id,
171
+ username: response.data.username,
172
+ shortname: response.data.shortname,
173
+ email: response.data.email,
174
+ role: response.data.role,
175
+ isActive: response.data.is_active,
176
+ }
177
+
178
+ authStore.setUserInfo(userInfo)
179
+ return userInfo
180
+ } catch {
181
+ return null
182
+ }
183
+ }
184
+
185
+ async function verifyToken(): Promise<boolean> {
186
+ if (!authStore.token) {
187
+ return false
188
+ }
189
+
190
+ try {
191
+ const response = await axios.get<TokenVerifyResponse>(
192
+ `${getApiBaseUrl()}/auth/verify`,
193
+ {
194
+ headers: {
195
+ Authorization: `Bearer ${authStore.token}`,
196
+ },
197
+ }
198
+ )
199
+
200
+ if (response.data.valid && response.data.username) {
201
+ authStore.setUsername(response.data.username)
202
+ return true
203
+ }
204
+
205
+ authStore.clearToken()
206
+ return false
207
+ } catch {
208
+ authStore.clearToken()
209
+ return false
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Refresh the authentication token.
215
+ * Called automatically before token expiration.
216
+ * Uses promise caching to prevent concurrent refresh requests.
217
+ */
218
+ let refreshPromise: Promise<boolean> | null = null
219
+
220
+ async function refreshToken(): Promise<boolean> {
221
+ if (!authStore.token) return false
222
+ if (refreshPromise) return refreshPromise
223
+
224
+ refreshPromise = (async () => {
225
+ isRefreshing.value = true
226
+
227
+ try {
228
+ const response = await axios.post<RefreshResponse>(
229
+ `${getApiBaseUrl()}/auth/refresh`,
230
+ {},
231
+ { headers: getAuthHeader() }
232
+ )
233
+
234
+ authStore.setToken(response.data.access_token, response.data.expires_in)
235
+
236
+ // Reschedule next refresh
237
+ scheduleTokenRefresh()
238
+
239
+ return true
240
+ } catch (error) {
241
+ // If refresh fails, the token may have been revoked
242
+ // Clear auth state and let user re-login
243
+ if (axios.isAxiosError(error) && error.response?.status === 401) {
244
+ console.warn('[Auth] Token refresh failed - session expired')
245
+ authStore.clearToken()
246
+ stopTokenRefresh()
247
+ }
248
+ return false
249
+ } finally {
250
+ isRefreshing.value = false
251
+ refreshPromise = null
252
+ }
253
+ })()
254
+
255
+ return refreshPromise
256
+ }
257
+
258
+ /**
259
+ * Schedule automatic token refresh before expiration.
260
+ */
261
+ function scheduleTokenRefresh(): void {
262
+ // Clear any existing timer
263
+ stopTokenRefresh()
264
+
265
+ if (!authStore.tokenExpires) {
266
+ return
267
+ }
268
+
269
+ const expiresAt = authStore.tokenExpires.getTime()
270
+ const refreshAt = expiresAt - TOKEN_REFRESH_MARGIN_MS
271
+ const now = Date.now()
272
+
273
+ if (refreshAt <= now) {
274
+ // Token is already close to expiring or expired, refresh now
275
+ refreshToken()
276
+ return
277
+ }
278
+
279
+ // Schedule refresh
280
+ const delay = refreshAt - now
281
+ refreshTimerId.value = window.setTimeout(() => {
282
+ refreshToken()
283
+ }, delay)
284
+ }
285
+
286
+ /**
287
+ * Stop automatic token refresh.
288
+ */
289
+ function stopTokenRefresh(): void {
290
+ if (refreshTimerId.value !== null) {
291
+ window.clearTimeout(refreshTimerId.value)
292
+ refreshTimerId.value = null
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Check if token needs refresh and refresh if necessary.
298
+ * Called periodically as a safety net.
299
+ */
300
+ function checkAndRefreshIfNeeded(): void {
301
+ if (!authStore.token || !authStore.tokenExpires) {
302
+ return
303
+ }
304
+
305
+ const expiresAt = authStore.tokenExpires.getTime()
306
+ const refreshAt = expiresAt - TOKEN_REFRESH_MARGIN_MS
307
+ const now = Date.now()
308
+
309
+ if (now >= refreshAt) {
310
+ refreshToken()
311
+ }
312
+ }
313
+
314
+ async function initializeAuth(): Promise<void> {
315
+ authStore.initialize()
316
+ await fetchAuthConfig()
317
+
318
+ if (authStore.token) {
319
+ const valid = await verifyToken()
320
+ if (valid) {
321
+ await getCurrentUser()
322
+ // Start auto-refresh for existing session
323
+ scheduleTokenRefresh()
324
+ }
325
+ }
326
+ }
327
+
328
+ function logout(): void {
329
+ stopTokenRefresh()
330
+ authStore.logout()
331
+ }
332
+
333
+ function getAuthHeader(): Record<string, string> {
334
+ if (authStore.token) {
335
+ return { Authorization: `Bearer ${authStore.token}` }
336
+ }
337
+ return {}
338
+ }
339
+
340
+ async function updateProfile(data: {
341
+ email?: string
342
+ shortname?: string
343
+ currentPassword?: string
344
+ newPassword?: string
345
+ }): Promise<{ success: boolean; error?: string }> {
346
+ if (!authStore.token) {
347
+ return { success: false, error: 'Not authenticated' }
348
+ }
349
+
350
+ try {
351
+ const requestData: UpdateProfileRequest = {}
352
+ if (data.email !== undefined) requestData.email = data.email
353
+ if (data.shortname !== undefined) requestData.shortname = data.shortname
354
+ if (data.currentPassword) requestData.current_password = data.currentPassword
355
+ if (data.newPassword) requestData.new_password = data.newPassword
356
+
357
+ const response = await axios.put<UserResponse>(
358
+ `${getApiBaseUrl()}/users/me`,
359
+ requestData,
360
+ { headers: getAuthHeader() }
361
+ )
362
+
363
+ const userInfo: UserInfo = {
364
+ id: response.data.id,
365
+ username: response.data.username,
366
+ shortname: response.data.shortname,
367
+ email: response.data.email,
368
+ role: response.data.role,
369
+ isActive: response.data.is_active,
370
+ }
371
+ authStore.setUserInfo(userInfo)
372
+
373
+ return { success: true }
374
+ } catch (error) {
375
+ if (axios.isAxiosError(error) && error.response) {
376
+ return { success: false, error: error.response.data.detail || 'Update failed' }
377
+ }
378
+ return { success: false, error: 'Network error. Please try again.' }
379
+ }
380
+ }
381
+
382
+ // Set up periodic check as safety net
383
+ let checkInterval: number | null = null
384
+
385
+ onMounted(() => {
386
+ // Start periodic check
387
+ checkInterval = window.setInterval(checkAndRefreshIfNeeded, TOKEN_REFRESH_CHECK_INTERVAL_MS)
388
+ })
389
+
390
+ onUnmounted(() => {
391
+ // Clean up on unmount
392
+ stopTokenRefresh()
393
+ if (checkInterval !== null) {
394
+ window.clearInterval(checkInterval)
395
+ checkInterval = null
396
+ }
397
+ })
398
+
399
+ // Watch for token changes to reschedule refresh
400
+ watch(
401
+ () => authStore.tokenExpires,
402
+ (newExpires) => {
403
+ if (newExpires) {
404
+ scheduleTokenRefresh()
405
+ } else {
406
+ stopTokenRefresh()
407
+ }
408
+ }
409
+ )
410
+
411
+ return {
412
+ // Core auth methods
413
+ login,
414
+ logout,
415
+ register,
416
+ verifyToken,
417
+ fetchAuthConfig,
418
+ initializeAuth,
419
+ getCurrentUser,
420
+ getAuthHeader,
421
+ updateProfile,
422
+
423
+ // Token refresh
424
+ refreshToken,
425
+ isRefreshing,
426
+ }
427
+ }
@@ -0,0 +1,274 @@
1
+ // Types
2
+ export interface ParsedElement {
3
+ symbol: string
4
+ count: number
5
+ }
6
+
7
+ export interface FormulaParseResult {
8
+ elements: Record<string, number>
9
+ valid: boolean
10
+ error?: string
11
+ }
12
+
13
+ export type FormulaPartType = 'element' | 'subscript' | 'superscript' | 'paren' | 'dot' | 'charge'
14
+
15
+ export interface FormulaPart {
16
+ type: FormulaPartType
17
+ text: string
18
+ }
19
+
20
+ // ~30 common elements with atomic weights
21
+ export const ATOMIC_WEIGHTS: Record<string, number> = {
22
+ H: 1.008, He: 4.003, Li: 6.941, Be: 9.012, B: 10.81,
23
+ C: 12.011, N: 14.007, O: 15.999, F: 18.998, Ne: 20.180,
24
+ Na: 22.990, Mg: 24.305, Al: 26.982, Si: 28.086, P: 30.974,
25
+ S: 32.065, Cl: 35.453, Ar: 39.948, K: 39.098, Ca: 40.078,
26
+ Mn: 54.938, Fe: 55.845, Co: 58.933, Ni: 58.693, Cu: 63.546,
27
+ Zn: 65.38, Br: 79.904, Ag: 107.868, I: 126.904, Au: 196.967,
28
+ }
29
+
30
+ function parseFormulaSegment(formula: string): FormulaParseResult {
31
+ const elements: Record<string, number> = {}
32
+ const stack: Record<string, number>[] = [{}]
33
+ let i = 0
34
+
35
+ while (i < formula.length) {
36
+ const ch = formula[i]
37
+
38
+ if (ch === '(') {
39
+ stack.push({})
40
+ i++
41
+ } else if (ch === ')') {
42
+ if (stack.length < 2) {
43
+ return { elements: {}, valid: false, error: 'Unmatched closing parenthesis' }
44
+ }
45
+ i++
46
+ // Read multiplier after closing paren
47
+ let numStr = ''
48
+ while (i < formula.length && /\d/.test(formula[i])) {
49
+ numStr += formula[i]
50
+ i++
51
+ }
52
+ const multiplier = numStr ? parseInt(numStr, 10) : 1
53
+ const top = stack.pop()!
54
+ const current = stack[stack.length - 1]
55
+ for (const [el, count] of Object.entries(top)) {
56
+ current[el] = (current[el] || 0) + count * multiplier
57
+ }
58
+ } else if (/[A-Z]/.test(ch)) {
59
+ // Element symbol: uppercase letter optionally followed by lowercase
60
+ let symbol = ch
61
+ i++
62
+ while (i < formula.length && /[a-z]/.test(formula[i])) {
63
+ symbol += formula[i]
64
+ i++
65
+ }
66
+ // Validate element
67
+ if (!(symbol in ATOMIC_WEIGHTS)) {
68
+ return { elements: {}, valid: false, error: `Unknown element: ${symbol}` }
69
+ }
70
+ // Read count
71
+ let numStr = ''
72
+ while (i < formula.length && /\d/.test(formula[i])) {
73
+ numStr += formula[i]
74
+ i++
75
+ }
76
+ const count = numStr ? parseInt(numStr, 10) : 1
77
+ const current = stack[stack.length - 1]
78
+ current[symbol] = (current[symbol] || 0) + count
79
+ } else {
80
+ return { elements: {}, valid: false, error: `Unexpected character: ${ch}` }
81
+ }
82
+ }
83
+
84
+ if (stack.length !== 1) {
85
+ return { elements: {}, valid: false, error: 'Unmatched opening parenthesis' }
86
+ }
87
+
88
+ Object.assign(elements, stack[0])
89
+ return { elements, valid: true }
90
+ }
91
+
92
+ export function useChemicalFormula() {
93
+ function parseFormula(formula: string): FormulaParseResult {
94
+ const trimmed = formula.trim()
95
+ if (!trimmed) {
96
+ return { elements: {}, valid: false, error: 'Empty formula' }
97
+ }
98
+
99
+ // Split on hydrate separator (· or .)
100
+ const parts = trimmed.split(/[·.]/)
101
+ const combined: Record<string, number> = {}
102
+
103
+ for (const part of parts) {
104
+ const segment = part.trim()
105
+ if (!segment) continue
106
+
107
+ // Check for leading coefficient (e.g., 5H2O)
108
+ let coefficient = 1
109
+ let formulaStr = segment
110
+ const coeffMatch = segment.match(/^(\d+)([A-Z].*)$/)
111
+ if (coeffMatch) {
112
+ coefficient = parseInt(coeffMatch[1], 10)
113
+ formulaStr = coeffMatch[2]
114
+ }
115
+
116
+ const result = parseFormulaSegment(formulaStr)
117
+ if (!result.valid) {
118
+ return result
119
+ }
120
+
121
+ for (const [el, count] of Object.entries(result.elements)) {
122
+ combined[el] = (combined[el] || 0) + count * coefficient
123
+ }
124
+ }
125
+
126
+ return { elements: combined, valid: true }
127
+ }
128
+
129
+ function calculateMW(elements: Record<string, number>): number {
130
+ let mw = 0
131
+ for (const [el, count] of Object.entries(elements)) {
132
+ const weight = ATOMIC_WEIGHTS[el]
133
+ if (weight !== undefined) {
134
+ mw += weight * count
135
+ }
136
+ }
137
+ return Math.round(mw * 1000) / 1000
138
+ }
139
+
140
+ function renderFormulaParts(formula: string): FormulaPart[] {
141
+ const parts: FormulaPart[] = []
142
+ const trimmed = formula.trim()
143
+ let i = 0
144
+
145
+ while (i < trimmed.length) {
146
+ const ch = trimmed[i]
147
+
148
+ // Hydrate dot separator
149
+ if (ch === '·' || (ch === '.' && i > 0 && /\d/.test(trimmed[i - 1]) && i + 1 < trimmed.length && /\d/.test(trimmed[i + 1]))) {
150
+ // Disambiguate: '.' as hydrate separator only if followed by a digit then uppercase
151
+ // For simplicity, treat '·' always as dot
152
+ if (ch === '·') {
153
+ parts.push({ type: 'dot', text: '·' })
154
+ i++
155
+ continue
156
+ }
157
+ }
158
+
159
+ // Parentheses
160
+ if (ch === '(' || ch === ')') {
161
+ parts.push({ type: 'paren', text: ch })
162
+ i++
163
+ // After ')' check for subscript number
164
+ if (ch === ')' && i < trimmed.length && /\d/.test(trimmed[i])) {
165
+ let numStr = ''
166
+ while (i < trimmed.length && /\d/.test(trimmed[i])) {
167
+ numStr += trimmed[i]
168
+ i++
169
+ }
170
+ parts.push({ type: 'subscript', text: numStr })
171
+ }
172
+ continue
173
+ }
174
+
175
+ // Charge notation: ^2+, ^2-, 2+, 2- at end, or +, -
176
+ if (ch === '^') {
177
+ i++
178
+ let chargeStr = ''
179
+ while (i < trimmed.length && /[\d+\-]/.test(trimmed[i])) {
180
+ chargeStr += trimmed[i]
181
+ i++
182
+ }
183
+ if (chargeStr) {
184
+ parts.push({ type: 'charge', text: chargeStr })
185
+ }
186
+ continue
187
+ }
188
+
189
+ // Element symbol: uppercase followed by optional lowercase
190
+ if (/[A-Z]/.test(ch)) {
191
+ let symbol = ch
192
+ i++
193
+ while (i < trimmed.length && /[a-z]/.test(trimmed[i])) {
194
+ symbol += trimmed[i]
195
+ i++
196
+ }
197
+ parts.push({ type: 'element', text: symbol })
198
+
199
+ // Check for subscript number after element
200
+ if (i < trimmed.length && /\d/.test(trimmed[i])) {
201
+ let numStr = ''
202
+ while (i < trimmed.length && /\d/.test(trimmed[i])) {
203
+ numStr += trimmed[i]
204
+ i++
205
+ }
206
+ parts.push({ type: 'subscript', text: numStr })
207
+
208
+ // Check for charge after subscript (e.g., Fe2+ or SO4^2-)
209
+ if (i < trimmed.length && (trimmed[i] === '+' || trimmed[i] === '-')) {
210
+ parts.push({ type: 'charge', text: numStr + trimmed[i] })
211
+ // Remove the subscript we just added since it's actually part of charge
212
+ // Actually, for cases like Fe2+, the 2+ is the charge, not a subscript
213
+ // But for H2O the 2 is a subscript. We need context.
214
+ // Simplification: if followed by +/-, treat trailing digits as part of charge
215
+ parts.splice(parts.length - 2, 1) // Remove the subscript
216
+ i++
217
+ continue
218
+ }
219
+ }
220
+ continue
221
+ }
222
+
223
+ // Leading digit (hydrate coefficient like 5H2O)
224
+ if (/\d/.test(ch)) {
225
+ let numStr = ''
226
+ while (i < trimmed.length && /\d/.test(trimmed[i])) {
227
+ numStr += trimmed[i]
228
+ i++
229
+ }
230
+ // Could be a subscript or coefficient depending on context
231
+ // If next char is uppercase, it's a coefficient (render as element-like text)
232
+ // If at end or followed by +/-, it's a charge component
233
+ if (i < trimmed.length && (trimmed[i] === '+' || trimmed[i] === '-')) {
234
+ parts.push({ type: 'charge', text: numStr + trimmed[i] })
235
+ i++
236
+ } else {
237
+ parts.push({ type: 'subscript', text: numStr })
238
+ }
239
+ continue
240
+ }
241
+
242
+ // Standalone + or - (charge)
243
+ if (ch === '+' || ch === '-') {
244
+ parts.push({ type: 'charge', text: ch })
245
+ i++
246
+ continue
247
+ }
248
+
249
+ // Dot as hydrate separator
250
+ if (ch === '.') {
251
+ parts.push({ type: 'dot', text: '·' })
252
+ i++
253
+ continue
254
+ }
255
+
256
+ // Skip whitespace
257
+ if (/\s/.test(ch)) {
258
+ i++
259
+ continue
260
+ }
261
+
262
+ // Unknown character, skip
263
+ i++
264
+ }
265
+
266
+ return parts
267
+ }
268
+
269
+ return {
270
+ parseFormula,
271
+ calculateMW,
272
+ renderFormulaParts,
273
+ }
274
+ }