@morscherlab/mint-sdk 1.2.0-beta.8 → 1.2.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.
- package/README.md +60 -0
- package/dist/{ExperimentPopover-CfOdh5Rm.js → ExperimentPopover-CJdNlCZb.js} +24 -15
- package/dist/ExperimentPopover-CJdNlCZb.js.map +1 -0
- package/dist/{ExperimentSelectorModal-DvIagrxY.js → ExperimentSelectorModal-DCXmwKNS.js} +5 -5
- package/dist/ExperimentSelectorModal-DCXmwKNS.js.map +1 -0
- package/dist/{SettingsModal-W3TYfnP9.js → SettingsModal-BYR20I4c.js} +5 -5
- package/dist/SettingsModal-BYR20I4c.js.map +1 -0
- package/dist/{EmptyState-NBqf2_8z.js → Skeleton-BAF3CKY7.js} +111 -93
- package/dist/Skeleton-BAF3CKY7.js.map +1 -0
- package/dist/__tests__/components/DataStates.test.d.ts +1 -0
- package/dist/__tests__/components/ExperimentTimeline.test.d.ts +1 -0
- package/dist/__tests__/components/FileBrowserModal.test.d.ts +1 -0
- package/dist/__tests__/components/FilePicker.state.test.d.ts +1 -0
- package/dist/__tests__/components/FilePicker.test.d.ts +1 -0
- package/dist/__tests__/components/PlotlyChart.test.d.ts +1 -0
- package/dist/__tests__/components/iconButtonIconSizing.test.d.ts +1 -0
- package/dist/__tests__/composables/useFileBrowser.test.d.ts +1 -0
- package/dist/__tests__/composables/useListReorder.test.d.ts +1 -0
- package/dist/__tests__/composables/usePlatformFilePickerAdapter.test.d.ts +1 -0
- package/dist/__tests__/styles/cssTokens.test.d.ts +1 -0
- package/dist/apiErrors.d.ts +26 -0
- package/dist/components/ActionMenu.vue.d.ts +1 -1
- package/dist/components/AppPluginSwitcher.vue.d.ts +2 -0
- package/dist/components/AuditTrail.vue.d.ts +13 -0
- package/dist/components/BaseInput.vue.d.ts +1 -1
- package/dist/components/BaseSelect.vue.d.ts +1 -1
- package/dist/components/BaseTextarea.vue.d.ts +1 -1
- package/dist/components/BatchProgressList.vue.d.ts +17 -0
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
- package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +2 -2
- package/dist/components/ConcentrationInput.vue.d.ts +1 -1
- package/dist/components/DataFrame.vue.d.ts +14 -2
- package/dist/components/DatePicker.vue.d.ts +2 -2
- package/dist/components/DateTimePicker.vue.d.ts +3 -1
- package/dist/components/ExperimentDataViewer.vue.d.ts +26 -1
- package/dist/components/ExperimentPopover.vue.d.ts +1 -0
- package/dist/components/ExperimentTimeline.vue.d.ts +29 -1
- package/dist/components/FileBrowserModal.vue.d.ts +99 -0
- package/dist/components/FilePicker.vue.d.ts +32 -0
- package/dist/components/FileUploader.vue.d.ts +5 -2
- package/dist/components/FormulaInput.vue.d.ts +1 -1
- package/dist/components/GeneratedPlotlyResult.vue.d.ts +1 -3
- package/dist/components/LcmsSequenceTable.vue.d.ts +29 -1
- package/dist/components/MobileSupportGate.vue.d.ts +1 -1
- package/dist/components/MoleculeInput.vue.d.ts +1 -1
- package/dist/components/NumberInput.vue.d.ts +1 -1
- package/dist/components/PlotlyChart.vue.d.ts +59 -0
- package/dist/components/ProgressBar.vue.d.ts +1 -1
- package/dist/components/ProtocolStepEditor.vue.d.ts +1 -0
- package/dist/components/ReagentList.vue.d.ts +29 -1
- package/dist/components/ResourceCard.vue.d.ts +1 -1
- package/dist/components/SampleHierarchyTree.vue.d.ts +29 -1
- package/dist/components/SampleSelector.drag.d.ts +13 -0
- package/dist/components/SampleSelector.tree.d.ts +2 -0
- package/dist/components/SampleSelector.vue.d.ts +34 -1
- package/dist/components/ScheduleCalendar.vue.d.ts +20 -0
- package/dist/components/SecretInput.vue.d.ts +1 -1
- package/dist/components/SequenceInput.vue.d.ts +1 -1
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +1 -0
- package/dist/components/StatusIndicator.vue.d.ts +1 -1
- package/dist/components/TagsInput.vue.d.ts +1 -1
- package/dist/components/TimePicker.vue.d.ts +2 -1
- package/dist/components/TimeRangeInput.vue.d.ts +1 -1
- package/dist/components/UnitInput.vue.d.ts +1 -1
- package/dist/components/WellPlate.geometry.d.ts +15 -0
- package/dist/components/WellPlate.interaction.d.ts +13 -0
- package/dist/components/WellPlate.vue.d.ts +30 -1
- package/dist/components/fileBrowserPresentation.d.ts +10 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +8 -8
- package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -0
- package/dist/components/internal/DataStateInternal.vue.d.ts +43 -0
- package/dist/components/internal/FileBrowserDialogInternal.vue.d.ts +34 -0
- package/dist/components/internal/FileBrowserListingInternal.vue.d.ts +25 -0
- package/dist/components/internal/FileBrowserToolbarInternal.vue.d.ts +26 -0
- package/dist/components/internal/FilePickerPreviewInternal.vue.d.ts +18 -0
- package/dist/components/internal/FilePickerTreeInternal.vue.d.ts +31 -0
- package/dist/{components-B80gV0P6.js → components-DIum5hkx.js} +5415 -2753
- package/dist/components-DIum5hkx.js.map +1 -0
- package/dist/composables/controlSchemaTypes.d.ts +34 -0
- package/dist/composables/filePicker/localFiles.d.ts +3 -0
- package/dist/composables/filePicker/usePickerNavigation.d.ts +96 -0
- package/dist/composables/filePicker/usePickerSelection.d.ts +31 -0
- package/dist/composables/filePicker/validation.d.ts +5 -0
- package/dist/composables/index.d.ts +3 -0
- package/dist/composables/index.js +10 -10
- package/dist/composables/useApi.d.ts +14 -7
- package/dist/composables/useDropdownState.d.ts +18 -2
- package/dist/composables/useEventStream.d.ts +9 -0
- package/dist/composables/useFileBrowser.d.ts +55 -0
- package/dist/composables/useFilePicker.d.ts +189 -0
- package/dist/composables/useListReorder.d.ts +90 -0
- package/dist/composables/useMenuKeyboard.d.ts +2 -2
- package/dist/composables/usePlatformFilePickerAdapter.d.ts +11 -0
- package/dist/composables/usePluginConfig.d.ts +3 -1
- package/dist/composables/useRequestSyncState.d.ts +42 -2
- package/dist/composables/useScheduleDrag.d.ts +1 -0
- package/dist/{composables-C8mmD5ta.js → composables-mFXsTA4g.js} +307 -74
- package/dist/composables-mFXsTA4g.js.map +1 -0
- package/dist/{useApi-CDBq6z23.js → errors-BLuqMee5.js} +178 -16
- package/dist/errors-BLuqMee5.js.map +1 -0
- package/dist/{experiment-Y9Iz7KYi.js → experiment-CVDRAnqp.js} +3 -8
- package/dist/experiment-CVDRAnqp.js.map +1 -0
- package/dist/{experiment-utils-DV3DrvE_.js → experiment-utils-CV2Wza3z.js} +1 -1
- package/dist/experiment-utils-CV2Wza3z.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -15
- package/dist/install.js +2 -7
- package/dist/install.js.map +1 -1
- package/dist/permissions.d.ts +42 -1
- package/dist/permissions.js +20 -10
- package/dist/permissions.js.map +1 -1
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.js +3 -3
- package/dist/stores/settings.d.ts +31 -1
- package/dist/styles.css +15162 -32475
- package/dist/templates/index.js +2 -4
- package/dist/{templates-CK72aKO1.js → templates-CmGfmBRA.js} +291 -85
- package/dist/templates-CmGfmBRA.js.map +1 -0
- package/dist/types/fileBrowserTypes.d.ts +77 -0
- package/dist/types/filePicker.d.ts +77 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/{useControlSchema-_OVhavKq.js → useControlSchema-DldpxNOP.js} +234 -1121
- package/dist/useControlSchema-DldpxNOP.js.map +1 -0
- package/dist/{useDropdownState-D5G4UvWd.js → useDropdownState-C7hRiVQf.js} +22 -6
- package/dist/useDropdownState-C7hRiVQf.js.map +1 -0
- package/dist/{useExperimentSelector-DSEckf_g.js → useExperimentSelector-CEcIaueJ.js} +4 -4
- package/dist/{useExperimentSelector-DSEckf_g.js.map → useExperimentSelector-CEcIaueJ.js.map} +1 -1
- package/dist/{useFormBuilder-Bd714C7y.js → useFormBuilder-z5L_dUIy.js} +2 -2
- package/dist/{useFormBuilder-Bd714C7y.js.map → useFormBuilder-z5L_dUIy.js.map} +1 -1
- package/dist/utils/color.d.ts +13 -0
- package/dist/utils/errors.d.ts +9 -0
- package/dist/{useProtocolTemplates-Dq5Pzwyd.js → validation-uwoJGEb6.js} +231 -158
- package/dist/validation-uwoJGEb6.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/apiErrors.test.ts +81 -0
- package/src/__tests__/components/ActionMenu.test.ts +94 -0
- package/src/__tests__/components/DataFrame.test.ts +183 -11
- package/src/__tests__/components/DataStates.test.ts +176 -0
- package/src/__tests__/components/DatePicker.test.ts +76 -0
- package/src/__tests__/components/DateTimePicker.test.ts +36 -0
- package/src/__tests__/components/ExperimentPopover.test.ts +30 -0
- package/src/__tests__/components/ExperimentTimeline.test.ts +63 -0
- package/src/__tests__/components/FileBrowserModal.test.ts +460 -0
- package/src/__tests__/components/FilePicker.state.test.ts +287 -0
- package/src/__tests__/components/FilePicker.test.ts +487 -0
- package/src/__tests__/components/GeneratedPlotlyResult.test.ts +52 -5
- package/src/__tests__/components/GroupAssigner.test.ts +53 -0
- package/src/__tests__/components/LcmsSequenceTable.test.ts +22 -0
- package/src/__tests__/components/PlotlyChart.test.ts +305 -0
- package/src/__tests__/components/RackEditor.test.ts +35 -0
- package/src/__tests__/components/ReagentList.test.ts +64 -2
- package/src/__tests__/components/RetiredStyles.test.ts +31 -0
- package/src/__tests__/components/SampleSelector.drag.test.ts +51 -1
- package/src/__tests__/components/ScheduleCalendar.test.ts +63 -0
- package/src/__tests__/components/SegmentedControl.test.ts +107 -0
- package/src/__tests__/components/TimePicker.test.ts +61 -0
- package/src/__tests__/components/WellPlate.interaction.test.ts +59 -0
- package/src/__tests__/components/iconButtonIconSizing.test.ts +18 -0
- package/src/__tests__/composables/useApi.test.ts +92 -9
- package/src/__tests__/composables/useDropdownState.test.ts +58 -1
- package/src/__tests__/composables/useFileBrowser.test.ts +305 -0
- package/src/__tests__/composables/useGeneratedAnalysis.test.ts +7 -2
- package/src/__tests__/composables/useListReorder.test.ts +145 -0
- package/src/__tests__/composables/usePlatformFilePickerAdapter.test.ts +67 -0
- package/src/__tests__/composables/usePluginClient.test.ts +3 -0
- package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
- package/src/__tests__/stores/settings.test.ts +71 -1
- package/src/__tests__/styles/cssTokens.test.ts +131 -0
- package/src/__tests__/utils/permissions.test.ts +68 -0
- package/src/apiErrors.ts +85 -0
- package/src/components/ActionMenu.vue +45 -88
- package/src/components/AlertBox.vue +0 -4
- package/src/components/AppAvatarMenu.vue +2 -14
- package/src/components/AppLayout.vue +0 -4
- package/src/components/AppPluginSwitcher.story.vue +87 -0
- package/src/components/AppPluginSwitcher.vue +2 -5
- package/src/components/AppSidebar.vue +0 -4
- package/src/components/AppToastContainer.vue +6 -8
- package/src/components/AppTopBar.vue +0 -4
- package/src/components/ArtifactSaveDialog.vue +0 -4
- package/src/components/ArtifactSelectorModal.vue +0 -4
- package/src/components/AuditTrail.story.vue +18 -0
- package/src/components/AuditTrail.vue +27 -7
- package/src/components/AutoGroupModal.vue +0 -4
- package/src/components/Avatar.vue +0 -4
- package/src/components/BaseButton.vue +0 -4
- package/src/components/BaseCheckbox.vue +0 -4
- package/src/components/BaseInput.vue +0 -4
- package/src/components/BaseModal.vue +0 -4
- package/src/components/BasePill.vue +0 -4
- package/src/components/BaseRadioGroup.vue +0 -4
- package/src/components/BaseSelect.vue +0 -4
- package/src/components/BaseSlider.vue +0 -4
- package/src/components/BaseTabs.vue +0 -4
- package/src/components/BaseTextarea.vue +0 -4
- package/src/components/BaseToggle.vue +0 -4
- package/src/components/BatchProgressList.story.vue +18 -0
- package/src/components/BatchProgressList.vue +32 -4
- package/src/components/Breadcrumb.vue +0 -4
- package/src/components/Calendar.vue +0 -4
- package/src/components/ChartContainer.story.vue +1 -1
- package/src/components/ChartContainer.vue +0 -4
- package/src/components/ChemicalFormula.vue +0 -4
- package/src/components/CollapsibleCard.vue +0 -4
- package/src/components/ColorSlider.vue +0 -4
- package/src/components/ConcentrationInput.vue +0 -4
- package/src/components/ConfirmDialog.vue +0 -4
- package/src/components/DataFrame.story.vue +14 -0
- package/src/components/DataFrame.vue +97 -23
- package/src/components/DatePicker.vue +73 -27
- package/src/components/DateTimePicker.vue +45 -17
- package/src/components/Divider.vue +0 -4
- package/src/components/DoseCalculator.vue +0 -4
- package/src/components/DropdownButton.vue +2 -14
- package/src/components/EmptyState.vue +0 -4
- package/src/components/ExperimentCodeBadge.vue +0 -4
- package/src/components/ExperimentDataViewer.story.vue +3 -0
- package/src/components/ExperimentDataViewer.vue +33 -13
- package/src/components/ExperimentPopover.vue +12 -7
- package/src/components/ExperimentSelectorModal.vue +0 -4
- package/src/components/ExperimentTimeline.story.vue +18 -0
- package/src/components/ExperimentTimeline.vue +81 -75
- package/src/components/FileBrowserModal.story.vue +203 -0
- package/src/components/FileBrowserModal.vue +384 -0
- package/src/components/FilePicker.story.vue +44 -0
- package/src/components/FilePicker.vue +284 -0
- package/src/components/FileUploader.vue +32 -18
- package/src/components/FitPanel.vue +0 -4
- package/src/components/FormActions.story.vue +96 -0
- package/src/components/FormActions.vue +0 -4
- package/src/components/FormBuilder.vue +0 -4
- package/src/components/FormField.vue +0 -4
- package/src/components/FormulaInput.vue +0 -4
- package/src/components/GeneratedPathInput.story.vue +68 -0
- package/src/components/GeneratedPlotlyResult.story.vue +96 -0
- package/src/components/GeneratedPlotlyResult.vue +34 -69
- package/src/components/GeneratedPluginView.story.vue +145 -0
- package/src/components/GeneratedPluginView.vue +10 -10
- package/src/components/GeneratedResultRenderer.story.vue +117 -0
- package/src/components/GeneratedResultRenderer.vue +4 -9
- package/src/components/GroupAssigner.vue +67 -5
- package/src/components/IconButton.vue +0 -4
- package/src/components/InstrumentAlertLog.vue +0 -4
- package/src/components/InstrumentStateBadge.vue +0 -4
- package/src/components/InstrumentStatusCard.vue +0 -4
- package/src/components/JobsStatusTray.vue +0 -4
- package/src/components/LcmsSequenceTable.vue +81 -50
- package/src/components/LoadingSpinner.vue +0 -4
- package/src/components/MobileSupportGate.vue +0 -4
- package/src/components/MoleculeInput.vue +0 -4
- package/src/components/MultiSelect.vue +0 -4
- package/src/components/NumberInput.vue +0 -4
- package/src/components/PlateMapEditor.vue +3 -14
- package/src/components/PlotlyChart.story.vue +61 -0
- package/src/components/PlotlyChart.vue +233 -0
- package/src/components/PluginIcon.vue +0 -4
- package/src/components/ProgressBar.vue +0 -4
- package/src/components/ProtocolStepEditor.vue +2 -4
- package/src/components/RackEditor.vue +51 -70
- package/src/components/ReagentEditor.vue +0 -4
- package/src/components/ReagentList.story.vue +18 -0
- package/src/components/ReagentList.vue +127 -82
- package/src/components/ResourceCard.vue +0 -4
- package/src/components/RuntimeUpdateNotice.story.vue +49 -0
- package/src/components/RuntimeUpdateNotice.vue +4 -4
- package/src/components/SampleHierarchyTree.story.vue +18 -0
- package/src/components/SampleHierarchyTree.vue +32 -9
- package/src/components/SampleLegend.vue +2 -8
- package/src/components/SampleSelector.drag.ts +51 -0
- package/src/components/SampleSelector.story.vue +6 -0
- package/src/components/SampleSelector.tree.ts +2 -0
- package/src/components/SampleSelector.vue +49 -10
- package/src/components/SampleSelectorGroupNode.vue +19 -0
- package/src/components/ScheduleCalendar.story.vue +6 -0
- package/src/components/ScheduleCalendar.vue +82 -7
- package/src/components/ScientificNumber.vue +0 -4
- package/src/components/SectionCard.vue +0 -4
- package/src/components/SegmentedControl.vue +50 -7
- package/src/components/SequenceInput.vue +0 -4
- package/src/components/SequenceProgressBar.story.vue +110 -0
- package/src/components/SequenceProgressBar.vue +0 -4
- package/src/components/SettingsModal.vue +0 -4
- package/src/components/Skeleton.vue +0 -4
- package/src/components/SmartGroupFieldRecipe.vue +8 -6
- package/src/components/SmartGroupManual.vue +0 -4
- package/src/components/StatusIndicator.vue +0 -4
- package/src/components/StepWizard.vue +0 -4
- package/src/components/TagsInput.vue +4 -6
- package/src/components/ThemeToggle.vue +0 -4
- package/src/components/TimePicker.vue +36 -7
- package/src/components/TimeRangeInput.vue +0 -4
- package/src/components/Tooltip.story.vue +3 -3
- package/src/components/Tooltip.vue +0 -4
- package/src/components/UnitInput.vue +0 -4
- package/src/components/WellPlate.geometry.ts +60 -1
- package/src/components/WellPlate.interaction.ts +44 -0
- package/src/components/WellPlate.story.vue +8 -0
- package/src/components/WellPlate.vue +53 -7
- package/src/components/fileBrowserPresentation.ts +39 -0
- package/src/components/index.ts +5 -0
- package/src/components/internal/AppTopBarPageSelectorInternal.vue +2 -5
- package/src/components/internal/AppTopBarPillNavInternal.vue +0 -4
- package/src/components/internal/DataStateInternal.vue +74 -0
- package/src/components/internal/FileBrowserDialogInternal.vue +75 -0
- package/src/components/internal/FileBrowserListingInternal.vue +197 -0
- package/src/components/internal/FileBrowserToolbarInternal.vue +136 -0
- package/src/components/internal/FilePickerPreviewInternal.vue +100 -0
- package/src/components/internal/FilePickerTreeInternal.vue +238 -0
- package/src/components/internal/FormSectionRenderer.vue +0 -4
- package/src/components/internal/WellEditPopupInternal.vue +0 -4
- package/src/composables/controlSchemaTypes.ts +34 -0
- package/src/composables/filePicker/localFiles.ts +58 -0
- package/src/composables/filePicker/usePickerNavigation.ts +220 -0
- package/src/composables/filePicker/usePickerSelection.ts +259 -0
- package/src/composables/filePicker/validation.ts +41 -0
- package/src/composables/index.ts +17 -0
- package/src/composables/useApi.ts +57 -13
- package/src/composables/useDropdownState.ts +35 -5
- package/src/composables/useEventStream.ts +14 -2
- package/src/composables/useFileBrowser.ts +314 -0
- package/src/composables/useFileImport.ts +2 -7
- package/src/composables/useFilePicker.ts +366 -0
- package/src/composables/useGeneratedAnalysis.ts +18 -26
- package/src/composables/useListReorder.story.vue +143 -0
- package/src/composables/useListReorder.ts +242 -0
- package/src/composables/useMenuKeyboard.ts +2 -2
- package/src/composables/useOptimisticMutation.ts +1 -15
- package/src/composables/usePlatformFilePickerAdapter.ts +104 -0
- package/src/composables/usePluginClient.ts +3 -0
- package/src/composables/usePluginConfig.ts +3 -1
- package/src/composables/useRackEditor.ts +2 -11
- package/src/composables/useRequestSyncState.ts +98 -28
- package/src/composables/useResourceCrud.ts +1 -15
- package/src/composables/useScheduleDrag.ts +37 -0
- package/src/composables/useWellPainting.ts +5 -29
- package/src/composables/useWellPlateEditor.ts +1 -11
- package/src/index.ts +3 -0
- package/src/permissions.ts +48 -9
- package/src/stores/experiment.ts +2 -7
- package/src/stores/index.ts +9 -1
- package/src/stores/settings.ts +93 -1
- package/src/styles/components/audit-trail.css +5 -5
- package/src/styles/components/batch-progress-list.css +7 -0
- package/src/styles/components/color-slider.css +2 -2
- package/src/styles/components/data-state.css +30 -0
- package/src/styles/components/dataframe.css +29 -35
- package/src/styles/components/experiment-data-viewer.css +4 -4
- package/src/styles/components/experiment-timeline.css +3 -30
- package/src/styles/components/file-browser-modal.css +489 -0
- package/src/styles/components/file-picker.css +502 -0
- package/src/styles/components/form-builder.css +5 -5
- package/src/styles/components/icon-button.css +9 -0
- package/src/styles/components/lcms-sequence-table.css +16 -1
- package/src/styles/components/multi-select.css +1 -1
- package/src/styles/components/reagent-list.css +38 -31
- package/src/styles/components/sample-hierarchy-tree.css +5 -19
- package/src/styles/components/sample-selector.css +9 -2
- package/src/styles/components/schedule-calendar.css +8 -0
- package/src/styles/components/scientific-number.css +1 -1
- package/src/styles/components/well-plate.css +8 -0
- package/src/styles/index.css +11 -0
- package/src/styles/variables.css +29 -0
- package/src/templates/builderPresetControls.ts +1 -1
- package/src/templates/componentPlateHelpers.ts +3 -1
- package/src/templates/doseResponseAdapters.ts +2 -13
- package/src/templates/elisaAssayCollectionBuilder.ts +2 -2
- package/src/templates/plateMapBuilder.ts +2 -2
- package/src/templates/qpcrPlateBuilder.ts +1 -1
- package/src/templates/templateValidators.ts +1 -1
- package/src/templates/timeCourseBuilder.ts +2 -2
- package/src/templates/wellPlateScreenCollectionBuilder.ts +2 -2
- package/src/types/fileBrowserTypes.ts +85 -0
- package/src/types/filePicker.ts +72 -0
- package/src/types/index.ts +14 -0
- package/src/utils/color.ts +51 -0
- package/src/utils/errors.ts +29 -0
- package/dist/EmptyState-NBqf2_8z.js.map +0 -1
- package/dist/ExperimentPopover-CfOdh5Rm.js.map +0 -1
- package/dist/ExperimentSelectorModal-DvIagrxY.js.map +0 -1
- package/dist/SettingsModal-W3TYfnP9.js.map +0 -1
- package/dist/components-B80gV0P6.js.map +0 -1
- package/dist/composables/autoGroupConstants.d.ts +0 -1
- package/dist/composables/autoGroupGrouping.d.ts +0 -3
- package/dist/composables/useAutoGroupInputSources.d.ts +0 -32
- package/dist/composables-C8mmD5ta.js.map +0 -1
- package/dist/experiment-Y9Iz7KYi.js.map +0 -1
- package/dist/experiment-utils-DV3DrvE_.js.map +0 -1
- package/dist/templates/builderDefaults.d.ts +0 -1
- package/dist/templates/plateGeometry.d.ts +0 -4
- package/dist/templates-CK72aKO1.js.map +0 -1
- package/dist/useApi-CDBq6z23.js.map +0 -1
- package/dist/useControlSchema-_OVhavKq.js.map +0 -1
- package/dist/useDropdownState-D5G4UvWd.js.map +0 -1
- package/dist/useProtocolTemplates-Dq5Pzwyd.js.map +0 -1
- package/src/__tests__/composables/useAutoGroupInputSources.test.ts +0 -127
- package/src/composables/autoGroupConstants.ts +0 -4
- package/src/composables/autoGroupGrouping.ts +0 -148
- package/src/composables/useAutoGroupInputSources.ts +0 -147
- package/src/templates/builderDefaults.ts +0 -11
- package/src/templates/plateGeometry.ts +0 -62
- /package/dist/__tests__/{composables/useAutoGroupInputSources.test.d.ts → apiErrors.test.d.ts} +0 -0
|
@@ -13,7 +13,8 @@ import type {
|
|
|
13
13
|
PluginJobCenterSource,
|
|
14
14
|
} from './useJobsStatusTray'
|
|
15
15
|
import { useEventStream, type EventStreamMessage } from './useEventStream'
|
|
16
|
-
import { normalizeJobState } from '../jobs'
|
|
16
|
+
import { isActiveJobStatus, normalizeJobState } from '../jobs'
|
|
17
|
+
import { MintApiError, mintApiErrorFromResponse } from '../apiErrors'
|
|
17
18
|
import type {
|
|
18
19
|
GeneratedAnalysisJob,
|
|
19
20
|
GeneratedAnalysisJobInput,
|
|
@@ -111,7 +112,6 @@ export interface UsePluginJobsReturn extends PluginJobCenterSource {
|
|
|
111
112
|
|
|
112
113
|
export type UseGeneratedAnalysisReturn = UsePluginJobsReturn
|
|
113
114
|
|
|
114
|
-
const ACTIVE_STATUSES = new Set(['awaiting_input', 'queued', 'running'])
|
|
115
115
|
const AUTH_TOKEN_KEY = 'mint-auth-token'
|
|
116
116
|
const SESSION_NOT_FOUND_DETAIL = 'Session not found'
|
|
117
117
|
const MAX_RESULT_CACHE_ENTRIES = 32
|
|
@@ -130,16 +130,6 @@ type PluginJobStreamData = GeneratedAnalysisJobInput | {
|
|
|
130
130
|
jobs: GeneratedAnalysisJobInput[]
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
class GeneratedAnalysisRequestError extends Error {
|
|
134
|
-
constructor(
|
|
135
|
-
readonly status: number,
|
|
136
|
-
readonly detail: unknown,
|
|
137
|
-
) {
|
|
138
|
-
super(typeof detail === 'string' ? detail : `Request failed (${status})`)
|
|
139
|
-
this.name = 'GeneratedAnalysisRequestError'
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
133
|
export function createGeneratedAnalysisTransport(
|
|
144
134
|
options: GeneratedAnalysisTransportOptions = {},
|
|
145
135
|
): GeneratedAnalysisTransport {
|
|
@@ -473,12 +463,12 @@ function createPluginJobs(
|
|
|
473
463
|
.then((payload) => {
|
|
474
464
|
if (disposed) return jobs.value
|
|
475
465
|
if (revision !== jobsRevision) {
|
|
476
|
-
if (jobs.value.some(job =>
|
|
466
|
+
if (jobs.value.some(job => isActiveJobStatus(job.status))) schedulePoll()
|
|
477
467
|
return jobs.value
|
|
478
468
|
}
|
|
479
469
|
replaceJobs(payload.jobs)
|
|
480
470
|
error.value = null
|
|
481
|
-
if (!paused && !jobStream.isConnected.value && jobs.value.some(job =>
|
|
471
|
+
if (!paused && !jobStream.isConnected.value && jobs.value.some(job => isActiveJobStatus(job.status))) {
|
|
482
472
|
schedulePoll()
|
|
483
473
|
}
|
|
484
474
|
return jobs.value
|
|
@@ -530,7 +520,7 @@ function createPluginJobs(
|
|
|
530
520
|
const current = typeof job === 'string'
|
|
531
521
|
? jobs.value.find(candidate => candidate.job_id === jobId)
|
|
532
522
|
: jobs.value.find(candidate => candidate.job_id === jobId) ?? job
|
|
533
|
-
if (current && !
|
|
523
|
+
if (current && !isActiveJobStatus(current.status)) {
|
|
534
524
|
return terminalWaitResult(current, options.rejectOnFailure === true)
|
|
535
525
|
}
|
|
536
526
|
if (paused) return Promise.reject(new Error('Plugin jobs runtime is paused'))
|
|
@@ -567,7 +557,7 @@ function createPluginJobs(
|
|
|
567
557
|
}
|
|
568
558
|
|
|
569
559
|
function settleJobWaiters(job: GeneratedAnalysisJob): void {
|
|
570
|
-
if (
|
|
560
|
+
if (isActiveJobStatus(job.status)) return
|
|
571
561
|
const waiting = jobWaiters.get(job.job_id)
|
|
572
562
|
if (!waiting) return
|
|
573
563
|
jobWaiters.delete(job.job_id)
|
|
@@ -839,7 +829,7 @@ function createPluginJobs(
|
|
|
839
829
|
job: GeneratedAnalysisJob,
|
|
840
830
|
cause: unknown,
|
|
841
831
|
): void {
|
|
842
|
-
if (cause instanceof
|
|
832
|
+
if (cause instanceof MintApiError) {
|
|
843
833
|
if (cause.status === 404 || cause.status === 410) {
|
|
844
834
|
discardCachedResult(job.job_id)
|
|
845
835
|
jobs.value = jobs.value.map(candidate => (
|
|
@@ -915,7 +905,7 @@ function createPluginJobs(
|
|
|
915
905
|
await refreshJobs()
|
|
916
906
|
} catch (cause) {
|
|
917
907
|
error.value = errorMessage(cause, 'Failed to refresh analysis jobs')
|
|
918
|
-
if (jobs.value.some(job =>
|
|
908
|
+
if (jobs.value.some(job => isActiveJobStatus(job.status))) schedulePoll()
|
|
919
909
|
}
|
|
920
910
|
}, pollInterval)
|
|
921
911
|
}
|
|
@@ -927,7 +917,7 @@ function createPluginJobs(
|
|
|
927
917
|
}
|
|
928
918
|
|
|
929
919
|
function schedulePollForActiveJobs(): void {
|
|
930
|
-
if (jobs.value.some(job =>
|
|
920
|
+
if (jobs.value.some(job => isActiveJobStatus(job.status))) schedulePoll()
|
|
931
921
|
}
|
|
932
922
|
|
|
933
923
|
function applyJobStreamMessage(message: EventStreamMessage<PluginJobStreamData>): void {
|
|
@@ -1005,7 +995,7 @@ function createPluginJobs(
|
|
|
1005
995
|
scheduleManagedDownloadRefresh(jobId, managedDownloadRefreshDelay(result))
|
|
1006
996
|
}
|
|
1007
997
|
if (streamEnabled) jobStream.start()
|
|
1008
|
-
else if (jobs.value.some(job =>
|
|
998
|
+
else if (jobs.value.some(job => isActiveJobStatus(job.status))) schedulePoll()
|
|
1009
999
|
}
|
|
1010
1000
|
|
|
1011
1001
|
function dispose(): void {
|
|
@@ -1038,8 +1028,10 @@ function createPluginJobs(
|
|
|
1038
1028
|
},
|
|
1039
1029
|
})
|
|
1040
1030
|
if (!response.ok) {
|
|
1041
|
-
const payload = await response.json().catch(() => null)
|
|
1042
|
-
throw
|
|
1031
|
+
const payload: unknown = await response.json().catch(() => null)
|
|
1032
|
+
throw mintApiErrorFromResponse(response.status, payload, {
|
|
1033
|
+
requestId: response.headers.get('X-Request-ID'),
|
|
1034
|
+
})
|
|
1043
1035
|
}
|
|
1044
1036
|
return await response.json() as T
|
|
1045
1037
|
}
|
|
@@ -1178,20 +1170,20 @@ function createGeneratedSessionStorage(candidate?: Storage): GeneratedSessionSto
|
|
|
1178
1170
|
}
|
|
1179
1171
|
|
|
1180
1172
|
function isSessionNotFoundError(error: unknown): boolean {
|
|
1181
|
-
return error instanceof
|
|
1173
|
+
return error instanceof MintApiError
|
|
1182
1174
|
&& error.status === 404
|
|
1183
|
-
&& error.detail === SESSION_NOT_FOUND_DETAIL
|
|
1175
|
+
&& (error.detail === SESSION_NOT_FOUND_DETAIL || error.message === SESSION_NOT_FOUND_DETAIL)
|
|
1184
1176
|
}
|
|
1185
1177
|
|
|
1186
1178
|
function isRetryableResultError(error: unknown): boolean {
|
|
1187
|
-
return !(error instanceof
|
|
1179
|
+
return !(error instanceof MintApiError)
|
|
1188
1180
|
|| error.status === 409
|
|
1189
1181
|
|| error.status === 429
|
|
1190
1182
|
|| error.status >= 500
|
|
1191
1183
|
}
|
|
1192
1184
|
|
|
1193
1185
|
function isRetryableJobLookupError(error: unknown): boolean {
|
|
1194
|
-
return !(error instanceof
|
|
1186
|
+
return !(error instanceof MintApiError)
|
|
1195
1187
|
|| error.status === 408
|
|
1196
1188
|
|| error.status === 429
|
|
1197
1189
|
|| error.status >= 500
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Demonstrates `useListReorder`: drag a row, or focus a handle and press Alt with the arrow keys. */
|
|
3
|
+
import { ref } from 'vue'
|
|
4
|
+
import { useListReorder } from './useListReorder'
|
|
5
|
+
|
|
6
|
+
interface Step { id: string; name: string }
|
|
7
|
+
|
|
8
|
+
const steps = ref<Step[]>([
|
|
9
|
+
{ id: 's1', name: 'Thaw samples' },
|
|
10
|
+
{ id: 's2', name: 'Quench metabolism' },
|
|
11
|
+
{ id: 's3', name: 'Extract metabolites' },
|
|
12
|
+
{ id: 's4', name: 'Dry down' },
|
|
13
|
+
])
|
|
14
|
+
|
|
15
|
+
const handles = new Map<string, HTMLElement>()
|
|
16
|
+
|
|
17
|
+
function setHandle(id: string, el: Element | null) {
|
|
18
|
+
if (el instanceof HTMLElement) handles.set(id, el)
|
|
19
|
+
else handles.delete(id)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
draggingId,
|
|
24
|
+
dragOverId,
|
|
25
|
+
announcement,
|
|
26
|
+
announcerStyle,
|
|
27
|
+
onDragStart,
|
|
28
|
+
onDragOver,
|
|
29
|
+
onDragLeave,
|
|
30
|
+
onDrop,
|
|
31
|
+
onDragEnd,
|
|
32
|
+
onKeydown,
|
|
33
|
+
} = useListReorder<Step>({
|
|
34
|
+
items: () => steps.value,
|
|
35
|
+
getId: (step) => step.id,
|
|
36
|
+
getLabel: (step) => step.name,
|
|
37
|
+
getElement: (id) => handles.get(id),
|
|
38
|
+
itemNoun: 'Step',
|
|
39
|
+
onReorder: ({ items }) => {
|
|
40
|
+
steps.value = items
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
function initState() {
|
|
45
|
+
return { showAnnouncement: true }
|
|
46
|
+
}
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<template>
|
|
50
|
+
<Story title="Composables/useListReorder">
|
|
51
|
+
<Variant title="Playground" :init-state="initState">
|
|
52
|
+
<template #default="{ state }">
|
|
53
|
+
<div style="padding: 2rem; max-width: 600px;">
|
|
54
|
+
<p class="mint-story-hint">
|
|
55
|
+
Drag a row, or focus its handle and press Alt with the up/down arrow keys.
|
|
56
|
+
</p>
|
|
57
|
+
<ul class="mint-story-list">
|
|
58
|
+
<li
|
|
59
|
+
v-for="(step, index) in steps"
|
|
60
|
+
:key="step.id"
|
|
61
|
+
class="mint-story-row"
|
|
62
|
+
:class="{
|
|
63
|
+
'mint-story-row--dragging': draggingId === step.id,
|
|
64
|
+
'mint-story-row--over': dragOverId === step.id,
|
|
65
|
+
}"
|
|
66
|
+
draggable="true"
|
|
67
|
+
@dragstart="onDragStart($event, step, index)"
|
|
68
|
+
@dragover="onDragOver($event, step, index)"
|
|
69
|
+
@dragleave="onDragLeave"
|
|
70
|
+
@drop="onDrop($event, step, index)"
|
|
71
|
+
@dragend="onDragEnd"
|
|
72
|
+
>
|
|
73
|
+
<button
|
|
74
|
+
:ref="(el) => setHandle(step.id, el as Element | null)"
|
|
75
|
+
type="button"
|
|
76
|
+
class="mint-story-handle"
|
|
77
|
+
:aria-label="`Reorder ${step.name}. Press Alt with the arrow keys to move it.`"
|
|
78
|
+
@keydown="onKeydown($event, step, index)"
|
|
79
|
+
>
|
|
80
|
+
⠿
|
|
81
|
+
</button>
|
|
82
|
+
<span>{{ index + 1 }}. {{ step.name }}</span>
|
|
83
|
+
</li>
|
|
84
|
+
</ul>
|
|
85
|
+
|
|
86
|
+
<p v-if="state.showAnnouncement" class="mint-story-hint">
|
|
87
|
+
Live region: <em>{{ announcement || '—' }}</em>
|
|
88
|
+
</p>
|
|
89
|
+
<div role="status" aria-live="polite" :style="announcerStyle">{{ announcement }}</div>
|
|
90
|
+
</div>
|
|
91
|
+
</template>
|
|
92
|
+
|
|
93
|
+
<template #controls="{ state }">
|
|
94
|
+
<HstCheckbox v-model="state.showAnnouncement" title="Show live-region text" />
|
|
95
|
+
</template>
|
|
96
|
+
</Variant>
|
|
97
|
+
</Story>
|
|
98
|
+
</template>
|
|
99
|
+
|
|
100
|
+
<style scoped>
|
|
101
|
+
.mint-story-list {
|
|
102
|
+
list-style: none;
|
|
103
|
+
margin: 0;
|
|
104
|
+
padding: 0;
|
|
105
|
+
display: flex;
|
|
106
|
+
flex-direction: column;
|
|
107
|
+
gap: 0.25rem;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.mint-story-row {
|
|
111
|
+
display: flex;
|
|
112
|
+
align-items: center;
|
|
113
|
+
gap: 0.5rem;
|
|
114
|
+
padding: 0.5rem 0.75rem;
|
|
115
|
+
border: 1px solid var(--border-color);
|
|
116
|
+
border-radius: 0.5rem;
|
|
117
|
+
background: var(--bg-primary);
|
|
118
|
+
color: var(--text-primary);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.mint-story-row--dragging {
|
|
122
|
+
opacity: 0.5;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.mint-story-row--over {
|
|
126
|
+
border-color: var(--color-primary);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.mint-story-handle {
|
|
130
|
+
padding: 0;
|
|
131
|
+
border: 0;
|
|
132
|
+
background: none;
|
|
133
|
+
color: var(--text-muted);
|
|
134
|
+
cursor: grab;
|
|
135
|
+
font-size: 1rem;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.mint-story-hint {
|
|
139
|
+
margin: 0 0 0.75rem;
|
|
140
|
+
color: var(--text-secondary);
|
|
141
|
+
font-size: 0.8125rem;
|
|
142
|
+
}
|
|
143
|
+
</style>
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { nextTick, ref, type Ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
/** Inline style for a visually hidden `aria-live` region (no global utility class in the SDK). */
|
|
4
|
+
export const REORDER_ANNOUNCER_STYLE = Object.freeze({
|
|
5
|
+
position: 'absolute',
|
|
6
|
+
width: '1px',
|
|
7
|
+
height: '1px',
|
|
8
|
+
padding: '0',
|
|
9
|
+
margin: '-1px',
|
|
10
|
+
overflow: 'hidden',
|
|
11
|
+
clip: 'rect(0, 0, 0, 0)',
|
|
12
|
+
whiteSpace: 'nowrap',
|
|
13
|
+
border: '0',
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
export interface UseReorderAnnouncerReturn {
|
|
17
|
+
/** Bind to a polite live region's text content. */
|
|
18
|
+
announcement: Ref<string>
|
|
19
|
+
/** Publishes a message to the live region. */
|
|
20
|
+
announce: (message: string) => void
|
|
21
|
+
/** Bind to the live region's `style` so it stays visually hidden. */
|
|
22
|
+
announcerStyle: typeof REORDER_ANNOUNCER_STYLE
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Screen-reader announcements for drag/keyboard move interactions. Bind
|
|
27
|
+
* `announcement` to a `role="status" aria-live="polite"` element styled with
|
|
28
|
+
* `announcerStyle`.
|
|
29
|
+
*/
|
|
30
|
+
export function useReorderAnnouncer(): UseReorderAnnouncerReturn {
|
|
31
|
+
const announcement = ref('')
|
|
32
|
+
|
|
33
|
+
function announce(message: string) {
|
|
34
|
+
// Re-announce identical consecutive messages by clearing first.
|
|
35
|
+
if (announcement.value === message) announcement.value = ''
|
|
36
|
+
void nextTick(() => {
|
|
37
|
+
announcement.value = message
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { announcement, announce, announcerStyle: REORDER_ANNOUNCER_STYLE }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ListReorderMove<T> {
|
|
45
|
+
/** Index the item moved from, within `items()`. */
|
|
46
|
+
fromIndex: number
|
|
47
|
+
/** Index the item moved to, within `items()`. */
|
|
48
|
+
toIndex: number
|
|
49
|
+
/** The reordered array (a copy — `items()` is never mutated). */
|
|
50
|
+
items: T[]
|
|
51
|
+
/** Whether the move came from the keyboard or a pointer drag. */
|
|
52
|
+
source: 'keyboard' | 'drag'
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface UseListReorderOptions<T> {
|
|
56
|
+
/** The list being reordered, in its canonical (not merely rendered) order. */
|
|
57
|
+
items: () => T[]
|
|
58
|
+
/** Stable id for an item; falls back to the render index when ids repeat. */
|
|
59
|
+
getId: (item: T, index: number) => string
|
|
60
|
+
/** Applies the move. The composable never mutates the source array. */
|
|
61
|
+
onReorder: (move: ListReorderMove<T>) => void
|
|
62
|
+
/** Human-readable name used in announcements. Defaults to the item's id. */
|
|
63
|
+
getLabel?: (item: T, index: number) => string
|
|
64
|
+
/** Blocks every reorder while it returns `true` (readonly / non-sortable lists). */
|
|
65
|
+
disabled?: () => boolean
|
|
66
|
+
/** Resolves the focusable element for an id so keyboard moves keep focus on the item. */
|
|
67
|
+
getElement?: (id: string) => HTMLElement | null | undefined
|
|
68
|
+
/** Noun used in announcements, e.g. `'Reagent'`. Default `'Item'`. */
|
|
69
|
+
itemNoun?: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface UseListReorderReturn<T> {
|
|
73
|
+
/** Id of the item currently being dragged, or `null`. */
|
|
74
|
+
draggingId: Ref<string | null>
|
|
75
|
+
/** Id of the item the pointer is hovering as a drop target, or `null`. */
|
|
76
|
+
dragOverId: Ref<string | null>
|
|
77
|
+
isDragging: (id: string) => boolean
|
|
78
|
+
isDragOver: (id: string) => boolean
|
|
79
|
+
announcement: Ref<string>
|
|
80
|
+
announcerStyle: typeof REORDER_ANNOUNCER_STYLE
|
|
81
|
+
announce: (message: string) => void
|
|
82
|
+
/** Bind to the draggable element's `@dragstart`. */
|
|
83
|
+
onDragStart: (event: DragEvent, item: T, index: number) => void
|
|
84
|
+
/** Bind to `@dragover` — calls `preventDefault()` so the drop is accepted. */
|
|
85
|
+
onDragOver: (event: DragEvent, item: T, index: number) => void
|
|
86
|
+
/** Bind to `@dragleave`. */
|
|
87
|
+
onDragLeave: () => void
|
|
88
|
+
/** Bind to `@drop`. */
|
|
89
|
+
onDrop: (event: DragEvent, item: T, index: number) => void
|
|
90
|
+
/** Bind to `@dragend`. */
|
|
91
|
+
onDragEnd: () => void
|
|
92
|
+
/**
|
|
93
|
+
* Bind to the item's (or its drag handle's) `@keydown`: Alt+ArrowUp / Alt+ArrowLeft
|
|
94
|
+
* moves the item one position earlier, Alt+ArrowDown / Alt+ArrowRight one later.
|
|
95
|
+
*/
|
|
96
|
+
onKeydown: (event: KeyboardEvent, item: T, index: number) => void
|
|
97
|
+
/** Moves an item between two indices of `items()` programmatically. */
|
|
98
|
+
move: (fromIndex: number, toIndex: number, source?: 'keyboard' | 'drag') => void
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Pure helper: a copy of `items` with the entry at `from` moved to `to`. */
|
|
102
|
+
export function reorderList<T>(items: T[], from: number, to: number): T[] {
|
|
103
|
+
const next = [...items]
|
|
104
|
+
const [moved] = next.splice(from, 1)
|
|
105
|
+
next.splice(to, 0, moved)
|
|
106
|
+
return next
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Keyboard-accessible list reordering shared by the SDK's drag-to-reorder lists.
|
|
111
|
+
* It owns the HTML5 drag state, the drop-index maths, and an Alt+Arrow keyboard
|
|
112
|
+
* alternative that announces every move through a polite live region — so a
|
|
113
|
+
* reorder never depends on a pointer.
|
|
114
|
+
*/
|
|
115
|
+
export function useListReorder<T>(options: UseListReorderOptions<T>): UseListReorderReturn<T> {
|
|
116
|
+
const draggingId = ref<string | null>(null)
|
|
117
|
+
const dragOverId = ref<string | null>(null)
|
|
118
|
+
const { announcement, announce, announcerStyle } = useReorderAnnouncer()
|
|
119
|
+
const noun = options.itemNoun ?? 'Item'
|
|
120
|
+
|
|
121
|
+
function isDisabled(): boolean {
|
|
122
|
+
return options.disabled?.() === true
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function idFor(item: T, index: number): string {
|
|
126
|
+
return options.getId(item, index)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Index within `items()`, resolved by id so filtered/sorted renders still map back. */
|
|
130
|
+
function indexOf(item: T, renderIndex: number): number {
|
|
131
|
+
const id = idFor(item, renderIndex)
|
|
132
|
+
const found = options.items().findIndex((candidate, index) => idFor(candidate, index) === id)
|
|
133
|
+
return found === -1 ? renderIndex : found
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function labelFor(index: number): string {
|
|
137
|
+
const item = options.items()[index]
|
|
138
|
+
if (item === undefined) return noun
|
|
139
|
+
return options.getLabel?.(item, index) ?? idFor(item, index)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function clearDragState() {
|
|
143
|
+
draggingId.value = null
|
|
144
|
+
dragOverId.value = null
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function move(fromIndex: number, toIndex: number, source: 'keyboard' | 'drag' = 'keyboard') {
|
|
148
|
+
if (isDisabled()) return
|
|
149
|
+
const items = options.items()
|
|
150
|
+
if (
|
|
151
|
+
fromIndex === toIndex ||
|
|
152
|
+
fromIndex < 0 ||
|
|
153
|
+
toIndex < 0 ||
|
|
154
|
+
fromIndex >= items.length ||
|
|
155
|
+
toIndex >= items.length
|
|
156
|
+
) return
|
|
157
|
+
|
|
158
|
+
const movedId = idFor(items[fromIndex], fromIndex)
|
|
159
|
+
const label = labelFor(fromIndex)
|
|
160
|
+
options.onReorder({
|
|
161
|
+
fromIndex,
|
|
162
|
+
toIndex,
|
|
163
|
+
items: reorderList(items, fromIndex, toIndex),
|
|
164
|
+
source,
|
|
165
|
+
})
|
|
166
|
+
announce(`${label} moved to position ${toIndex + 1} of ${items.length}.`)
|
|
167
|
+
void nextTick(() => options.getElement?.(movedId)?.focus())
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function onDragStart(event: DragEvent, item: T, index: number) {
|
|
171
|
+
if (isDisabled()) return
|
|
172
|
+
const id = idFor(item, index)
|
|
173
|
+
draggingId.value = id
|
|
174
|
+
if (event.dataTransfer) {
|
|
175
|
+
event.dataTransfer.effectAllowed = 'move'
|
|
176
|
+
event.dataTransfer.setData('text/plain', id)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function onDragOver(event: DragEvent, item: T, index: number) {
|
|
181
|
+
if (isDisabled() || draggingId.value === null) return
|
|
182
|
+
event.preventDefault()
|
|
183
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'
|
|
184
|
+
const id = idFor(item, index)
|
|
185
|
+
dragOverId.value = id === draggingId.value ? null : id
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function onDragLeave() {
|
|
189
|
+
dragOverId.value = null
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function onDrop(event: DragEvent, item: T, index: number) {
|
|
193
|
+
if (isDisabled() || draggingId.value === null) {
|
|
194
|
+
clearDragState()
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
event.preventDefault()
|
|
198
|
+
|
|
199
|
+
const items = options.items()
|
|
200
|
+
const fromIndex = items.findIndex((candidate, i) => idFor(candidate, i) === draggingId.value)
|
|
201
|
+
const toIndex = indexOf(item, index)
|
|
202
|
+
clearDragState()
|
|
203
|
+
if (fromIndex === -1) return
|
|
204
|
+
move(fromIndex, toIndex, 'drag')
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function onDragEnd() {
|
|
208
|
+
clearDragState()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function onKeydown(event: KeyboardEvent, item: T, index: number) {
|
|
212
|
+
if (isDisabled() || !event.altKey) return
|
|
213
|
+
const direction =
|
|
214
|
+
event.key === 'ArrowUp' || event.key === 'ArrowLeft'
|
|
215
|
+
? -1
|
|
216
|
+
: event.key === 'ArrowDown' || event.key === 'ArrowRight'
|
|
217
|
+
? 1
|
|
218
|
+
: 0
|
|
219
|
+
if (direction === 0) return
|
|
220
|
+
|
|
221
|
+
event.preventDefault()
|
|
222
|
+
const fromIndex = indexOf(item, index)
|
|
223
|
+
move(fromIndex, fromIndex + direction, 'keyboard')
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
draggingId,
|
|
228
|
+
dragOverId,
|
|
229
|
+
isDragging: (id: string) => draggingId.value === id,
|
|
230
|
+
isDragOver: (id: string) => dragOverId.value === id,
|
|
231
|
+
announcement,
|
|
232
|
+
announcerStyle,
|
|
233
|
+
announce,
|
|
234
|
+
onDragStart,
|
|
235
|
+
onDragOver,
|
|
236
|
+
onDragLeave,
|
|
237
|
+
onDrop,
|
|
238
|
+
onDragEnd,
|
|
239
|
+
onKeydown,
|
|
240
|
+
move,
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -27,8 +27,8 @@ export interface UseMenuKeyboardReturn {
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Shared keyboard contract for menu- and listbox-style disclosure panels (the
|
|
30
|
-
* WAI-ARIA menu button pattern). Used by AppAvatarMenu and DropdownButton
|
|
31
|
-
*
|
|
30
|
+
* WAI-ARIA menu button pattern). Used by ActionMenu, AppAvatarMenu and DropdownButton,
|
|
31
|
+
* each pairing it with `useDropdownState` for the disclosure state itself.
|
|
32
32
|
*/
|
|
33
33
|
export function useMenuKeyboard(options: UseMenuKeyboardOptions): UseMenuKeyboardReturn {
|
|
34
34
|
function focusItem(position: 'first' | 'last') {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { computed, ref, shallowRef, type ComputedRef, type Ref, type ShallowRef } from 'vue'
|
|
2
|
+
import { readErrorMessage } from '../utils/errors'
|
|
2
3
|
|
|
3
4
|
export interface OptimisticMutationContext<TVariables, TSnapshot> {
|
|
4
5
|
variables: TVariables
|
|
@@ -94,18 +95,3 @@ export function useOptimisticMutation<TVariables, TData, TSnapshot = unknown>(
|
|
|
94
95
|
reset,
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
|
-
|
|
98
|
-
function readErrorMessage(value: unknown): string {
|
|
99
|
-
if (value instanceof Error && value.message) return value.message
|
|
100
|
-
if (typeof value === 'string' && value.trim()) return value
|
|
101
|
-
if (
|
|
102
|
-
typeof value === 'object'
|
|
103
|
-
&& value !== null
|
|
104
|
-
&& 'message' in value
|
|
105
|
-
&& typeof (value as { message?: unknown }).message === 'string'
|
|
106
|
-
&& (value as { message: string }).message.trim()
|
|
107
|
-
) {
|
|
108
|
-
return (value as { message: string }).message
|
|
109
|
-
}
|
|
110
|
-
return 'Request failed.'
|
|
111
|
-
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { PickerAdapter, PickerNode, PickerRequest } from '../types/filePicker'
|
|
2
|
+
import type { FileDirectoryListing, ServerMount } from '../types/fileBrowserTypes'
|
|
3
|
+
import { useApi } from './useApi'
|
|
4
|
+
import { getInjectedPlatformContext } from './platformContextHelpers'
|
|
5
|
+
import { pickerFormat } from './filePicker/validation'
|
|
6
|
+
|
|
7
|
+
/** Opaque picker identity; decode before passing a selection to a mount-scoped API. */
|
|
8
|
+
export function encodePlatformPickerPath(mountId: string, path = ''): string {
|
|
9
|
+
return JSON.stringify([mountId, path])
|
|
10
|
+
}
|
|
11
|
+
export function decodePlatformPickerPath(path: string): { mountId: string; path: string } {
|
|
12
|
+
const decoded: unknown = JSON.parse(path)
|
|
13
|
+
if (
|
|
14
|
+
!Array.isArray(decoded) ||
|
|
15
|
+
decoded.length !== 2 ||
|
|
16
|
+
!decoded.every((item) => typeof item === 'string') ||
|
|
17
|
+
!decoded[0]
|
|
18
|
+
) {
|
|
19
|
+
throw new Error('Invalid platform picker path.')
|
|
20
|
+
}
|
|
21
|
+
return { mountId: decoded[0], path: decoded[1] }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Adapt the authenticated MINT mount API without exposing absolute host paths. */
|
|
25
|
+
export function usePlatformFilePickerAdapter(options: { apiBaseUrl?: string } = {}): PickerAdapter {
|
|
26
|
+
const api = useApi({
|
|
27
|
+
baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl,
|
|
28
|
+
})
|
|
29
|
+
const mountLabels = new Map<string, string>()
|
|
30
|
+
async function listRoot(request?: PickerRequest): Promise<PickerNode[]> {
|
|
31
|
+
try {
|
|
32
|
+
const { mounts } = await api.get<{ mounts: ServerMount[] }>('/filesystem/mounts', {
|
|
33
|
+
signal: request?.signal,
|
|
34
|
+
})
|
|
35
|
+
return mounts.map((mount) => {
|
|
36
|
+
mountLabels.set(mount.id, mount.label)
|
|
37
|
+
return {
|
|
38
|
+
kind: 'folder',
|
|
39
|
+
path: encodePlatformPickerPath(mount.id),
|
|
40
|
+
name: mount.label,
|
|
41
|
+
parentPath: null,
|
|
42
|
+
parentName: 'Server mounts',
|
|
43
|
+
unavailableReason: mount.available === false ? 'This mount is offline.' : undefined,
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
} catch (cause) {
|
|
47
|
+
if ((cause as { response?: { status?: number } }).response?.status === 404) return []
|
|
48
|
+
throw cause
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function listChildren(path: string, request?: PickerRequest): Promise<PickerNode[]> {
|
|
52
|
+
const location = decodePlatformPickerPath(path)
|
|
53
|
+
const query = new URLSearchParams({ mount_id: location.mountId, path: location.path })
|
|
54
|
+
const listing = await api.get<FileDirectoryListing>(`/filesystem/browse?${query}`, {
|
|
55
|
+
signal: request?.signal,
|
|
56
|
+
})
|
|
57
|
+
if (listing.truncated)
|
|
58
|
+
throw new Error(
|
|
59
|
+
'This directory exceeds the server listing limit. Choose a smaller data directory.',
|
|
60
|
+
)
|
|
61
|
+
return (listing.entries ?? []).map(
|
|
62
|
+
(entry) =>
|
|
63
|
+
({
|
|
64
|
+
kind: entry.kind === 'directory' ? 'folder' : 'file',
|
|
65
|
+
path: encodePlatformPickerPath(location.mountId, entry.path),
|
|
66
|
+
parentPath: path,
|
|
67
|
+
parentName: `${mountLabels.get(location.mountId) ?? location.mountId}${location.path ? ` / ${location.path}` : ''}`,
|
|
68
|
+
name: entry.name,
|
|
69
|
+
bytes: entry.size_bytes,
|
|
70
|
+
...(entry.kind === 'directory'
|
|
71
|
+
? { hasChildren: entry.child_count !== 0 }
|
|
72
|
+
: { format: pickerFormat(entry.name) }),
|
|
73
|
+
}) as PickerNode,
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
listRoot,
|
|
78
|
+
listChildren,
|
|
79
|
+
async search(query, scope, request) {
|
|
80
|
+
const matches: PickerNode[] = []
|
|
81
|
+
const queue = [scope.path]
|
|
82
|
+
const visited = new Set<string>()
|
|
83
|
+
while (queue.length) {
|
|
84
|
+
request?.signal?.throwIfAborted()
|
|
85
|
+
const path = queue.shift()!
|
|
86
|
+
if (visited.has(path)) continue
|
|
87
|
+
visited.add(path)
|
|
88
|
+
if (visited.size > 500)
|
|
89
|
+
throw new Error(
|
|
90
|
+
'Search reached its folder limit. Search a smaller location or turn off Subfolders.',
|
|
91
|
+
)
|
|
92
|
+
const nodes = path ? await listChildren(path, request) : await listRoot(request)
|
|
93
|
+
for (const node of nodes) {
|
|
94
|
+
if (node.name.toLowerCase().includes(query.toLowerCase())) matches.push(node)
|
|
95
|
+
if (scope.recursive && node.kind === 'folder' && !node.unavailableReason)
|
|
96
|
+
queue.push(node.path)
|
|
97
|
+
}
|
|
98
|
+
if (matches.length > 10000)
|
|
99
|
+
throw new Error('Too many search results. Use a more specific search.')
|
|
100
|
+
}
|
|
101
|
+
return matches
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -423,6 +423,7 @@ export function createPluginClient<TClient extends object = GeneratedPluginClien
|
|
|
423
423
|
const api = useApi({
|
|
424
424
|
baseUrl: parts.baseUrl,
|
|
425
425
|
withAuth: endpoint.requiresAuth !== false,
|
|
426
|
+
typedErrors: true,
|
|
426
427
|
})
|
|
427
428
|
const config = endpointRequestConfig(endpoint, parts.query, callOptions)
|
|
428
429
|
|
|
@@ -469,6 +470,7 @@ export async function uploadPluginEndpoint<TResponse = unknown>(
|
|
|
469
470
|
const api = useApi({
|
|
470
471
|
baseUrl: parts.baseUrl,
|
|
471
472
|
withAuth: endpoint.requiresAuth !== false,
|
|
473
|
+
typedErrors: true,
|
|
472
474
|
})
|
|
473
475
|
const body = endpoint.hasBody
|
|
474
476
|
? requestBodyFromPayload(payload, parts.requestPayload, endpoint)
|
|
@@ -507,6 +509,7 @@ export async function downloadPluginEndpoint(
|
|
|
507
509
|
const api = useApi({
|
|
508
510
|
baseUrl: parts.baseUrl,
|
|
509
511
|
withAuth: endpoint.requiresAuth !== false,
|
|
512
|
+
typedErrors: true,
|
|
510
513
|
})
|
|
511
514
|
const config = {
|
|
512
515
|
...(endpointRequestConfig(endpoint, parts.query, { signal: options.signal }) ?? {}),
|