@morscherlab/mint-sdk 1.0.62 → 1.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +101 -2
  2. package/dist/EmptyState-D1c4RdC7.js +638 -0
  3. package/dist/EmptyState-D1c4RdC7.js.map +1 -0
  4. package/dist/{ExperimentPopover-BbPkIFsI.js → ExperimentPopover-BFw3QX8L.js} +10 -9
  5. package/dist/{ExperimentPopover-BbPkIFsI.js.map → ExperimentPopover-BFw3QX8L.js.map} +1 -1
  6. package/dist/{ExperimentSelectorModal-DZeSd-yr.js → ExperimentSelectorModal-B7w6k3Y8.js} +12 -216
  7. package/dist/ExperimentSelectorModal-B7w6k3Y8.js.map +1 -0
  8. package/dist/{SettingsModal-CJgLadsN.js → SettingsModal-DPDwCbXK.js} +12 -9
  9. package/dist/{SettingsModal-CJgLadsN.js.map → SettingsModal-DPDwCbXK.js.map} +1 -1
  10. package/dist/__tests__/components/GeneratedPathInput.test.d.ts +1 -0
  11. package/dist/__tests__/components/GeneratedPlotlyResult.test.d.ts +1 -0
  12. package/dist/__tests__/components/GeneratedPluginView.test.d.ts +1 -0
  13. package/dist/__tests__/components/GeneratedResultRenderer.test.d.ts +1 -0
  14. package/dist/__tests__/components/InstrumentAlertLog.test.d.ts +1 -0
  15. package/dist/__tests__/composables/useGeneratedAnalysis.test.d.ts +1 -0
  16. package/dist/__tests__/generatedUi.test.d.ts +1 -0
  17. package/dist/{analysisArtifactTypes-Cu40dzSG.js → analysisArtifactTypes-ejqR8MX3.js} +1 -1
  18. package/dist/{analysisArtifactTypes-Cu40dzSG.js.map → analysisArtifactTypes-ejqR8MX3.js.map} +1 -1
  19. package/dist/{auth-DZAwEeis.js → auth-B87xM--s.js} +1 -1
  20. package/dist/{auth-DZAwEeis.js.map → auth-B87xM--s.js.map} +1 -1
  21. package/dist/chunk-BoAXSpZd.js +33 -0
  22. package/dist/components/AppSidebar.vue.d.ts +1 -1
  23. package/dist/components/AutoGroupModal.vue.d.ts +2 -2
  24. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
  25. package/dist/components/CollapsibleCard.vue.d.ts +1 -1
  26. package/dist/components/ComponentBindingRenderer.vue.d.ts +1 -1
  27. package/dist/components/ConfirmDialog.vue.d.ts +1 -1
  28. package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
  29. package/dist/components/ExperimentTimeline.vue.d.ts +1 -1
  30. package/dist/components/FileUploader.vue.d.ts +4 -2
  31. package/dist/components/FitPanel.vue.d.ts +1 -1
  32. package/dist/components/FormActions.vue.d.ts +1 -1
  33. package/dist/components/FormBuilder.vue.d.ts +1 -1
  34. package/dist/components/GeneratedPathInput.vue.d.ts +12 -0
  35. package/dist/components/GeneratedPlotlyResult.vue.d.ts +11 -0
  36. package/dist/components/GeneratedPluginView.vue.d.ts +10 -0
  37. package/dist/components/GeneratedResultRenderer.vue.d.ts +6 -0
  38. package/dist/components/JobsStatusTray.vue.d.ts +5 -1
  39. package/dist/components/LcmsSequenceTable.vue.d.ts +1 -1
  40. package/dist/components/SampleLegend.vue.d.ts +1 -1
  41. package/dist/components/SampleSelector.vue.d.ts +2 -2
  42. package/dist/components/SettingsModal.vue.d.ts +1 -1
  43. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -2
  44. package/dist/components/index.d.ts +4 -0
  45. package/dist/components/index.js +8 -9
  46. package/dist/components/internal/PlateMapEditorToolbarInternal.vue.d.ts +2 -2
  47. package/dist/{components-CpnOaPbP.js → components-UfkPN1bK.js} +2255 -2303
  48. package/dist/components-UfkPN1bK.js.map +1 -0
  49. package/dist/composables/index.d.ts +2 -1
  50. package/dist/composables/index.js +10 -11
  51. package/dist/composables/useGeneratedAnalysis.d.ts +62 -0
  52. package/dist/composables/useJobsStatusTray.d.ts +48 -4
  53. package/dist/composables/usePluginClient.d.ts +6 -3
  54. package/dist/{composables-GpJD6R1R.js → composables-F4JFK9K1.js} +22 -13
  55. package/dist/{composables-GpJD6R1R.js.map → composables-F4JFK9K1.js.map} +1 -1
  56. package/dist/experiment-utils-CxkruZ3G.js +392 -0
  57. package/dist/experiment-utils-CxkruZ3G.js.map +1 -0
  58. package/dist/generated/schema.d.ts +7 -0
  59. package/dist/generated-ui/main.d.ts +0 -0
  60. package/dist/index.d.ts +1 -0
  61. package/dist/index.js +15 -18
  62. package/dist/index.js.map +1 -1
  63. package/dist/install.js +7 -8
  64. package/dist/install.js.map +1 -1
  65. package/dist/plotly.min-BR53hja4.js +180634 -0
  66. package/dist/plotly.min-BR53hja4.js.map +1 -0
  67. package/dist/stores/auth.d.ts +2 -2
  68. package/dist/stores/index.js +1 -1
  69. package/dist/styles.css +10543 -10404
  70. package/dist/templates/index.js +4 -3
  71. package/dist/templates/packs.d.ts +0 -1
  72. package/dist/{templates-CNbPQNID.js → templates-Cttbjbyc.js} +2 -4
  73. package/dist/templates-Cttbjbyc.js.map +1 -0
  74. package/dist/types/form-builder.d.ts +1 -1
  75. package/dist/types/generated-ui.d.ts +170 -0
  76. package/dist/types/index.d.ts +1 -0
  77. package/dist/types/index.js +1 -1
  78. package/dist/types/instrument.d.ts +1 -0
  79. package/dist/types/jobs.d.ts +7 -1
  80. package/dist/{useControlSchema-BZNdalmL.js → useControlSchema-Dallso-L.js} +1852 -171
  81. package/dist/useControlSchema-Dallso-L.js.map +1 -0
  82. package/dist/{useDropdownState-Bb-c_PeE.js → useDropdownState-gVaMXtwW.js} +2 -2
  83. package/dist/{useDropdownState-Bb-c_PeE.js.map → useDropdownState-gVaMXtwW.js.map} +1 -1
  84. package/dist/{useExperimentSelector-DRbj6vse.js → useExperimentSelector-BMjzNcf2.js} +4 -255
  85. package/dist/useExperimentSelector-BMjzNcf2.js.map +1 -0
  86. package/dist/{useFormBuilder-DKekvXRs.js → useFormBuilder-BXaaDI41.js} +2 -2
  87. package/dist/{useFormBuilder-DKekvXRs.js.map → useFormBuilder-BXaaDI41.js.map} +1 -1
  88. package/dist/{useAnalysisArtifacts-Ob8UtVwl.js → useProtocolTemplates-BP7_4RGQ.js} +307 -449
  89. package/dist/useProtocolTemplates-BP7_4RGQ.js.map +1 -0
  90. package/package.json +7 -3
  91. package/src/__tests__/components/GeneratedPathInput.test.ts +132 -0
  92. package/src/__tests__/components/GeneratedPlotlyResult.test.ts +117 -0
  93. package/src/__tests__/components/GeneratedPluginView.test.ts +230 -0
  94. package/src/__tests__/components/GeneratedResultRenderer.test.ts +93 -0
  95. package/src/__tests__/components/InstrumentAlertLog.test.ts +59 -0
  96. package/src/__tests__/composables/formBuilderRegistry.test.ts +5 -1
  97. package/src/__tests__/composables/useApi.test.ts +8 -0
  98. package/src/__tests__/composables/useGeneratedAnalysis.test.ts +1740 -0
  99. package/src/__tests__/composables/useJobsStatusTray.test.ts +179 -1
  100. package/src/__tests__/composables/usePluginClient.test.ts +37 -0
  101. package/src/__tests__/docs/extractDocsTemplates.test.ts +0 -1
  102. package/src/__tests__/generatedUi.test.ts +119 -0
  103. package/src/__tests__/templates/templates.test.ts +1 -1
  104. package/src/components/FileUploader.vue +49 -11
  105. package/src/components/GeneratedPathInput.vue +218 -0
  106. package/src/components/GeneratedPlotlyResult.vue +87 -0
  107. package/src/components/GeneratedPluginView.vue +279 -0
  108. package/src/components/GeneratedResultRenderer.vue +137 -0
  109. package/src/components/InstrumentAlertLog.vue +2 -2
  110. package/src/components/JobsStatusTray.vue +14 -8
  111. package/src/components/index.ts +4 -0
  112. package/src/composables/formBuilderRegistry.ts +2 -0
  113. package/src/composables/index.ts +18 -0
  114. package/src/composables/useApi.ts +1 -0
  115. package/src/composables/useGeneratedAnalysis.ts +1252 -0
  116. package/src/composables/useJobsStatusTray.ts +234 -45
  117. package/src/composables/usePluginClient.ts +34 -10
  118. package/src/generated/schema.ts +130 -0
  119. package/src/generated-ui/main.ts +21 -0
  120. package/src/index.ts +3 -0
  121. package/src/jobs.ts +5 -0
  122. package/src/plotly-dist.d.ts +3 -0
  123. package/src/templates/packs.ts +0 -3
  124. package/src/types/form-builder.ts +1 -0
  125. package/src/types/generated-ui.ts +176 -0
  126. package/src/types/index.ts +2 -0
  127. package/src/types/instrument.ts +1 -0
  128. package/src/types/jobs.ts +10 -1
  129. package/dist/BaseButton-Dgqrze41.js +0 -74
  130. package/dist/BaseButton-Dgqrze41.js.map +0 -1
  131. package/dist/BaseModal-B9UA8Y_I.js +0 -165
  132. package/dist/BaseModal-B9UA8Y_I.js.map +0 -1
  133. package/dist/BaseSelect-ekgr9fDo.js +0 -179
  134. package/dist/BaseSelect-ekgr9fDo.js.map +0 -1
  135. package/dist/ExperimentPopover-Ryusjrhv.js +0 -4
  136. package/dist/ExperimentSelectorModal-Cg4-LXBD.js +0 -5
  137. package/dist/ExperimentSelectorModal-DZeSd-yr.js.map +0 -1
  138. package/dist/SettingsModal-Bl_9w2Y3.js +0 -5
  139. package/dist/components-CpnOaPbP.js.map +0 -1
  140. package/dist/experiment-utils-Bfa7CwPU.js +0 -108
  141. package/dist/experiment-utils-Bfa7CwPU.js.map +0 -1
  142. package/dist/templates-CNbPQNID.js.map +0 -1
  143. package/dist/useAnalysisArtifacts-Ob8UtVwl.js.map +0 -1
  144. package/dist/useControlSchema-BZNdalmL.js.map +0 -1
  145. package/dist/useEventListener-CfVkP9Xz.js +0 -57
  146. package/dist/useEventListener-CfVkP9Xz.js.map +0 -1
  147. package/dist/useExperimentSelector-DRbj6vse.js.map +0 -1
