@morscherlab/mint-sdk 1.2.0-beta.2 → 1.2.0-beta.20
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 +65 -5
- package/dist/{ExperimentPopover-tLwJ7XF4.js → ExperimentPopover-CJdNlCZb.js} +24 -15
- package/dist/ExperimentPopover-CJdNlCZb.js.map +1 -0
- package/dist/{ExperimentSelectorModal-DyPAUCLw.js → ExperimentSelectorModal-DCXmwKNS.js} +5 -5
- package/dist/ExperimentSelectorModal-DCXmwKNS.js.map +1 -0
- package/dist/{SettingsModal-BgKY93zh.js → SettingsModal-BYR20I4c.js} +5 -8
- package/dist/SettingsModal-BYR20I4c.js.map +1 -0
- package/dist/{EmptyState-p90quq8Z.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/PluginWorkspaceView.internal.test.d.ts +1 -0
- package/dist/__tests__/components/SecretInput.test.d.ts +1 -0
- package/dist/__tests__/components/WellEditPopupInternal.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__/composables/usePluginConfig.test.d.ts +15 -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/AppAvatarMenu.vue.d.ts +4 -0
- package/dist/components/AppPluginSwitcher.vue.d.ts +2 -0
- package/dist/components/AppSidebar.vue.d.ts +1 -1
- package/dist/components/AuditTrail.vue.d.ts +13 -0
- package/dist/components/AutoGroupModal.vue.d.ts +1 -1
- 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 +4 -4
- 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/DropdownButton.vue.d.ts +2 -0
- 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/FitPanel.vue.d.ts +1 -1
- package/dist/components/FormulaInput.vue.d.ts +1 -1
- package/dist/components/GeneratedPlotlyResult.vue.d.ts +1 -3
- package/dist/components/JobsStatusTray.vue.d.ts +9 -2
- 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/PluginWorkspaceView.props.d.ts +29 -1
- package/dist/components/PluginWorkspaceView.vue.d.ts +5 -5
- 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/SampleLegend.vue.d.ts +1 -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 +35 -2
- package/dist/components/ScheduleCalendar.vue.d.ts +20 -0
- package/dist/components/SecretInput.vue.d.ts +28 -0
- package/dist/components/SequenceInput.vue.d.ts +1 -1
- package/dist/components/SettingsModal.vue.d.ts +1 -1
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -1
- 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 +13 -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/internal/WellEditPopupInternal.vue.d.ts +3 -1
- package/dist/components/internal/WorkspaceControlsInternal.vue.d.ts +23 -0
- package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +70 -0
- package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +43 -0
- package/dist/{components-DkHddRMl.js → components-DIum5hkx.js} +5882 -3200
- 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/flattenWorkspaceBindings.d.ts +61 -0
- package/dist/composables/index.d.ts +11 -2
- package/dist/composables/index.js +10 -10
- package/dist/composables/pluginWorkspaceModel.d.ts +164 -0
- package/dist/composables/useApi.d.ts +14 -7
- package/dist/composables/useAsync.d.ts +5 -1
- package/dist/composables/useBioTemplateWorkspace.d.ts +4 -20
- package/dist/composables/useCommandHistory.d.ts +5 -1
- 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/useFocusTrap.d.ts +21 -0
- package/dist/composables/useGeneratedAnalysis.d.ts +3 -1
- package/dist/composables/useJobsStatusTray.d.ts +25 -1
- package/dist/composables/useListReorder.d.ts +90 -0
- package/dist/composables/useMenuKeyboard.d.ts +29 -0
- package/dist/composables/useOptimisticMutation.d.ts +5 -1
- package/dist/composables/usePlatformFilePickerAdapter.d.ts +11 -0
- package/dist/composables/usePluginClient.d.ts +2 -0
- package/dist/composables/usePluginConfig.d.ts +27 -0
- package/dist/composables/usePluginWorkspace.d.ts +51 -0
- package/dist/composables/useRequestSyncState.d.ts +42 -2
- package/dist/composables/useResourceCrud.d.ts +5 -1
- package/dist/composables/useRovingFocus.d.ts +24 -0
- package/dist/composables/useScheduleDrag.d.ts +1 -0
- package/dist/composables/useWellPainting.d.ts +5 -1
- package/dist/composables/useWellPlateAdapter.d.ts +5 -1
- package/dist/composables/useWellPlateValidation.d.ts +5 -1
- package/dist/composables/workspaceRecipes.d.ts +188 -0
- package/dist/{composables-C_i90sGS.js → composables-mFXsTA4g.js} +502 -86
- package/dist/composables-mFXsTA4g.js.map +1 -0
- package/dist/{useApi-J6zFkHmn.js → errors-BLuqMee5.js} +179 -16
- package/dist/errors-BLuqMee5.js.map +1 -0
- package/dist/{experiment-Cq145oEW.js → experiment-CVDRAnqp.js} +3 -8
- package/dist/experiment-CVDRAnqp.js.map +1 -0
- package/dist/{experiment-utils-CLnzPPq5.js → experiment-utils-CV2Wza3z.js} +70 -51
- package/dist/experiment-utils-CV2Wza3z.js.map +1 -0
- package/dist/index.d.ts +2 -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/auth.d.ts +6 -0
- 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 +15135 -32397
- package/dist/templates/builderReadUtils.d.ts +1 -1
- package/dist/templates/index.js +2 -4
- package/dist/{templates-DsP51i-S.js → templates-CmGfmBRA.js} +303 -106
- package/dist/templates-CmGfmBRA.js.map +1 -0
- package/dist/types/auth.d.ts +1 -0
- package/dist/types/fileBrowserTypes.d.ts +77 -0
- package/dist/types/filePicker.d.ts +77 -0
- package/dist/types/form-builder.d.ts +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/{useControlSchema-BDx2Olha.js → useControlSchema-DldpxNOP.js} +499 -1162
- package/dist/useControlSchema-DldpxNOP.js.map +1 -0
- package/dist/{useDropdownState-DSb3BC3N.js → useDropdownState-C7hRiVQf.js} +22 -6
- package/dist/useDropdownState-C7hRiVQf.js.map +1 -0
- package/dist/{useExperimentSelector-CV4iNfMS.js → useExperimentSelector-CEcIaueJ.js} +4 -4
- package/dist/{useExperimentSelector-CV4iNfMS.js.map → useExperimentSelector-CEcIaueJ.js.map} +1 -1
- package/dist/{useFormBuilder-BLxG3BOX.js → useFormBuilder-z5L_dUIy.js} +2 -2
- package/dist/{useFormBuilder-BLxG3BOX.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/utils/pluginSecrets.d.ts +53 -0
- package/dist/utils/records.d.ts +4 -0
- package/dist/{useProtocolTemplates-CS_SQl6U.js → validation-uwoJGEb6.js} +811 -212
- 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/AppAvatarMenu.test.ts +123 -0
- package/src/__tests__/components/BaseTabs.test.ts +67 -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/DropdownButton.test.ts +134 -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/PluginWorkspaceView.internal.test.ts +230 -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/SecretInput.test.ts +87 -0
- package/src/__tests__/components/SegmentedControl.test.ts +107 -0
- package/src/__tests__/components/TimePicker.test.ts +61 -0
- package/src/__tests__/components/WellEditPopupInternal.test.ts +108 -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/useAuth.test.ts +2 -0
- 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 +143 -0
- package/src/__tests__/composables/usePluginConfig.test.ts +497 -0
- package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +39 -7
- 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 +26 -7
- 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 +8 -60
- 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 +26 -5
- 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/BioTemplateExperimentWorkspaceView.vue +10 -21
- package/src/components/BioTemplatePackWorkspaceView.vue +11 -15
- package/src/components/BioTemplatePresetWorkspaceView.vue +8 -13
- 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/ComponentBindingRenderer.vue +1 -3
- package/src/components/ConcentrationInput.vue +0 -4
- package/src/components/ConfirmDialog.vue +0 -4
- package/src/components/ControlWorkspaceView.vue +11 -22
- 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/DoseDesignWorkspaceView.vue +6 -25
- package/src/components/DropdownButton.vue +24 -6
- 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 +16 -5
- 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/PluginWorkspaceView.props.ts +38 -0
- package/src/components/PluginWorkspaceView.vue +53 -224
- 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/SecretInput.story.vue +79 -0
- package/src/components/SecretInput.vue +150 -0
- 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 +14 -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 +20 -5
- package/src/components/internal/WorkspaceControlsInternal.vue +20 -0
- package/src/components/internal/WorkspaceShellInternal.vue +125 -0
- package/src/components/internal/WorkspaceSidebarInternal.vue +82 -0
- package/src/composables/controlSchemaTypes.ts +34 -0
- package/src/composables/experimentDesignData.ts +1 -3
- 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/flattenWorkspaceBindings.ts +104 -0
- package/src/composables/formBuilderRegistry.ts +5 -0
- package/src/composables/index.ts +50 -0
- package/src/composables/pluginWorkspaceModel.ts +182 -0
- package/src/composables/useApi.ts +57 -13
- package/src/composables/useAsync.ts +5 -1
- package/src/composables/useAuth.ts +3 -0
- package/src/composables/useBioTemplatePackWorkspace.ts +15 -9
- package/src/composables/useBioTemplatePresetWorkspace.ts +14 -15
- package/src/composables/useBioTemplateWorkspace.ts +18 -43
- package/src/composables/useCommandHistory.ts +5 -1
- 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/useFocusTrap.ts +91 -0
- package/src/composables/useGeneratedAnalysis.ts +32 -27
- package/src/composables/useJobsStatusTray.ts +184 -17
- package/src/composables/useListReorder.story.vue +143 -0
- package/src/composables/useListReorder.ts +242 -0
- package/src/composables/useMenuKeyboard.ts +90 -0
- package/src/composables/useOptimisticMutation.ts +6 -16
- package/src/composables/usePlatformFilePickerAdapter.ts +104 -0
- package/src/composables/usePluginClient.ts +50 -4
- package/src/composables/usePluginConfig.ts +252 -0
- package/src/composables/usePluginWorkspace.ts +208 -0
- package/src/composables/useRackEditor.ts +2 -11
- package/src/composables/useRequestSyncState.ts +98 -28
- package/src/composables/useResourceCrud.ts +6 -16
- package/src/composables/useRovingFocus.ts +66 -0
- package/src/composables/useScheduleDrag.ts +37 -0
- package/src/composables/useWellPainting.ts +10 -30
- package/src/composables/useWellPlateAdapter.ts +5 -1
- package/src/composables/useWellPlateEditor.ts +1 -11
- package/src/composables/useWellPlateValidation.ts +5 -1
- package/src/composables/workspaceRecipes.ts +267 -0
- package/src/index.ts +19 -0
- package/src/permissions.ts +48 -9
- package/src/stores/auth.ts +1 -0
- 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/secret-input.css +60 -0
- package/src/styles/components/well-plate.css +8 -0
- package/src/styles/index.css +12 -0
- package/src/styles/variables.css +29 -0
- package/src/templates/builderPresetControls.ts +1 -1
- package/src/templates/builderReadUtils.ts +1 -3
- package/src/templates/componentPlateHelpers.ts +3 -1
- package/src/templates/componentTargetResolvers.ts +1 -3
- package/src/templates/controlSchemaTargets.ts +1 -3
- 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/auth.ts +1 -0
- package/src/types/fileBrowserTypes.ts +85 -0
- package/src/types/filePicker.ts +72 -0
- package/src/types/form-builder.ts +1 -0
- package/src/types/index.ts +14 -0
- package/src/utils/color.ts +51 -0
- package/src/utils/errors.ts +29 -0
- package/src/utils/formModelSync.ts +1 -3
- package/src/utils/pluginIcon.ts +1 -3
- package/src/utils/pluginSecrets.ts +102 -0
- package/src/utils/records.ts +9 -0
- package/dist/EmptyState-p90quq8Z.js.map +0 -1
- package/dist/ExperimentPopover-tLwJ7XF4.js.map +0 -1
- package/dist/ExperimentSelectorModal-DyPAUCLw.js.map +0 -1
- package/dist/SettingsModal-BgKY93zh.js.map +0 -1
- package/dist/components-DkHddRMl.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-C_i90sGS.js.map +0 -1
- package/dist/experiment-Cq145oEW.js.map +0 -1
- package/dist/experiment-utils-CLnzPPq5.js.map +0 -1
- package/dist/templates/builderDefaults.d.ts +0 -1
- package/dist/templates/plateGeometry.d.ts +0 -4
- package/dist/templates-DsP51i-S.js.map +0 -1
- package/dist/useApi-J6zFkHmn.js.map +0 -1
- package/dist/useControlSchema-BDx2Olha.js.map +0 -1
- package/dist/useDropdownState-DSb3BC3N.js.map +0 -1
- package/dist/useProtocolTemplates-CS_SQl6U.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
|
@@ -22,7 +22,7 @@ interface FrontendManifest {
|
|
|
22
22
|
}>
|
|
23
23
|
composables: Array<{
|
|
24
24
|
name: string
|
|
25
|
-
members?: Array<{ name: string }>
|
|
25
|
+
members?: Array<{ name: string; type?: string }>
|
|
26
26
|
examples?: Array<{ title: string; language: string; code: string }>
|
|
27
27
|
}>
|
|
28
28
|
templateExports: Array<{ name: string }>
|
|
@@ -271,11 +271,14 @@ describe('frontend docs catalog coverage', () => {
|
|
|
271
271
|
})
|
|
272
272
|
})
|
|
273
273
|
|
|
274
|
-
it('documents
|
|
274
|
+
it('documents the source and MINT 1.1 JobsStatusTray props', () => {
|
|
275
275
|
const manifest = extractFrontendManifest()
|
|
276
276
|
const jobsStatusTray = manifest.categories.components.find(item => item.name === 'JobsStatusTray')
|
|
277
277
|
|
|
278
278
|
expect(jobsStatusTray?.props?.map(prop => prop.name).sort()).toEqual([
|
|
279
|
+
'adapter',
|
|
280
|
+
'eventStream',
|
|
281
|
+
'jobs',
|
|
279
282
|
'loadOnMount',
|
|
280
283
|
'source',
|
|
281
284
|
'teleportTo',
|
|
@@ -319,6 +322,18 @@ describe('frontend docs catalog coverage', () => {
|
|
|
319
322
|
'AppPillNav',
|
|
320
323
|
'usePluginApi',
|
|
321
324
|
'UsePluginApiOptions',
|
|
325
|
+
]
|
|
326
|
+
|
|
327
|
+
for (const name of retiredNames) {
|
|
328
|
+
expect(rootIndex).not.toMatch(new RegExp(`\\b${name}\\b`))
|
|
329
|
+
expect(componentsIndex).not.toMatch(new RegExp(`\\b${name}\\b`))
|
|
330
|
+
expect(composableExports).not.toMatch(new RegExp(`\\b${name}\\b`))
|
|
331
|
+
}
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
it('keeps the MINT 1.1 composable compatibility exports', () => {
|
|
335
|
+
const composablesIndex = readFileSync(join(SDK_ROOT, 'src', 'composables', 'index.ts'), 'utf8')
|
|
336
|
+
const expected = [
|
|
322
337
|
'useJobsStatusTray',
|
|
323
338
|
'UseJobsStatusTrayOptions',
|
|
324
339
|
'UseJobsStatusTrayReturn',
|
|
@@ -329,11 +344,7 @@ describe('frontend docs catalog coverage', () => {
|
|
|
329
344
|
'usePluginClient',
|
|
330
345
|
]
|
|
331
346
|
|
|
332
|
-
|
|
333
|
-
expect(rootIndex).not.toMatch(new RegExp(`\\b${name}\\b`))
|
|
334
|
-
expect(componentsIndex).not.toMatch(new RegExp(`\\b${name}\\b`))
|
|
335
|
-
expect(composableExports).not.toMatch(new RegExp(`\\b${name}\\b`))
|
|
336
|
-
}
|
|
347
|
+
expect(expected.filter(name => !new RegExp(`\\b${name}\\b`).test(composablesIndex))).toEqual([])
|
|
337
348
|
})
|
|
338
349
|
|
|
339
350
|
it('documents every public composable/helper export used by docs and doctor', () => {
|
|
@@ -422,4 +433,25 @@ describe('frontend docs catalog coverage', () => {
|
|
|
422
433
|
|
|
423
434
|
expect(useFormBuilder?.members?.map(member => member.name)).toContain('updateSchema')
|
|
424
435
|
})
|
|
436
|
+
|
|
437
|
+
it('extracts inherited return interface members', () => {
|
|
438
|
+
const manifest = extractFrontendManifest()
|
|
439
|
+
const workspace = manifest.categories.composables.find(item => item.name === 'useBioTemplateWorkspace')
|
|
440
|
+
|
|
441
|
+
expect(workspace?.members?.map(member => member.name)).toContain('componentUsage')
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
it('ignores generic constraints when extracting inherited return members', () => {
|
|
445
|
+
const manifest = extractFrontendManifest()
|
|
446
|
+
const history = manifest.categories.composables.find(item => item.name === 'useCommandHistory')
|
|
447
|
+
|
|
448
|
+
expect(history?.members?.map(member => member.name)).not.toContain('description')
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
it('extracts exact array return member types while resolving inheritance', () => {
|
|
452
|
+
const manifest = extractFrontendManifest()
|
|
453
|
+
const bindings = manifest.categories.composables.find(item => item.name === 'controlValuesToComponentBindings')
|
|
454
|
+
|
|
455
|
+
expect(bindings?.members?.find(member => member.name === 'props')?.type).toBe('Record<string, unknown>')
|
|
456
|
+
})
|
|
425
457
|
})
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
import { createPinia, setActivePinia } from 'pinia'
|
|
3
|
-
import {
|
|
3
|
+
import { nextTick } from 'vue'
|
|
4
|
+
import {
|
|
5
|
+
paletteCssVariables,
|
|
6
|
+
tableDensityToSize,
|
|
7
|
+
tryUseSettingsStore,
|
|
8
|
+
useSettingsStore,
|
|
9
|
+
} from '../../stores/settings'
|
|
4
10
|
|
|
5
11
|
function mockMatchMedia(matches = false) {
|
|
6
12
|
Object.defineProperty(window, 'matchMedia', {
|
|
@@ -46,6 +52,8 @@ describe('useSettingsStore', () => {
|
|
|
46
52
|
setActivePinia(createPinia())
|
|
47
53
|
installLocalStorage()
|
|
48
54
|
document.documentElement.classList.remove('dark')
|
|
55
|
+
document.documentElement.removeAttribute('style')
|
|
56
|
+
delete document.documentElement.dataset.density
|
|
49
57
|
mockMatchMedia(false)
|
|
50
58
|
})
|
|
51
59
|
|
|
@@ -75,4 +83,66 @@ describe('useSettingsStore', () => {
|
|
|
75
83
|
tableDensity: 'compact',
|
|
76
84
|
}))
|
|
77
85
|
})
|
|
86
|
+
|
|
87
|
+
describe('palette and density DOM sync', () => {
|
|
88
|
+
it('writes no palette overrides for the default palette', () => {
|
|
89
|
+
const settings = useSettingsStore()
|
|
90
|
+
settings.initialize()
|
|
91
|
+
|
|
92
|
+
expect(paletteCssVariables('default')).toEqual({})
|
|
93
|
+
expect(document.documentElement.style.getPropertyValue('--color-primary')).toBe('')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('applies the stored palette hues to html on initialize', () => {
|
|
97
|
+
localStorage.setItem('mint-settings', JSON.stringify({ colorPalette: 'viridis' }))
|
|
98
|
+
|
|
99
|
+
const settings = useSettingsStore()
|
|
100
|
+
settings.initialize()
|
|
101
|
+
|
|
102
|
+
const style = document.documentElement.style
|
|
103
|
+
expect(style.getPropertyValue('--color-primary')).toBe('hsl(280 84% 67%)')
|
|
104
|
+
expect(style.getPropertyValue('--color-primary-hover')).toBe('hsl(280 75% 59%)')
|
|
105
|
+
expect(style.getPropertyValue('--color-cta')).toBe('hsl(80 95% 53%)')
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('re-applies the overrides when the palette changes and clears them on default', async () => {
|
|
109
|
+
const settings = useSettingsStore()
|
|
110
|
+
settings.initialize()
|
|
111
|
+
|
|
112
|
+
settings.colorPalette = 'pastel'
|
|
113
|
+
await nextTick()
|
|
114
|
+
expect(document.documentElement.style.getPropertyValue('--color-primary')).toBe('hsl(200 84% 67%)')
|
|
115
|
+
|
|
116
|
+
settings.colorPalette = 'default'
|
|
117
|
+
await nextTick()
|
|
118
|
+
expect(document.documentElement.style.getPropertyValue('--color-primary')).toBe('')
|
|
119
|
+
expect(document.documentElement.style.getPropertyValue('--color-primary-soft')).toBe('')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('mirrors the table density onto html[data-density]', async () => {
|
|
123
|
+
const settings = useSettingsStore()
|
|
124
|
+
settings.initialize()
|
|
125
|
+
expect(document.documentElement.dataset.density).toBe('normal')
|
|
126
|
+
|
|
127
|
+
settings.tableDensity = 'compact'
|
|
128
|
+
await nextTick()
|
|
129
|
+
expect(document.documentElement.dataset.density).toBe('compact')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('maps each density onto a table size', () => {
|
|
133
|
+
const settings = useSettingsStore()
|
|
134
|
+
settings.initialize()
|
|
135
|
+
|
|
136
|
+
expect(tableDensityToSize('compact')).toBe('sm')
|
|
137
|
+
expect(tableDensityToSize('normal')).toBe('md')
|
|
138
|
+
expect(tableDensityToSize('comfortable')).toBe('lg')
|
|
139
|
+
|
|
140
|
+
settings.tableDensity = 'comfortable'
|
|
141
|
+
expect(settings.getTableSize()).toBe('lg')
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('exposes the store through tryUseSettingsStore when Pinia is active', () => {
|
|
146
|
+
expect(tryUseSettingsStore()).not.toBeNull()
|
|
147
|
+
})
|
|
78
148
|
})
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
|
2
|
+
import { dirname, join, relative, resolve } from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { describe, expect, it } from 'vitest'
|
|
5
|
+
|
|
6
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
const srcRoot = resolve(here, '../..')
|
|
8
|
+
const stylesDir = join(srcRoot, 'styles')
|
|
9
|
+
const variablesCss = readFileSync(join(stylesDir, 'variables.css'), 'utf-8')
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Custom properties that are legitimately *not* declared in variables.css:
|
|
13
|
+
* they are set at runtime (inline `:style` bindings, JS-built names) or are
|
|
14
|
+
* component-local knobs declared in the same stylesheet that consumes them.
|
|
15
|
+
* Anything else must be a canonical token from variables.css.
|
|
16
|
+
*/
|
|
17
|
+
const RUNTIME_PROPERTY_ALLOWLIST = new Set<string>([
|
|
18
|
+
// Names assembled in JS (`--grp-${i}`) — the literal prefix is what the
|
|
19
|
+
// scanner sees in a template string.
|
|
20
|
+
'--grp-',
|
|
21
|
+
])
|
|
22
|
+
|
|
23
|
+
const SOURCE_EXTENSIONS = ['.vue', '.css', '.ts']
|
|
24
|
+
const IGNORED_DIRS = new Set(['__tests__', 'generated-ui', 'node_modules'])
|
|
25
|
+
|
|
26
|
+
function walk(dir: string, out: string[] = []): string[] {
|
|
27
|
+
for (const entry of readdirSync(dir)) {
|
|
28
|
+
const full = join(dir, entry)
|
|
29
|
+
if (statSync(full).isDirectory()) {
|
|
30
|
+
if (!IGNORED_DIRS.has(entry)) walk(full, out)
|
|
31
|
+
} else if (SOURCE_EXTENSIONS.some((ext) => entry.endsWith(ext))) {
|
|
32
|
+
out.push(full)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return out
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const sourceFiles = walk(srcRoot)
|
|
39
|
+
|
|
40
|
+
function declaredIn(text: string): Set<string> {
|
|
41
|
+
const names = new Set<string>()
|
|
42
|
+
// `--foo: value` declarations, in CSS blocks and in JS style objects.
|
|
43
|
+
for (const m of text.matchAll(/(--[A-Za-z0-9_-]+)\s*:/g)) names.add(m[1])
|
|
44
|
+
// setProperty('--foo', …) / `'--foo'` keys in style bindings.
|
|
45
|
+
for (const m of text.matchAll(/['"`](--[A-Za-z0-9_-]+)['"`]/g)) names.add(m[1])
|
|
46
|
+
return names
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const canonicalTokens = declaredIn(variablesCss)
|
|
50
|
+
|
|
51
|
+
// Component-local custom properties: declared in a stylesheet or set from an
|
|
52
|
+
// inline `:style` binding in the SFC that owns it. They are legitimate, they
|
|
53
|
+
// just are not part of the global token set.
|
|
54
|
+
const componentLocalProperties = new Set<string>()
|
|
55
|
+
for (const file of sourceFiles) {
|
|
56
|
+
for (const name of declaredIn(readFileSync(file, 'utf-8'))) {
|
|
57
|
+
componentLocalProperties.add(name)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface Usage {
|
|
62
|
+
file: string
|
|
63
|
+
token: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const usages: Usage[] = []
|
|
67
|
+
for (const file of sourceFiles) {
|
|
68
|
+
const text = readFileSync(file, 'utf-8')
|
|
69
|
+
for (const m of text.matchAll(/var\(\s*(--[A-Za-z0-9_-]+)/g)) {
|
|
70
|
+
usages.push({ file, token: m[1] })
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
describe('CSS custom properties', () => {
|
|
75
|
+
it('scans a meaningful number of var() references', () => {
|
|
76
|
+
expect(usages.length).toBeGreaterThan(500)
|
|
77
|
+
expect(canonicalTokens.size).toBeGreaterThan(70)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('only references custom properties that are actually defined', () => {
|
|
81
|
+
const undefinedUsages = usages.filter(({ token }) => {
|
|
82
|
+
if (canonicalTokens.has(token)) return false
|
|
83
|
+
if (RUNTIME_PROPERTY_ALLOWLIST.has(token)) return false
|
|
84
|
+
return !componentLocalProperties.has(token)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const report = [
|
|
88
|
+
...new Set(
|
|
89
|
+
undefinedUsages.map(
|
|
90
|
+
({ file, token }) => `${token} (${relative(srcRoot, file)})`,
|
|
91
|
+
),
|
|
92
|
+
),
|
|
93
|
+
].sort()
|
|
94
|
+
|
|
95
|
+
expect(report).toEqual([])
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('does not use the platform-only --color-bg-*/--color-text-*/--color-border aliases', () => {
|
|
99
|
+
const platformOnly = /^--color-(bg|text)-|^--color-border$/
|
|
100
|
+
const offenders = [
|
|
101
|
+
...new Set(
|
|
102
|
+
usages
|
|
103
|
+
.filter(({ token }) => platformOnly.test(token))
|
|
104
|
+
.map(({ file, token }) => `${token} (${relative(srcRoot, file)})`),
|
|
105
|
+
),
|
|
106
|
+
].sort()
|
|
107
|
+
|
|
108
|
+
expect(offenders).toEqual([])
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('aggregates every component stylesheet in styles/index.css', () => {
|
|
112
|
+
const index = readFileSync(join(stylesDir, 'index.css'), 'utf-8')
|
|
113
|
+
const imported = new Set(
|
|
114
|
+
[...index.matchAll(/@import\s+'\.\/components\/([^']+)'/g)].map((m) => m[1]),
|
|
115
|
+
)
|
|
116
|
+
const onDisk = readdirSync(join(stylesDir, 'components')).filter((f) =>
|
|
117
|
+
f.endsWith('.css'),
|
|
118
|
+
)
|
|
119
|
+
expect([...onDisk].filter((f) => !imported.has(f)).sort()).toEqual([])
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('does not re-import aggregated stylesheets from SFC <style> blocks', () => {
|
|
123
|
+
const offenders = sourceFiles
|
|
124
|
+
.filter((f) => f.endsWith('.vue'))
|
|
125
|
+
.filter((f) => /@import\s+['"][^'"]*styles\/components\//.test(readFileSync(f, 'utf-8')))
|
|
126
|
+
.map((f) => relative(srcRoot, f))
|
|
127
|
+
.sort()
|
|
128
|
+
|
|
129
|
+
expect(offenders).toEqual([])
|
|
130
|
+
})
|
|
131
|
+
})
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
hasAllPermissions,
|
|
8
8
|
hasAnyPermission,
|
|
9
9
|
isAdminUser,
|
|
10
|
+
normalizeAccessPolicy,
|
|
10
11
|
} from '../../permissions'
|
|
11
12
|
|
|
12
13
|
describe('permission helpers', () => {
|
|
@@ -60,3 +61,70 @@ describe('permission helpers', () => {
|
|
|
60
61
|
expect(canAccessByPolicy(member, { plugin: 'reports' })).toBe(false)
|
|
61
62
|
})
|
|
62
63
|
})
|
|
64
|
+
|
|
65
|
+
describe('normalizeAccessPolicy precedence', () => {
|
|
66
|
+
it('returns an empty policy for an absent rule', () => {
|
|
67
|
+
expect(normalizeAccessPolicy(undefined)).toEqual({})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('reads a flat-only rule as-is', () => {
|
|
71
|
+
expect(normalizeAccessPolicy({
|
|
72
|
+
visibleFor: 'admin',
|
|
73
|
+
requiresAdmin: true,
|
|
74
|
+
permissions: ['users.manage'],
|
|
75
|
+
anyPermissions: ['plugins.install'],
|
|
76
|
+
})).toEqual({
|
|
77
|
+
visibleFor: 'admin',
|
|
78
|
+
requiresAdmin: true,
|
|
79
|
+
permissions: ['users.manage'],
|
|
80
|
+
anyPermissions: ['plugins.install'],
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('lifts a nested-only policy to the top level', () => {
|
|
85
|
+
expect(normalizeAccessPolicy({
|
|
86
|
+
access: { requiresAuth: true, plugin: 'reports', visibleFor: 'admin' },
|
|
87
|
+
})).toEqual({
|
|
88
|
+
requiresAuth: true,
|
|
89
|
+
plugin: 'reports',
|
|
90
|
+
visibleFor: 'admin',
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('lets the nested policy win over the deprecated flat duplicates', () => {
|
|
95
|
+
expect(normalizeAccessPolicy({
|
|
96
|
+
access: {
|
|
97
|
+
visibleFor: 'admin',
|
|
98
|
+
requiresAdmin: true,
|
|
99
|
+
permissions: ['users.manage'],
|
|
100
|
+
anyPermissions: ['plugins.install'],
|
|
101
|
+
},
|
|
102
|
+
visibleFor: 'user',
|
|
103
|
+
requiresAdmin: false,
|
|
104
|
+
permissions: ['experiments.view'],
|
|
105
|
+
anyPermissions: ['experiments.edit'],
|
|
106
|
+
})).toEqual({
|
|
107
|
+
visibleFor: 'admin',
|
|
108
|
+
requiresAdmin: true,
|
|
109
|
+
permissions: ['users.manage'],
|
|
110
|
+
anyPermissions: ['plugins.install'],
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('treats an undefined nested field as unset instead of masking the flat one', () => {
|
|
115
|
+
expect(normalizeAccessPolicy({
|
|
116
|
+
access: { visibleFor: undefined, requiresAdmin: true },
|
|
117
|
+
visibleFor: 'user',
|
|
118
|
+
})).toEqual({
|
|
119
|
+
visibleFor: 'user',
|
|
120
|
+
requiresAdmin: true,
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('feeds canAccessByPolicy from the nested policy', () => {
|
|
125
|
+
const member = { role: 'member', permissions: ['experiments.view'] }
|
|
126
|
+
|
|
127
|
+
expect(canAccessByPolicy(member, { access: { requiresAdmin: true }, requiresAdmin: false })).toBe(false)
|
|
128
|
+
expect(canAccessByPolicy(member, { access: { visibleFor: 'user' }, visibleFor: 'admin' })).toBe(true)
|
|
129
|
+
})
|
|
130
|
+
})
|
package/src/apiErrors.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export interface MintApiErrorEnvelope {
|
|
2
|
+
code: string
|
|
3
|
+
message: string
|
|
4
|
+
status: number
|
|
5
|
+
request_id: string | null
|
|
6
|
+
details: unknown
|
|
7
|
+
detail?: unknown
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const STATUS_MESSAGES: Readonly<Record<number, string>> = {
|
|
11
|
+
400: 'Bad request',
|
|
12
|
+
401: 'Authentication required',
|
|
13
|
+
403: 'Permission denied',
|
|
14
|
+
404: 'Resource not found',
|
|
15
|
+
405: 'Method not allowed',
|
|
16
|
+
409: 'Conflict',
|
|
17
|
+
413: 'Payload too large',
|
|
18
|
+
415: 'Unsupported media type',
|
|
19
|
+
422: 'Validation error',
|
|
20
|
+
429: 'Rate limit exceeded',
|
|
21
|
+
500: 'Internal server error',
|
|
22
|
+
502: 'Bad gateway',
|
|
23
|
+
503: 'Service unavailable',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
27
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null
|
|
28
|
+
return value as Record<string, unknown>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Public error thrown by typed MINT SDK requests. */
|
|
32
|
+
export class MintApiError extends Error {
|
|
33
|
+
override readonly name: string = 'MintApiError'
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
message: string,
|
|
37
|
+
readonly code: string,
|
|
38
|
+
readonly status: number,
|
|
39
|
+
readonly requestId: string | null,
|
|
40
|
+
readonly details: unknown,
|
|
41
|
+
readonly detail: unknown,
|
|
42
|
+
readonly body: unknown,
|
|
43
|
+
readonly cause?: unknown,
|
|
44
|
+
) {
|
|
45
|
+
super(message)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface MintApiErrorOptions {
|
|
50
|
+
requestId?: string | null
|
|
51
|
+
cause?: unknown
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Normalize current typed envelopes and the previous FastAPI detail shape. */
|
|
55
|
+
export function mintApiErrorFromResponse(
|
|
56
|
+
status: number,
|
|
57
|
+
payload: unknown,
|
|
58
|
+
options: MintApiErrorOptions = {},
|
|
59
|
+
): MintApiError {
|
|
60
|
+
const body = asRecord(payload)
|
|
61
|
+
const legacyDetail = body?.detail
|
|
62
|
+
const inner = asRecord(legacyDetail) ?? body
|
|
63
|
+
const text = (value: unknown): string | undefined =>
|
|
64
|
+
typeof value === 'string' && value ? value : undefined
|
|
65
|
+
// Complete envelopes own the public code; older wrappers defer to the plugin payload.
|
|
66
|
+
const canonical = typeof body?.status === 'number' && text(body.code) && text(body.message)
|
|
67
|
+
const code = (canonical ? text(body.code) : text(inner?.error) ?? text(inner?.code))
|
|
68
|
+
?? text(body?.code) ?? 'unknown'
|
|
69
|
+
const message = (canonical ? text(body.message) : text(inner?.message))
|
|
70
|
+
?? text(body?.message) ?? text(legacyDetail)
|
|
71
|
+
?? STATUS_MESSAGES[status] ?? `HTTP ${status}`
|
|
72
|
+
const details = body?.details ?? inner?.details ?? null
|
|
73
|
+
const requestId = text(body?.request_id) ?? text(inner?.request_id) ?? options.requestId ?? null
|
|
74
|
+
|
|
75
|
+
return new MintApiError(
|
|
76
|
+
message,
|
|
77
|
+
code,
|
|
78
|
+
status,
|
|
79
|
+
requestId,
|
|
80
|
+
details,
|
|
81
|
+
legacyDetail,
|
|
82
|
+
payload,
|
|
83
|
+
options.cause,
|
|
84
|
+
)
|
|
85
|
+
}
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
/** Compact overflow actions with an IconButton trigger and a viewport-safe teleported menu. */
|
|
3
3
|
import { computePosition, flip, offset, shift } from '@floating-ui/dom'
|
|
4
4
|
import { Ellipsis } from 'lucide-vue-next'
|
|
5
|
-
import { nextTick,
|
|
5
|
+
import { nextTick, onMounted, ref, watch } from 'vue'
|
|
6
|
+
import { useDropdownState } from '../composables/useDropdownState'
|
|
7
|
+
import { useMenuKeyboard } from '../composables/useMenuKeyboard'
|
|
6
8
|
import type { ActionMenuItem } from '../types/components'
|
|
7
9
|
import IconButton from './IconButton.vue'
|
|
8
10
|
|
|
@@ -22,14 +24,26 @@ const emit = defineEmits<{
|
|
|
22
24
|
close: []
|
|
23
25
|
}>()
|
|
24
26
|
|
|
25
|
-
const isOpen = ref(false)
|
|
26
|
-
const triggerRootRef = ref<HTMLElement | null>(null)
|
|
27
27
|
const panelRef = ref<HTMLElement | null>(null)
|
|
28
28
|
const panelStyle = ref<Record<string, string>>({ visibility: 'hidden' })
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
30
|
+
// Disclosure state (outside-click, Escape, scroll/resize closing, focus return)
|
|
31
|
+
// comes from the shared composable; only floating-ui placement stays local.
|
|
32
|
+
const { isOpen, rootRef, triggerRef, open, close } = useDropdownState({
|
|
33
|
+
closeOnScroll: true,
|
|
34
|
+
insideRefs: [panelRef],
|
|
35
|
+
onOpen: () => {
|
|
36
|
+
panelStyle.value = { visibility: 'hidden' }
|
|
37
|
+
emit('open')
|
|
38
|
+
},
|
|
39
|
+
onClose: () => emit('close'),
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
// IconButton owns the actual <button>, so the trigger element is resolved from
|
|
43
|
+
// the wrapper once mounted rather than bound with a template ref.
|
|
44
|
+
onMounted(() => {
|
|
45
|
+
triggerRef.value = rootRef.value?.querySelector('button') ?? null
|
|
46
|
+
})
|
|
33
47
|
|
|
34
48
|
function menuItems(): HTMLElement[] {
|
|
35
49
|
if (!panelRef.value) return []
|
|
@@ -39,7 +53,7 @@ function menuItems(): HTMLElement[] {
|
|
|
39
53
|
}
|
|
40
54
|
|
|
41
55
|
async function positionPanel() {
|
|
42
|
-
const trigger =
|
|
56
|
+
const trigger = triggerRef.value
|
|
43
57
|
const panel = panelRef.value
|
|
44
58
|
if (!trigger || !panel) return
|
|
45
59
|
|
|
@@ -59,97 +73,43 @@ async function positionPanel() {
|
|
|
59
73
|
}
|
|
60
74
|
}
|
|
61
75
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
window.addEventListener('resize', handleViewportChange)
|
|
65
|
-
window.addEventListener('scroll', handleViewportChange, true)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function removeGlobalListeners() {
|
|
69
|
-
document.removeEventListener('pointerdown', handleDocumentPointerDown, true)
|
|
70
|
-
window.removeEventListener('resize', handleViewportChange)
|
|
71
|
-
window.removeEventListener('scroll', handleViewportChange, true)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async function openMenu() {
|
|
76
|
+
/** `focusFirst` is false when the keyboard composable owns the initial focus. */
|
|
77
|
+
function openMenu(focusFirst = true) {
|
|
75
78
|
if (props.disabled || isOpen.value) return
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
addGlobalListeners()
|
|
80
|
-
await nextTick()
|
|
81
|
-
await positionPanel()
|
|
82
|
-
menuItems()[0]?.focus()
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function closeMenu(returnFocus = false) {
|
|
86
|
-
if (!isOpen.value) return
|
|
87
|
-
isOpen.value = false
|
|
88
|
-
removeGlobalListeners()
|
|
89
|
-
emit('close')
|
|
90
|
-
if (returnFocus) {
|
|
91
|
-
nextTick(() => triggerElement()?.focus())
|
|
79
|
+
open()
|
|
80
|
+
if (focusFirst) {
|
|
81
|
+
void nextTick(() => menuItems()[0]?.focus())
|
|
92
82
|
}
|
|
93
83
|
}
|
|
94
84
|
|
|
95
85
|
function toggleMenu() {
|
|
96
|
-
if (
|
|
97
|
-
|
|
86
|
+
if (props.disabled) return
|
|
87
|
+
if (isOpen.value) close()
|
|
88
|
+
else openMenu()
|
|
98
89
|
}
|
|
99
90
|
|
|
91
|
+
const { handleTriggerKeydown, handlePanelKeydown } = useMenuKeyboard({
|
|
92
|
+
items: menuItems,
|
|
93
|
+
isOpen: () => isOpen.value,
|
|
94
|
+
open: () => openMenu(false),
|
|
95
|
+
close,
|
|
96
|
+
})
|
|
97
|
+
|
|
100
98
|
function handleSelect(item: ActionMenuItem) {
|
|
101
99
|
if (item.disabled) return
|
|
102
100
|
emit('select', item)
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function handleDocumentPointerDown(event: PointerEvent) {
|
|
107
|
-
const target = event.target
|
|
108
|
-
if (!(target instanceof Node)) return
|
|
109
|
-
if (triggerRootRef.value?.contains(target) || panelRef.value?.contains(target)) return
|
|
110
|
-
closeMenu()
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function handleViewportChange() {
|
|
114
|
-
closeMenu()
|
|
101
|
+
close()
|
|
115
102
|
}
|
|
116
103
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
event.preventDefault()
|
|
123
|
-
closeMenu(true)
|
|
124
|
-
return
|
|
125
|
-
}
|
|
126
|
-
if (event.key === 'Tab') {
|
|
127
|
-
closeMenu()
|
|
128
|
-
return
|
|
129
|
-
}
|
|
130
|
-
if (event.key === 'Home') {
|
|
131
|
-
event.preventDefault()
|
|
132
|
-
items[0]?.focus()
|
|
133
|
-
return
|
|
134
|
-
}
|
|
135
|
-
if (event.key === 'End') {
|
|
136
|
-
event.preventDefault()
|
|
137
|
-
items[items.length - 1]?.focus()
|
|
138
|
-
return
|
|
139
|
-
}
|
|
140
|
-
if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return
|
|
141
|
-
|
|
142
|
-
event.preventDefault()
|
|
143
|
-
const direction = event.key === 'ArrowDown' ? 1 : -1
|
|
144
|
-
const nextIndex = (Math.max(currentIndex, 0) + direction + items.length) % items.length
|
|
145
|
-
items[nextIndex]?.focus()
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
onBeforeUnmount(removeGlobalListeners)
|
|
104
|
+
watch(isOpen, async (opened) => {
|
|
105
|
+
if (!opened) return
|
|
106
|
+
await nextTick()
|
|
107
|
+
await positionPanel()
|
|
108
|
+
})
|
|
149
109
|
</script>
|
|
150
110
|
|
|
151
111
|
<template>
|
|
152
|
-
<span ref="
|
|
112
|
+
<span ref="rootRef" class="mint-action-menu">
|
|
153
113
|
<IconButton
|
|
154
114
|
variant="secondary"
|
|
155
115
|
size="sm"
|
|
@@ -158,6 +118,7 @@ onBeforeUnmount(removeGlobalListeners)
|
|
|
158
118
|
aria-haspopup="menu"
|
|
159
119
|
:aria-expanded="isOpen"
|
|
160
120
|
@click="toggleMenu"
|
|
121
|
+
@keydown="handleTriggerKeydown"
|
|
161
122
|
>
|
|
162
123
|
<Ellipsis
|
|
163
124
|
class="mint-action-menu__trigger-icon"
|
|
@@ -175,7 +136,7 @@ onBeforeUnmount(removeGlobalListeners)
|
|
|
175
136
|
class="mint-action-menu__panel"
|
|
176
137
|
:style="panelStyle"
|
|
177
138
|
role="menu"
|
|
178
|
-
@keydown="
|
|
139
|
+
@keydown="handlePanelKeydown"
|
|
179
140
|
>
|
|
180
141
|
<template v-for="item in items" :key="item.id">
|
|
181
142
|
<a
|
|
@@ -203,7 +164,3 @@ onBeforeUnmount(removeGlobalListeners)
|
|
|
203
164
|
</div>
|
|
204
165
|
</Teleport>
|
|
205
166
|
</template>
|
|
206
|
-
|
|
207
|
-
<style>
|
|
208
|
-
@import '../styles/components/action-menu.css';
|
|
209
|
-
</style>
|