@morscherlab/mint-sdk 1.4.0-beta.9 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -3
- package/dist/{AlertBox-B2Bgr3ei.js → AlertBox-DwgNTEqf.js} +3 -3
- package/dist/{AlertBox-B2Bgr3ei.js.map → AlertBox-DwgNTEqf.js.map} +1 -1
- package/dist/{AppSidebar-Dq9W1jE2.js → AppSidebar-CZZaxni0.js} +1103 -144
- package/dist/AppSidebar-CZZaxni0.js.map +1 -0
- package/dist/{ConfirmDialog-nbJj51is.js → ConfirmDialog-CjWoxAD1.js} +2 -2
- package/dist/{ConfirmDialog-nbJj51is.js.map → ConfirmDialog-CjWoxAD1.js.map} +1 -1
- package/dist/{ExperimentPopover-5T1fAq2L.js → ExperimentPopover-DujiuQIO.js} +5 -5
- package/dist/{ExperimentPopover-5T1fAq2L.js.map → ExperimentPopover-DujiuQIO.js.map} +1 -1
- package/dist/{ExperimentSelectorModal-65peG4Up.js → ExperimentSelectorModal-B4j4eUOJ.js} +114 -98
- package/dist/ExperimentSelectorModal-B4j4eUOJ.js.map +1 -0
- package/dist/{SegmentedControl-BtnnjWzs.js → SegmentedControl-DyUPH3I5.js} +2 -2
- package/dist/{SegmentedControl-BtnnjWzs.js.map → SegmentedControl-DyUPH3I5.js.map} +1 -1
- package/dist/{SettingsModal-483nyI6b.js → SettingsModal-D2MOOtuO.js} +26 -25
- package/dist/SettingsModal-D2MOOtuO.js.map +1 -0
- package/dist/__stories__/experiment-helpers.d.ts +10 -0
- package/dist/__tests__/components/AppTopBarOnboarding.test.d.ts +1 -0
- package/dist/__tests__/components/FloatingPanelsInModal.test.d.ts +1 -0
- package/dist/__tests__/composables/useFloatingPanel.test.d.ts +1 -0
- package/dist/__tests__/composables/useOnboarding.test.d.ts +1 -0
- package/dist/__tests__/styles/paletteTokens.test.d.ts +1 -0
- package/dist/__tests__/styles/v2AuditRules.test.d.ts +1 -0
- package/dist/components/AppTopBar.vue.d.ts +20 -5
- package/dist/components/BaseButton.vue.d.ts +4 -2
- package/dist/components/ExperimentSelectorModal.vue.d.ts +15 -1
- package/dist/components/JobsStatusTray.vue.d.ts +15 -4
- package/dist/components/PluginWorkspaceView.props.d.ts +3 -0
- package/dist/components/PluginWorkspaceView.vue.d.ts +3 -0
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +3 -3
- package/dist/components/internal/AppTopBarOnboardingInternal.vue.d.ts +7 -0
- package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -1
- package/dist/components/internal/AutoGroupModalInternal.vue.d.ts +2 -2
- package/dist/components/internal/OnboardingLayerInternal.vue.d.ts +7 -0
- package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +8 -0
- package/dist/composables/pluginWorkspaceModel.d.ts +2 -2
- package/dist/composables/useFileImport.d.ts +7 -0
- package/dist/composables/useFloatingPanel.d.ts +29 -0
- package/dist/composables/useJobCenter.d.ts +41 -3
- package/dist/composables/useOnboarding.d.ts +119 -0
- package/dist/{controlSchemaModel-B8CTXagP.js → controlSchemaModel-Bsg0t5Ab.js} +2 -2
- package/dist/{controlSchemaModel-B8CTXagP.js.map → controlSchemaModel-Bsg0t5Ab.js.map} +1 -1
- package/dist/{data-CgILxDLW.js → data-Boeh4uPI.js} +13 -12
- package/dist/{data-CgILxDLW.js.map → data-Boeh4uPI.js.map} +1 -1
- package/dist/data.d.ts +1 -1
- package/dist/data.js +9 -9
- package/dist/{experiment-43OJtH7R.js → experiment-DyO54FQB.js} +25 -61
- package/dist/experiment-DyO54FQB.js.map +1 -0
- package/dist/{experimentStatus-Cqp6qNrR.js → experimentStatus-DCn2QKG7.js} +2 -2
- package/dist/{experimentStatus-Cqp6qNrR.js.map → experimentStatus-DCn2QKG7.js.map} +1 -1
- package/dist/index.js +20 -20
- package/dist/{install-BtLWTk6H.js → install-BEHp7Mo-.js} +15 -15
- package/dist/{install-BtLWTk6H.js.map → install-BEHp7Mo-.js.map} +1 -1
- package/dist/install.js +1 -1
- package/dist/{lab-MHzp-_J8.js → lab-D61eaX6f.js} +68 -61
- package/dist/{lab-MHzp-_J8.js.map → lab-D61eaX6f.js.map} +1 -1
- package/dist/lab.js +4 -4
- package/dist/{records-B6gpM_3e.js → records-D4aQXyZu.js} +193 -133
- package/dist/records-D4aQXyZu.js.map +1 -0
- package/dist/{runtime-7vfl_bI3.js → runtime-q-R0CNLh.js} +400 -33
- package/dist/runtime-q-R0CNLh.js.map +1 -0
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +7 -7
- package/dist/{shell-Cs9OJMnm.js → shell-6CGFvUUw.js} +10 -5
- package/dist/{shell-Cs9OJMnm.js.map → shell-6CGFvUUw.js.map} +1 -1
- package/dist/shell.d.ts +1 -0
- package/dist/shell.js +6 -6
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.js +2 -2
- package/dist/stores/settings.d.ts +4 -12
- package/dist/styles.css +1191 -479
- package/dist/types/components.d.ts +2 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/jobs.d.ts +9 -0
- package/dist/types/platform.d.ts +1 -1
- package/dist/{useDropdownState-8yWMu-7_.js → useDropdownState-uFIz6Yv8.js} +16 -4
- package/dist/useDropdownState-uFIz6Yv8.js.map +1 -0
- package/dist/{useExperimentSamples-DHnxKFAF.js → useExperimentSamples-QEFoq4tf.js} +48 -6
- package/dist/useExperimentSamples-QEFoq4tf.js.map +1 -0
- package/dist/{useListReorder-BitdUhxn.js → useListReorder-DsuzwWGS.js} +122 -172
- package/dist/useListReorder-DsuzwWGS.js.map +1 -0
- package/dist/{useRackEditor-DiwCt77M.js → useRackEditor-KpZ4iJ6o.js} +22 -32
- package/dist/useRackEditor-KpZ4iJ6o.js.map +1 -0
- package/dist/useRealtime-B58Lo-Tm.js +508 -0
- package/dist/useRealtime-B58Lo-Tm.js.map +1 -0
- package/dist/{useRequest-D506eHi8.js → useRequest-BuDmW8Jc.js} +8 -37
- package/dist/useRequest-BuDmW8Jc.js.map +1 -0
- package/package.json +3 -2
- package/src/__stories__/experiment-helpers.ts +3 -3
- package/src/__tests__/components/ActionMenu.test.ts +2 -2
- package/src/__tests__/components/AppTopBar.test.ts +206 -1
- package/src/__tests__/components/AppTopBarOnboarding.test.ts +160 -0
- package/src/__tests__/components/AppTopBarPageSelector.test.ts +125 -9
- package/src/__tests__/components/BaseButton.test.ts +19 -0
- package/src/__tests__/components/BaseModal.test.ts +13 -0
- package/src/__tests__/components/ExperimentSelectorModal.test.ts +34 -1
- package/src/__tests__/components/FloatingPanelsInModal.test.ts +94 -0
- package/src/__tests__/components/GeneratedPluginView.test.ts +1 -1
- package/src/__tests__/components/JobsStatusTray.test.ts +373 -71
- package/src/__tests__/components/PluginWorkspaceView.test.ts +63 -1
- package/src/__tests__/components/SampleSelector.test.ts +20 -0
- package/src/__tests__/components/SettingsModal.test.ts +9 -2
- package/src/__tests__/composables/useFileImport.test.ts +36 -0
- package/src/__tests__/composables/useFloatingPanel.test.ts +68 -0
- package/src/__tests__/composables/useJobCenter.test.ts +262 -3
- package/src/__tests__/composables/useOnboarding.test.ts +317 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +4 -3
- package/src/__tests__/fixtures/table-import/cells.xlsx +0 -0
- package/src/__tests__/fixtures/table-import/samples.xlsx +0 -0
- package/src/__tests__/stores/settings.test.ts +21 -21
- package/src/__tests__/styles/cssTokens.test.ts +4 -1
- package/src/__tests__/styles/menusShellRefresh.test.ts +8 -2
- package/src/__tests__/styles/paletteTokens.test.ts +44 -0
- package/src/__tests__/styles/v2AuditRules.test.ts +104 -0
- package/src/components/ActionMenu.vue +6 -37
- package/src/components/AppTopBar.onboarding.story.vue +69 -0
- package/src/components/AppTopBar.story.vue +59 -4
- package/src/components/AppTopBar.vue +132 -10
- package/src/components/BaseButton.vue +16 -2
- package/src/components/BaseModal.vue +2 -1
- package/src/components/ExperimentSelectorModal.story.vue +38 -0
- package/src/components/ExperimentSelectorModal.vue +20 -1
- package/src/components/GeneratedPluginView.vue +4 -9
- package/src/components/GeneratedResultRenderer.vue +3 -4
- package/src/components/JobsStatusTray.story.vue +97 -43
- package/src/components/JobsStatusTray.vue +287 -290
- package/src/components/LayoutResizeHandle.story.vue +65 -4
- package/src/components/PluginWorkspaceView.props.ts +3 -0
- package/src/components/PluginWorkspaceView.vue +17 -0
- package/src/components/SampleSelector.story.vue +21 -6
- package/src/components/SampleSelector.vue +10 -3
- package/src/components/SectionCard.story.vue +33 -0
- package/src/components/SettingsModal.vue +7 -12
- package/src/components/Tooltip.vue +9 -32
- package/src/components/internal/AppTopBarOnboardingInternal.vue +172 -0
- package/src/components/internal/AppTopBarPageSelectorInternal.vue +140 -72
- package/src/components/internal/DatePickerDateInternal.vue +7 -45
- package/src/components/internal/DatePickerDateTimeInternal.vue +7 -1
- package/src/components/internal/DatePickerTimeInternal.vue +8 -1
- package/src/components/internal/NumberUnitInputInternal.vue +4 -18
- package/src/components/internal/OnboardingLayerInternal.vue +173 -0
- package/src/components/internal/SelectMultipleInternal.vue +6 -1
- package/src/components/internal/SelectSearchableInternal.vue +6 -1
- package/src/components/internal/WellEditPopupInternal.vue +11 -22
- package/src/components/internal/WorkspaceShellInternal.vue +12 -0
- package/src/composables/pluginWorkspaceModel.ts +2 -2
- package/src/composables/useFileImport.ts +51 -1
- package/src/composables/useFloatingPanel.ts +107 -0
- package/src/composables/useJobCenter.ts +188 -6
- package/src/composables/useOnboarding.ts +465 -0
- package/src/composables/usePluginWorkspace.ts +1 -1
- package/src/data.ts +1 -0
- package/src/runtime.ts +2 -1
- package/src/shell.ts +17 -0
- package/src/stores/index.ts +0 -2
- package/src/stores/settings.ts +20 -50
- package/src/styles/components/action-menu.css +1 -1
- package/src/styles/components/app-layout.css +6 -3
- package/src/styles/components/app-page-selector.css +120 -45
- package/src/styles/components/app-top-bar.css +165 -3
- package/src/styles/components/button.css +8 -0
- package/src/styles/components/dataframe.css +1 -1
- package/src/styles/components/date-picker.css +4 -2
- package/src/styles/components/datetime-picker.css +4 -4
- package/src/styles/components/experiment-selector-modal.css +13 -0
- package/src/styles/components/jobs-status-tray.css +134 -296
- package/src/styles/components/layout-resize-handle.css +59 -16
- package/src/styles/components/modal.css +3 -3
- package/src/styles/components/number-input.css +1 -1
- package/src/styles/components/onboarding.css +368 -0
- package/src/styles/components/progress-bar.css +3 -3
- package/src/styles/components/sample-selector.css +43 -0
- package/src/styles/components/searchable-select.css +3 -7
- package/src/styles/components/section-card.css +30 -6
- package/src/styles/components/settings-modal.css +7 -2
- package/src/styles/components/time-picker.css +3 -6
- package/src/styles/components/toast.css +1 -1
- package/src/styles/components/tooltip.css +2 -2
- package/src/styles/components/unit-input.css +1 -1
- package/src/styles/components/well-edit-popup.css +2 -2
- package/src/styles/index.css +1 -0
- package/src/styles/variables.css +153 -15
- package/src/types/components.ts +2 -0
- package/src/types/index.ts +1 -0
- package/src/types/jobs.ts +10 -0
- package/src/types/platform.ts +1 -1
- package/dist/AppSidebar-Dq9W1jE2.js.map +0 -1
- package/dist/ExperimentSelectorModal-65peG4Up.js.map +0 -1
- package/dist/SettingsModal-483nyI6b.js.map +0 -1
- package/dist/experiment-43OJtH7R.js.map +0 -1
- package/dist/records-B6gpM_3e.js.map +0 -1
- package/dist/runtime-7vfl_bI3.js.map +0 -1
- package/dist/useDropdownState-8yWMu-7_.js.map +0 -1
- package/dist/useExperimentSamples-DHnxKFAF.js.map +0 -1
- package/dist/useListReorder-BitdUhxn.js.map +0 -1
- package/dist/useRackEditor-DiwCt77M.js.map +0 -1
- package/dist/useRealtime-C3iy9QkU.js +0 -699
- package/dist/useRealtime-C3iy9QkU.js.map +0 -1
- package/dist/useRequest-D506eHi8.js.map +0 -1
|
@@ -1,699 +0,0 @@
|
|
|
1
|
-
import { d as normalizeJobState, l as jobStatusLabel, s as isActiveJobStatus, u as normalizeJobPercent } from "./records-B6gpM_3e.js";
|
|
2
|
-
import { Fragment, Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, getCurrentScope, nextTick, normalizeClass, normalizeStyle, onMounted, onScopeDispose, onUnmounted, openBlock, readonly, ref, renderList, renderSlot, shallowRef, toDisplayString, toRef, toValue, unref, useId, watch, withCtx } from "vue";
|
|
3
|
-
//#region src/composables/useJobCenter.ts
|
|
4
|
-
/**
|
|
5
|
-
* Combines independent job runtimes without copying their reactive state.
|
|
6
|
-
*
|
|
7
|
-
* Job ids must be globally unique across the sources. A duplicate id throws
|
|
8
|
-
* when jobs are read or an id-routed action is requested, rather than silently
|
|
9
|
-
* dispatching a mutation to the wrong owner.
|
|
10
|
-
*/
|
|
11
|
-
function combinePluginJobSources(sources) {
|
|
12
|
-
const jobs = computed(() => {
|
|
13
|
-
const combined = [];
|
|
14
|
-
const owners = /* @__PURE__ */ new Set();
|
|
15
|
-
for (const source of sources) for (const job of toValue(source.jobs)) {
|
|
16
|
-
if (owners.has(job.job_id)) throw duplicatePluginJobId(job.job_id);
|
|
17
|
-
owners.add(job.job_id);
|
|
18
|
-
combined.push(job);
|
|
19
|
-
}
|
|
20
|
-
return combined;
|
|
21
|
-
});
|
|
22
|
-
function ownerFor(jobId) {
|
|
23
|
-
let owner;
|
|
24
|
-
for (const source of sources) {
|
|
25
|
-
if (!toValue(source.jobs).some((job) => job.job_id === jobId)) continue;
|
|
26
|
-
if (owner) throw duplicatePluginJobId(jobId);
|
|
27
|
-
owner = source;
|
|
28
|
-
}
|
|
29
|
-
if (!owner) throw new Error(`Unknown plugin job id: ${jobId}`);
|
|
30
|
-
return owner;
|
|
31
|
-
}
|
|
32
|
-
const combined = { jobs };
|
|
33
|
-
if (sources.some((source) => source.refresh)) combined.refresh = async () => {
|
|
34
|
-
await Promise.all(sources.map((source) => source.refresh?.()));
|
|
35
|
-
return jobs.value;
|
|
36
|
-
};
|
|
37
|
-
if (sources.some((source) => source.cancel)) combined.cancel = async (jobId) => {
|
|
38
|
-
const owner = ownerFor(jobId);
|
|
39
|
-
if (!owner.cancel) throw new Error(`Job ${jobId} cannot be cancelled by its source`);
|
|
40
|
-
return owner.cancel(jobId);
|
|
41
|
-
};
|
|
42
|
-
if (sources.length > 0 && sources.every((source) => source.delete)) combined.delete = async (jobId) => {
|
|
43
|
-
const owner = ownerFor(jobId);
|
|
44
|
-
if (!owner.delete) throw new Error(`Job ${jobId} cannot be deleted by its source`);
|
|
45
|
-
return owner.delete(jobId);
|
|
46
|
-
};
|
|
47
|
-
if (sources.length > 0 && sources.every((source) => source.clear)) combined.clear = async () => {
|
|
48
|
-
const results = await Promise.all(sources.map((source) => source.clear()));
|
|
49
|
-
return results.every((result) => typeof result === "number") ? results.reduce((total, result) => total + result, 0) : void 0;
|
|
50
|
-
};
|
|
51
|
-
if (sources.some((source) => source.dispose)) combined.dispose = () => {
|
|
52
|
-
const errors = [];
|
|
53
|
-
for (const source of sources) try {
|
|
54
|
-
source.dispose?.();
|
|
55
|
-
} catch (cause) {
|
|
56
|
-
errors.push(cause);
|
|
57
|
-
}
|
|
58
|
-
if (errors.length === 1) throw errors[0];
|
|
59
|
-
if (errors.length > 1) throw new PluginJobSourceDisposeError(errors);
|
|
60
|
-
};
|
|
61
|
-
return combined;
|
|
62
|
-
}
|
|
63
|
-
var PluginJobSourceDisposeError = class extends Error {
|
|
64
|
-
cause;
|
|
65
|
-
errors;
|
|
66
|
-
constructor(errors) {
|
|
67
|
-
super("Failed to dispose plugin job sources");
|
|
68
|
-
this.name = "PluginJobSourceDisposeError";
|
|
69
|
-
this.cause = errors[0];
|
|
70
|
-
this.errors = Object.freeze([...errors]);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
function duplicatePluginJobId(jobId) {
|
|
74
|
-
return /* @__PURE__ */ new Error(`Duplicate plugin job id across combined sources: ${jobId}`);
|
|
75
|
-
}
|
|
76
|
-
function errorMessage(error, fallback) {
|
|
77
|
-
return error instanceof Error ? error.message : fallback;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Headless controller for plugin job presentation and actions.
|
|
81
|
-
*
|
|
82
|
-
* Every job list is derived directly from the source. The controller never
|
|
83
|
-
* mirrors source jobs into another ref.
|
|
84
|
-
*/
|
|
85
|
-
function useJobCenter(options = {}) {
|
|
86
|
-
const isOpen = ref(false);
|
|
87
|
-
const activeTab = ref("active");
|
|
88
|
-
const isLoading = ref(false);
|
|
89
|
-
const error = shallowRef(null);
|
|
90
|
-
const pendingJobIds = ref(/* @__PURE__ */ new Set());
|
|
91
|
-
let requestGeneration = 0;
|
|
92
|
-
let active = true;
|
|
93
|
-
const source = computed(() => toValue(options.source));
|
|
94
|
-
const jobs = computed(() => {
|
|
95
|
-
const currentSource = source.value;
|
|
96
|
-
return currentSource ? [...toValue(currentSource.jobs)].map(normalizeJobState) : [];
|
|
97
|
-
});
|
|
98
|
-
const activeJobs = computed(() => jobs.value.filter((job) => isActiveJobStatus(job.status)));
|
|
99
|
-
const historyJobs = computed(() => jobs.value.filter((job) => !isActiveJobStatus(job.status)));
|
|
100
|
-
const deletableJobs = computed(() => historyJobs.value.filter((job) => job.can_delete));
|
|
101
|
-
const featuredJob = computed(() => activeJobs.value[0] ?? historyJobs.value[0] ?? null);
|
|
102
|
-
const canRefresh = computed(() => Boolean(source.value?.refresh));
|
|
103
|
-
const canCancel = computed(() => Boolean(source.value?.cancel));
|
|
104
|
-
const canDelete = computed(() => Boolean(source.value?.delete));
|
|
105
|
-
const canClearFinished = computed(() => Boolean(source.value?.clear || source.value?.delete));
|
|
106
|
-
function setPending(jobId, pending) {
|
|
107
|
-
const next = new Set(pendingJobIds.value);
|
|
108
|
-
if (pending) next.add(jobId);
|
|
109
|
-
else next.delete(jobId);
|
|
110
|
-
pendingJobIds.value = next;
|
|
111
|
-
}
|
|
112
|
-
async function refresh() {
|
|
113
|
-
const listJobs = source.value?.refresh;
|
|
114
|
-
if (!listJobs) return false;
|
|
115
|
-
const generation = ++requestGeneration;
|
|
116
|
-
isLoading.value = true;
|
|
117
|
-
error.value = null;
|
|
118
|
-
try {
|
|
119
|
-
await listJobs();
|
|
120
|
-
return active && generation === requestGeneration;
|
|
121
|
-
} catch (cause) {
|
|
122
|
-
if (active && generation === requestGeneration) error.value = errorMessage(cause, "Failed to load jobs");
|
|
123
|
-
return false;
|
|
124
|
-
} finally {
|
|
125
|
-
if (active && generation === requestGeneration) isLoading.value = false;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
async function cancelJob(job) {
|
|
129
|
-
const sourceCancel = source.value?.cancel;
|
|
130
|
-
if (!job.can_cancel || !sourceCancel || pendingJobIds.value.has(job.job_id)) return false;
|
|
131
|
-
setPending(job.job_id, true);
|
|
132
|
-
error.value = null;
|
|
133
|
-
try {
|
|
134
|
-
await sourceCancel(job.job_id);
|
|
135
|
-
return active;
|
|
136
|
-
} catch (cause) {
|
|
137
|
-
if (active) error.value = errorMessage(cause, "Failed to cancel job");
|
|
138
|
-
return false;
|
|
139
|
-
} finally {
|
|
140
|
-
if (active) setPending(job.job_id, false);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
async function deleteJob(job) {
|
|
144
|
-
const sourceDelete = source.value?.delete;
|
|
145
|
-
if (!job.can_delete || !sourceDelete || pendingJobIds.value.has(job.job_id)) return false;
|
|
146
|
-
setPending(job.job_id, true);
|
|
147
|
-
error.value = null;
|
|
148
|
-
try {
|
|
149
|
-
const deleted = await sourceDelete(job.job_id);
|
|
150
|
-
return active && deleted !== false;
|
|
151
|
-
} catch (cause) {
|
|
152
|
-
if (active) error.value = errorMessage(cause, "Failed to delete job");
|
|
153
|
-
return false;
|
|
154
|
-
} finally {
|
|
155
|
-
if (active) setPending(job.job_id, false);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
async function clearFinished() {
|
|
159
|
-
const targets = deletableJobs.value;
|
|
160
|
-
if (targets.length === 0) return false;
|
|
161
|
-
error.value = null;
|
|
162
|
-
try {
|
|
163
|
-
const currentSource = source.value;
|
|
164
|
-
if (currentSource?.clear) {
|
|
165
|
-
const deleted = await currentSource.clear();
|
|
166
|
-
if (!active) return false;
|
|
167
|
-
return typeof deleted !== "number" || deleted === targets.length;
|
|
168
|
-
}
|
|
169
|
-
if (!currentSource?.delete) return false;
|
|
170
|
-
return (await Promise.all(targets.map((job) => deleteJob(job)))).every(Boolean);
|
|
171
|
-
} catch (cause) {
|
|
172
|
-
if (active) error.value = errorMessage(cause, "Failed to clear jobs");
|
|
173
|
-
return false;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
onMounted(() => {
|
|
177
|
-
if (options.loadOnMount !== false) refresh();
|
|
178
|
-
});
|
|
179
|
-
onUnmounted(() => {
|
|
180
|
-
active = false;
|
|
181
|
-
requestGeneration += 1;
|
|
182
|
-
});
|
|
183
|
-
return {
|
|
184
|
-
jobs,
|
|
185
|
-
activeJobs,
|
|
186
|
-
historyJobs,
|
|
187
|
-
deletableJobs,
|
|
188
|
-
featuredJob,
|
|
189
|
-
canRefresh,
|
|
190
|
-
canCancel,
|
|
191
|
-
canDelete,
|
|
192
|
-
canClearFinished,
|
|
193
|
-
isOpen,
|
|
194
|
-
activeTab,
|
|
195
|
-
isLoading,
|
|
196
|
-
error,
|
|
197
|
-
pendingJobIds,
|
|
198
|
-
open: () => {
|
|
199
|
-
isOpen.value = true;
|
|
200
|
-
},
|
|
201
|
-
close: () => {
|
|
202
|
-
isOpen.value = false;
|
|
203
|
-
},
|
|
204
|
-
toggle: () => {
|
|
205
|
-
isOpen.value = !isOpen.value;
|
|
206
|
-
},
|
|
207
|
-
refresh,
|
|
208
|
-
cancelJob,
|
|
209
|
-
deleteJob,
|
|
210
|
-
clearFinished,
|
|
211
|
-
isPending: (job) => pendingJobIds.value.has(job.job_id)
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
//#endregion
|
|
215
|
-
//#region src/components/JobsStatusTray.vue?vue&type=script&setup=true&lang.ts
|
|
216
|
-
var _hoisted_1 = { class: "mint-jobs-tray" };
|
|
217
|
-
var _hoisted_2 = ["aria-expanded"];
|
|
218
|
-
var _hoisted_3 = { class: "mint-jobs-tray__trigger-label" };
|
|
219
|
-
var _hoisted_4 = {
|
|
220
|
-
key: 0,
|
|
221
|
-
class: "mint-jobs-tray__count",
|
|
222
|
-
"aria-live": "polite"
|
|
223
|
-
};
|
|
224
|
-
var _hoisted_5 = { class: "mint-jobs-tray__header" };
|
|
225
|
-
var _hoisted_6 = ["title"];
|
|
226
|
-
var _hoisted_7 = { class: "mint-jobs-tray__header-actions" };
|
|
227
|
-
var _hoisted_8 = ["disabled"];
|
|
228
|
-
var _hoisted_9 = {
|
|
229
|
-
class: "mint-jobs-tray__tabs",
|
|
230
|
-
role: "tablist",
|
|
231
|
-
"aria-label": "Job groups"
|
|
232
|
-
};
|
|
233
|
-
var _hoisted_10 = ["aria-selected"];
|
|
234
|
-
var _hoisted_11 = { class: "mint-jobs-tray__tab-count" };
|
|
235
|
-
var _hoisted_12 = ["aria-selected"];
|
|
236
|
-
var _hoisted_13 = { class: "mint-jobs-tray__tab-count" };
|
|
237
|
-
var _hoisted_14 = {
|
|
238
|
-
key: 0,
|
|
239
|
-
class: "mint-jobs-tray__error",
|
|
240
|
-
role: "alert"
|
|
241
|
-
};
|
|
242
|
-
var _hoisted_15 = { class: "mint-jobs-tray__body" };
|
|
243
|
-
var _hoisted_16 = {
|
|
244
|
-
key: 0,
|
|
245
|
-
class: "mint-jobs-tray__empty"
|
|
246
|
-
};
|
|
247
|
-
var _hoisted_17 = { class: "mint-jobs-tray__job-heading" };
|
|
248
|
-
var _hoisted_18 = { class: "mint-jobs-tray__job-identity" };
|
|
249
|
-
var _hoisted_19 = ["onClick"];
|
|
250
|
-
var _hoisted_20 = { class: "mint-jobs-tray__kind" };
|
|
251
|
-
var _hoisted_21 = {
|
|
252
|
-
key: 0,
|
|
253
|
-
class: "mint-jobs-tray__percent"
|
|
254
|
-
};
|
|
255
|
-
var _hoisted_22 = ["aria-valuenow"];
|
|
256
|
-
var _hoisted_23 = { class: "mint-jobs-tray__job-footer" };
|
|
257
|
-
var _hoisted_24 = { class: "mint-jobs-tray__job-actions" };
|
|
258
|
-
var _hoisted_25 = ["disabled", "onClick"];
|
|
259
|
-
var _hoisted_26 = ["disabled", "onClick"];
|
|
260
|
-
var _hoisted_27 = {
|
|
261
|
-
key: 0,
|
|
262
|
-
class: "mint-jobs-tray__clear-row"
|
|
263
|
-
};
|
|
264
|
-
var _hoisted_28 = {
|
|
265
|
-
key: 1,
|
|
266
|
-
class: "mint-jobs-tray__empty"
|
|
267
|
-
};
|
|
268
|
-
var _hoisted_29 = { class: "mint-jobs-tray__job-heading" };
|
|
269
|
-
var _hoisted_30 = { class: "mint-jobs-tray__job-identity" };
|
|
270
|
-
var _hoisted_31 = ["onClick"];
|
|
271
|
-
var _hoisted_32 = { class: "mint-jobs-tray__kind" };
|
|
272
|
-
var _hoisted_33 = { class: "mint-jobs-tray__job-footer" };
|
|
273
|
-
var _hoisted_34 = { class: "mint-jobs-tray__job-actions" };
|
|
274
|
-
var _hoisted_35 = ["disabled", "onClick"];
|
|
275
|
-
//#endregion
|
|
276
|
-
//#region src/components/JobsStatusTray.vue
|
|
277
|
-
var JobsStatusTray_default = /* @__PURE__ */ defineComponent({
|
|
278
|
-
__name: "JobsStatusTray",
|
|
279
|
-
props: {
|
|
280
|
-
source: {},
|
|
281
|
-
loadOnMount: {
|
|
282
|
-
type: Boolean,
|
|
283
|
-
default: true
|
|
284
|
-
},
|
|
285
|
-
title: { default: "Analysis jobs" },
|
|
286
|
-
teleportTo: {
|
|
287
|
-
type: [String, Boolean],
|
|
288
|
-
default: "body"
|
|
289
|
-
}
|
|
290
|
-
},
|
|
291
|
-
emits: ["select", "error"],
|
|
292
|
-
setup(__props, { emit: __emit }) {
|
|
293
|
-
/**
|
|
294
|
-
* Floating, source-driven job status tray for plugin and platform workflows.
|
|
295
|
-
*
|
|
296
|
-
* @example Recommended
|
|
297
|
-
* ```vue
|
|
298
|
-
* <JobsStatusTray :source="jobs" />
|
|
299
|
-
* ```
|
|
300
|
-
*/
|
|
301
|
-
const props = __props;
|
|
302
|
-
const emit = __emit;
|
|
303
|
-
const panelId = `mint-jobs-tray-${useId().replace(/:/g, "")}`;
|
|
304
|
-
const titleId = `${panelId}-title`;
|
|
305
|
-
const triggerRef = ref(null);
|
|
306
|
-
const panelRef = ref(null);
|
|
307
|
-
const teleportTarget = computed(() => props.teleportTo || "body");
|
|
308
|
-
const tray = useJobCenter({
|
|
309
|
-
source: toRef(props, "source"),
|
|
310
|
-
loadOnMount: props.loadOnMount
|
|
311
|
-
});
|
|
312
|
-
const activeRows = computed(() => [...tray.activeJobs.value.filter((job) => job.status === "awaiting_input"), ...tray.activeJobs.value.filter((job) => job.status !== "awaiting_input")]);
|
|
313
|
-
const subtitle = computed(() => {
|
|
314
|
-
const count = (status) => tray.activeJobs.value.filter((job) => job.status === status).length;
|
|
315
|
-
const parts = [
|
|
316
|
-
[count("running"), "running"],
|
|
317
|
-
[count("queued"), "queued"],
|
|
318
|
-
[count("awaiting_input"), "waiting"]
|
|
319
|
-
].filter(([n]) => n).map(([n, word]) => `${n} ${word}`);
|
|
320
|
-
return parts.length ? parts.join(" · ") : `${tray.jobs.value.length} total`;
|
|
321
|
-
});
|
|
322
|
-
const canClear = computed(() => tray.deletableJobs.value.length > 0 && tray.canClearFinished.value);
|
|
323
|
-
async function openTray() {
|
|
324
|
-
tray.open();
|
|
325
|
-
await nextTick();
|
|
326
|
-
panelRef.value?.focus();
|
|
327
|
-
}
|
|
328
|
-
async function closeTray() {
|
|
329
|
-
tray.close();
|
|
330
|
-
await nextTick();
|
|
331
|
-
triggerRef.value?.focus();
|
|
332
|
-
}
|
|
333
|
-
async function toggleTray() {
|
|
334
|
-
if (tray.isOpen.value) await closeTray();
|
|
335
|
-
else await openTray();
|
|
336
|
-
}
|
|
337
|
-
function onPanelKeydown(event) {
|
|
338
|
-
if (event.key === "Escape") {
|
|
339
|
-
closeTray();
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
if (event.key !== "Tab" || !panelRef.value) return;
|
|
343
|
-
const focusable = Array.from(panelRef.value.querySelectorAll("button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])"));
|
|
344
|
-
if (focusable.length === 0) {
|
|
345
|
-
event.preventDefault();
|
|
346
|
-
panelRef.value.focus();
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
const first = focusable[0];
|
|
350
|
-
const last = focusable[focusable.length - 1];
|
|
351
|
-
const activeElement = document.activeElement;
|
|
352
|
-
if (!activeElement || !focusable.includes(activeElement)) {
|
|
353
|
-
event.preventDefault();
|
|
354
|
-
if (event.shiftKey) last?.focus();
|
|
355
|
-
else first?.focus();
|
|
356
|
-
} else if (event.shiftKey && activeElement === first) {
|
|
357
|
-
event.preventDefault();
|
|
358
|
-
last?.focus();
|
|
359
|
-
} else if (!event.shiftKey && activeElement === last) {
|
|
360
|
-
event.preventDefault();
|
|
361
|
-
first?.focus();
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
function progressText(job) {
|
|
365
|
-
if (job.status === "awaiting_input") return job.progress.message || "Waiting for external input";
|
|
366
|
-
if (job.status === "queued" && job.queue_position > 0) return `Queue position ${job.queue_position}`;
|
|
367
|
-
return job.progress.message || job.progress.stage || jobStatusLabel(job.status);
|
|
368
|
-
}
|
|
369
|
-
function formatTimestamp(value) {
|
|
370
|
-
if (!value) return "";
|
|
371
|
-
const date = new Date(value);
|
|
372
|
-
return Number.isNaN(date.getTime()) ? "" : new Intl.DateTimeFormat(void 0, {
|
|
373
|
-
dateStyle: "medium",
|
|
374
|
-
timeStyle: "short"
|
|
375
|
-
}).format(date);
|
|
376
|
-
}
|
|
377
|
-
function statusClass(job) {
|
|
378
|
-
return `mint-jobs-tray__status--${job.status.replace("_", "-")}`;
|
|
379
|
-
}
|
|
380
|
-
function jobPercent(job) {
|
|
381
|
-
return Math.round(normalizeJobPercent(job.progress.percent));
|
|
382
|
-
}
|
|
383
|
-
async function cancelJob(job) {
|
|
384
|
-
if (!await tray.cancelJob(job) && tray.error.value) emit("error", tray.error.value);
|
|
385
|
-
}
|
|
386
|
-
async function deleteJob(job) {
|
|
387
|
-
if (!await tray.deleteJob(job) && tray.error.value) emit("error", tray.error.value);
|
|
388
|
-
}
|
|
389
|
-
async function clearFinished() {
|
|
390
|
-
if (!await tray.clearFinished() && tray.error.value) emit("error", tray.error.value);
|
|
391
|
-
}
|
|
392
|
-
return (_ctx, _cache) => {
|
|
393
|
-
return openBlock(), createBlock(Teleport, {
|
|
394
|
-
to: teleportTarget.value,
|
|
395
|
-
disabled: __props.teleportTo === false
|
|
396
|
-
}, [createElementVNode("div", _hoisted_1, [
|
|
397
|
-
createElementVNode("button", {
|
|
398
|
-
ref_key: "triggerRef",
|
|
399
|
-
ref: triggerRef,
|
|
400
|
-
type: "button",
|
|
401
|
-
class: "mint-jobs-tray__trigger",
|
|
402
|
-
"aria-haspopup": "dialog",
|
|
403
|
-
"aria-expanded": unref(tray).isOpen.value,
|
|
404
|
-
"aria-controls": panelId,
|
|
405
|
-
onClick: toggleTray
|
|
406
|
-
}, [
|
|
407
|
-
(openBlock(), createElementBlock("svg", {
|
|
408
|
-
class: normalizeClass(["mint-jobs-tray__trigger-icon", { "mint-jobs-tray__trigger-icon--active": unref(tray).activeJobs.value.length > 0 }]),
|
|
409
|
-
viewBox: "0 0 24 24",
|
|
410
|
-
fill: "none",
|
|
411
|
-
stroke: "currentColor",
|
|
412
|
-
"aria-hidden": "true"
|
|
413
|
-
}, [..._cache[3] || (_cache[3] = [createElementVNode("path", { d: "M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2" }, null, -1), createElementVNode("path", { d: "M9 5a3 3 0 0 1 6 0v2H9V5Z" }, null, -1)])], 2)),
|
|
414
|
-
createElementVNode("span", _hoisted_3, toDisplayString(unref(tray).featuredJob.value ? unref(jobStatusLabel)(unref(tray).featuredJob.value.status) : "Jobs"), 1),
|
|
415
|
-
unref(tray).activeJobs.value.length > 0 ? (openBlock(), createElementBlock("span", _hoisted_4, toDisplayString(unref(tray).activeJobs.value.length), 1)) : createCommentVNode("", true)
|
|
416
|
-
], 8, _hoisted_2),
|
|
417
|
-
createVNode(Transition, { name: "mint-jobs-tray-fade" }, {
|
|
418
|
-
default: withCtx(() => [unref(tray).isOpen.value ? (openBlock(), createElementBlock("div", {
|
|
419
|
-
key: 0,
|
|
420
|
-
class: "mint-jobs-tray__backdrop",
|
|
421
|
-
"aria-hidden": "true",
|
|
422
|
-
onClick: closeTray
|
|
423
|
-
})) : createCommentVNode("", true)]),
|
|
424
|
-
_: 1
|
|
425
|
-
}),
|
|
426
|
-
createVNode(Transition, { name: "mint-jobs-tray-slide" }, {
|
|
427
|
-
default: withCtx(() => [unref(tray).isOpen.value ? (openBlock(), createElementBlock("section", {
|
|
428
|
-
key: 0,
|
|
429
|
-
id: panelId,
|
|
430
|
-
ref_key: "panelRef",
|
|
431
|
-
ref: panelRef,
|
|
432
|
-
class: "mint-jobs-tray__panel",
|
|
433
|
-
role: "dialog",
|
|
434
|
-
"aria-modal": "true",
|
|
435
|
-
"aria-labelledby": titleId,
|
|
436
|
-
tabindex: "-1",
|
|
437
|
-
onKeydown: onPanelKeydown
|
|
438
|
-
}, [
|
|
439
|
-
createElementVNode("header", _hoisted_5, [createElementVNode("div", null, [createElementVNode("h2", {
|
|
440
|
-
id: titleId,
|
|
441
|
-
class: "mint-jobs-tray__title"
|
|
442
|
-
}, toDisplayString(__props.title), 1), createElementVNode("p", {
|
|
443
|
-
class: "mint-jobs-tray__subtitle",
|
|
444
|
-
title: subtitle.value
|
|
445
|
-
}, toDisplayString(subtitle.value), 9, _hoisted_6)]), createElementVNode("div", _hoisted_7, [unref(tray).canRefresh.value ? (openBlock(), createElementBlock("button", {
|
|
446
|
-
key: 0,
|
|
447
|
-
type: "button",
|
|
448
|
-
class: "mint-jobs-tray__icon-button",
|
|
449
|
-
"aria-label": "Refresh jobs",
|
|
450
|
-
disabled: unref(tray).isLoading.value,
|
|
451
|
-
onClick: _cache[0] || (_cache[0] = (...args) => unref(tray).refresh && unref(tray).refresh(...args))
|
|
452
|
-
}, [..._cache[4] || (_cache[4] = [createElementVNode("svg", {
|
|
453
|
-
viewBox: "0 0 24 24",
|
|
454
|
-
fill: "none",
|
|
455
|
-
stroke: "currentColor",
|
|
456
|
-
"aria-hidden": "true"
|
|
457
|
-
}, [createElementVNode("path", { d: "M20 6v5h-5M4 18v-5h5" }), createElementVNode("path", { d: "M18.4 9A7 7 0 0 0 6.1 6.1L4 8m2 7a7 7 0 0 0 11.9 2.9L20 16" })], -1)])], 8, _hoisted_8)) : createCommentVNode("", true), createElementVNode("button", {
|
|
458
|
-
type: "button",
|
|
459
|
-
class: "mint-jobs-tray__icon-button",
|
|
460
|
-
"aria-label": "Close jobs",
|
|
461
|
-
onClick: closeTray
|
|
462
|
-
}, [..._cache[5] || (_cache[5] = [createElementVNode("svg", {
|
|
463
|
-
viewBox: "0 0 24 24",
|
|
464
|
-
fill: "none",
|
|
465
|
-
stroke: "currentColor",
|
|
466
|
-
"aria-hidden": "true"
|
|
467
|
-
}, [createElementVNode("path", { d: "m6 6 12 12M18 6 6 18" })], -1)])])])]),
|
|
468
|
-
createElementVNode("div", _hoisted_9, [createElementVNode("button", {
|
|
469
|
-
type: "button",
|
|
470
|
-
role: "tab",
|
|
471
|
-
"data-tab": "active",
|
|
472
|
-
class: normalizeClass(["mint-jobs-tray__tab", { "mint-jobs-tray__tab--active": unref(tray).activeTab.value === "active" }]),
|
|
473
|
-
"aria-selected": unref(tray).activeTab.value === "active",
|
|
474
|
-
onClick: _cache[1] || (_cache[1] = ($event) => unref(tray).activeTab.value = "active")
|
|
475
|
-
}, [_cache[6] || (_cache[6] = createTextVNode(" Active ", -1)), createElementVNode("span", _hoisted_11, toDisplayString(unref(tray).activeJobs.value.length), 1)], 10, _hoisted_10), createElementVNode("button", {
|
|
476
|
-
type: "button",
|
|
477
|
-
role: "tab",
|
|
478
|
-
"data-tab": "history",
|
|
479
|
-
class: normalizeClass(["mint-jobs-tray__tab", { "mint-jobs-tray__tab--active": unref(tray).activeTab.value === "history" }]),
|
|
480
|
-
"aria-selected": unref(tray).activeTab.value === "history",
|
|
481
|
-
onClick: _cache[2] || (_cache[2] = ($event) => unref(tray).activeTab.value = "history")
|
|
482
|
-
}, [_cache[7] || (_cache[7] = createTextVNode(" History ", -1)), createElementVNode("span", _hoisted_13, toDisplayString(unref(tray).historyJobs.value.length), 1)], 10, _hoisted_12)]),
|
|
483
|
-
unref(tray).error.value ? (openBlock(), createElementBlock("p", _hoisted_14, toDisplayString(unref(tray).error.value), 1)) : createCommentVNode("", true),
|
|
484
|
-
createElementVNode("div", _hoisted_15, [unref(tray).activeTab.value === "active" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [unref(tray).activeJobs.value.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_16, " No active jobs ")) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(activeRows.value, (job) => {
|
|
485
|
-
return openBlock(), createElementBlock("article", {
|
|
486
|
-
key: job.job_id,
|
|
487
|
-
class: normalizeClass(["mint-jobs-tray__job", { "mint-jobs-tray__job--awaiting-input": job.status === "awaiting_input" }])
|
|
488
|
-
}, [
|
|
489
|
-
createElementVNode("div", _hoisted_17, [
|
|
490
|
-
createElementVNode("div", _hoisted_18, [createElementVNode("button", {
|
|
491
|
-
type: "button",
|
|
492
|
-
class: "mint-jobs-tray__job-select",
|
|
493
|
-
onClick: ($event) => emit("select", job)
|
|
494
|
-
}, toDisplayString(job.name), 9, _hoisted_19), createElementVNode("span", _hoisted_20, toDisplayString(job.kind), 1)]),
|
|
495
|
-
createElementVNode("span", { class: normalizeClass(["mint-jobs-tray__status", statusClass(job)]) }, [_cache[8] || (_cache[8] = createElementVNode("span", {
|
|
496
|
-
class: "mint-jobs-tray__status-dot",
|
|
497
|
-
"aria-hidden": "true"
|
|
498
|
-
}, null, -1)), createTextVNode(" " + toDisplayString(unref(jobStatusLabel)(job.status)), 1)], 2),
|
|
499
|
-
job.status === "running" ? (openBlock(), createElementBlock("span", _hoisted_21, toDisplayString(jobPercent(job)) + "%", 1)) : createCommentVNode("", true)
|
|
500
|
-
]),
|
|
501
|
-
job.status === "running" ? (openBlock(), createElementBlock("div", {
|
|
502
|
-
key: 0,
|
|
503
|
-
class: "mint-jobs-tray__progress",
|
|
504
|
-
role: "progressbar",
|
|
505
|
-
"aria-label": "Job progress",
|
|
506
|
-
"aria-valuemin": "0",
|
|
507
|
-
"aria-valuemax": "100",
|
|
508
|
-
"aria-valuenow": jobPercent(job)
|
|
509
|
-
}, [createElementVNode("span", { style: normalizeStyle({ width: `${jobPercent(job)}%` }) }, null, 4)], 8, _hoisted_22)) : createCommentVNode("", true),
|
|
510
|
-
createElementVNode("div", _hoisted_23, [createElementVNode("span", null, toDisplayString(progressText(job)), 1), createElementVNode("div", _hoisted_24, [
|
|
511
|
-
renderSlot(_ctx.$slots, "job-actions", { job }),
|
|
512
|
-
job.can_cancel && unref(tray).canCancel.value ? (openBlock(), createElementBlock("button", {
|
|
513
|
-
key: 0,
|
|
514
|
-
type: "button",
|
|
515
|
-
class: "mint-jobs-tray__action mint-jobs-tray__action--danger",
|
|
516
|
-
"aria-label": "Cancel job",
|
|
517
|
-
disabled: unref(tray).isPending(job),
|
|
518
|
-
onClick: ($event) => cancelJob(job)
|
|
519
|
-
}, " Cancel ", 8, _hoisted_25)) : createCommentVNode("", true),
|
|
520
|
-
job.can_delete && unref(tray).canDelete.value ? (openBlock(), createElementBlock("button", {
|
|
521
|
-
key: 1,
|
|
522
|
-
type: "button",
|
|
523
|
-
class: "mint-jobs-tray__icon-button mint-jobs-tray__icon-button--danger",
|
|
524
|
-
"aria-label": "Delete job",
|
|
525
|
-
disabled: unref(tray).isPending(job),
|
|
526
|
-
onClick: ($event) => deleteJob(job)
|
|
527
|
-
}, [..._cache[9] || (_cache[9] = [createElementVNode("svg", {
|
|
528
|
-
viewBox: "0 0 24 24",
|
|
529
|
-
fill: "none",
|
|
530
|
-
stroke: "currentColor",
|
|
531
|
-
"aria-hidden": "true"
|
|
532
|
-
}, [createElementVNode("path", { d: "M4 7h16M9 7V4h6v3m-8 0 1 13h8l1-13M10 11v5M14 11v5" })], -1)])], 8, _hoisted_26)) : createCommentVNode("", true)
|
|
533
|
-
])])
|
|
534
|
-
], 2);
|
|
535
|
-
}), 128))], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
536
|
-
canClear.value ? (openBlock(), createElementBlock("div", _hoisted_27, [createElementVNode("button", {
|
|
537
|
-
type: "button",
|
|
538
|
-
class: "mint-jobs-tray__action mint-jobs-tray__action--danger",
|
|
539
|
-
onClick: clearFinished
|
|
540
|
-
}, " Clear finished ")])) : createCommentVNode("", true),
|
|
541
|
-
unref(tray).historyJobs.value.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_28, " No job history ")) : createCommentVNode("", true),
|
|
542
|
-
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(tray).historyJobs.value, (job) => {
|
|
543
|
-
return openBlock(), createElementBlock("article", {
|
|
544
|
-
key: job.job_id,
|
|
545
|
-
class: "mint-jobs-tray__job"
|
|
546
|
-
}, [createElementVNode("div", _hoisted_29, [createElementVNode("div", _hoisted_30, [createElementVNode("button", {
|
|
547
|
-
type: "button",
|
|
548
|
-
class: "mint-jobs-tray__job-select",
|
|
549
|
-
onClick: ($event) => emit("select", job)
|
|
550
|
-
}, toDisplayString(job.name), 9, _hoisted_31), createElementVNode("span", _hoisted_32, toDisplayString(job.kind), 1)]), createElementVNode("span", { class: normalizeClass(["mint-jobs-tray__status", statusClass(job)]) }, [_cache[10] || (_cache[10] = createElementVNode("span", {
|
|
551
|
-
class: "mint-jobs-tray__status-dot",
|
|
552
|
-
"aria-hidden": "true"
|
|
553
|
-
}, null, -1)), createTextVNode(" " + toDisplayString(unref(jobStatusLabel)(job.status)), 1)], 2)]), createElementVNode("div", _hoisted_33, [createElementVNode("span", null, toDisplayString(formatTimestamp(job.completed_at || job.created_at)), 1), createElementVNode("div", _hoisted_34, [renderSlot(_ctx.$slots, "job-actions", { job }), job.can_delete && unref(tray).canDelete.value ? (openBlock(), createElementBlock("button", {
|
|
554
|
-
key: 0,
|
|
555
|
-
type: "button",
|
|
556
|
-
class: "mint-jobs-tray__icon-button mint-jobs-tray__icon-button--danger",
|
|
557
|
-
"aria-label": "Delete job",
|
|
558
|
-
disabled: unref(tray).isPending(job),
|
|
559
|
-
onClick: ($event) => deleteJob(job)
|
|
560
|
-
}, [..._cache[11] || (_cache[11] = [createElementVNode("svg", {
|
|
561
|
-
viewBox: "0 0 24 24",
|
|
562
|
-
fill: "none",
|
|
563
|
-
stroke: "currentColor",
|
|
564
|
-
"aria-hidden": "true"
|
|
565
|
-
}, [createElementVNode("path", { d: "M4 7h16M9 7V4h6v3m-8 0 1 13h8l1-13M10 11v5M14 11v5" })], -1)])], 8, _hoisted_35)) : createCommentVNode("", true)])])]);
|
|
566
|
-
}), 128))
|
|
567
|
-
], 64))])
|
|
568
|
-
], 544)) : createCommentVNode("", true)]),
|
|
569
|
-
_: 3
|
|
570
|
-
})
|
|
571
|
-
])], 8, ["to", "disabled"]);
|
|
572
|
-
};
|
|
573
|
-
}
|
|
574
|
-
});
|
|
575
|
-
//#endregion
|
|
576
|
-
//#region src/composables/useRealtime.ts
|
|
577
|
-
var EVENTS_URL = "/api/events";
|
|
578
|
-
var RECONNECT_BASE_MS = 1e3;
|
|
579
|
-
var RECONNECT_MAX_MS = 3e4;
|
|
580
|
-
/** Merges the subscriptions of components that mount together into one connection. */
|
|
581
|
-
var TOPIC_DEBOUNCE_MS = 50;
|
|
582
|
-
var RESYNC = { type: "resync" };
|
|
583
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
584
|
-
/** The last `presence.changed` of each topic of the open stream; the server sends one per topic at open. */
|
|
585
|
-
var presence = /* @__PURE__ */ new Map();
|
|
586
|
-
var connected = ref(false);
|
|
587
|
-
var source = null;
|
|
588
|
-
var sourceTopics = "";
|
|
589
|
-
var failures = 0;
|
|
590
|
-
var syncTimer = null;
|
|
591
|
-
var retryTimer = null;
|
|
592
|
-
function dispatch(event) {
|
|
593
|
-
const isPresence = event.type === "presence.changed";
|
|
594
|
-
if (isPresence && event.topic) presence.set(event.topic, event);
|
|
595
|
-
for (const listener of [...listeners]) {
|
|
596
|
-
if (isPresence && !listener.presence) continue;
|
|
597
|
-
if (event.type === "resync" || event.topic && listener.topics().includes(event.topic)) listener.handler(event);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
function close() {
|
|
601
|
-
source?.close();
|
|
602
|
-
source = null;
|
|
603
|
-
sourceTopics = "";
|
|
604
|
-
presence.clear();
|
|
605
|
-
connected.value = false;
|
|
606
|
-
}
|
|
607
|
-
function open(topics) {
|
|
608
|
-
const stream = new EventSource(`${EVENTS_URL}?topics=${encodeURIComponent(topics)}`);
|
|
609
|
-
source = stream;
|
|
610
|
-
sourceTopics = topics;
|
|
611
|
-
stream.onopen = () => {
|
|
612
|
-
connected.value = true;
|
|
613
|
-
dispatch(RESYNC);
|
|
614
|
-
failures = 0;
|
|
615
|
-
};
|
|
616
|
-
stream.onmessage = (message) => {
|
|
617
|
-
dispatch(JSON.parse(message.data));
|
|
618
|
-
};
|
|
619
|
-
stream.onerror = () => {
|
|
620
|
-
if (source !== stream) return;
|
|
621
|
-
close();
|
|
622
|
-
if (syncTimer !== null) clearTimeout(syncTimer);
|
|
623
|
-
syncTimer = null;
|
|
624
|
-
retryTimer = setTimeout(() => {
|
|
625
|
-
retryTimer = null;
|
|
626
|
-
sync();
|
|
627
|
-
}, Math.min(RECONNECT_MAX_MS, RECONNECT_BASE_MS * 2 ** failures));
|
|
628
|
-
failures += 1;
|
|
629
|
-
};
|
|
630
|
-
}
|
|
631
|
-
function sync() {
|
|
632
|
-
syncTimer = null;
|
|
633
|
-
const topics = [...new Set([...listeners].flatMap((listener) => listener.topics()))].sort().join(",");
|
|
634
|
-
if (source && topics === sourceTopics) return;
|
|
635
|
-
close();
|
|
636
|
-
if (topics) open(topics);
|
|
637
|
-
}
|
|
638
|
-
function schedule() {
|
|
639
|
-
if (retryTimer !== null) return;
|
|
640
|
-
if (syncTimer !== null) clearTimeout(syncTimer);
|
|
641
|
-
syncTimer = setTimeout(sync, TOPIC_DEBOUNCE_MS);
|
|
642
|
-
}
|
|
643
|
-
/**
|
|
644
|
-
* Subscribe to topics of the platform's realtime stream.
|
|
645
|
-
*
|
|
646
|
-
* Every caller in a tab shares one `EventSource`, opened on the union of
|
|
647
|
-
* their topics and reopened when that union changes. `handler` receives the
|
|
648
|
-
* write events of the caller's own topics (not `presence.changed`, which goes
|
|
649
|
-
* to `usePresence`) plus every `resync`: one comes from the
|
|
650
|
-
* server when this tab fell behind, and one follows each open of the stream:
|
|
651
|
-
* the first open, each reconnect (backoff from 1 s to 30 s) and each reopen for
|
|
652
|
-
* a changed topic union. On `resync`, refetch. The union of all callers' topics
|
|
653
|
-
* in a tab may hold at most 50 topics; the platform refuses a larger stream.
|
|
654
|
-
*
|
|
655
|
-
* The stream authenticates with the session cookie, so it works in the
|
|
656
|
-
* platform and in plugin frontends served by it. It exists only when the
|
|
657
|
-
* platform's `realtime` feature flag is on; check that flag first.
|
|
658
|
-
*
|
|
659
|
-
* A plugin topic reaches a caller whose role may use the plugin; an
|
|
660
|
-
* experiment plugin topic also needs a caller who may view the experiment.
|
|
661
|
-
*
|
|
662
|
-
* @example
|
|
663
|
-
* useRealtime(() => [`experiment:${id.value}`], () => refetch())
|
|
664
|
-
* @example
|
|
665
|
-
* useRealtime(['plugin:drp'], (event) => {
|
|
666
|
-
* if (event.type === 'drp.run.progress') progress.value = event.payload as RunProgress
|
|
667
|
-
* })
|
|
668
|
-
*/
|
|
669
|
-
function useRealtime(topics, handler) {
|
|
670
|
-
return subscribeRealtime(topics, handler, false);
|
|
671
|
-
}
|
|
672
|
-
/** The last `presence.changed` of `topic` while the open stream views it (for `usePresence`). */
|
|
673
|
-
function currentPresence(topic) {
|
|
674
|
-
return presence.get(topic);
|
|
675
|
-
}
|
|
676
|
-
/** `useRealtime`, and with `withPresence` also the `presence.changed` events (for `usePresence`). */
|
|
677
|
-
function subscribeRealtime(topics, handler, withPresence) {
|
|
678
|
-
const listener = {
|
|
679
|
-
topics: () => toValue(topics),
|
|
680
|
-
handler,
|
|
681
|
-
presence: withPresence
|
|
682
|
-
};
|
|
683
|
-
listeners.add(listener);
|
|
684
|
-
const stopWatch = watch(() => listener.topics().join(","), () => schedule());
|
|
685
|
-
schedule();
|
|
686
|
-
function stop() {
|
|
687
|
-
stopWatch();
|
|
688
|
-
if (listeners.delete(listener)) schedule();
|
|
689
|
-
}
|
|
690
|
-
if (getCurrentScope()) onScopeDispose(stop);
|
|
691
|
-
return {
|
|
692
|
-
connected: readonly(connected),
|
|
693
|
-
stop
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
//#endregion
|
|
697
|
-
export { combinePluginJobSources as a, JobsStatusTray_default as i, subscribeRealtime as n, useJobCenter as o, useRealtime as r, currentPresence as t };
|
|
698
|
-
|
|
699
|
-
//# sourceMappingURL=useRealtime-C3iy9QkU.js.map
|