@@ -0,0 +1,137 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+
4
+ import type { DataFrameColumn } from '../types'
5
+ import type { GeneratedAnalysisResult } from '../types/generated-ui'
6
+ import ChartContainer from './ChartContainer.vue'
7
+ import DataFrame from './DataFrame.vue'
8
+ import GeneratedPlotlyResult from './GeneratedPlotlyResult.vue'
9
+
10
+ const props = defineProps<{
11
+ result: GeneratedAnalysisResult
12
+ }>()
13
+
14
+ const tableColumns = computed<DataFrameColumn[]>(() => {
15
+ if (props.result.kind !== 'table') return []
16
+ return props.result.columns.map(key => ({
17
+ key,
18
+ label: humanize(key),
19
+ sortable: true,
20
+ }))
21
+ })
22
+
23
+ const formattedJson = computed(() => (
24
+ props.result.kind === 'json'
25
+ ? JSON.stringify(props.result.value, null, 2)
26
+ : ''
27
+ ))
28
+
29
+ const artifactHref = computed(() => {
30
+ if (props.result.kind !== 'artifact') return ''
31
+ if (props.result.download_url) return props.result.download_url
32
+ return props.result.data
33
+ ? `data:${props.result.media_type};base64,${props.result.data}`
34
+ : ''
35
+ })
36
+
37
+ function humanize(value: string): string {
38
+ return value.replace(/[_-]+/g, ' ').replace(/^\w/, character => character.toUpperCase())
39
+ }
40
+ </script>
41
+
42
+ <template>
43
+ <article class="mint-generated-result" aria-live="polite">
44
+ <p v-if="result.kind === 'text'" class="mint-generated-result__text">
45
+ {{ result.text }}
46
+ </p>
47
+
48
+ <pre v-else-if="result.kind === 'json'" class="mint-generated-result__json">{{ formattedJson }}</pre>
49
+
50
+ <DataFrame
51
+ v-else-if="result.kind === 'table'"
52
+ :data="result.rows"
53
+ :columns="tableColumns"
54
+ sortable
55
+ searchable
56
+ sticky-header
57
+ max-height="32rem"
58
+ />
59
+
60
+ <ChartContainer v-else-if="result.kind === 'image'" title="Analysis chart">
61
+ <img
62
+ class="mint-generated-result__image"
63
+ :src="`data:${result.media_type};base64,${result.data}`"
64
+ :alt="result.alt"
65
+ >
66
+ </ChartContainer>
67
+
68
+ <GeneratedPlotlyResult
69
+ v-else-if="result.kind === 'plotly'"
70
+ :spec="result.spec"
71
+ :fallback="result.fallback"
72
+ />
73
+
74
+ <a
75
+ v-else
76
+ class="mint-generated-result__artifact"
77
+ :href="artifactHref"
78
+ :download="result.name"
79
+ >
80
+ <span>{{ result.name }}</span>
81
+ <small>{{ result.media_type }}</small>
82
+ </a>
83
+ </article>
84
+ </template>
85
+
86
+ <style scoped>
87
+ .mint-generated-result {
88
+ width: 100%;
89
+ }
90
+
91
+ .mint-generated-result__text,
92
+ .mint-generated-result__json {
93
+ margin: 0;
94
+ color: var(--color-text-primary);
95
+ }
96
+
97
+ .mint-generated-result__text {
98
+ max-width: 72ch;
99
+ white-space: pre-wrap;
100
+ line-height: 1.65;
101
+ }
102
+
103
+ .mint-generated-result__json {
104
+ overflow: auto;
105
+ padding: 1rem;
106
+ border: 1px solid var(--color-border);
107
+ border-radius: 0.75rem;
108
+ background: var(--color-bg-secondary);
109
+ font-size: 0.8125rem;
110
+ line-height: 1.55;
111
+ white-space: pre-wrap;
112
+ }
113
+
114
+ .mint-generated-result__image {
115
+ display: block;
116
+ max-width: 100%;
117
+ height: auto;
118
+ margin: 0 auto;
119
+ }
120
+
121
+ .mint-generated-result__plotly {
122
+ width: 100%;
123
+ min-height: 24rem;
124
+ }
125
+
126
+ .mint-generated-result__artifact {
127
+ display: flex;
128
+ align-items: center;
129
+ justify-content: space-between;
130
+ gap: 1rem;
131
+ padding: 1rem;
132
+ border: 1px solid var(--color-border);
133
+ border-radius: 0.75rem;
134
+ color: var(--color-primary);
135
+ text-decoration: none;
136
+ }
137
+ </style>
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- /** Filterable instrument alert/event list with optional acknowledge actions. */
2
+ /** Filterable instrument alert/event list keyed by each alert's required stable event_key. */
3
3
  import { computed, ref } from 'vue'
