@opencor/opencor 0.20250823.0 → 0.20250827.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/dist/opencor.css +1 -1
- package/dist/opencor.es.js +29735 -3
- package/dist/{quill-pOzwAhrV.js → quill-CuAVUOyy.js} +8 -5
- package/package.json +6 -11
- package/dist/index-in3ZZStA.js +0 -97538
- package/dist/opencor.umd.js +0 -6020
- package/src/App.vue +0 -7
- package/src/ContainerApp.vue +0 -21
- package/src/assets/app.css +0 -14
- package/src/assets/base.css +0 -31
- package/src/assets/logo.svg +0 -17
- package/src/common/common.ts +0 -86
- package/src/common/constants.ts +0 -12
- package/src/common/electron.ts +0 -23
- package/src/common/electronApi.ts +0 -63
- package/src/common/locCommon.ts +0 -170
- package/src/common/settings.ts +0 -95
- package/src/common/vueCommon.ts +0 -69
- package/src/components/BackgroundComponent.vue +0 -26
- package/src/components/BlockingMessageComponent.vue +0 -34
- package/src/components/ContentsComponent.vue +0 -277
- package/src/components/DragNDropComponent.vue +0 -35
- package/src/components/MainMenu.vue +0 -225
- package/src/components/OpenCOR.vue +0 -624
- package/src/components/dialogs/AboutDialog.vue +0 -51
- package/src/components/dialogs/BaseDialog.vue +0 -58
- package/src/components/dialogs/OpenRemoteDialog.vue +0 -37
- package/src/components/dialogs/ResetAllDialog.vue +0 -13
- package/src/components/dialogs/SettingsDialog.vue +0 -42
- package/src/components/dialogs/UpdateAvailableDialog.vue +0 -16
- package/src/components/dialogs/UpdateDownloadProgressDialog.vue +0 -11
- package/src/components/dialogs/UpdateErrorDialog.vue +0 -18
- package/src/components/dialogs/UpdateNotAvailableDialog.vue +0 -12
- package/src/components/propertyEditors/GraphsPropertyEditor.vue +0 -3
- package/src/components/propertyEditors/ParametersPropertyEditor.vue +0 -3
- package/src/components/propertyEditors/PropertyEditor.vue +0 -60
- package/src/components/propertyEditors/SimulationPropertyEditor.vue +0 -45
- package/src/components/propertyEditors/SolversPropertyEditor.vue +0 -3
- package/src/components/views/IssuesView.vue +0 -50
- package/src/components/views/SimulationExperimentUiView.vue +0 -154
- package/src/components/views/SimulationExperimentView.vue +0 -218
- package/src/components/widgets/GraphPanelWidget.vue +0 -140
- package/src/components/widgets/InputWidget.vue +0 -128
- package/src/libopencor/locApi.ts +0 -167
- package/src/libopencor/locFileApi.ts +0 -263
- package/src/libopencor/locLoggerApi.ts +0 -36
- package/src/libopencor/locSedApi.ts +0 -486
- package/src/libopencor/locUiJsonApi.ts +0 -485
- package/src/libopencor/locVersionApi.ts +0 -17
- package/src/libopencor/src/common.cpp +0 -67
- package/src/libopencor/src/common.h +0 -27
- package/src/libopencor/src/file.cpp +0 -72
- package/src/libopencor/src/file.h +0 -15
- package/src/libopencor/src/main.cpp +0 -78
- package/src/libopencor/src/sed.cpp +0 -348
- package/src/libopencor/src/sed.h +0 -53
- package/src/libopencor/src/version.cpp +0 -8
- package/src/libopencor/src/version.h +0 -5
- package/src/main.ts +0 -6
- package/src/types/types.d.ts +0 -9
|
@@ -1,624 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<BlockUI id="blockUi" :blocked="!uiEnabled" class="overflow-hidden" :style="blockUiStyle">
|
|
3
|
-
<Toast id="toast" :pt:root:style="{ position: 'absolute' }" />
|
|
4
|
-
<BackgroundComponent v-show="loadingOpencorMessageVisible || loadingModelMessageVisible || omex === undefined" />
|
|
5
|
-
<BlockingMessageComponent message="Loading OpenCOR..." v-show="loadingOpencorMessageVisible" />
|
|
6
|
-
<BlockingMessageComponent message="Loading model..." v-show="loadingModelMessageVisible" />
|
|
7
|
-
<IssuesView v-if="issues.length !== 0" class="h-full" :issues="issues" :simulationOnly="omex !== undefined" />
|
|
8
|
-
<div
|
|
9
|
-
v-else
|
|
10
|
-
@dragenter="onDragEnter"
|
|
11
|
-
class="h-full"
|
|
12
|
-
@dragover.prevent
|
|
13
|
-
@drop.prevent="onDrop"
|
|
14
|
-
@dragleave="onDragLeave"
|
|
15
|
-
>
|
|
16
|
-
<input ref="files" type="file" multiple style="display: none" @change="onChange" />
|
|
17
|
-
<DragNDropComponent v-show="dragAndDropCounter > 0" />
|
|
18
|
-
<div v-show="!electronApi && omex === undefined">
|
|
19
|
-
<MainMenu
|
|
20
|
-
v-show="mainMenuVisible"
|
|
21
|
-
:uiEnabled="compUiEnabled"
|
|
22
|
-
:hasFiles="hasFiles"
|
|
23
|
-
@about="onAbout"
|
|
24
|
-
@open="($refs.files as HTMLInputElement).click()"
|
|
25
|
-
@openRemote="openRemoteVisible = true"
|
|
26
|
-
@openSampleLorenz="onOpenSampleLorenz"
|
|
27
|
-
@openSampleInteractiveLorenz="onOpenSampleInteractiveLorenz"
|
|
28
|
-
@close="onClose"
|
|
29
|
-
@closeAll="onCloseAll"
|
|
30
|
-
@settings="onSettings"
|
|
31
|
-
/>
|
|
32
|
-
</div>
|
|
33
|
-
<ContentsComponent ref="contents" :uiEnabled="compUiEnabled" :simulationOnly="omex !== undefined" />
|
|
34
|
-
<OpenRemoteDialog
|
|
35
|
-
v-model:visible="openRemoteVisible"
|
|
36
|
-
@openRemote="onOpenRemote"
|
|
37
|
-
@close="openRemoteVisible = false"
|
|
38
|
-
/>
|
|
39
|
-
<SettingsDialog v-model:visible="settingsVisible" @close="settingsVisible = false" />
|
|
40
|
-
<ResetAllDialog v-model:visible="resetAllVisible" @resetAll="onResetAll" @close="resetAllVisible = false" />
|
|
41
|
-
<AboutDialog v-model:visible="aboutVisible" @close="aboutVisible = false" />
|
|
42
|
-
</div>
|
|
43
|
-
<UpdateErrorDialog
|
|
44
|
-
v-model:visible="updateErrorVisible"
|
|
45
|
-
:title="updateErrorTitle"
|
|
46
|
-
:issue="updateErrorIssue"
|
|
47
|
-
@close="onUpdateErrorDialogClose"
|
|
48
|
-
/>
|
|
49
|
-
<UpdateAvailableDialog
|
|
50
|
-
v-model:visible="updateAvailableVisible"
|
|
51
|
-
:version="updateVersion"
|
|
52
|
-
@downloadAndInstall="onDownloadAndInstall"
|
|
53
|
-
@close="updateAvailableVisible = false"
|
|
54
|
-
/>
|
|
55
|
-
<UpdateDownloadProgressDialog v-model:visible="updateDownloadProgressVisible" :percent="updateDownloadPercent" />
|
|
56
|
-
<UpdateNotAvailableDialog v-model:visible="updateNotAvailableVisible" @close="updateNotAvailableVisible = false" />
|
|
57
|
-
</BlockUI>
|
|
58
|
-
</template>
|
|
59
|
-
|
|
60
|
-
<script setup lang="ts">
|
|
61
|
-
import primeVueAuraTheme from '@primeuix/themes/aura'
|
|
62
|
-
import * as vueusecore from '@vueuse/core'
|
|
63
|
-
|
|
64
|
-
import 'primeicons/primeicons.css'
|
|
65
|
-
import primeVueConfig from 'primevue/config'
|
|
66
|
-
import primeVueConfirmationService from 'primevue/confirmationservice'
|
|
67
|
-
import primeVueToastService from 'primevue/toastservice'
|
|
68
|
-
import { useToast } from 'primevue/usetoast'
|
|
69
|
-
import * as vue from 'vue'
|
|
70
|
-
|
|
71
|
-
import type { IOpenCORProps } from '../../index'
|
|
72
|
-
|
|
73
|
-
import '../assets/app.css'
|
|
74
|
-
import * as common from '../common/common'
|
|
75
|
-
import { SHORT_DELAY, TOAST_LIFE } from '../common/constants'
|
|
76
|
-
import { electronApi } from '../common/electronApi'
|
|
77
|
-
import * as locCommon from '../common/locCommon'
|
|
78
|
-
import * as vueCommon from '../common/vueCommon'
|
|
79
|
-
import IContentsComponent from '../components/ContentsComponent.vue'
|
|
80
|
-
import * as locApi from '../libopencor/locApi'
|
|
81
|
-
|
|
82
|
-
const props = defineProps<IOpenCORProps>()
|
|
83
|
-
|
|
84
|
-
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
|
|
85
|
-
const contents = vue.ref<InstanceType<typeof IContentsComponent> | null>(null)
|
|
86
|
-
const issues = vue.ref<locApi.IIssue[]>([])
|
|
87
|
-
|
|
88
|
-
const compUiEnabled = vue.computed(() => {
|
|
89
|
-
return (
|
|
90
|
-
uiEnabled.value &&
|
|
91
|
-
!openRemoteVisible.value &&
|
|
92
|
-
!settingsVisible.value &&
|
|
93
|
-
!resetAllVisible.value &&
|
|
94
|
-
!aboutVisible.value &&
|
|
95
|
-
!updateErrorVisible.value &&
|
|
96
|
-
!updateAvailableVisible.value &&
|
|
97
|
-
!updateDownloadProgressVisible.value &&
|
|
98
|
-
!updateNotAvailableVisible.value
|
|
99
|
-
)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
// Get the current Vue app instance to use some PrimeVue components.
|
|
103
|
-
|
|
104
|
-
const getCurrentInstance = vue.getCurrentInstance()
|
|
105
|
-
|
|
106
|
-
if (getCurrentInstance !== null) {
|
|
107
|
-
const app = getCurrentInstance.appContext.app
|
|
108
|
-
let options = {}
|
|
109
|
-
|
|
110
|
-
if (props.theme === 'light') {
|
|
111
|
-
options = {
|
|
112
|
-
darkModeSelector: false
|
|
113
|
-
}
|
|
114
|
-
} else if (props.theme === 'dark') {
|
|
115
|
-
document.documentElement.classList.add('opencor-dark-mode')
|
|
116
|
-
document.body.classList.add('opencor-dark-mode')
|
|
117
|
-
|
|
118
|
-
options = {
|
|
119
|
-
darkModeSelector: '.opencor-dark-mode'
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
app.use(primeVueConfig as unknown as vue.Plugin, {
|
|
124
|
-
theme: {
|
|
125
|
-
preset: primeVueAuraTheme,
|
|
126
|
-
options: options
|
|
127
|
-
}
|
|
128
|
-
})
|
|
129
|
-
app.use(primeVueConfirmationService as unknown as vue.Plugin)
|
|
130
|
-
app.use(primeVueToastService as unknown as vue.Plugin)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (props.theme !== undefined) {
|
|
134
|
-
vueCommon.setTheme(props.theme)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const toast = useToast()
|
|
138
|
-
|
|
139
|
-
// Asynchronously initialise our libOpenCOR API.
|
|
140
|
-
|
|
141
|
-
const locApiInitialised = vue.ref(false)
|
|
142
|
-
|
|
143
|
-
void locApi.initialiseLocApi().then(() => {
|
|
144
|
-
locApiInitialised.value = true
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
// Handle an action.
|
|
148
|
-
|
|
149
|
-
electronApi?.onAction((action: string) => {
|
|
150
|
-
handleAction(action)
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
function handleAction(action: string): void {
|
|
154
|
-
function isAction(actionName: string, expectedActionName: string): boolean {
|
|
155
|
-
return actionName.localeCompare(expectedActionName, undefined, { sensitivity: 'base' }) === 0
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const index = action.indexOf('/')
|
|
159
|
-
const actionName = index !== -1 ? action.substring(0, index) : action
|
|
160
|
-
const actionArguments = index !== -1 ? action.substring(index + 1) : ''
|
|
161
|
-
|
|
162
|
-
if (isAction(actionName, 'openAboutDialog')) {
|
|
163
|
-
onAbout()
|
|
164
|
-
} else if (isAction(actionName, 'openSettingsDialog')) {
|
|
165
|
-
onSettings()
|
|
166
|
-
} else {
|
|
167
|
-
const filePaths = actionArguments.split('%7C')
|
|
168
|
-
|
|
169
|
-
if (
|
|
170
|
-
(isAction(actionName, 'openFile') && filePaths.length === 1) ||
|
|
171
|
-
(isAction(actionName, 'openFiles') && filePaths.length > 1)
|
|
172
|
-
) {
|
|
173
|
-
for (const filePath of filePaths) {
|
|
174
|
-
openFile(filePath)
|
|
175
|
-
}
|
|
176
|
-
} else {
|
|
177
|
-
toast.add({
|
|
178
|
-
severity: 'error',
|
|
179
|
-
summary: 'Handling an action',
|
|
180
|
-
detail: `${action}\n\nThe action could not be handled.`,
|
|
181
|
-
life: TOAST_LIFE
|
|
182
|
-
})
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Enable/disable the UI.
|
|
188
|
-
|
|
189
|
-
const uiEnabled = vue.ref<boolean>(true)
|
|
190
|
-
|
|
191
|
-
electronApi?.onEnableDisableUi((enable: boolean) => {
|
|
192
|
-
enableDisableUi(enable)
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
function enableDisableUi(enable: boolean): void {
|
|
196
|
-
uiEnabled.value = enable
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Enable/disable some menu items.
|
|
200
|
-
|
|
201
|
-
const hasFiles = vue.computed(() => {
|
|
202
|
-
return contents.value?.hasFiles() ?? false
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
vue.watch(hasFiles, (hasFiles) => {
|
|
206
|
-
electronApi?.enableDisableFileCloseAndCloseAllMenuItems(hasFiles)
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
// Loading OpenCOR.
|
|
210
|
-
// Note: this is only done if window.locApi is not defined, which means that we are running OpenCOR's Web app.
|
|
211
|
-
|
|
212
|
-
const loadingOpencorMessageVisible = vue.ref<boolean>(false)
|
|
213
|
-
|
|
214
|
-
// @ts-expect-error (window.locApi may or may not be defined which is why we test it)
|
|
215
|
-
if (window.locApi === undefined) {
|
|
216
|
-
enableDisableUi(false)
|
|
217
|
-
|
|
218
|
-
loadingOpencorMessageVisible.value = true
|
|
219
|
-
|
|
220
|
-
vue.watch(locApiInitialised, (initialised) => {
|
|
221
|
-
if (initialised) {
|
|
222
|
-
loadingOpencorMessageVisible.value = false
|
|
223
|
-
|
|
224
|
-
enableDisableUi(true)
|
|
225
|
-
}
|
|
226
|
-
})
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Loading model.
|
|
230
|
-
|
|
231
|
-
const loadingModelMessageVisible = vue.ref<boolean>(false)
|
|
232
|
-
|
|
233
|
-
function showLoadingModelMessage(): void {
|
|
234
|
-
enableDisableUi(false)
|
|
235
|
-
|
|
236
|
-
loadingModelMessageVisible.value = true
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function hideLoadingModelMessage(): void {
|
|
240
|
-
enableDisableUi(true)
|
|
241
|
-
|
|
242
|
-
loadingModelMessageVisible.value = false
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Auto update.
|
|
246
|
-
|
|
247
|
-
electronApi?.onCheckForUpdates(() => {
|
|
248
|
-
electronApi?.checkForUpdates(false)
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
const updateErrorVisible = vue.ref<boolean>(false)
|
|
252
|
-
const updateErrorTitle = vue.ref<string>('')
|
|
253
|
-
const updateErrorIssue = vue.ref<string>('')
|
|
254
|
-
|
|
255
|
-
function onUpdateErrorDialogClose(): void {
|
|
256
|
-
updateErrorVisible.value = false
|
|
257
|
-
updateDownloadProgressVisible.value = false
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const updateAvailableVisible = vue.ref<boolean>(false)
|
|
261
|
-
const updateDownloadProgressVisible = vue.ref<boolean>(false)
|
|
262
|
-
const updateVersion = vue.ref<string>('')
|
|
263
|
-
const updateDownloadPercent = vue.ref<number>(0)
|
|
264
|
-
|
|
265
|
-
electronApi?.onUpdateAvailable((version: string) => {
|
|
266
|
-
updateAvailableVisible.value = true
|
|
267
|
-
updateVersion.value = version
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
function onDownloadAndInstall(): void {
|
|
271
|
-
updateDownloadPercent.value = 0 // Just to be on the safe side.
|
|
272
|
-
updateDownloadProgressVisible.value = true
|
|
273
|
-
updateAvailableVisible.value = false
|
|
274
|
-
|
|
275
|
-
electronApi?.downloadAndInstallUpdate()
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
electronApi?.onUpdateDownloadError((issue: string) => {
|
|
279
|
-
updateErrorTitle.value = 'Downloading Update...'
|
|
280
|
-
updateErrorIssue.value = `An error occurred while downloading the update (${issue}).`
|
|
281
|
-
updateErrorVisible.value = true
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
electronApi?.onUpdateDownloadProgress((percent: number) => {
|
|
285
|
-
updateDownloadPercent.value = percent
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
electronApi?.onUpdateDownloaded(() => {
|
|
289
|
-
updateDownloadPercent.value = 100 // Just to be on the safe side.
|
|
290
|
-
|
|
291
|
-
electronApi?.installUpdateAndRestart()
|
|
292
|
-
})
|
|
293
|
-
|
|
294
|
-
const updateNotAvailableVisible = vue.ref<boolean>(false)
|
|
295
|
-
|
|
296
|
-
electronApi?.onUpdateNotAvailable(() => {
|
|
297
|
-
updateNotAvailableVisible.value = true
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
electronApi?.onUpdateCheckError((issue: string) => {
|
|
301
|
-
updateErrorTitle.value = 'Checking For Updates...'
|
|
302
|
-
updateErrorIssue.value = `An error occurred while checking for updates (${issue}).`
|
|
303
|
-
updateErrorVisible.value = true
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
// About dialog.
|
|
307
|
-
|
|
308
|
-
const aboutVisible = vue.ref<boolean>(false)
|
|
309
|
-
|
|
310
|
-
electronApi?.onAbout(() => {
|
|
311
|
-
onAbout()
|
|
312
|
-
})
|
|
313
|
-
|
|
314
|
-
function onAbout(): void {
|
|
315
|
-
aboutVisible.value = true
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Settings dialog.
|
|
319
|
-
|
|
320
|
-
const settingsVisible = vue.ref<boolean>(false)
|
|
321
|
-
|
|
322
|
-
electronApi?.onSettings(() => {
|
|
323
|
-
onSettings()
|
|
324
|
-
})
|
|
325
|
-
|
|
326
|
-
function onSettings(): void {
|
|
327
|
-
settingsVisible.value = true
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Open a file.
|
|
331
|
-
|
|
332
|
-
function openFile(fileOrFilePath: string | File): void {
|
|
333
|
-
// Check whether the file is already open and if so then select it.
|
|
334
|
-
|
|
335
|
-
const filePath = locCommon.filePath(fileOrFilePath)
|
|
336
|
-
|
|
337
|
-
if (contents.value?.hasFile(filePath) ?? false) {
|
|
338
|
-
contents.value?.selectFile(filePath)
|
|
339
|
-
|
|
340
|
-
return
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Retrieve a locApi.File object for the given file or file path and add it to the contents.
|
|
344
|
-
|
|
345
|
-
if (locCommon.isRemoteFilePath(filePath)) {
|
|
346
|
-
showLoadingModelMessage()
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
locCommon
|
|
350
|
-
.file(fileOrFilePath)
|
|
351
|
-
.then((file) => {
|
|
352
|
-
const fileType = file.type()
|
|
353
|
-
|
|
354
|
-
if (
|
|
355
|
-
fileType === locApi.EFileType.IRRETRIEVABLE_FILE ||
|
|
356
|
-
fileType === locApi.EFileType.UNKNOWN_FILE ||
|
|
357
|
-
(props.omex !== undefined && fileType !== locApi.EFileType.COMBINE_ARCHIVE)
|
|
358
|
-
) {
|
|
359
|
-
if (props.omex !== undefined) {
|
|
360
|
-
void vue.nextTick().then(() => {
|
|
361
|
-
issues.value.push({
|
|
362
|
-
type: locApi.EIssueType.ERROR,
|
|
363
|
-
description:
|
|
364
|
-
fileType === locApi.EFileType.IRRETRIEVABLE_FILE
|
|
365
|
-
? 'The file could not be retrieved.'
|
|
366
|
-
: 'Only COMBINE archives are supported.'
|
|
367
|
-
})
|
|
368
|
-
})
|
|
369
|
-
} else {
|
|
370
|
-
toast.add({
|
|
371
|
-
severity: 'error',
|
|
372
|
-
summary: 'Opening a file',
|
|
373
|
-
detail:
|
|
374
|
-
filePath +
|
|
375
|
-
'\n\n' +
|
|
376
|
-
(fileType === locApi.EFileType.IRRETRIEVABLE_FILE
|
|
377
|
-
? 'The file could not be retrieved.'
|
|
378
|
-
: 'Only CellML files, SED-ML files, and COMBINE archives are supported.'),
|
|
379
|
-
life: TOAST_LIFE
|
|
380
|
-
})
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
electronApi?.fileIssue(filePath)
|
|
384
|
-
} else {
|
|
385
|
-
contents.value?.openFile(file)
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
if (locCommon.isRemoteFilePath(filePath)) {
|
|
389
|
-
hideLoadingModelMessage()
|
|
390
|
-
}
|
|
391
|
-
})
|
|
392
|
-
.catch((error: unknown) => {
|
|
393
|
-
if (locCommon.isRemoteFilePath(filePath)) {
|
|
394
|
-
hideLoadingModelMessage()
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
if (props.omex !== undefined) {
|
|
398
|
-
void vue.nextTick().then(() => {
|
|
399
|
-
issues.value.push({
|
|
400
|
-
type: locApi.EIssueType.ERROR,
|
|
401
|
-
description: common.formatIssue(error instanceof Error ? error.message : String(error))
|
|
402
|
-
})
|
|
403
|
-
})
|
|
404
|
-
} else {
|
|
405
|
-
toast.add({
|
|
406
|
-
severity: 'error',
|
|
407
|
-
summary: 'Opening a file',
|
|
408
|
-
detail: `${filePath}\n\n${common.formatIssue(error instanceof Error ? error.message : String(error))}`,
|
|
409
|
-
life: TOAST_LIFE
|
|
410
|
-
})
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
electronApi?.fileIssue(filePath)
|
|
414
|
-
})
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// Open file(s) dialog.
|
|
418
|
-
|
|
419
|
-
function onChange(event: Event): void {
|
|
420
|
-
const files = (event.target as HTMLInputElement).files
|
|
421
|
-
|
|
422
|
-
if (files !== null) {
|
|
423
|
-
for (const file of Array.from(files)) {
|
|
424
|
-
openFile(file)
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Drag and drop.
|
|
430
|
-
|
|
431
|
-
const dragAndDropCounter = vue.ref<number>(0)
|
|
432
|
-
|
|
433
|
-
function onDragEnter(): void {
|
|
434
|
-
if (!uiEnabled.value || props.omex !== undefined) {
|
|
435
|
-
return
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
dragAndDropCounter.value += 1
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
function onDrop(event: DragEvent): void {
|
|
442
|
-
if (dragAndDropCounter.value === 0) {
|
|
443
|
-
return
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
dragAndDropCounter.value = 0
|
|
447
|
-
|
|
448
|
-
const files = event.dataTransfer?.files
|
|
449
|
-
|
|
450
|
-
if (files !== undefined) {
|
|
451
|
-
for (const file of Array.from(files)) {
|
|
452
|
-
openFile(file)
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
function onDragLeave(): void {
|
|
458
|
-
if (dragAndDropCounter.value === 0) {
|
|
459
|
-
return
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
dragAndDropCounter.value -= 1
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// Open.
|
|
466
|
-
|
|
467
|
-
electronApi?.onOpen((filePath: string) => {
|
|
468
|
-
openFile(filePath)
|
|
469
|
-
})
|
|
470
|
-
|
|
471
|
-
// Open remote.
|
|
472
|
-
|
|
473
|
-
const openRemoteVisible = vue.ref<boolean>(false)
|
|
474
|
-
|
|
475
|
-
electronApi?.onOpenRemote(() => {
|
|
476
|
-
openRemoteVisible.value = true
|
|
477
|
-
})
|
|
478
|
-
|
|
479
|
-
function onOpenRemote(url: string): void {
|
|
480
|
-
// Note: no matter whether this is OpenCOR or OpenCOR's Web app, we always retrieve the file contents of a remote
|
|
481
|
-
// file. We could, in OpenCOR, rely on libOpenCOR to retrieve it for us, but this would block the UI. To
|
|
482
|
-
// retrieve the file here means that it is done asynchronously, which in turn means that the UI is not blocked
|
|
483
|
-
// and that we can show a spinning wheel to indicate that something is happening.
|
|
484
|
-
|
|
485
|
-
openFile(url)
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// Open sample Lorenz.
|
|
489
|
-
|
|
490
|
-
electronApi?.onOpenSampleLorenz(() => {
|
|
491
|
-
onOpenSampleLorenz()
|
|
492
|
-
})
|
|
493
|
-
|
|
494
|
-
function onOpenSampleLorenz(): void {
|
|
495
|
-
openFile('https://github.com/opencor/webapp/raw/refs/heads/main/tests/models/lorenz.omex')
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// Open sample interactive Lorenz.
|
|
499
|
-
|
|
500
|
-
electronApi?.onOpenSampleInteractiveLorenz(() => {
|
|
501
|
-
onOpenSampleInteractiveLorenz()
|
|
502
|
-
})
|
|
503
|
-
|
|
504
|
-
function onOpenSampleInteractiveLorenz(): void {
|
|
505
|
-
openFile('https://github.com/opencor/webapp/raw/refs/heads/main/tests/models/ui/lorenz.omex')
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// Close.
|
|
509
|
-
|
|
510
|
-
electronApi?.onClose(() => {
|
|
511
|
-
onClose()
|
|
512
|
-
})
|
|
513
|
-
|
|
514
|
-
function onClose(): void {
|
|
515
|
-
contents.value?.closeCurrentFile()
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
// Close all.
|
|
519
|
-
|
|
520
|
-
electronApi?.onCloseAll(() => {
|
|
521
|
-
onCloseAll()
|
|
522
|
-
})
|
|
523
|
-
|
|
524
|
-
function onCloseAll(): void {
|
|
525
|
-
contents.value?.closeAllFiles()
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// Reset all.
|
|
529
|
-
|
|
530
|
-
const resetAllVisible = vue.ref<boolean>(false)
|
|
531
|
-
|
|
532
|
-
electronApi?.onResetAll(() => {
|
|
533
|
-
resetAllVisible.value = true
|
|
534
|
-
})
|
|
535
|
-
|
|
536
|
-
function onResetAll(): void {
|
|
537
|
-
electronApi?.resetAll()
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// Select.
|
|
541
|
-
|
|
542
|
-
electronApi?.onSelect((filePath: string) => {
|
|
543
|
-
contents.value?.selectFile(filePath)
|
|
544
|
-
})
|
|
545
|
-
|
|
546
|
-
// Track the height of our block UI.
|
|
547
|
-
|
|
548
|
-
vueCommon.trackElementHeight('blockUi')
|
|
549
|
-
|
|
550
|
-
// Set the height of our block UI.
|
|
551
|
-
|
|
552
|
-
const blockUiStyle = vue.ref({})
|
|
553
|
-
const mainMenuVisible = vue.ref<boolean>(false)
|
|
554
|
-
|
|
555
|
-
vue.onMounted(() => {
|
|
556
|
-
// Set the height of our block UI to either '100vh' or '100%', depending on the height of our document element.
|
|
557
|
-
|
|
558
|
-
blockUiStyle.value =
|
|
559
|
-
window.getComputedStyle(document.documentElement).height === '0px' ? { height: '100vh' } : { height: '100%' }
|
|
560
|
-
|
|
561
|
-
// We have set the height of our block UI, so we can now safely show our main menu.
|
|
562
|
-
// Note: indeed, to properly determine the height of our document element, we must ensure that our own height is zero.
|
|
563
|
-
|
|
564
|
-
mainMenuVisible.value = true
|
|
565
|
-
})
|
|
566
|
-
|
|
567
|
-
// If a COMBINE archive is provided then open it (and then the Simulation Experiment view will be shown in isolation) or
|
|
568
|
-
// carry as normal (i.e. the whole OpenCOR UI will be shown).
|
|
569
|
-
|
|
570
|
-
if (props.omex !== undefined) {
|
|
571
|
-
vue.watch(locApiInitialised, (initialised) => {
|
|
572
|
-
if (initialised) {
|
|
573
|
-
if (props.omex !== undefined) {
|
|
574
|
-
openFile(props.omex)
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
})
|
|
578
|
-
} else {
|
|
579
|
-
// (Also) track the height of our main menu.
|
|
580
|
-
|
|
581
|
-
vueCommon.trackElementHeight('mainMenu')
|
|
582
|
-
|
|
583
|
-
// Things that need to be done when the component is mounted.
|
|
584
|
-
|
|
585
|
-
vue.onMounted(() => {
|
|
586
|
-
// Do what follows with a bit of a delay to give our background (with the OpenCOR logo) time to be renderered.
|
|
587
|
-
|
|
588
|
-
setTimeout(() => {
|
|
589
|
-
// Ensure that our toasts are shown within our container.
|
|
590
|
-
|
|
591
|
-
const toastElement = document.getElementById('toast')
|
|
592
|
-
const blockUiElement = document.getElementById('blockUi')
|
|
593
|
-
|
|
594
|
-
if (toastElement !== null && blockUiElement !== null) {
|
|
595
|
-
blockUiElement.appendChild(toastElement)
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
if (electronApi !== undefined) {
|
|
599
|
-
// Check for updates.
|
|
600
|
-
// Note: the main process will actually check for updates if requested and if OpenCOR is packaged.
|
|
601
|
-
|
|
602
|
-
electronApi.checkForUpdates(true)
|
|
603
|
-
} else {
|
|
604
|
-
// Handle the action passed to our Web app, if any.
|
|
605
|
-
// Note: to use vue.nextTick() doesn't do the trick, so we have no choice but to use setTimeout().
|
|
606
|
-
|
|
607
|
-
const action = vueusecore.useStorage('action', '')
|
|
608
|
-
|
|
609
|
-
if (window.location.search !== '') {
|
|
610
|
-
action.value = window.location.search.substring(1)
|
|
611
|
-
|
|
612
|
-
window.location.search = ''
|
|
613
|
-
} else if (action.value !== '') {
|
|
614
|
-
setTimeout(() => {
|
|
615
|
-
handleAction(action.value)
|
|
616
|
-
|
|
617
|
-
action.value = ''
|
|
618
|
-
}, SHORT_DELAY)
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}, SHORT_DELAY)
|
|
622
|
-
})
|
|
623
|
-
}
|
|
624
|
-
</script>
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<BaseDialog header=" " style="width: 39rem">
|
|
3
|
-
<div class="space-y-7">
|
|
4
|
-
<div class="text-center">
|
|
5
|
-
<div class="text-3xl font-bold">OpenCOR {{ version }}</div>
|
|
6
|
-
<div v-if="electronApi !== undefined" class="text-xl italic font-bold">{{ electronApi.operatingSystem() }}</div>
|
|
7
|
-
<div class="text-sm italic">Copyright {{ constants.COPYRIGHT }}</div>
|
|
8
|
-
</div>
|
|
9
|
-
<div class="space-y-2">
|
|
10
|
-
<div>
|
|
11
|
-
<a href="https://opencor.ws/" target="_blank" rel="noopener">OpenCOR</a> is a frontend to
|
|
12
|
-
<a href="https://opencor.ws/libopencor/" target="_blank" rel="noopener">libOpenCOR</a> {{ locApi.version() }},
|
|
13
|
-
a library that can be used to organise, edit, simulate, and analyse
|
|
14
|
-
<a href="https://cellml.org/" target="_blank" rel="noopener">CellML</a> files.
|
|
15
|
-
</div>
|
|
16
|
-
<div class="space-y-0">
|
|
17
|
-
<div>There are two versions of OpenCOR:</div>
|
|
18
|
-
|
|
19
|
-
<ol class="list-decimal list-inside ml-2">
|
|
20
|
-
<li>
|
|
21
|
-
<span class="font-bold">OpenCOR:</span> a desktop application that can be run on
|
|
22
|
-
<a href="https://en.wikipedia.org/wiki/List_of_Intel_processors">Intel</a>-based and
|
|
23
|
-
<a href="https://en.wikipedia.org/wiki/ARM_architecture_family">ARM</a>-based
|
|
24
|
-
<a href="https://en.wikipedia.org/wiki/Microsoft_Windows" target="_blank" rel="noopener">Windows</a>,
|
|
25
|
-
<a href="https://en.wikipedia.org/wiki/Linux" target="_blank" rel="noopener">Linux</a>, and
|
|
26
|
-
<a href="https://en.wikipedia.org/wiki/MacOS" target="_blank" rel="noopener">macOS</a> machines; and
|
|
27
|
-
</li>
|
|
28
|
-
<li>
|
|
29
|
-
<span class="font-bold">OpenCOR's Web app:</span> a
|
|
30
|
-
<a href="https://en.wikipedia.org/wiki/Web_application" target="_blank" rel="noopener">Web app</a> that
|
|
31
|
-
can be run on a Web browser.
|
|
32
|
-
</li>
|
|
33
|
-
</ol>
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
37
|
-
<template #footer>
|
|
38
|
-
<Button label="OK" autofocus @click="$emit('close')" />
|
|
39
|
-
</template>
|
|
40
|
-
</BaseDialog>
|
|
41
|
-
</template>
|
|
42
|
-
|
|
43
|
-
<script setup lang="ts">
|
|
44
|
-
import * as constants from '../../common/constants'
|
|
45
|
-
import { electronApi } from '../../common/electronApi'
|
|
46
|
-
import * as locApi from '../../libopencor/locApi'
|
|
47
|
-
|
|
48
|
-
defineEmits(['close'])
|
|
49
|
-
|
|
50
|
-
import { version } from '../../../package.json'
|
|
51
|
-
</script>
|