@morscherlab/mint-sdk 1.0.63 → 1.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -2
- package/dist/EmptyState-D1c4RdC7.js +638 -0
- package/dist/EmptyState-D1c4RdC7.js.map +1 -0
- package/dist/{ExperimentPopover-BbPkIFsI.js → ExperimentPopover-BFw3QX8L.js} +10 -9
- package/dist/{ExperimentPopover-BbPkIFsI.js.map → ExperimentPopover-BFw3QX8L.js.map} +1 -1
- package/dist/{ExperimentSelectorModal-DZeSd-yr.js → ExperimentSelectorModal-B7w6k3Y8.js} +12 -216
- package/dist/ExperimentSelectorModal-B7w6k3Y8.js.map +1 -0
- package/dist/{SettingsModal-CJgLadsN.js → SettingsModal-DPDwCbXK.js} +12 -9
- package/dist/{SettingsModal-CJgLadsN.js.map → SettingsModal-DPDwCbXK.js.map} +1 -1
- package/dist/__tests__/components/GeneratedPlotlyResult.test.d.ts +1 -0
- package/dist/__tests__/components/GeneratedPluginView.test.d.ts +1 -0
- package/dist/__tests__/components/GeneratedResultRenderer.test.d.ts +1 -0
- package/dist/__tests__/components/InstrumentAlertLog.test.d.ts +1 -0
- package/dist/__tests__/composables/useGeneratedAnalysis.test.d.ts +1 -0
- package/dist/__tests__/generatedUi.test.d.ts +1 -0
- package/dist/{analysisArtifactTypes-Cu40dzSG.js → analysisArtifactTypes-ejqR8MX3.js} +1 -1
- package/dist/analysisArtifactTypes-ejqR8MX3.js.map +1 -0
- package/dist/{auth-DZAwEeis.js → auth-B87xM--s.js} +1 -1
- package/dist/{auth-DZAwEeis.js.map → auth-B87xM--s.js.map} +1 -1
- package/dist/chunk-BoAXSpZd.js +33 -0
- package/dist/components/AppSidebar.vue.d.ts +1 -1
- package/dist/components/AutoGroupModal.vue.d.ts +2 -2
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
- package/dist/components/CollapsibleCard.vue.d.ts +1 -1
- package/dist/components/ComponentBindingRenderer.vue.d.ts +1 -1
- package/dist/components/ConfirmDialog.vue.d.ts +1 -1
- package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
- package/dist/components/ExperimentTimeline.vue.d.ts +1 -1
- package/dist/components/FileUploader.vue.d.ts +4 -2
- package/dist/components/FitPanel.vue.d.ts +1 -1
- package/dist/components/FormActions.vue.d.ts +1 -1
- package/dist/components/FormBuilder.vue.d.ts +1 -1
- package/dist/components/GeneratedPathInput.vue.d.ts +12 -0
- package/dist/components/GeneratedPlotlyResult.vue.d.ts +11 -0
- package/dist/components/GeneratedPluginView.vue.d.ts +10 -0
- package/dist/components/GeneratedResultRenderer.vue.d.ts +6 -0
- package/dist/components/JobsStatusTray.vue.d.ts +5 -1
- package/dist/components/LcmsSequenceTable.vue.d.ts +1 -1
- package/dist/components/SampleLegend.vue.d.ts +1 -1
- package/dist/components/SampleSelector.vue.d.ts +2 -2
- package/dist/components/SettingsModal.vue.d.ts +1 -1
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -2
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +8 -9
- package/dist/components/internal/PlateMapEditorToolbarInternal.vue.d.ts +2 -2
- package/dist/{components-DdzHh3a_.js → components-UfkPN1bK.js} +2255 -2303
- package/dist/components-UfkPN1bK.js.map +1 -0
- package/dist/composables/index.d.ts +2 -3
- package/dist/composables/index.js +10 -11
- package/dist/composables/platformContextHelpers.d.ts +0 -2
- package/dist/composables/useAnalysisArtifacts.d.ts +1 -3
- package/dist/composables/useGeneratedAnalysis.d.ts +62 -0
- package/dist/composables/useJobsStatusTray.d.ts +48 -4
- package/dist/composables/usePlatformContext.d.ts +3 -0
- package/dist/composables/usePluginClient.d.ts +6 -3
- package/dist/{composables-BUUphBqg.js → composables-B512bWJP.js} +155 -74
- package/dist/composables-B512bWJP.js.map +1 -0
- package/dist/experiment-utils-CxkruZ3G.js +392 -0
- package/dist/experiment-utils-CxkruZ3G.js.map +1 -0
- package/dist/generated/schema.d.ts +7 -0
- package/dist/generated-ui/main.d.ts +0 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -18
- package/dist/index.js.map +1 -1
- package/dist/install.js +7 -8
- package/dist/install.js.map +1 -1
- package/dist/plotly.min-BR53hja4.js +180634 -0
- package/dist/plotly.min-BR53hja4.js.map +1 -0
- package/dist/stores/auth.d.ts +2 -2
- package/dist/stores/index.js +1 -1
- package/dist/styles.css +10543 -10404
- package/dist/templates/index.js +4 -3
- package/dist/templates/packs.d.ts +0 -1
- package/dist/{templates-CNbPQNID.js → templates-Cttbjbyc.js} +2 -4
- package/dist/templates-Cttbjbyc.js.map +1 -0
- package/dist/types/analysisArtifactTypes.d.ts +0 -8
- package/dist/types/form-builder.d.ts +1 -1
- package/dist/types/generated-ui.d.ts +170 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +1 -1
- package/dist/types/instrument.d.ts +1 -0
- package/dist/types/jobs.d.ts +7 -1
- package/dist/types/platform.d.ts +2 -0
- package/dist/{useControlSchema-BZNdalmL.js → useControlSchema-Dallso-L.js} +1852 -171
- package/dist/useControlSchema-Dallso-L.js.map +1 -0
- package/dist/{useDropdownState-Bb-c_PeE.js → useDropdownState-gVaMXtwW.js} +2 -2
- package/dist/{useDropdownState-Bb-c_PeE.js.map → useDropdownState-gVaMXtwW.js.map} +1 -1
- package/dist/{useExperimentSelector-DRbj6vse.js → useExperimentSelector-BMjzNcf2.js} +4 -255
- package/dist/useExperimentSelector-BMjzNcf2.js.map +1 -0
- package/dist/{useFormBuilder-DKekvXRs.js → useFormBuilder-BXaaDI41.js} +2 -2
- package/dist/{useFormBuilder-DKekvXRs.js.map → useFormBuilder-BXaaDI41.js.map} +1 -1
- package/dist/{useAnalysisArtifacts-C3l0a0vu.js → useProtocolTemplates-BP7_4RGQ.js} +307 -475
- package/dist/useProtocolTemplates-BP7_4RGQ.js.map +1 -0
- package/package.json +7 -3
- package/src/__tests__/components/GeneratedPathInput.test.ts +132 -0
- package/src/__tests__/components/GeneratedPlotlyResult.test.ts +117 -0
- package/src/__tests__/components/GeneratedPluginView.test.ts +230 -0
- package/src/__tests__/components/GeneratedResultRenderer.test.ts +93 -0
- package/src/__tests__/components/InstrumentAlertLog.test.ts +59 -0
- package/src/__tests__/composables/formBuilderRegistry.test.ts +5 -1
- package/src/__tests__/composables/useAnalysisArtifacts.test.ts +1 -74
- package/src/__tests__/composables/useApi.test.ts +8 -0
- package/src/__tests__/composables/useGeneratedAnalysis.test.ts +1740 -0
- package/src/__tests__/composables/useJobsStatusTray.test.ts +179 -1
- package/src/__tests__/composables/usePlatformContext.test.ts +28 -0
- package/src/__tests__/composables/usePluginClient.test.ts +366 -20
- package/src/__tests__/composables/usePluginConfig.test.ts +323 -7
- package/src/__tests__/docs/extractDocsTemplates.test.ts +0 -1
- package/src/__tests__/generatedUi.test.ts +119 -0
- package/src/__tests__/templates/templates.test.ts +1 -1
- package/src/components/FileUploader.vue +49 -11
- package/src/components/GeneratedPathInput.vue +218 -0
- package/src/components/GeneratedPlotlyResult.vue +87 -0
- package/src/components/GeneratedPluginView.vue +279 -0
- package/src/components/GeneratedResultRenderer.vue +137 -0
- package/src/components/InstrumentAlertLog.vue +2 -2
- package/src/components/JobsStatusTray.vue +14 -8
- package/src/components/index.ts +4 -0
- package/src/composables/formBuilderRegistry.ts +2 -0
- package/src/composables/index.ts +18 -5
- package/src/composables/platformContextHelpers.ts +3 -10
- package/src/composables/useAnalysisArtifacts.ts +0 -48
- package/src/composables/useApi.ts +1 -0
- package/src/composables/useGeneratedAnalysis.ts +1252 -0
- package/src/composables/useJobsStatusTray.ts +234 -45
- package/src/composables/usePluginClient.ts +152 -25
- package/src/composables/usePluginConfig.ts +156 -20
- package/src/generated/schema.ts +130 -0
- package/src/generated-ui/main.ts +21 -0
- package/src/index.ts +3 -0
- package/src/jobs.ts +5 -0
- package/src/plotly-dist.d.ts +3 -0
- package/src/templates/packs.ts +0 -3
- package/src/types/analysisArtifactTypes.ts +0 -9
- package/src/types/form-builder.ts +1 -0
- package/src/types/generated-ui.ts +176 -0
- package/src/types/index.ts +2 -1
- package/src/types/instrument.ts +1 -0
- package/src/types/jobs.ts +10 -1
- package/src/types/platform.ts +2 -0
- package/dist/BaseButton-Dgqrze41.js +0 -74
- package/dist/BaseButton-Dgqrze41.js.map +0 -1
- package/dist/BaseModal-B9UA8Y_I.js +0 -165
- package/dist/BaseModal-B9UA8Y_I.js.map +0 -1
- package/dist/BaseSelect-ekgr9fDo.js +0 -179
- package/dist/BaseSelect-ekgr9fDo.js.map +0 -1
- package/dist/ExperimentPopover-Ryusjrhv.js +0 -4
- package/dist/ExperimentSelectorModal-Cg4-LXBD.js +0 -5
- package/dist/ExperimentSelectorModal-DZeSd-yr.js.map +0 -1
- package/dist/SettingsModal-Bl_9w2Y3.js +0 -5
- package/dist/analysisArtifactTypes-Cu40dzSG.js.map +0 -1
- package/dist/components-DdzHh3a_.js.map +0 -1
- package/dist/composables/analysisArtifactTarget.d.ts +0 -5
- package/dist/composables-BUUphBqg.js.map +0 -1
- package/dist/experiment-utils-Bfa7CwPU.js +0 -108
- package/dist/experiment-utils-Bfa7CwPU.js.map +0 -1
- package/dist/templates-CNbPQNID.js.map +0 -1
- package/dist/useAnalysisArtifacts-C3l0a0vu.js.map +0 -1
- package/dist/useControlSchema-BZNdalmL.js.map +0 -1
- package/dist/useEventListener-CfVkP9Xz.js +0 -57
- package/dist/useEventListener-CfVkP9Xz.js.map +0 -1
- package/dist/useExperimentSelector-DRbj6vse.js.map +0 -1
- package/src/__tests__/composables/analysisArtifactTarget.test.ts +0 -77
- package/src/composables/analysisArtifactTarget.ts +0 -53
- /package/dist/__tests__/{composables/analysisArtifactTarget.test.d.ts → components/GeneratedPathInput.test.d.ts} +0 -0
|
@@ -26,6 +26,127 @@ import type {
|
|
|
26
26
|
|
|
27
27
|
export type JobsStatusTrayTab = 'active' | 'history'
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* The canonical owner of a collection of plugin jobs.
|
|
31
|
+
*
|
|
32
|
+
* Sources, rather than job-center UI, own polling, event streams, and all job
|
|
33
|
+
* mutations. This keeps components and consumers on one reactive job graph.
|
|
34
|
+
*/
|
|
35
|
+
export interface PluginJobCenterSource {
|
|
36
|
+
jobs: MaybeRefOrGetter<readonly JobStateInput[]>
|
|
37
|
+
refreshJobs?: () => Promise<JobListPayload | readonly JobStateInput[] | void>
|
|
38
|
+
cancelJob?: (jobId: string) => Promise<JobStateInput | void>
|
|
39
|
+
deleteJob?: (jobId: string) => Promise<boolean | void>
|
|
40
|
+
clearFinishedJobs?: () => Promise<number | void>
|
|
41
|
+
dispose?: () => void
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface CombinedPluginJobCenterSource extends PluginJobCenterSource {
|
|
45
|
+
jobs: ComputedRef<readonly JobStateInput[]>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Combines independent job runtimes without copying their reactive state.
|
|
50
|
+
*
|
|
51
|
+
* Job ids must be globally unique across the sources. A duplicate id throws
|
|
52
|
+
* when jobs are read or an id-routed action is requested, rather than silently
|
|
53
|
+
* dispatching a mutation to the wrong owner.
|
|
54
|
+
*/
|
|
55
|
+
export function combinePluginJobSources(
|
|
56
|
+
sources: readonly PluginJobCenterSource[],
|
|
57
|
+
): CombinedPluginJobCenterSource {
|
|
58
|
+
const jobs = computed<readonly JobStateInput[]>(() => {
|
|
59
|
+
const combined: JobStateInput[] = []
|
|
60
|
+
const owners = new Set<string>()
|
|
61
|
+
for (const source of sources) {
|
|
62
|
+
for (const job of toValue(source.jobs)) {
|
|
63
|
+
if (owners.has(job.job_id)) throw duplicatePluginJobId(job.job_id)
|
|
64
|
+
owners.add(job.job_id)
|
|
65
|
+
combined.push(job)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return combined
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
function ownerFor(jobId: string): PluginJobCenterSource {
|
|
72
|
+
let owner: PluginJobCenterSource | undefined
|
|
73
|
+
for (const source of sources) {
|
|
74
|
+
if (!toValue(source.jobs).some(job => job.job_id === jobId)) continue
|
|
75
|
+
if (owner) throw duplicatePluginJobId(jobId)
|
|
76
|
+
owner = source
|
|
77
|
+
}
|
|
78
|
+
if (!owner) throw new Error(`Unknown plugin job id: ${jobId}`)
|
|
79
|
+
return owner
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const combined: CombinedPluginJobCenterSource = { jobs }
|
|
83
|
+
if (sources.some(source => source.refreshJobs)) {
|
|
84
|
+
combined.refreshJobs = async () => {
|
|
85
|
+
await Promise.all(sources.map(source => source.refreshJobs?.()))
|
|
86
|
+
return jobs.value
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (sources.some(source => source.cancelJob)) {
|
|
90
|
+
combined.cancelJob = async (jobId) => {
|
|
91
|
+
const owner = ownerFor(jobId)
|
|
92
|
+
if (!owner.cancelJob) throw new Error(`Job ${jobId} cannot be cancelled by its source`)
|
|
93
|
+
return owner.cancelJob(jobId)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (sources.length > 0 && sources.every(source => source.deleteJob)) {
|
|
97
|
+
combined.deleteJob = async (jobId) => {
|
|
98
|
+
const owner = ownerFor(jobId)
|
|
99
|
+
if (!owner.deleteJob) throw new Error(`Job ${jobId} cannot be deleted by its source`)
|
|
100
|
+
return owner.deleteJob(jobId)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Never report a successful combined clear after mutating only a subset.
|
|
104
|
+
// Without bulk support from every source, the job center can still fall
|
|
105
|
+
// back to this source's id-routed deleteJob method when all owners expose it.
|
|
106
|
+
if (sources.length > 0 && sources.every(source => source.clearFinishedJobs)) {
|
|
107
|
+
combined.clearFinishedJobs = async () => {
|
|
108
|
+
const results = await Promise.all(
|
|
109
|
+
sources.map(source => source.clearFinishedJobs!()),
|
|
110
|
+
)
|
|
111
|
+
return results.every((result): result is number => typeof result === 'number')
|
|
112
|
+
? results.reduce((total, result) => total + result, 0)
|
|
113
|
+
: undefined
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (sources.some(source => source.dispose)) {
|
|
117
|
+
combined.dispose = () => {
|
|
118
|
+
const errors: unknown[] = []
|
|
119
|
+
for (const source of sources) {
|
|
120
|
+
try {
|
|
121
|
+
source.dispose?.()
|
|
122
|
+
} catch (cause) {
|
|
123
|
+
errors.push(cause)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (errors.length === 1) throw errors[0]
|
|
127
|
+
if (errors.length > 1) throw new PluginJobSourceDisposeError(errors)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return combined
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
class PluginJobSourceDisposeError extends Error {
|
|
134
|
+
readonly cause: unknown
|
|
135
|
+
readonly errors: readonly unknown[]
|
|
136
|
+
|
|
137
|
+
constructor(errors: readonly unknown[]) {
|
|
138
|
+
super('Failed to dispose plugin job sources')
|
|
139
|
+
this.name = 'PluginJobSourceDisposeError'
|
|
140
|
+
this.cause = errors[0]
|
|
141
|
+
this.errors = Object.freeze([...errors])
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function duplicatePluginJobId(jobId: string): Error {
|
|
146
|
+
return new Error(`Duplicate plugin job id across combined sources: ${jobId}`)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** @deprecated Prefer a {@link PluginJobCenterSource}. */
|
|
29
150
|
export interface JobsStatusTrayAdapter {
|
|
30
151
|
listJobs?: () => Promise<JobListPayload | readonly JobStateInput[]>
|
|
31
152
|
cancelJob?: (job: JobState) => Promise<JobStateInput | void>
|
|
@@ -33,24 +154,34 @@ export interface JobsStatusTrayAdapter {
|
|
|
33
154
|
clearFinishedJobs?: (jobs: readonly JobState[]) => Promise<number | void>
|
|
34
155
|
}
|
|
35
156
|
|
|
36
|
-
export interface
|
|
157
|
+
export interface UsePluginJobCenterOptions {
|
|
158
|
+
source?: MaybeRefOrGetter<PluginJobCenterSource | undefined>
|
|
159
|
+
/** @deprecated Pass a source with its canonical jobs ref. */
|
|
37
160
|
jobs?: MaybeRefOrGetter<readonly JobStateInput[] | undefined>
|
|
161
|
+
/** @deprecated Pass source actions instead. */
|
|
38
162
|
adapter?: MaybeRefOrGetter<JobsStatusTrayAdapter | undefined>
|
|
163
|
+
/** @deprecated Event handling belongs to the source runtime. */
|
|
39
164
|
eventStream?: MaybeRefOrGetter<UsePluginEventStreamReturn<JobStreamData> | undefined>
|
|
40
165
|
loadOnMount?: boolean
|
|
41
166
|
}
|
|
42
167
|
|
|
168
|
+
export interface UseJobsStatusTrayOptions extends UsePluginJobCenterOptions {}
|
|
169
|
+
|
|
43
170
|
export interface JobStreamMessage {
|
|
44
171
|
event: string
|
|
45
172
|
data: JobStreamData
|
|
46
173
|
}
|
|
47
174
|
|
|
48
|
-
export interface
|
|
49
|
-
jobs:
|
|
175
|
+
export interface UsePluginJobCenterReturn {
|
|
176
|
+
jobs: ComputedRef<JobState[]>
|
|
50
177
|
activeJobs: ComputedRef<JobState[]>
|
|
51
178
|
historyJobs: ComputedRef<JobState[]>
|
|
52
179
|
deletableJobs: ComputedRef<JobState[]>
|
|
53
180
|
featuredJob: ComputedRef<JobState | null>
|
|
181
|
+
canRefresh: ComputedRef<boolean>
|
|
182
|
+
canCancel: ComputedRef<boolean>
|
|
183
|
+
canDelete: ComputedRef<boolean>
|
|
184
|
+
canClearFinished: ComputedRef<boolean>
|
|
54
185
|
isOpen: Ref<boolean>
|
|
55
186
|
activeTab: Ref<JobsStatusTrayTab>
|
|
56
187
|
isLoading: Ref<boolean>
|
|
@@ -67,6 +198,13 @@ export interface UseJobsStatusTrayReturn {
|
|
|
67
198
|
isPending: (job: JobState) => boolean
|
|
68
199
|
}
|
|
69
200
|
|
|
201
|
+
export type UseJobsStatusTrayReturn = UsePluginJobCenterReturn
|
|
202
|
+
|
|
203
|
+
interface LegacyJobsOverride {
|
|
204
|
+
readonly input: readonly JobStateInput[] | undefined
|
|
205
|
+
readonly jobs: JobState[]
|
|
206
|
+
}
|
|
207
|
+
|
|
70
208
|
function responseJobs(
|
|
71
209
|
response: JobListPayload | readonly JobStateInput[],
|
|
72
210
|
): readonly JobStateInput[] {
|
|
@@ -81,11 +219,16 @@ function errorMessage(error: unknown, fallback: string): string {
|
|
|
81
219
|
return error instanceof Error ? error.message : fallback
|
|
82
220
|
}
|
|
83
221
|
|
|
84
|
-
/**
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
222
|
+
/**
|
|
223
|
+
* Headless controller for plugin job presentation and actions.
|
|
224
|
+
*
|
|
225
|
+
* When a source is supplied, every job list is derived directly from that
|
|
226
|
+
* source. The controller never mirrors source jobs into another ref.
|
|
227
|
+
*/
|
|
228
|
+
export function usePluginJobCenter(
|
|
229
|
+
options: UsePluginJobCenterOptions = {},
|
|
230
|
+
): UsePluginJobCenterReturn {
|
|
231
|
+
const legacyOverride = shallowRef<LegacyJobsOverride | null>(null)
|
|
89
232
|
const isOpen = ref(false)
|
|
90
233
|
const activeTab = ref<JobsStatusTrayTab>('active')
|
|
91
234
|
const isLoading = ref(false)
|
|
@@ -95,44 +238,70 @@ export function useJobsStatusTray(
|
|
|
95
238
|
let stateRevision = 0
|
|
96
239
|
let requestGeneration = 0
|
|
97
240
|
let active = true
|
|
241
|
+
const source = computed(() => toValue(options.source))
|
|
98
242
|
const adapter = computed(() => toValue(options.adapter))
|
|
99
243
|
|
|
244
|
+
const jobs = computed<JobState[]>(() => {
|
|
245
|
+
const currentSource = source.value
|
|
246
|
+
if (currentSource) {
|
|
247
|
+
return [...toValue(currentSource.jobs)].map(normalizeJobState)
|
|
248
|
+
}
|
|
249
|
+
const input = toValue(options.jobs)
|
|
250
|
+
const override = legacyOverride.value
|
|
251
|
+
if (override && override.input === input) return override.jobs
|
|
252
|
+
return input ? [...input].map(normalizeJobState) : []
|
|
253
|
+
})
|
|
100
254
|
const activeJobs = computed(() => jobs.value.filter(job => isActiveJobStatus(job.status)))
|
|
101
255
|
const historyJobs = computed(() => jobs.value.filter(job => !isActiveJobStatus(job.status)))
|
|
102
256
|
const deletableJobs = computed(() => historyJobs.value.filter(job => job.can_delete))
|
|
103
257
|
const featuredJob = computed(() => activeJobs.value[0] ?? historyJobs.value[0] ?? null)
|
|
258
|
+
const canRefresh = computed(() => Boolean(source.value?.refreshJobs || adapter.value?.listJobs))
|
|
259
|
+
const canCancel = computed(() => Boolean(source.value?.cancelJob || adapter.value?.cancelJob))
|
|
260
|
+
const canDelete = computed(() => Boolean(source.value?.deleteJob || adapter.value?.deleteJob))
|
|
261
|
+
const canClearFinished = computed(() => Boolean(
|
|
262
|
+
source.value?.clearFinishedJobs
|
|
263
|
+
|| source.value?.deleteJob
|
|
264
|
+
|| adapter.value?.clearFinishedJobs
|
|
265
|
+
|| adapter.value?.deleteJob,
|
|
266
|
+
))
|
|
104
267
|
|
|
105
268
|
function bumpJobRevision(jobId: string): void {
|
|
106
269
|
jobRevisions.set(jobId, (jobRevisions.get(jobId) ?? 0) + 1)
|
|
107
270
|
}
|
|
108
271
|
|
|
109
|
-
function
|
|
272
|
+
function replaceLegacyJobs(nextJobs: readonly JobStateInput[]): void {
|
|
110
273
|
const normalized = nextJobs.map(normalizeJobState)
|
|
111
274
|
const touchedJobIds = new Set([
|
|
112
275
|
...jobs.value.map(job => job.job_id),
|
|
113
276
|
...normalized.map(job => job.job_id),
|
|
114
277
|
])
|
|
115
278
|
touchedJobIds.forEach(bumpJobRevision)
|
|
116
|
-
|
|
279
|
+
legacyOverride.value = {
|
|
280
|
+
input: toValue(options.jobs),
|
|
281
|
+
jobs: normalized,
|
|
282
|
+
}
|
|
117
283
|
stateRevision += 1
|
|
118
284
|
}
|
|
119
285
|
|
|
120
|
-
function
|
|
286
|
+
function upsertLegacyJob(nextJob: JobStateInput): void {
|
|
121
287
|
const normalized = normalizeJobState(nextJob)
|
|
122
288
|
const existingIndex = jobs.value.findIndex(job => job.job_id === normalized.job_id)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
jobs
|
|
289
|
+
const nextJobs = [...jobs.value]
|
|
290
|
+
if (existingIndex === -1) nextJobs.unshift(normalized)
|
|
291
|
+
else nextJobs[existingIndex] = normalized
|
|
292
|
+
legacyOverride.value = {
|
|
293
|
+
input: toValue(options.jobs),
|
|
294
|
+
jobs: nextJobs,
|
|
129
295
|
}
|
|
130
296
|
bumpJobRevision(normalized.job_id)
|
|
131
297
|
stateRevision += 1
|
|
132
298
|
}
|
|
133
299
|
|
|
134
|
-
function
|
|
135
|
-
|
|
300
|
+
function removeLegacyJobs(jobIds: Set<string>): void {
|
|
301
|
+
legacyOverride.value = {
|
|
302
|
+
input: toValue(options.jobs),
|
|
303
|
+
jobs: jobs.value.filter(job => !jobIds.has(job.job_id)),
|
|
304
|
+
}
|
|
136
305
|
jobIds.forEach(bumpJobRevision)
|
|
137
306
|
stateRevision += 1
|
|
138
307
|
}
|
|
@@ -145,21 +314,22 @@ export function useJobsStatusTray(
|
|
|
145
314
|
}
|
|
146
315
|
|
|
147
316
|
function applyStreamMessage(message: JobStreamMessage): void {
|
|
148
|
-
if (!active) return
|
|
317
|
+
if (!active || source.value) return
|
|
149
318
|
if (message.event === 'snapshot' && isSnapshotPayload(message.data)) {
|
|
150
|
-
|
|
319
|
+
replaceLegacyJobs(message.data.jobs)
|
|
151
320
|
return
|
|
152
321
|
}
|
|
153
322
|
if (
|
|
154
323
|
(message.event === 'job' || message.event === 'terminal')
|
|
155
324
|
&& !isSnapshotPayload(message.data)
|
|
156
325
|
) {
|
|
157
|
-
|
|
326
|
+
upsertLegacyJob(message.data)
|
|
158
327
|
}
|
|
159
328
|
}
|
|
160
329
|
|
|
161
330
|
async function refresh(): Promise<boolean> {
|
|
162
|
-
const
|
|
331
|
+
const currentSource = source.value
|
|
332
|
+
const listJobs = currentSource?.refreshJobs ?? adapter.value?.listJobs
|
|
163
333
|
if (!listJobs) return false
|
|
164
334
|
const generation = ++requestGeneration
|
|
165
335
|
const revision = stateRevision
|
|
@@ -170,7 +340,7 @@ export function useJobsStatusTray(
|
|
|
170
340
|
if (!active || generation !== requestGeneration || revision !== stateRevision) {
|
|
171
341
|
return false
|
|
172
342
|
}
|
|
173
|
-
|
|
343
|
+
if (!currentSource && response) replaceLegacyJobs(responseJobs(response))
|
|
174
344
|
return true
|
|
175
345
|
} catch (cause) {
|
|
176
346
|
if (active && generation === requestGeneration) {
|
|
@@ -183,18 +353,21 @@ export function useJobsStatusTray(
|
|
|
183
353
|
}
|
|
184
354
|
|
|
185
355
|
async function cancelJob(job: JobState): Promise<boolean> {
|
|
186
|
-
const
|
|
187
|
-
|
|
356
|
+
const sourceCancel = source.value?.cancelJob
|
|
357
|
+
const adapterCancel = adapter.value?.cancelJob
|
|
358
|
+
if (!job.can_cancel || (!sourceCancel && !adapterCancel) || pendingJobIds.value.has(job.job_id)) {
|
|
188
359
|
return false
|
|
189
360
|
}
|
|
190
361
|
const revision = jobRevisions.get(job.job_id) ?? 0
|
|
191
362
|
setPending(job.job_id, true)
|
|
192
363
|
error.value = null
|
|
193
364
|
try {
|
|
194
|
-
const updated =
|
|
365
|
+
const updated = sourceCancel
|
|
366
|
+
? await sourceCancel(job.job_id)
|
|
367
|
+
: await adapterCancel!(job)
|
|
195
368
|
if (!active) return false
|
|
196
|
-
if (revision === (jobRevisions.get(job.job_id) ?? 0)) {
|
|
197
|
-
if (updated)
|
|
369
|
+
if (!sourceCancel && revision === (jobRevisions.get(job.job_id) ?? 0)) {
|
|
370
|
+
if (updated) upsertLegacyJob(updated)
|
|
198
371
|
else await refresh()
|
|
199
372
|
}
|
|
200
373
|
return true
|
|
@@ -207,17 +380,22 @@ export function useJobsStatusTray(
|
|
|
207
380
|
}
|
|
208
381
|
|
|
209
382
|
async function deleteJob(job: JobState): Promise<boolean> {
|
|
210
|
-
const
|
|
211
|
-
|
|
383
|
+
const sourceDelete = source.value?.deleteJob
|
|
384
|
+
const adapterDelete = adapter.value?.deleteJob
|
|
385
|
+
if (!job.can_delete || (!sourceDelete && !adapterDelete) || pendingJobIds.value.has(job.job_id)) {
|
|
212
386
|
return false
|
|
213
387
|
}
|
|
214
388
|
setPending(job.job_id, true)
|
|
215
389
|
error.value = null
|
|
216
390
|
try {
|
|
217
|
-
const deleted =
|
|
391
|
+
const deleted = sourceDelete
|
|
392
|
+
? await sourceDelete(job.job_id)
|
|
393
|
+
: await adapterDelete!(job)
|
|
218
394
|
if (!active) return false
|
|
219
|
-
if (
|
|
220
|
-
|
|
395
|
+
if (!sourceDelete) {
|
|
396
|
+
if (deleted === false) await refresh()
|
|
397
|
+
else removeLegacyJobs(new Set([job.job_id]))
|
|
398
|
+
}
|
|
221
399
|
return deleted !== false
|
|
222
400
|
} catch (cause) {
|
|
223
401
|
if (active) error.value = errorMessage(cause, 'Failed to delete job')
|
|
@@ -232,6 +410,16 @@ export function useJobsStatusTray(
|
|
|
232
410
|
if (targets.length === 0) return false
|
|
233
411
|
error.value = null
|
|
234
412
|
try {
|
|
413
|
+
const currentSource = source.value
|
|
414
|
+
if (currentSource?.clearFinishedJobs) {
|
|
415
|
+
const deleted = await currentSource.clearFinishedJobs()
|
|
416
|
+
if (!active) return false
|
|
417
|
+
return typeof deleted !== 'number' || deleted === targets.length
|
|
418
|
+
}
|
|
419
|
+
if (currentSource?.deleteJob) {
|
|
420
|
+
const results = await Promise.all(targets.map(job => deleteJob(job)))
|
|
421
|
+
return results.every(Boolean)
|
|
422
|
+
}
|
|
235
423
|
const currentAdapter = adapter.value
|
|
236
424
|
if (currentAdapter?.clearFinishedJobs) {
|
|
237
425
|
const deleted = await currentAdapter.clearFinishedJobs(targets)
|
|
@@ -240,7 +428,7 @@ export function useJobsStatusTray(
|
|
|
240
428
|
await refresh()
|
|
241
429
|
return false
|
|
242
430
|
}
|
|
243
|
-
|
|
431
|
+
removeLegacyJobs(new Set(targets.map(job => job.job_id)))
|
|
244
432
|
return true
|
|
245
433
|
}
|
|
246
434
|
if (!currentAdapter?.deleteJob) return false
|
|
@@ -252,16 +440,6 @@ export function useJobsStatusTray(
|
|
|
252
440
|
}
|
|
253
441
|
}
|
|
254
442
|
|
|
255
|
-
if (options.jobs !== undefined) {
|
|
256
|
-
watch(
|
|
257
|
-
() => toValue(options.jobs),
|
|
258
|
-
value => {
|
|
259
|
-
if (value) replaceJobs(value)
|
|
260
|
-
},
|
|
261
|
-
{ immediate: true, deep: true },
|
|
262
|
-
)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
443
|
if (options.eventStream !== undefined) {
|
|
266
444
|
watch(
|
|
267
445
|
() => toValue(options.eventStream)?.lastMessage.value ?? null,
|
|
@@ -286,6 +464,10 @@ export function useJobsStatusTray(
|
|
|
286
464
|
historyJobs,
|
|
287
465
|
deletableJobs,
|
|
288
466
|
featuredJob,
|
|
467
|
+
canRefresh,
|
|
468
|
+
canCancel,
|
|
469
|
+
canDelete,
|
|
470
|
+
canClearFinished,
|
|
289
471
|
isOpen,
|
|
290
472
|
activeTab,
|
|
291
473
|
isLoading,
|
|
@@ -302,3 +484,10 @@ export function useJobsStatusTray(
|
|
|
302
484
|
isPending: job => pendingJobIds.value.has(job.job_id),
|
|
303
485
|
}
|
|
304
486
|
}
|
|
487
|
+
|
|
488
|
+
/** Backward-compatible name for the plugin job-center controller. */
|
|
489
|
+
export function useJobsStatusTray(
|
|
490
|
+
options: UseJobsStatusTrayOptions = {},
|
|
491
|
+
): UseJobsStatusTrayReturn {
|
|
492
|
+
return usePluginJobCenter(options)
|
|
493
|
+
}
|