4
4
  import type { InstrumentAlert, InstrumentAlertLevel } from '../types'
5
5
 
@@ -146,7 +146,7 @@ function alertDetail(alert: InstrumentAlert): string {
146
146
  <TransitionGroup name="mint-instrument-alert" tag="div" class="mint-instrument-alert-log__list">
147
147
  <div
148
148
  v-for="alert in filteredAlerts"
149
- :key="alert.id ?? `${alert.instrument_id}-${alert.rule ?? alert.message}-${alert.timestamp ?? ''}`"
149
+ :key="alert.event_key"
150
150
  class="mint-instrument-alert"
151
151
  :class="[
152
152
  levelConfig[alert.level]?.cssClass,
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- /** Floating, adapter-driven job status tray for plugin and platform workflows. */
2
+ /** Floating, source-driven job status tray for plugin and platform workflows. */
3
3
  import { computed, nextTick, ref, toRef, useId } from 'vue'
4
4
 
5
5
  import {
@@ -7,8 +7,9 @@ import {
7
7
  normalizeJobPercent,
8
8
  } from '../jobs'
9
9
  import {
10
- useJobsStatusTray,
10
+ usePluginJobCenter,
11
11
  type JobsStatusTrayAdapter,
12
+ type PluginJobCenterSource,
12
13
  } from '../composables/useJobsStatusTray'
13
14
  import type { UsePluginEventStreamReturn } from '../composables/usePluginClient'
14
15
  import type {
@@ -18,8 +19,12 @@ import type {
18
19
  } from '../types/jobs'
19
20
 
20
21
  interface Props {
22
+ source?: PluginJobCenterSource
23
+ /** @deprecated Pass a source instead. */
21
24
  jobs?: readonly JobStateInput[]
25
+ /** @deprecated Pass source actions instead. */
22
26
  adapter?: JobsStatusTrayAdapter
27
+ /** @deprecated Event streams belong to the source runtime. */
23
28
  eventStream?: UsePluginEventStreamReturn<JobStreamData>
24
29
  loadOnMount?: boolean
25
30
  title?: string
@@ -42,7 +47,8 @@ const titleId = `${panelId}-title`
42
47
  const triggerRef = ref<HTMLButtonElement | null>(null)
43
48
  const panelRef = ref<HTMLElement | null>(null)
44
49
  const teleportTarget = computed(() => props.teleportTo || 'body')
45
- const tray = useJobsStatusTray({
50
+ const tray = usePluginJobCenter({
51
+ source: toRef(props, 'source'),
46
52
  jobs: toRef(props, 'jobs'),
47
53
  adapter: toRef(props, 'adapter'),
48
54
  eventStream: toRef(props, 'eventStream'),
@@ -50,7 +56,7 @@ const tray = useJobsStatusTray({
50
56
  })
51
57
  const canClear = computed(() => (
52
58
  tray.deletableJobs.value.length > 0
53
- && Boolean(props.adapter?.clearFinishedJobs || props.adapter?.deleteJob)
59
+ && tray.canClearFinished.value
54
60
  ))
55
61
 
56
62
  async function openTray(): Promise<void> {
@@ -201,7 +207,7 @@ async function clearFinished(): Promise<void> {
201
207
  </div>
202
208
  <div class="mint-jobs-tray__header-actions">
203
209
  <button
204
- v-if="adapter?.listJobs"
210
+ v-if="tray.canRefresh.value"
205
211
  type="button"
206
212
  class="mint-jobs-tray__icon-button"
207
213
  aria-label="Refresh jobs"
@@ -297,7 +303,7 @@ async function clearFinished(): Promise<void> {
297
303
  <span>{{ progressText(job) }}</span>
298
304
  <div class="mint-jobs-tray__job-actions">
299
305
  <button
300
- v-if="job.can_cancel && adapter?.cancelJob"
306
+ v-if="job.can_cancel && tray.canCancel.value"
301
307
  type="button"
302
308
  class="mint-jobs-tray__action mint-jobs-tray__action--danger"
303
309
  aria-label="Cancel job"
@@ -307,7 +313,7 @@ async function clearFinished(): Promise<void> {
307
313
  Cancel
308
314
  </button>
309
315
  <button
310
- v-if="job.can_delete && adapter?.deleteJob"
316
+ v-if="job.can_delete && tray.canDelete.value"
311
317
  type="button"
312
318
  class="mint-jobs-tray__icon-button mint-jobs-tray__icon-button--danger"
313
319
  aria-label="Delete job"
@@ -359,7 +365,7 @@ async function clearFinished(): Promise<void> {
359
365
  <div class="mint-jobs-tray__job-footer">
360
366
  <span>{{ formatTimestamp(job.completed_at || job.created_at) }}</span>
361
367
  <button
362
- v-if="job.can_delete && adapter?.deleteJob"
368
+ v-if="job.can_delete && tray.canDelete.value"
363
369
  type="button"
364
370
  class="mint-jobs-tray__icon-button mint-jobs-tray__icon-button--danger"
365
371
  aria-label="Delete job"
@@ -42,6 +42,9 @@ export { default as AppSidebar } from './AppSidebar.vue'
42
42
  export { default as AppLayout } from './AppLayout.vue'
43
43
  export { default as MobileSupportGate } from './MobileSupportGate.vue'
44
44
  export { default as PluginWorkspaceView } from './PluginWorkspaceView.vue'
45
+ export { default as GeneratedPluginView } from './GeneratedPluginView.vue'
46
+ export { default as GeneratedResultRenderer } from './GeneratedResultRenderer.vue'
47
+ export { default as GeneratedPlotlyResult } from './GeneratedPlotlyResult.vue'
45
48
  export { default as ComponentBindingRenderer } from './ComponentBindingRenderer.vue'
46
49
  export { default as ControlWorkspaceView } from './ControlWorkspaceView.vue'
47
50
  export { default as DoseDesignWorkspaceView } from './DoseDesignWorkspaceView.vue'
@@ -127,6 +130,7 @@ export { default as ResourceCard } from './ResourceCard.vue'
127
130
  // Experiment / analysis components
128
131
  export { default as ExperimentSelectorModal } from './ExperimentSelectorModal.vue'
129
132
  export { default as ArtifactSelectorModal } from './ArtifactSelectorModal.vue'
133
+ export { default as GeneratedPathInput } from './GeneratedPathInput.vue'
130
134
  export { default as ArtifactSaveDialog } from './ArtifactSaveDialog.vue'
131
135
  export { default as ExperimentPopover } from './ExperimentPopover.vue'
132
136
  export { default as FitPanel } from './FitPanel.vue'
@@ -15,6 +15,7 @@ import DatePicker from '../components/DatePicker.vue'
15
15
  import TimePicker from '../components/TimePicker.vue'
16
16
  import DateTimePicker from '../components/DateTimePicker.vue'
17
17
  import FileUploader from '../components/FileUploader.vue'
18
+ import GeneratedPathInput from '../components/GeneratedPathInput.vue'
18
19
  import FormulaInput from '../components/FormulaInput.vue'
19
20
  import SequenceInput from '../components/SequenceInput.vue'
20
21
  import MoleculeInput from '../components/MoleculeInput.vue'
@@ -49,6 +50,7 @@ const registry: Record<FormFieldType, RegistryEntry> = {
49
50
  time: { component: TimePicker, defaults: {}, vModel: true },
50
51
  datetime: { component: DateTimePicker, defaults: {}, vModel: true },
51
52
  file: { component: FileUploader, defaults: {}, vModel: false },
53
+ path: { component: GeneratedPathInput, defaults: {}, vModel: true },
52
54
  formula: { component: FormulaInput, defaults: {}, vModel: true },
53
55
  sequence: { component: SequenceInput, defaults: {}, vModel: true },
54
56
  molecule: { component: MoleculeInput, defaults: {}, vModel: true },
@@ -125,6 +125,18 @@ export {
125
125
  type UseDebouncedWatchReturn,
126
126
  } from './useDebouncedWatch'
127
127
  export { useFormBuilder, evaluateCondition } from './useFormBuilder'
128
+ export {
129
+ createGeneratedAnalysisTransport,
130
+ useGeneratedAnalysis,
131
+ usePluginJobs,
132
+ type GeneratedAnalysisTransport,
133
+ type GeneratedAnalysisOptions,
134
+ type PluginJobRequestOptions,
135
+ type PluginJobWaitOptions,
136
+ type UsePluginJobsOptions,
137
+ type UsePluginJobsReturn,
138
+ type UseGeneratedAnalysisReturn,
139
+ } from './useGeneratedAnalysis'
128
140
  export { useAutoGroup, DEFAULT_COLORS, extractSamplesFromDesignData } from './useAutoGroup'
129
141
  export {
130
142
  useReagentSeries,
@@ -482,10 +494,16 @@ export {
482
494
  type UsePluginSettingsReturn,
483
495
  } from './usePluginClient'
484
496
  export {
497
+ combinePluginJobSources,
498
+ usePluginJobCenter,
485
499
  useJobsStatusTray,
500
+ type CombinedPluginJobCenterSource,
501
+ type PluginJobCenterSource,
486
502
  type JobsStatusTrayAdapter,
487
503
  type JobsStatusTrayTab,
488
504
  type JobStreamMessage,
505
+ type UsePluginJobCenterOptions,
506
+ type UsePluginJobCenterReturn,
489
507
  type UseJobsStatusTrayOptions,
490
508
  type UseJobsStatusTrayReturn,
491
509
  } from './useJobsStatusTray'
@@ -22,6 +22,7 @@ type MutableHeaders = Record<string, unknown> & {
22
22
  }
23
23
 
24
24
  function joinUrlPath(baseUrl: string, path: string): string {
25
+ if (/^https?:\/\//i.test(path)) return path
25
26
  if (!path) return baseUrl
26
27
  if (path.startsWith('?') || path.startsWith('#')) return `${baseUrl.replace(/\/+$/, '')}${path}`
27
28
  const normalizedBase = baseUrl.replace(/\/+$/, '')