diff --git a/bun.lock b/bun.lock index d34d793c..09889ae4 100644 --- a/bun.lock +++ b/bun.lock @@ -33,7 +33,7 @@ "electron-vite": "^5.0.0", "esbuild": "^0.27.3", "node-addon-api": "^8.6.0", - "rollup-plugin-visualizer": "^7.0.0", + "rollup-plugin-visualizer": "^7.0.1", "stylelint": "^17.4.0", "stylelint-config-standard": "^40.0.0", "tailwindcss": "^4.2.1", @@ -499,7 +499,7 @@ "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], - "caniuse-lite": ["caniuse-lite@1.0.30001775", "", {}, "sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A=="], + "caniuse-lite": ["caniuse-lite@1.0.30001776", "", {}, "sha512-sg01JDPzZ9jGshqKSckOQthXnYwOEP50jeVFhaSFbZcOy05TiuuaffDOfcwtCisJ9kNQuLBFibYywv2Bgm9osw=="], "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -613,7 +613,7 @@ "electron-publish": ["electron-publish@26.8.1", "", { "dependencies": { "@types/fs-extra": "^9.0.11", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "form-data": "^4.0.5", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" } }, "sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w=="], - "electron-to-chromium": ["electron-to-chromium@1.5.302", "", {}, "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg=="], + "electron-to-chromium": ["electron-to-chromium@1.5.307", "", {}, "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg=="], "electron-updater": ["electron-updater@6.8.3", "", { "dependencies": { "builder-util-runtime": "9.5.1", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", "lodash.escaperegexp": "^4.1.2", "lodash.isequal": "^4.5.0", "semver": "~7.7.3", "tiny-typed-emitter": "^2.1.0" } }, "sha512-Z6sgw3jgbikWKXei1ENdqFOxBP0WlXg3TtKfz0rgw2vIZFJUyI4pD7ZN7jrkm7EoMK+tcm/qTnPUdqfZukBlBQ=="], @@ -695,7 +695,7 @@ "fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="], - "fs-extra": ["fs-extra@11.3.3", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg=="], + "fs-extra": ["fs-extra@11.3.4", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA=="], "fs-minipass": ["fs-minipass@3.0.3", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw=="], @@ -1075,7 +1075,7 @@ "rollup": ["rollup@4.59.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.59.0", "@rollup/rollup-android-arm64": "4.59.0", "@rollup/rollup-darwin-arm64": "4.59.0", "@rollup/rollup-darwin-x64": "4.59.0", "@rollup/rollup-freebsd-arm64": "4.59.0", "@rollup/rollup-freebsd-x64": "4.59.0", "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", "@rollup/rollup-linux-arm-musleabihf": "4.59.0", "@rollup/rollup-linux-arm64-gnu": "4.59.0", "@rollup/rollup-linux-arm64-musl": "4.59.0", "@rollup/rollup-linux-loong64-gnu": "4.59.0", "@rollup/rollup-linux-loong64-musl": "4.59.0", "@rollup/rollup-linux-ppc64-gnu": "4.59.0", "@rollup/rollup-linux-ppc64-musl": "4.59.0", "@rollup/rollup-linux-riscv64-gnu": "4.59.0", "@rollup/rollup-linux-riscv64-musl": "4.59.0", "@rollup/rollup-linux-s390x-gnu": "4.59.0", "@rollup/rollup-linux-x64-gnu": "4.59.0", "@rollup/rollup-linux-x64-musl": "4.59.0", "@rollup/rollup-openbsd-x64": "4.59.0", "@rollup/rollup-openharmony-arm64": "4.59.0", "@rollup/rollup-win32-arm64-msvc": "4.59.0", "@rollup/rollup-win32-ia32-msvc": "4.59.0", "@rollup/rollup-win32-x64-gnu": "4.59.0", "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="], - "rollup-plugin-visualizer": ["rollup-plugin-visualizer@7.0.0", "", { "dependencies": { "open": "^11.0.0", "picomatch": "^4.0.2", "source-map": "^0.7.4", "yargs": "^18.0.0" }, "peerDependencies": { "rolldown": "1.x || ^1.0.0-beta || ^1.0.0-rc", "rollup": "2.x || 3.x || 4.x" }, "optionalPeers": ["rolldown", "rollup"], "bin": { "rollup-plugin-visualizer": "dist/bin/cli.js" } }, "sha512-loo4kmhTg7GMO0hqaUv/azvLPUT2B4jXU3gNMG35gm1mWKpOzhV6rspb/Mqmsfg7oOTdkzdmOckCIwGB5Ca1CA=="], + "rollup-plugin-visualizer": ["rollup-plugin-visualizer@7.0.1", "", { "dependencies": { "open": "^11.0.0", "picomatch": "^4.0.2", "source-map": "^0.7.4", "yargs": "^18.0.0" }, "peerDependencies": { "rolldown": "1.x || ^1.0.0-beta || ^1.0.0-rc", "rollup": "2.x || 3.x || 4.x" }, "optionalPeers": ["rolldown", "rollup"], "bin": { "rollup-plugin-visualizer": "dist/bin/cli.js" } }, "sha512-UJUT4+1Ho4OcWmPYU3sYXgUqI8B8Ayfe06MX7y0qCJ1K8aGoKtR/NDd/2nZqM7ADkrzny+I99Ul7GgyoiVNAgg=="], "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], diff --git a/package.json b/package.json index 6f81ee2f..164b04c2 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "url": "git+https://github.com/opencor/webapp.git" }, "type": "module", - "version": "0.20260303.1", + "version": "0.20260304.0", "engines": { "bun": ">=1.2.0" }, @@ -82,7 +82,7 @@ "electron-vite": "^5.0.0", "esbuild": "^0.27.3", "node-addon-api": "^8.6.0", - "rollup-plugin-visualizer": "^7.0.0", + "rollup-plugin-visualizer": "^7.0.1", "stylelint": "^17.4.0", "stylelint-config-standard": "^40.0.0", "tailwindcss": "^4.2.1", diff --git a/src/renderer/bun.lock b/src/renderer/bun.lock index c1cb2312..142d48c4 100644 --- a/src/renderer/bun.lock +++ b/src/renderer/bun.lock @@ -343,7 +343,7 @@ "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], - "caniuse-lite": ["caniuse-lite@1.0.30001775", "", {}, "sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A=="], + "caniuse-lite": ["caniuse-lite@1.0.30001776", "", {}, "sha512-sg01JDPzZ9jGshqKSckOQthXnYwOEP50jeVFhaSFbZcOy05TiuuaffDOfcwtCisJ9kNQuLBFibYywv2Bgm9osw=="], "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], @@ -369,7 +369,7 @@ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "electron-to-chromium": ["electron-to-chromium@1.5.302", "", {}, "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg=="], + "electron-to-chromium": ["electron-to-chromium@1.5.307", "", {}, "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg=="], "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], diff --git a/src/renderer/index.d.ts b/src/renderer/index.d.ts index e5f9e76f..639da399 100644 --- a/src/renderer/index.d.ts +++ b/src/renderer/index.d.ts @@ -1,4 +1,4 @@ -export { IOpenCORProps, Theme } from './index.ts'; +export { IOpenCORProps, IOpenCORSimulationData, OpenCORSimulationData, OpenCORTheme } from './index.ts'; export declare const OpenCOR: import('vue').Component; export default OpenCOR; diff --git a/src/renderer/index.ts b/src/renderer/index.ts index 3ef8042e..fec2b38b 100644 --- a/src/renderer/index.ts +++ b/src/renderer/index.ts @@ -1,8 +1,15 @@ -export type Theme = 'light' | 'dark' | 'system'; +export type OpenCORTheme = 'light' | 'dark' | 'system'; export interface IOpenCORProps { omex?: string | Uint8Array; - theme?: Theme; + theme?: OpenCORTheme; +} + +export type OpenCORSimulationData = Record; + +export interface IOpenCORSimulationData { + simulationData: OpenCORSimulationData; + issues: string[]; } export { default, default as OpenCOR } from './src/components/OpenCOR.vue'; diff --git a/src/renderer/package.json b/src/renderer/package.json index 9f819716..1c902100 100644 --- a/src/renderer/package.json +++ b/src/renderer/package.json @@ -42,7 +42,7 @@ }, "./style.css": "./dist/opencor.css" }, - "version": "0.20260303.1", + "version": "0.20260304.0", "scripts": { "build": "vite build && bun scripts/generate.version.js", "build:lib": "vite build --config vite.lib.config.ts && bun scripts/copy.indexdts.js", diff --git a/src/renderer/src/AppWithSimulationData.vue b/src/renderer/src/AppWithSimulationData.vue index 13fc9e08..256bd771 100644 --- a/src/renderer/src/AppWithSimulationData.vue +++ b/src/renderer/src/AppWithSimulationData.vue @@ -11,17 +11,24 @@ const opencorRef = vue.ref | null>(null); vue.onMounted(() => { if (opencorRef.value) { - for (const modelParameter of ['VOI', 'main/t', 'main/x', 'main/y', 'main/z', 'unknown']) { - opencorRef.value - .simulationData(modelParameter) - .then((simulationData: Float64Array) => { - console.log(`Simulation data for "${modelParameter}":`); - console.log(simulationData); - }) - .catch((error) => { - console.error(error); + const params = ['VOI', 'main/t', 'main/x', 'main/y', 'main/z', 'unknown']; + + opencorRef.value.simulationData(params).then((res) => { + for (const modelParameter of params) { + console.log(`Simulation data for "${modelParameter}":`); + console.log(res.simulationData[modelParameter]); + } + + if (res.issues.length > 0) { + console.log('Issues:'); + + res.issues.forEach((issue) => { + console.log(` - ${issue}`); }); - } + } else { + console.log('No issues.'); + } + }); } }); diff --git a/src/renderer/src/common/common.ts b/src/renderer/src/common/common.ts index 42d5f2ee..091ebc43 100644 --- a/src/renderer/src/common/common.ts +++ b/src/renderer/src/common/common.ts @@ -1,3 +1,5 @@ +import type { OpenCORSimulationData } from '../../index.ts'; + import * as dependencies from './dependencies.ts'; import { electronApi } from './electronApi.ts'; @@ -212,3 +214,15 @@ export const fileName = (filePath: string): string => { export const sleep = (ms: number): Promise => { return new Promise((resolve) => setTimeout(resolve, ms)); }; + +// A method to get an object with undefined values for a list of model parameters. + +export const undefinedSimulationData = (modelParameters: string[]): OpenCORSimulationData => { + const res: OpenCORSimulationData = {}; + + for (const modelParameter of modelParameters) { + res[modelParameter] = undefined; + } + + return res; +}; diff --git a/src/renderer/src/common/vueCommon.ts b/src/renderer/src/common/vueCommon.ts index cd5532b0..9ed9e7a1 100644 --- a/src/renderer/src/common/vueCommon.ts +++ b/src/renderer/src/common/vueCommon.ts @@ -2,7 +2,7 @@ import * as vueusecore from '@vueuse/core'; import * as vue from 'vue'; -import type { Theme } from '../../index.ts'; +import type { OpenCORTheme } from '../../index.ts'; // A constant to know the UID of the active instance of OpenCOR. @@ -14,7 +14,7 @@ export const useTheme = vueusecore.createGlobalState(() => { const prefersColorScheme = window.matchMedia('(prefers-color-scheme: light)'); const isLightMode = vue.ref(prefersColorScheme.matches); const isDarkMode = vue.ref(!prefersColorScheme.matches); - const _theme = vue.ref('system'); + const _theme = vue.ref('system'); const updateLightAndDarkModes = (prefersColorScheme: MediaQueryList | MediaQueryListEvent) => { isLightMode.value = prefersColorScheme.matches; @@ -32,11 +32,11 @@ export const useTheme = vueusecore.createGlobalState(() => { } }); - const theme = (): Theme => { + const theme = (): OpenCORTheme => { return _theme.value; }; - const setTheme = (newTheme: Theme | undefined) => { + const setTheme = (newTheme: OpenCORTheme | undefined) => { _theme.value = newTheme ?? 'system'; if (_theme.value === 'light') { diff --git a/src/renderer/src/components/ContentsComponent.vue b/src/renderer/src/components/ContentsComponent.vue index f626d28f..b3b0a589 100644 --- a/src/renderer/src/components/ContentsComponent.vue +++ b/src/renderer/src/components/ContentsComponent.vue @@ -71,6 +71,7 @@ import { electronApi } from '../common/electronApi.ts'; import * as locApi from '../libopencor/locApi.ts'; import SimulationExperimentView from './views/SimulationExperimentView.vue'; +import type { IOpenCORSimulationData } from '../../index.ts'; interface IFileTab { file: locApi.File; @@ -173,31 +174,39 @@ const closeAllFiles = (): void => { } }; -const simulationData = (modelParameter: string, attempt: number = 0): Promise => { +const simulationData = (modelParameters: string[], attempt: number = 0): Promise => { if (!props.simulationOnly) { - return Promise.reject(new Error('Simulation data can only be retrieved in simulation only mode.')); + return Promise.resolve({ + simulationData: common.undefinedSimulationData(modelParameters), + issues: ['Simulation data can only be retrieved in simulation only mode.'] + }); } const simulationExperimentViews = simulationExperimentViewRef.value; if (!simulationExperimentViews.length) { - // In simulation only mode, there should always be a simulation experiment view available, but we add this check - // just in case. If there is no simulation experiment view available, we retry a few times with a delay to give - // it some time to load before giving up. + // In simulation only mode, there should always be a simulation experiment view available, but we retry a few times + // with a delay to give it some time to load before giving up. if (attempt < 3) { return new Promise((resolve) => { setTimeout(() => { - resolve(simulationData(modelParameter, attempt + 1)); + resolve(simulationData(modelParameters, attempt + 1)); }, HUGE_DELAY); }); } - return Promise.reject(new Error('No simulation experiment view available.')); + return Promise.resolve({ + simulationData: common.undefinedSimulationData(modelParameters), + issues: ['No simulation experiment view available.'] + }); } - return simulationExperimentViews[0].simulationData(modelParameter).catch((error: unknown) => { - throw new Error(common.formatError(error)); + return simulationExperimentViews[0].simulationData(modelParameters).catch((error: unknown) => { + return { + simulationData: common.undefinedSimulationData(modelParameters), + issues: [common.formatError(error)] + }; }); }; diff --git a/src/renderer/src/components/OpenCOR.vue b/src/renderer/src/components/OpenCOR.vue index 1b3a0fd9..d69ee348 100644 --- a/src/renderer/src/components/OpenCOR.vue +++ b/src/renderer/src/components/OpenCOR.vue @@ -116,7 +116,7 @@ import primeVueToastService from 'primevue/toastservice'; import { useToast } from 'primevue/usetoast'; import * as vue from 'vue'; -import type { IOpenCORProps } from '../../index.ts'; +import type { IOpenCORProps, IOpenCORSimulationData } from '../../index.ts'; import '../assets/app.css'; import '../assets/primeicons-assets.ts'; @@ -139,15 +139,21 @@ import MainMenu from './MainMenu.vue'; const props = defineProps(); -const simulationData = (modelParameter: string): Promise => { +const simulationData = (modelParameters: string[]): Promise => { const contents = contentsRef.value; if (!contents) { - return Promise.reject(new Error('No contents available.')); + return Promise.resolve({ + simulationData: common.undefinedSimulationData(modelParameters), + issues: ['No contents available.'] + }); } - return contents.simulationData(modelParameter).catch((error: unknown) => { - throw new Error(common.formatError(error)); + return contents.simulationData(modelParameters).catch((error: unknown) => { + return { + simulationData: common.undefinedSimulationData(modelParameters), + issues: [common.formatError(error)] + }; }); }; diff --git a/src/renderer/src/components/views/SimulationExperimentView.vue b/src/renderer/src/components/views/SimulationExperimentView.vue index 9b7b9cec..a9fb8b3c 100644 --- a/src/renderer/src/components/views/SimulationExperimentView.vue +++ b/src/renderer/src/components/views/SimulationExperimentView.vue @@ -247,6 +247,8 @@ import * as vueusecore from '@vueuse/core'; import Popover from 'primevue/popover'; import * as vue from 'vue'; +import type { IOpenCORSimulationData } from '../../../index.ts'; + import * as colors from '../../common/colors.ts'; import * as common from '../../common/common.ts'; import * as dependencies from '../../common/dependencies.ts'; @@ -659,30 +661,44 @@ const interactiveSettings = vue.computed(() = })); const interactiveOldSettings = vue.ref(JSON.stringify(vue.toRaw(interactiveSettings.value))); -// A helper function to get the simulation data for a given model parameter. +// A helper function to retrieve simulation data for one or more model parameters. + +const simulationData = (modelParameters: string[]): Promise => { + const res: IOpenCORSimulationData = { + simulationData: common.undefinedSimulationData(modelParameters), + issues: [] + }; -const simulationData = (modelParameter: string): Promise => { if (!interactiveInstanceTask) { - return Promise.reject(new Error('No SED-ML instance task available.')); + res.issues.push('No SED-ML instance task available.'); + + return Promise.resolve(res); } const instanceTask = interactiveInstanceTask as locSedApi.SedInstanceTask; - const info = locCommon.simulationDataInfo( - instanceTask, - modelParameter === 'VOI' ? instanceTask.voiName() : modelParameter - ); - if (isNoSimulationDataInfo(info)) { - return Promise.reject( - new Error(`No simulation data information was found for model parameter "${modelParameter}".`) + for (const modelParameter of modelParameters) { + const info = locCommon.simulationDataInfo( + instanceTask, + modelParameter === 'VOI' ? instanceTask.voiName() : modelParameter ); - } - try { - return Promise.resolve(locCommon.simulationData(instanceTask, info)); - } catch (error: unknown) { - return Promise.reject(new Error(common.formatError(error))); + if (isNoSimulationDataInfo(info)) { + res.simulationData[modelParameter] = undefined; + res.issues.push(`No simulation data information was found for model parameter "${modelParameter}".`); + + continue; + } + + try { + res.simulationData[modelParameter] = locCommon.simulationData(instanceTask, info); + } catch (error: unknown) { + res.simulationData[modelParameter] = undefined; + res.issues.push(`Error for model parameter "${modelParameter}": ${common.formatError(error)}`); + } } + + return Promise.resolve(res); }; defineExpose({