@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
|
@@ -28,7 +28,13 @@ import {
|
|
|
28
28
|
pluginEndpointRequestParts,
|
|
29
29
|
requestBodyFromPayload,
|
|
30
30
|
} from './pluginEndpointBuilder'
|
|
31
|
-
import type {
|
|
31
|
+
import type {
|
|
32
|
+
ExperimentSummary,
|
|
33
|
+
GeneratedJobDefinition,
|
|
34
|
+
PageSelectorItem,
|
|
35
|
+
SettingsModalSchema,
|
|
36
|
+
TopBarSettingsConfig,
|
|
37
|
+
} from '../types'
|
|
32
38
|
|
|
33
39
|
export {
|
|
34
40
|
buildPluginEndpointUrl,
|
|
@@ -41,6 +47,7 @@ export interface PluginEndpointContract {
|
|
|
41
47
|
name: string
|
|
42
48
|
method: PluginHttpMethod
|
|
43
49
|
path: string
|
|
50
|
+
requiresAuth?: boolean
|
|
44
51
|
pathParams?: PluginEndpointParamContract[]
|
|
45
52
|
queryParams?: PluginEndpointParamContract[]
|
|
46
53
|
requestType?: string | null
|
|
@@ -90,12 +97,14 @@ export interface PluginContract {
|
|
|
90
97
|
schema?: SettingsModalSchema | null
|
|
91
98
|
}
|
|
92
99
|
endpoints: PluginEndpointContract[]
|
|
100
|
+
jobs?: GeneratedJobDefinition[]
|
|
93
101
|
hash: string
|
|
94
102
|
}
|
|
95
103
|
|
|
96
104
|
export interface PluginEndpointDefinition {
|
|
97
105
|
method: PluginHttpMethod
|
|
98
106
|
path: string
|
|
107
|
+
requiresAuth?: boolean
|
|
99
108
|
pathParams?: PluginEndpointParamDefinition[]
|
|
100
109
|
queryParams?: PluginEndpointParamDefinition[]
|
|
101
110
|
hasBody?: boolean
|
|
@@ -157,7 +166,7 @@ export interface UsePluginSettingsOptions {
|
|
|
157
166
|
apiBaseUrl?: string
|
|
158
167
|
/** Platform API base URL for /plugins/{name}/config. Defaults to the injected platform API URL. */
|
|
159
168
|
platformApiBaseUrl?: string
|
|
160
|
-
/** SettingsModal schema generated from the backend
|
|
169
|
+
/** SettingsModal schema generated from the backend plugin config model. */
|
|
161
170
|
schema?: SettingsModalSchema | null
|
|
162
171
|
/** SettingsModal title when using settingsConfig. */
|
|
163
172
|
title?: string
|
|
@@ -383,7 +392,7 @@ function pluginPageIdFromPath(path: string, fallbackIndex: number): string {
|
|
|
383
392
|
return normalizedPath.replace(/^\/+/, '').replace(/\/+/g, '-') || `page-${fallbackIndex + 1}`
|
|
384
393
|
}
|
|
385
394
|
|
|
386
|
-
/** Convert
|
|
395
|
+
/** Convert resolved @mint_plugin nav_items into page selectors for the topbar and homepage PluginCards. */
|
|
387
396
|
export function getPluginPageSelectorItems(contract: PluginContract): PageSelectorItem[] {
|
|
388
397
|
const pluginIcon = contract.plugin.icon ?? ''
|
|
389
398
|
const navItems = contract.plugin.navItems ?? []
|
|
@@ -413,7 +422,10 @@ export function createPluginClient<TClient extends object = GeneratedPluginClien
|
|
|
413
422
|
? explicitOptions
|
|
414
423
|
: (payloadOrOptions as PluginClientRequestOptions | undefined) ?? explicitOptions
|
|
415
424
|
const parts = pluginEndpointRequestParts(contract, endpoint, payload, options.baseUrl)
|
|
416
|
-
const api = useApi({
|
|
425
|
+
const api = useApi({
|
|
426
|
+
baseUrl: parts.baseUrl,
|
|
427
|
+
withAuth: endpoint.requiresAuth !== false,
|
|
428
|
+
})
|
|
417
429
|
const config = endpointRequestConfig(endpoint, parts.query, callOptions)
|
|
418
430
|
|
|
419
431
|
if (endpoint.method === 'get') {
|
|
@@ -456,7 +468,10 @@ export async function uploadPluginEndpoint<TResponse = unknown>(
|
|
|
456
468
|
}
|
|
457
469
|
|
|
458
470
|
const parts = pluginEndpointRequestParts(contract, endpoint, payload, options.baseUrl)
|
|
459
|
-
const api = useApi({
|
|
471
|
+
const api = useApi({
|
|
472
|
+
baseUrl: parts.baseUrl,
|
|
473
|
+
withAuth: endpoint.requiresAuth !== false,
|
|
474
|
+
})
|
|
460
475
|
const body = endpoint.hasBody
|
|
461
476
|
? requestBodyFromPayload(payload, parts.requestPayload, endpoint)
|
|
462
477
|
: payload
|
|
@@ -491,7 +506,10 @@ export async function downloadPluginEndpoint(
|
|
|
491
506
|
}
|
|
492
507
|
|
|
493
508
|
const parts = pluginEndpointRequestParts(contract, endpoint, payload, options.baseUrl)
|
|
494
|
-
const api = useApi({
|
|
509
|
+
const api = useApi({
|
|
510
|
+
baseUrl: parts.baseUrl,
|
|
511
|
+
withAuth: endpoint.requiresAuth !== false,
|
|
512
|
+
})
|
|
495
513
|
const config = {
|
|
496
514
|
...(endpointRequestConfig(endpoint, parts.query, { signal: options.signal }) ?? {}),
|
|
497
515
|
responseType: 'blob' as const,
|
|
@@ -528,8 +546,14 @@ function normalizePluginSettingsOptions(options: UsePluginSettingsOptions | stri
|
|
|
528
546
|
return typeof options === 'string' ? { pluginName: options } : options
|
|
529
547
|
}
|
|
530
548
|
|
|
549
|
+
const PLATFORM_OWNED_SETTINGS_KEY = 'allowed_experiment_types'
|
|
550
|
+
|
|
531
551
|
function cloneRecord(value: Record<string, unknown>): Record<string, unknown> {
|
|
532
|
-
return
|
|
552
|
+
return JSON.parse(JSON.stringify(value)) as Record<string, unknown>
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function hasOwnSetting(settings: Record<string, unknown>, key: string): boolean {
|
|
556
|
+
return Object.prototype.hasOwnProperty.call(settings, key)
|
|
533
557
|
}
|
|
534
558
|
|
|
535
559
|
function responseSettingsPayload(response: unknown): Record<string, unknown> {
|
|
@@ -547,6 +571,43 @@ function responseSettingsPayload(response: unknown): Record<string, unknown> {
|
|
|
547
571
|
return {}
|
|
548
572
|
}
|
|
549
573
|
|
|
574
|
+
function responseSettingsRevision(response: unknown): string | null {
|
|
575
|
+
if (!response || typeof response !== 'object') return null
|
|
576
|
+
const revision = (response as { revision?: unknown }).revision
|
|
577
|
+
return typeof revision === 'string' && revision ? revision : null
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function topLevelSettingsPatch(
|
|
581
|
+
current: Record<string, unknown>,
|
|
582
|
+
keys: Iterable<string>,
|
|
583
|
+
): Record<string, unknown> {
|
|
584
|
+
return Object.fromEntries(
|
|
585
|
+
[...keys].map(key => [
|
|
586
|
+
key,
|
|
587
|
+
hasOwnSetting(current, key) ? current[key] : null,
|
|
588
|
+
]),
|
|
589
|
+
)
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function changedTopLevelSettingKeys(
|
|
593
|
+
current: Record<string, unknown>,
|
|
594
|
+
saved: Record<string, unknown>,
|
|
595
|
+
): Set<string> {
|
|
596
|
+
return new Set(
|
|
597
|
+
[...new Set([...Object.keys(current), ...Object.keys(saved)])].filter(
|
|
598
|
+
key => JSON.stringify(current[key]) !== JSON.stringify(saved[key]),
|
|
599
|
+
),
|
|
600
|
+
)
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function platformOwnedSettingsPatch(settings: Record<string, unknown>): Record<string, unknown> {
|
|
604
|
+
return {
|
|
605
|
+
[PLATFORM_OWNED_SETTINGS_KEY]: hasOwnSetting(settings, PLATFORM_OWNED_SETTINGS_KEY)
|
|
606
|
+
? settings[PLATFORM_OWNED_SETTINGS_KEY]
|
|
607
|
+
: null,
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
550
611
|
/** Load, edit, and persist plugin settings from platform config or the plugin-local settings router. */
|
|
551
612
|
export function usePluginSettings<TSettings = Record<string, unknown>>(
|
|
552
613
|
options: UsePluginSettingsOptions | string = {},
|
|
@@ -557,8 +618,9 @@ export function usePluginSettings<TSettings = Record<string, unknown>>(
|
|
|
557
618
|
const platformApi = useApi({ baseUrl: resolvedOptions.platformApiBaseUrl ?? injectedContext?.platformApiUrl })
|
|
558
619
|
const pluginApi = useApi({ baseUrl: resolvedOptions.apiBaseUrl ?? injectedContext?.plugin?.api_prefix })
|
|
559
620
|
|
|
560
|
-
const values =
|
|
621
|
+
const values = ref({}) as Ref<PluginSettingsValues<TSettings>>
|
|
561
622
|
const savedValues = shallowRef<Record<string, unknown>>({})
|
|
623
|
+
const revision = ref<string | null>(null)
|
|
562
624
|
const request = useRequestSyncState('Plugin settings request failed.')
|
|
563
625
|
const isLoading = ref(false)
|
|
564
626
|
const isSaving = ref(false)
|
|
@@ -566,8 +628,17 @@ export function usePluginSettings<TSettings = Record<string, unknown>>(
|
|
|
566
628
|
const resolvedPluginName = computed(() =>
|
|
567
629
|
resolvedOptions.pluginName ?? plugin.value?.name ?? injectedContext?.plugin?.name ?? '',
|
|
568
630
|
)
|
|
631
|
+
const usesPluginSettingsApi = computed(() => {
|
|
632
|
+
const currentPlugin = injectedContext?.plugin ?? plugin.value
|
|
633
|
+
return currentPlugin?.settings_api === 'plugin'
|
|
634
|
+
&& resolvedPluginName.value === currentPlugin.name
|
|
635
|
+
})
|
|
569
636
|
const usesPlatformConfig = computed(() =>
|
|
570
|
-
Boolean(
|
|
637
|
+
Boolean(
|
|
638
|
+
(isIntegrated.value || injectedContext?.isIntegrated)
|
|
639
|
+
&& resolvedPluginName.value
|
|
640
|
+
&& !usesPluginSettingsApi.value,
|
|
641
|
+
),
|
|
571
642
|
)
|
|
572
643
|
const settings = computed(() => values.value)
|
|
573
644
|
const isDirty = computed(() => JSON.stringify(values.value) !== JSON.stringify(savedValues.value))
|
|
@@ -582,10 +653,11 @@ export function usePluginSettings<TSettings = Record<string, unknown>>(
|
|
|
582
653
|
values.value = cloneRecord(nextValues) as PluginSettingsValues<TSettings>
|
|
583
654
|
}
|
|
584
655
|
|
|
585
|
-
function applyLoaded(nextValues: Record<string, unknown
|
|
656
|
+
function applyLoaded(nextValues: Record<string, unknown>, nextRevision: string | null): void {
|
|
586
657
|
const cloned = cloneRecord(nextValues)
|
|
587
658
|
values.value = cloned as PluginSettingsValues<TSettings>
|
|
588
659
|
savedValues.value = cloneRecord(cloned)
|
|
660
|
+
revision.value = nextRevision
|
|
589
661
|
}
|
|
590
662
|
|
|
591
663
|
async function load(): Promise<void> {
|
|
@@ -602,7 +674,10 @@ export function usePluginSettings<TSettings = Record<string, unknown>>(
|
|
|
602
674
|
() => pluginApi.get<{ settings: Record<string, unknown>; mode?: string }>('/settings'),
|
|
603
675
|
{ success: 'load', errorMessage: 'Failed to load plugin settings' },
|
|
604
676
|
)
|
|
605
|
-
applyLoaded(
|
|
677
|
+
applyLoaded(
|
|
678
|
+
responseSettingsPayload(response),
|
|
679
|
+
responseSettingsRevision(response),
|
|
680
|
+
)
|
|
606
681
|
} catch {
|
|
607
682
|
// Error state is handled by request.run().
|
|
608
683
|
} finally {
|
|
@@ -618,19 +693,65 @@ export function usePluginSettings<TSettings = Record<string, unknown>>(
|
|
|
618
693
|
isSaving.value = true
|
|
619
694
|
try {
|
|
620
695
|
const payload = cloneRecord(values.value)
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
{ success: 'save', errorMessage: 'Failed to save plugin settings' },
|
|
696
|
+
let response: unknown
|
|
697
|
+
if (usesPlatformConfig.value) {
|
|
698
|
+
const dirtyKeys = changedTopLevelSettingKeys(payload, savedValues.value)
|
|
699
|
+
const platformOwnedChanged = dirtyKeys.has(PLATFORM_OWNED_SETTINGS_KEY)
|
|
700
|
+
const typedKeys = [...dirtyKeys].filter(
|
|
701
|
+
key => key !== PLATFORM_OWNED_SETTINGS_KEY,
|
|
628
702
|
)
|
|
629
|
-
|
|
630
|
-
|
|
703
|
+
let nextRevision = revision.value
|
|
704
|
+
|
|
705
|
+
if (typedKeys.length) {
|
|
706
|
+
response = await request.run(
|
|
707
|
+
() => platformApi.patch<{
|
|
708
|
+
plugin_name: string
|
|
709
|
+
config: Record<string, unknown>
|
|
710
|
+
revision?: string
|
|
711
|
+
}>(
|
|
712
|
+
`/plugins/${encodeURIComponent(resolvedPluginName.value)}/config`,
|
|
713
|
+
{ config: topLevelSettingsPatch(payload, typedKeys) },
|
|
714
|
+
),
|
|
715
|
+
{ success: 'save', errorMessage: 'Failed to save plugin settings' },
|
|
716
|
+
)
|
|
717
|
+
nextRevision = responseSettingsRevision(response) ?? nextRevision
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (platformOwnedChanged) {
|
|
721
|
+
response = await request.run(
|
|
722
|
+
() => platformApi.patch<{
|
|
723
|
+
plugin_name: string
|
|
724
|
+
config: Record<string, unknown>
|
|
725
|
+
revision?: string
|
|
726
|
+
}>(
|
|
727
|
+
`/plugins/${encodeURIComponent(resolvedPluginName.value)}/config`,
|
|
728
|
+
{ config: platformOwnedSettingsPatch(payload) },
|
|
729
|
+
),
|
|
730
|
+
{ success: 'save', errorMessage: 'Failed to save plugin settings' },
|
|
731
|
+
)
|
|
732
|
+
nextRevision = responseSettingsRevision(response) ?? nextRevision
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (!response) return true
|
|
736
|
+
applyLoaded(responseSettingsPayload(response), nextRevision)
|
|
737
|
+
return true
|
|
738
|
+
} else {
|
|
739
|
+
const ifMatch = revision.value
|
|
740
|
+
? { headers: { 'If-Match': `"${revision.value}"` } }
|
|
741
|
+
: undefined
|
|
742
|
+
response = await request.run(
|
|
743
|
+
() => pluginApi.put<{
|
|
744
|
+
settings: Record<string, unknown>
|
|
745
|
+
mode?: string
|
|
746
|
+
revision?: string
|
|
747
|
+
}>('/settings', payload, ifMatch),
|
|
631
748
|
{ success: 'save', errorMessage: 'Failed to save plugin settings' },
|
|
632
749
|
)
|
|
633
|
-
|
|
750
|
+
}
|
|
751
|
+
applyLoaded(
|
|
752
|
+
responseSettingsPayload(response),
|
|
753
|
+
responseSettingsRevision(response) ?? revision.value,
|
|
754
|
+
)
|
|
634
755
|
return true
|
|
635
756
|
} catch {
|
|
636
757
|
return false
|
|
@@ -668,7 +789,10 @@ export function usePluginSettings<TSettings = Record<string, unknown>>(
|
|
|
668
789
|
}
|
|
669
790
|
}
|
|
670
791
|
|
|
671
|
-
function streamRequestAuth(
|
|
792
|
+
function streamRequestAuth(
|
|
793
|
+
options: PluginEventStreamOptions<unknown>,
|
|
794
|
+
policy: { requiresAuth: boolean },
|
|
795
|
+
): {
|
|
672
796
|
headers: Headers
|
|
673
797
|
token?: string
|
|
674
798
|
} {
|
|
@@ -677,11 +801,11 @@ function streamRequestAuth(options: PluginEventStreamOptions<unknown>): {
|
|
|
677
801
|
if (!authStore.isInitialized) {
|
|
678
802
|
authStore.initialize()
|
|
679
803
|
}
|
|
680
|
-
if (authStore.token && !headers.has('Authorization')) {
|
|
804
|
+
if (policy.requiresAuth && authStore.token && !headers.has('Authorization')) {
|
|
681
805
|
headers.set('Authorization', `Bearer ${authStore.token}`)
|
|
682
806
|
}
|
|
683
807
|
headers.set('Accept', 'text/event-stream')
|
|
684
|
-
const token = authStore.token
|
|
808
|
+
const token = policy.requiresAuth ? authStore.token : undefined
|
|
685
809
|
return {
|
|
686
810
|
headers,
|
|
687
811
|
token: token && headers.get('Authorization') === `Bearer ${token}` ? token : undefined,
|
|
@@ -832,7 +956,10 @@ export function usePluginEventStream<TData = string>(
|
|
|
832
956
|
const url = buildPluginEndpointUrl(contract, endpoint, payload, {
|
|
833
957
|
baseUrl: options.baseUrl,
|
|
834
958
|
})
|
|
835
|
-
const requestAuth = streamRequestAuth(
|
|
959
|
+
const requestAuth = streamRequestAuth(
|
|
960
|
+
options as PluginEventStreamOptions<unknown>,
|
|
961
|
+
{ requiresAuth: endpoint.requiresAuth !== false },
|
|
962
|
+
)
|
|
836
963
|
const response = await fetch(url, {
|
|
837
964
|
method: 'GET',
|
|
838
965
|
headers: requestAuth.headers,
|
|
@@ -1,8 +1,57 @@
|
|
|
1
1
|
import { ref, computed, onMounted, type Ref, type ComputedRef } from 'vue'
|
|
2
2
|
import { useApi } from './useApi'
|
|
3
3
|
import { usePlatformContext } from './usePlatformContext'
|
|
4
|
+
import { getInjectedPlatformContext } from './platformContextHelpers'
|
|
4
5
|
import { useRequestSyncState } from './useRequestSyncState'
|
|
5
6
|
|
|
7
|
+
const PLATFORM_OWNED_SETTINGS_KEY = 'allowed_experiment_types'
|
|
8
|
+
|
|
9
|
+
function cloneConfig(config: Record<string, unknown>): Record<string, unknown> {
|
|
10
|
+
return JSON.parse(JSON.stringify(config)) as Record<string, unknown>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function hasOwnConfig(config: Record<string, unknown>, key: string): boolean {
|
|
14
|
+
return Object.prototype.hasOwnProperty.call(config, key)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function changedTopLevelKeys(
|
|
18
|
+
current: Record<string, unknown>,
|
|
19
|
+
saved: Record<string, unknown>,
|
|
20
|
+
): Set<string> {
|
|
21
|
+
return new Set(
|
|
22
|
+
[...new Set([...Object.keys(current), ...Object.keys(saved)])].filter(
|
|
23
|
+
key => JSON.stringify(current[key]) !== JSON.stringify(saved[key]),
|
|
24
|
+
),
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function topLevelConfigPatch(
|
|
29
|
+
current: Record<string, unknown>,
|
|
30
|
+
keys: Iterable<string>,
|
|
31
|
+
): Record<string, unknown> {
|
|
32
|
+
return Object.fromEntries(
|
|
33
|
+
[...keys].map(key => [
|
|
34
|
+
key,
|
|
35
|
+
hasOwnConfig(current, key) ? current[key] : null,
|
|
36
|
+
]),
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function platformOwnedConfigPatch(config: Record<string, unknown>): Record<string, unknown> {
|
|
41
|
+
return {
|
|
42
|
+
[PLATFORM_OWNED_SETTINGS_KEY]: hasOwnConfig(config, PLATFORM_OWNED_SETTINGS_KEY)
|
|
43
|
+
? config[PLATFORM_OWNED_SETTINGS_KEY]
|
|
44
|
+
: null,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function responseConfigPayload(response: {
|
|
49
|
+
config?: Record<string, unknown>
|
|
50
|
+
settings?: Record<string, unknown>
|
|
51
|
+
}): Record<string, unknown> {
|
|
52
|
+
return response.config ?? response.settings ?? {}
|
|
53
|
+
}
|
|
54
|
+
|
|
6
55
|
export interface UsePluginConfigReturn {
|
|
7
56
|
/** Editable plugin configuration values. */
|
|
8
57
|
config: Ref<Record<string, unknown>>
|
|
@@ -28,13 +77,25 @@ export interface UsePluginConfigReturn {
|
|
|
28
77
|
|
|
29
78
|
/** Loads, saves, and tracks dirty state for a plugin's persistent configuration via the platform API. */
|
|
30
79
|
export function usePluginConfig(pluginName?: string): UsePluginConfigReturn {
|
|
31
|
-
const
|
|
80
|
+
const injectedContext = getInjectedPlatformContext()
|
|
32
81
|
const { plugin } = usePlatformContext()
|
|
82
|
+
const platformApi = useApi()
|
|
83
|
+
const pluginApi = useApi({
|
|
84
|
+
baseUrl: injectedContext?.plugin?.api_prefix ?? plugin.value?.api_prefix,
|
|
85
|
+
})
|
|
33
86
|
|
|
34
|
-
const resolvedName = computed(
|
|
87
|
+
const resolvedName = computed(
|
|
88
|
+
() => pluginName ?? plugin.value?.name ?? injectedContext?.plugin?.name ?? '',
|
|
89
|
+
)
|
|
90
|
+
const usesPluginSettingsApi = computed(() => {
|
|
91
|
+
const currentPlugin = injectedContext?.plugin ?? plugin.value
|
|
92
|
+
return currentPlugin?.settings_api === 'plugin'
|
|
93
|
+
&& resolvedName.value === currentPlugin.name
|
|
94
|
+
})
|
|
35
95
|
|
|
36
96
|
const config = ref<Record<string, unknown>>({})
|
|
37
97
|
const savedConfig = ref<Record<string, unknown>>({})
|
|
98
|
+
const revision = ref<string | null>(null)
|
|
38
99
|
const request = useRequestSyncState('Plugin config request failed.')
|
|
39
100
|
const isLoading = ref(false)
|
|
40
101
|
const isSaving = ref(false)
|
|
@@ -52,14 +113,29 @@ export function usePluginConfig(pluginName?: string): UsePluginConfigReturn {
|
|
|
52
113
|
|
|
53
114
|
isLoading.value = true
|
|
54
115
|
try {
|
|
55
|
-
const response =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
116
|
+
const response = usesPluginSettingsApi.value
|
|
117
|
+
? await request.run(
|
|
118
|
+
() => pluginApi.get<{
|
|
119
|
+
settings: Record<string, unknown>
|
|
120
|
+
mode?: string
|
|
121
|
+
revision?: string
|
|
122
|
+
}>('/settings'),
|
|
123
|
+
{ success: 'load', errorMessage: 'Failed to load plugin config' },
|
|
124
|
+
)
|
|
125
|
+
: await request.run(
|
|
126
|
+
() => platformApi.get<{
|
|
127
|
+
plugin_name: string
|
|
128
|
+
config: Record<string, unknown>
|
|
129
|
+
revision?: string
|
|
130
|
+
}>(
|
|
131
|
+
`/plugins/${encodeURIComponent(name)}/config`,
|
|
132
|
+
),
|
|
133
|
+
{ success: 'load', errorMessage: 'Failed to load plugin config' },
|
|
134
|
+
)
|
|
135
|
+
const responseConfig = responseConfigPayload(response)
|
|
136
|
+
config.value = cloneConfig(responseConfig)
|
|
137
|
+
savedConfig.value = cloneConfig(responseConfig)
|
|
138
|
+
revision.value = response.revision ?? null
|
|
63
139
|
} catch {
|
|
64
140
|
// Error state is handled by request.run().
|
|
65
141
|
} finally {
|
|
@@ -73,15 +149,75 @@ export function usePluginConfig(pluginName?: string): UsePluginConfigReturn {
|
|
|
73
149
|
|
|
74
150
|
isSaving.value = true
|
|
75
151
|
try {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
152
|
+
const payload = cloneConfig(config.value)
|
|
153
|
+
let response: {
|
|
154
|
+
plugin_name?: string
|
|
155
|
+
config?: Record<string, unknown>
|
|
156
|
+
settings?: Record<string, unknown>
|
|
157
|
+
mode?: string
|
|
158
|
+
revision?: string
|
|
159
|
+
} | undefined
|
|
160
|
+
let nextRevision = revision.value
|
|
161
|
+
|
|
162
|
+
if (usesPluginSettingsApi.value) {
|
|
163
|
+
response = await request.run(
|
|
164
|
+
() => pluginApi.put<{
|
|
165
|
+
settings: Record<string, unknown>
|
|
166
|
+
mode?: string
|
|
167
|
+
revision?: string
|
|
168
|
+
}>(
|
|
169
|
+
'/settings',
|
|
170
|
+
payload,
|
|
171
|
+
revision.value
|
|
172
|
+
? { headers: { 'If-Match': `"${revision.value}"` } }
|
|
173
|
+
: undefined,
|
|
174
|
+
),
|
|
175
|
+
{ success: 'save', errorMessage: 'Failed to save plugin config' },
|
|
176
|
+
)
|
|
177
|
+
nextRevision = response.revision ?? nextRevision
|
|
178
|
+
} else {
|
|
179
|
+
const dirtyKeys = changedTopLevelKeys(payload, savedConfig.value)
|
|
180
|
+
const platformOwnedChanged = dirtyKeys.has(PLATFORM_OWNED_SETTINGS_KEY)
|
|
181
|
+
const typedKeys = [...dirtyKeys].filter(
|
|
182
|
+
key => key !== PLATFORM_OWNED_SETTINGS_KEY,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
if (typedKeys.length) {
|
|
186
|
+
response = await request.run(
|
|
187
|
+
() => platformApi.patch<{
|
|
188
|
+
plugin_name: string
|
|
189
|
+
config: Record<string, unknown>
|
|
190
|
+
revision?: string
|
|
191
|
+
}>(
|
|
192
|
+
`/plugins/${encodeURIComponent(name)}/config`,
|
|
193
|
+
{ config: topLevelConfigPatch(payload, typedKeys) },
|
|
194
|
+
),
|
|
195
|
+
{ success: 'save', errorMessage: 'Failed to save plugin config' },
|
|
196
|
+
)
|
|
197
|
+
nextRevision = response.revision ?? nextRevision
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (platformOwnedChanged) {
|
|
201
|
+
response = await request.run(
|
|
202
|
+
() => platformApi.patch<{
|
|
203
|
+
plugin_name: string
|
|
204
|
+
config: Record<string, unknown>
|
|
205
|
+
revision?: string
|
|
206
|
+
}>(
|
|
207
|
+
`/plugins/${encodeURIComponent(name)}/config`,
|
|
208
|
+
{ config: platformOwnedConfigPatch(payload) },
|
|
209
|
+
),
|
|
210
|
+
{ success: 'save', errorMessage: 'Failed to save plugin config' },
|
|
211
|
+
)
|
|
212
|
+
nextRevision = response.revision ?? nextRevision
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (!response) return true
|
|
217
|
+
const responseConfig = responseConfigPayload(response)
|
|
218
|
+
config.value = cloneConfig(responseConfig)
|
|
219
|
+
savedConfig.value = cloneConfig(responseConfig)
|
|
220
|
+
revision.value = response.revision ?? nextRevision
|
|
85
221
|
return true
|
|
86
222
|
} catch {
|
|
87
223
|
return false
|
|
@@ -91,7 +227,7 @@ export function usePluginConfig(pluginName?: string): UsePluginConfigReturn {
|
|
|
91
227
|
}
|
|
92
228
|
|
|
93
229
|
function reset(): void {
|
|
94
|
-
config.value =
|
|
230
|
+
config.value = cloneConfig(savedConfig.value)
|
|
95
231
|
request.clearError()
|
|
96
232
|
}
|
|
97
233
|
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FormFieldSchema,
|
|
3
|
+
FormFieldType,
|
|
4
|
+
FormSchema,
|
|
5
|
+
} from '../types/form-builder'
|
|
6
|
+
import type {
|
|
7
|
+
GeneratedJsonSchema,
|
|
8
|
+
GeneratedJobDefinition,
|
|
9
|
+
} from '../types/generated-ui'
|
|
10
|
+
|
|
11
|
+
type GeneratedReference = 'path'
|
|
12
|
+
|
|
13
|
+
interface ResolvedGeneratedField {
|
|
14
|
+
schema: GeneratedJsonSchema
|
|
15
|
+
reference?: GeneratedReference
|
|
16
|
+
hasDefault: boolean
|
|
17
|
+
defaultValue?: unknown
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function generatedJobFormSchema(definition: GeneratedJobDefinition): FormSchema & { sections: NonNullable<FormSchema['sections']> } {
|
|
21
|
+
const root = definition.input_schema
|
|
22
|
+
const required = new Set(root.required ?? [])
|
|
23
|
+
const fields = Object.entries(root.properties ?? {}).map(([name, schema]) =>
|
|
24
|
+
toFormField(name, resolveGeneratedField(root, schema), required.has(name)),
|
|
25
|
+
)
|
|
26
|
+
return {
|
|
27
|
+
sections: [{
|
|
28
|
+
id: definition.id,
|
|
29
|
+
title: definition.title,
|
|
30
|
+
description: definition.description || undefined,
|
|
31
|
+
columns: 1,
|
|
32
|
+
fields,
|
|
33
|
+
}],
|
|
34
|
+
submitLabel: 'Run analysis',
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function generatedJobDefaults(definition: GeneratedJobDefinition): Record<string, unknown> {
|
|
39
|
+
return Object.fromEntries(
|
|
40
|
+
Object.entries(definition.input_schema.properties ?? {})
|
|
41
|
+
.filter(([, schema]) => hasOwnDefault(schema))
|
|
42
|
+
.map(([name, schema]) => [name, structuredClone(schema.default)]),
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function normalizeGeneratedJobInput(
|
|
47
|
+
definition: GeneratedJobDefinition,
|
|
48
|
+
values: Record<string, unknown>,
|
|
49
|
+
): Record<string, unknown> {
|
|
50
|
+
const normalized: Record<string, unknown> = {}
|
|
51
|
+
for (const [name, value] of Object.entries(values)) {
|
|
52
|
+
const field = definition.input_schema.properties?.[name]
|
|
53
|
+
if (!field) continue
|
|
54
|
+
normalized[name] = value
|
|
55
|
+
}
|
|
56
|
+
return normalized
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function toFormField(
|
|
60
|
+
name: string,
|
|
61
|
+
resolved: ResolvedGeneratedField,
|
|
62
|
+
required: boolean,
|
|
63
|
+
): FormFieldSchema {
|
|
64
|
+
const { schema, reference } = resolved
|
|
65
|
+
const validation: NonNullable<FormFieldSchema['validation']> = {}
|
|
66
|
+
if (required) validation.required = true
|
|
67
|
+
if (schema.minimum !== undefined) validation.min = schema.minimum
|
|
68
|
+
if (schema.maximum !== undefined) validation.max = schema.maximum
|
|
69
|
+
if (schema.minLength !== undefined) validation.minLength = schema.minLength
|
|
70
|
+
if (schema.maxLength !== undefined) validation.maxLength = schema.maxLength
|
|
71
|
+
|
|
72
|
+
const props: Record<string, unknown> = {}
|
|
73
|
+
if (schema.enum) props.options = [...schema.enum]
|
|
74
|
+
if (reference === 'path' && schema.type === 'array') props.multiple = true
|
|
75
|
+
return {
|
|
76
|
+
name,
|
|
77
|
+
label: schema.title || humanize(name),
|
|
78
|
+
type: fieldType(schema, reference),
|
|
79
|
+
defaultValue: resolved.hasDefault ? structuredClone(resolved.defaultValue) : undefined,
|
|
80
|
+
hint: schema.description,
|
|
81
|
+
validation,
|
|
82
|
+
props,
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function resolveGeneratedField(
|
|
87
|
+
root: GeneratedJsonSchema,
|
|
88
|
+
field: GeneratedJsonSchema,
|
|
89
|
+
): ResolvedGeneratedField {
|
|
90
|
+
const hasDefault = hasOwnDefault(field)
|
|
91
|
+
const candidate = field.anyOf?.find(item => item.type !== 'null')
|
|
92
|
+
?? field.allOf?.[0]
|
|
93
|
+
?? field
|
|
94
|
+
if (candidate.$ref) {
|
|
95
|
+
const referenceParts = candidate.$ref.split('/')
|
|
96
|
+
const key = referenceParts[referenceParts.length - 1]
|
|
97
|
+
const schema = key ? root.$defs?.[key] : undefined
|
|
98
|
+
if (!schema) throw new Error(`Generated field reference is missing: ${candidate.$ref}`)
|
|
99
|
+
return {
|
|
100
|
+
schema: { ...schema, title: field.title ?? schema.title },
|
|
101
|
+
reference: field['x-mint-component'] ?? schema['x-mint-component'],
|
|
102
|
+
hasDefault,
|
|
103
|
+
defaultValue: field.default,
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
schema: candidate,
|
|
108
|
+
reference: field['x-mint-component'] ?? candidate['x-mint-component'],
|
|
109
|
+
hasDefault,
|
|
110
|
+
defaultValue: field.default,
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function fieldType(schema: GeneratedJsonSchema, reference?: GeneratedReference): FormFieldType {
|
|
115
|
+
if (reference === 'path') return 'path'
|
|
116
|
+
if (schema.enum) return 'select'
|
|
117
|
+
const type = Array.isArray(schema.type) ? schema.type.find(value => value !== 'null') : schema.type
|
|
118
|
+
if (type === 'boolean') return 'toggle'
|
|
119
|
+
if (type === 'number' || type === 'integer') return 'number'
|
|
120
|
+
if (type === 'array') return 'tags'
|
|
121
|
+
return 'text'
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function hasOwnDefault(schema: GeneratedJsonSchema): boolean {
|
|
125
|
+
return Object.prototype.hasOwnProperty.call(schema, 'default')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function humanize(value: string): string {
|
|
129
|
+
return value.replace(/[_-]+/g, ' ').replace(/^\w/, character => character.toUpperCase())
|
|
130
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createPinia } from 'pinia'
|
|
2
|
+
import { createApp, h } from 'vue'
|
|
3
|
+
|
|
4
|
+
import GeneratedPluginView from '../components/GeneratedPluginView.vue'
|
|
5
|
+
import '../styles/index.css'
|
|
6
|
+
|
|
7
|
+
function generatedApiBase(location: Location): string {
|
|
8
|
+
const pluginPath = new URL('.', location.href).pathname.replace(/\/$/, '')
|
|
9
|
+
return `/api${pluginPath}`
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const app = createApp({
|
|
13
|
+
name: 'MintGeneratedUiApp',
|
|
14
|
+
render: () => h(GeneratedPluginView, {
|
|
15
|
+
baseUrl: generatedApiBase(window.location),
|
|
16
|
+
showStandaloneLabel: true,
|
|
17
|
+
}),
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
app.use(createPinia())
|
|
21
|
+
app.mount('#app')
|