@opencor/opencor 0.20250813.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.
Files changed (63) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +61 -0
  3. package/dist/index-BxzyBYAw.js +98574 -0
  4. package/dist/index.d.ts +8 -0
  5. package/dist/opencor.css +1 -0
  6. package/dist/opencor.es.js +5 -0
  7. package/dist/opencor.umd.js +6275 -0
  8. package/dist/quill-BGLY3Ud3.js +7521 -0
  9. package/package.json +90 -0
  10. package/src/App.vue +27 -0
  11. package/src/assets/app.css +14 -0
  12. package/src/assets/base.css +28 -0
  13. package/src/assets/logo.svg +17 -0
  14. package/src/common/common.ts +86 -0
  15. package/src/common/constants.ts +12 -0
  16. package/src/common/electron.ts +23 -0
  17. package/src/common/electronApi.ts +63 -0
  18. package/src/common/locCommon.ts +170 -0
  19. package/src/common/settings.ts +95 -0
  20. package/src/common/vueCommon.ts +50 -0
  21. package/src/components/BackgroundComponent.vue +26 -0
  22. package/src/components/ContentsComponent.vue +262 -0
  23. package/src/components/DragNDropComponent.vue +29 -0
  24. package/src/components/LoadOpencorComponent.vue +33 -0
  25. package/src/components/MainMenu.vue +220 -0
  26. package/src/components/OpenCOR.vue +546 -0
  27. package/src/components/SpinningWheelComponent.vue +15 -0
  28. package/src/components/dialogs/AboutDialog.vue +51 -0
  29. package/src/components/dialogs/BaseDialog.vue +11 -0
  30. package/src/components/dialogs/OpenRemoteDialog.vue +37 -0
  31. package/src/components/dialogs/ResetAllDialog.vue +13 -0
  32. package/src/components/dialogs/SettingsDialog.vue +42 -0
  33. package/src/components/dialogs/UpdateAvailableDialog.vue +16 -0
  34. package/src/components/dialogs/UpdateDownloadProgressDialog.vue +17 -0
  35. package/src/components/dialogs/UpdateErrorDialog.vue +18 -0
  36. package/src/components/dialogs/UpdateNotAvailableDialog.vue +12 -0
  37. package/src/components/propertyEditors/GraphsPropertyEditor.vue +3 -0
  38. package/src/components/propertyEditors/ParametersPropertyEditor.vue +3 -0
  39. package/src/components/propertyEditors/PropertyEditor.vue +61 -0
  40. package/src/components/propertyEditors/SimulationPropertyEditor.vue +45 -0
  41. package/src/components/propertyEditors/SolversPropertyEditor.vue +3 -0
  42. package/src/components/views/IssuesView.vue +37 -0
  43. package/src/components/views/SimulationExperimentUiView.vue +152 -0
  44. package/src/components/views/SimulationExperimentView.vue +214 -0
  45. package/src/components/widgets/GraphPanelWidget.vue +137 -0
  46. package/src/components/widgets/InputWidget.vue +128 -0
  47. package/src/libopencor/locApi.ts +167 -0
  48. package/src/libopencor/locFileApi.ts +263 -0
  49. package/src/libopencor/locLoggerApi.ts +36 -0
  50. package/src/libopencor/locSedApi.ts +486 -0
  51. package/src/libopencor/locUiJsonApi.ts +485 -0
  52. package/src/libopencor/locVersionApi.ts +17 -0
  53. package/src/libopencor/src/common.cpp +67 -0
  54. package/src/libopencor/src/common.h +27 -0
  55. package/src/libopencor/src/file.cpp +72 -0
  56. package/src/libopencor/src/file.h +15 -0
  57. package/src/libopencor/src/main.cpp +78 -0
  58. package/src/libopencor/src/sed.cpp +348 -0
  59. package/src/libopencor/src/sed.h +53 -0
  60. package/src/libopencor/src/version.cpp +8 -0
  61. package/src/libopencor/src/version.h +5 -0
  62. package/src/main.ts +5 -0
  63. package/src/types/types.d.ts +9 -0
@@ -0,0 +1,42 @@
1
+ <template>
2
+ <BaseDialog header="Settings..." @hide="initialiseDialog">
3
+ <div class="flex gap-2 items-center">
4
+ <Checkbox inputId="checkForUpdatesAtStartup" :binary="true" v-model="checkForUpdatesAtStartup" />
5
+ <label for="checkForUpdatesAtStartup">Check for updates at startup</label>
6
+ </div>
7
+ <template #footer>
8
+ <Button autofocus label="OK" @click="onOk" />
9
+ <Button label="Cancel" severity="secondary" @click="$emit('close')" />
10
+ </template>
11
+ </BaseDialog>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import * as vue from 'vue'
16
+
17
+ import { settings } from '../../common/settings'
18
+
19
+ const emit = defineEmits(['close'])
20
+
21
+ const checkForUpdatesAtStartup = vue.ref(settings.general.checkForUpdatesAtStartup)
22
+
23
+ function initialiseDialog() {
24
+ // Note: we can come here as a result of hiding the dialog and this in case the dialog gets opened multiple times. We
25
+ // could do this when showing the dialog, but it might result in the UI flashing (e.g., a checkbox was checked
26
+ // and then it gets unchecked), hence we do it when hiding the dialog.
27
+
28
+ checkForUpdatesAtStartup.value = settings.general.checkForUpdatesAtStartup
29
+ }
30
+
31
+ settings.onInitialised(() => {
32
+ initialiseDialog()
33
+ })
34
+
35
+ function onOk() {
36
+ settings.general.checkForUpdatesAtStartup = checkForUpdatesAtStartup.value
37
+
38
+ settings.save()
39
+
40
+ emit('close')
41
+ }
42
+ </script>
@@ -0,0 +1,16 @@
1
+ <template>
2
+ <BaseDialog header="Check For Updates...">
3
+ <div class="mt-2 mb-4">Version {{ version }} is available. Do you want to download it and install it?</div>
4
+ <template #footer>
5
+ <Button autofocus label="Yes" @click="$emit('downloadAndInstall')" />
6
+ <Button label="No" severity="secondary" @click="$emit('close')" />
7
+ </template>
8
+ </BaseDialog>
9
+ </template>
10
+
11
+ <script setup lang="ts">
12
+ defineEmits(['downloadAndInstall', 'close'])
13
+ defineProps<{
14
+ version: string
15
+ }>()
16
+ </script>
@@ -0,0 +1,17 @@
1
+ <template>
2
+ <BaseDialog header="Downloading Update..." :closable="false" :closeOnEscape="false" style="width: 39rem">
3
+ <ProgressBar class="no-animation" :showValue="false" :value="percent" />
4
+ </BaseDialog>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ defineProps<{
9
+ percent: number
10
+ }>()
11
+ </script>
12
+
13
+ <style scoped>
14
+ .no-animation :deep(.p-progressbar-value) {
15
+ transition: none !important;
16
+ }
17
+ </style>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <BaseDialog :header="title">
3
+ <div class="mt-2 mb-4">
4
+ {{ issue }}
5
+ </div>
6
+ <template #footer>
7
+ <Button autofocus label="OK" @click="$emit('close')" />
8
+ </template>
9
+ </BaseDialog>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ defineEmits(['close'])
14
+ defineProps<{
15
+ title: string
16
+ issue: string
17
+ }>()
18
+ </script>
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <BaseDialog header="Check For Updates...">
3
+ <div class="mt-2 mb-4">No updates are available at this time.</div>
4
+ <template #footer>
5
+ <Button autofocus label="OK" @click="$emit('close')" />
6
+ </template>
7
+ </BaseDialog>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ defineEmits(['close'])
12
+ </script>
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <PropertyEditor name="Graphs" :hasUnits="false" :properties="[]" />
3
+ </template>
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <PropertyEditor name="Parameters" :properties="[]" />
3
+ </template>
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <Fieldset :legend="name">
3
+ <DataTable
4
+ editMode="cell"
5
+ resizableColumns
6
+ showGridlines
7
+ size="small"
8
+ :value="properties"
9
+ @cell-edit-complete="onCellEditComplete"
10
+ >
11
+ <Column field="property" header="Property" :style="columnWidth" />
12
+ <Column field="value" header="Value" :style="columnWidth">
13
+ <template #body="{ data, field }">
14
+ {{ data[field as string] }}
15
+ </template>
16
+ <template #editor="{ data, field }">
17
+ <InputNumber fluid :maxFractionDigits="15" v-model="data[field]" size="small" />
18
+ </template>
19
+ </Column>
20
+ <Column v-if="hasUnits" field="unit" header="Unit" :style="columnWidth" />
21
+ </DataTable>
22
+ </Fieldset>
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ import { type DataTableCellEditCompleteEvent } from 'primevue/datatable'
27
+
28
+ interface IProps {
29
+ name: string
30
+ hasUnits?: boolean
31
+ properties: {
32
+ property: string
33
+ value: number
34
+ unit?: string
35
+ }[]
36
+ }
37
+
38
+ const { hasUnits = true, name, properties } = defineProps<IProps>()
39
+ const columnWidth = `width: calc(100% / ${hasUnits ? '3' : '2'})`
40
+ const emit = defineEmits(['propertyUpdated'])
41
+
42
+ function onCellEditComplete(event: DataTableCellEditCompleteEvent): void {
43
+ const { data, newValue, field } = event
44
+
45
+ data[field] = newValue
46
+
47
+ emit('propertyUpdated', event.index, event.newValue)
48
+ }
49
+ </script>
50
+
51
+ <style scoped>
52
+ :deep(.p-datatable-header-cell) {
53
+ transition: none;
54
+ }
55
+
56
+ :deep(.p-inputnumber-input) {
57
+ padding: 0 2px !important;
58
+ margin: 0 !important;
59
+ line-height: 1.2px !important;
60
+ }
61
+ </style>
@@ -0,0 +1,45 @@
1
+ <template>
2
+ <PropertyEditor name="Simulation" :properties="properties" @propertyUpdated="onPropertyUpdated" />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import * as vue from 'vue'
7
+
8
+ import * as locApi from '../../libopencor/locApi'
9
+
10
+ const props = defineProps<{
11
+ file: locApi.File
12
+ }>()
13
+
14
+ const uniformTimeCourse = props.file.document().simulation(0) as locApi.SedSimulationUniformTimeCourse
15
+ const voiUnit = props.file.instance().task(0).voiUnit()
16
+
17
+ const properties = vue.ref([
18
+ {
19
+ property: 'Starting point',
20
+ value: uniformTimeCourse.outputStartTime(),
21
+ unit: voiUnit
22
+ },
23
+ {
24
+ property: 'Ending point',
25
+ value: uniformTimeCourse.outputEndTime(),
26
+ unit: voiUnit
27
+ },
28
+ {
29
+ property: 'Point interval',
30
+ value:
31
+ (uniformTimeCourse.outputEndTime() - uniformTimeCourse.outputStartTime()) / uniformTimeCourse.numberOfSteps(),
32
+ unit: voiUnit
33
+ }
34
+ ])
35
+
36
+ function onPropertyUpdated(index: number, value: number): void {
37
+ if (index === 0) {
38
+ uniformTimeCourse.setOutputStartTime(value)
39
+ } else if (index === 1) {
40
+ uniformTimeCourse.setOutputEndTime(value)
41
+ } else if (index === 2) {
42
+ uniformTimeCourse.setNumberOfSteps((properties.value[1].value - properties.value[0].value) / value)
43
+ }
44
+ }
45
+ </script>
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <PropertyEditor name="Solvers" :properties="[]" />
3
+ </template>
@@ -0,0 +1,37 @@
1
+ <template>
2
+ <Fieldset class="ml-4! mr-4! mb-4!" legend="Issues">
3
+ <ScrollPanel :class="simulationOnly ? 'issues-scroll-panel-only' : 'issues-scroll-panel'">
4
+ <div v-for="(issue, index) in issues" :key="`issue_${index}`" :class="`issue ${index > 0 ? 'mt-4!' : ''}`">
5
+ <Message v-if="issue.type === locApi.EIssueType.ERROR" severity="error" icon="pi pi-times-circle">
6
+ {{ issue.description }}
7
+ </Message>
8
+ <Message v-else severity="warn" icon="pi pi-exclamation-triangle">
9
+ {{ issue.description }}
10
+ </Message>
11
+ </div>
12
+ </ScrollPanel>
13
+ </Fieldset>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import * as locApi from '../../libopencor/locApi'
18
+
19
+ defineProps<{
20
+ issues: locApi.IIssue[]
21
+ simulationOnly?: boolean
22
+ }>()
23
+ </script>
24
+
25
+ <style scoped>
26
+ .issue {
27
+ user-select: text;
28
+ }
29
+
30
+ .issues-scroll-panel-only {
31
+ height: calc(100vh - 4.75rem);
32
+ }
33
+
34
+ .issues-scroll-panel {
35
+ height: calc(100vh - var(--main-menu-height) - var(--file-tablist-height) - 4.75rem);
36
+ }
37
+ </style>
@@ -0,0 +1,152 @@
1
+ <template>
2
+ <div :class="`flex flex-row h-full ${simulationOnly ? 'simulation-experiment-only' : 'simulation-experiment'}`">
3
+ <IssuesView v-if="issues.length !== 0" class="grow" :issues="issues" :simulationOnly="simulationOnly" />
4
+ <div v-else class="flex flex-row grow">
5
+ <div class="ml-4 mr-4 mb-4">
6
+ <Fieldset legend="Input parameters">
7
+ <InputWidget
8
+ v-for="(input, index) in (uiJson as any).input"
9
+ v-model="inputValues[index]"
10
+ v-show="showInput[index]"
11
+ :key="`input_${index}`"
12
+ :name="input.name"
13
+ :maximumValue="input.maximumValue"
14
+ :minimumValue="input.minimumValue"
15
+ :possibleValues="input.possibleValues"
16
+ :stepValue="input.stepValue"
17
+ :class="index !== 0 ? 'mt-6' : ''"
18
+ @change="updateUiAndSimulation"
19
+ />
20
+ </Fieldset>
21
+ </div>
22
+ <div :id="plotsDivId" class="grow">
23
+ <GraphPanelWidget
24
+ v-for="(_plot, index) in (uiJson as any).output.plots"
25
+ :key="`plot_${index}`"
26
+ class="graph-panel-widget"
27
+ :plots="plots.length !== 0 ? plots[index] : []"
28
+ />
29
+ </div>
30
+ </div>
31
+ </div>
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ import * as mathjs from 'mathjs'
36
+ import * as vue from 'vue'
37
+
38
+ import * as locCommon from '../../common/locCommon'
39
+ import * as locApi from '../../libopencor/locApi'
40
+
41
+ import { type IGraphPanelPlot } from '../widgets/GraphPanelWidget.vue'
42
+
43
+ const props = defineProps<{
44
+ file: locApi.File
45
+ simulationOnly?: boolean
46
+ uiJson: locApi.IUiJson
47
+ }>()
48
+
49
+ const math = mathjs.create(mathjs.all, {})
50
+ const model = props.file.document().model(0)
51
+ const instance = props.file.instance()
52
+ const instanceTask = instance.task(0)
53
+ const plotsDivId = `plotsDiv_${props.file.path()}`
54
+ const plots = vue.ref<IGraphPanelPlot[][]>([])
55
+ const issues = vue.ref(locApi.uiJsonIssues(props.uiJson))
56
+ const inputValues = vue.ref<number[]>([])
57
+ const showInput = vue.ref<boolean[]>([])
58
+ const idToInfo: Record<string, locCommon.ISimulationDataInfo> = {}
59
+
60
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
+ function evaluateValue(value: string): any {
62
+ let index = -1
63
+ const parser = math.parser()
64
+
65
+ props.uiJson.input.forEach((input: locApi.IUiJsonInput) => {
66
+ if (input.id !== undefined && input.id !== '') {
67
+ parser.set(input.id, inputValues.value[++index])
68
+ }
69
+ })
70
+
71
+ return parser.evaluate(value)
72
+ }
73
+
74
+ props.uiJson.input.forEach((input: locApi.IUiJsonInput) => {
75
+ inputValues.value.push(input.defaultValue)
76
+ })
77
+
78
+ props.uiJson.input.forEach((input: locApi.IUiJsonInput) => {
79
+ showInput.value.push(evaluateValue(input.visible ?? 'true'))
80
+ })
81
+
82
+ props.uiJson.output.data.forEach((data: locApi.IUiJsonOutputData) => {
83
+ idToInfo[data.id] = locCommon.simulationDataInfo(instanceTask, data.name)
84
+ })
85
+
86
+ vue.onMounted(() => {
87
+ updateUiAndSimulation()
88
+
89
+ // Determine the number of graph panel widgets (needed to set their height).
90
+
91
+ const plotsDiv = document.getElementById(plotsDivId)
92
+
93
+ plotsDiv?.style.setProperty('--graph-panel-widget-count', String(plotsDiv.children.length))
94
+ })
95
+
96
+ function updateUiAndSimulation() {
97
+ // Make sure that there are no issues.
98
+
99
+ if (issues.value.length > 0) {
100
+ return
101
+ }
102
+
103
+ // Show/hide the input widgets.
104
+
105
+ props.uiJson.input.forEach((input: locApi.IUiJsonInput, index: number) => {
106
+ showInput.value[index] = evaluateValue(input.visible ?? 'true')
107
+ })
108
+
109
+ // Update the SED-ML document.
110
+
111
+ model.removeAllChanges()
112
+
113
+ props.uiJson.parameters.forEach((parameter: locApi.IUiJsonParameter) => {
114
+ const componentVariableNames = parameter.name.split('/')
115
+
116
+ model.addChange(componentVariableNames[0], componentVariableNames[1], String(evaluateValue(parameter.value)))
117
+ })
118
+
119
+ // Run the instance and update the plots.
120
+
121
+ instance.run()
122
+
123
+ const parser = math.parser()
124
+
125
+ props.uiJson.output.data.forEach((data: locApi.IUiJsonOutputData) => {
126
+ parser.set(data.id, locCommon.simulationData(instanceTask, idToInfo[data.id]))
127
+ })
128
+
129
+ plots.value = props.uiJson.output.plots.map((plot: locApi.IUiJsonOutputPlot) => {
130
+ return [
131
+ {
132
+ x: { data: parser.evaluate(plot.xValue) },
133
+ y: { data: parser.evaluate(plot.yValue) }
134
+ }
135
+ ]
136
+ })
137
+ }
138
+ </script>
139
+
140
+ <style scoped>
141
+ .graph-panel-widget {
142
+ height: calc(100% / var(--graph-panel-widget-count));
143
+ }
144
+
145
+ .simulation-experiment-only {
146
+ height: 100vh;
147
+ }
148
+
149
+ .simulation-experiment {
150
+ height: calc(100vh - var(--main-menu-height) - var(--file-tablist-height));
151
+ }
152
+ </style>
@@ -0,0 +1,214 @@
1
+ <template>
2
+ <div :class="`h-full ${simulationOnly ? 'simulation-experiment-only' : 'simulation-experiment'}`">
3
+ <Toolbar :id="toolbarId" class="p-1!">
4
+ <template #start>
5
+ <Button class="p-1!" icon="pi pi-play-circle" severity="secondary" text @click="onRun()" />
6
+ <Button class="p-1!" disabled icon="pi pi-stop-circle" severity="secondary" text />
7
+ </template>
8
+ </Toolbar>
9
+ <Splitter class="border-none! h-full m-0" layout="vertical">
10
+ <SplitterPanel :size="simulationOnly ? 100 : 89">
11
+ <Splitter>
12
+ <SplitterPanel class="ml-4 mr-4 mb-4 min-w-fit" :size="25">
13
+ <SimulationPropertyEditor :file="file" />
14
+ <!--
15
+ <SolversPropertyEditor />
16
+ <GraphsPropertyEditor />
17
+ <ParametersPropertyEditor />
18
+ -->
19
+ <Fieldset legend="X-axis">
20
+ <Select
21
+ v-model="xParameter"
22
+ filter
23
+ filterMode="lenient"
24
+ :options="parameters"
25
+ size="small"
26
+ class="w-full"
27
+ @change="updatePlot()"
28
+ />
29
+ </Fieldset>
30
+ <Fieldset legend="Y-axis">
31
+ <Select
32
+ v-model="yParameter"
33
+ filter
34
+ filterMode="lenient"
35
+ :options="parameters"
36
+ size="small"
37
+ class="w-full"
38
+ @change="updatePlot()"
39
+ />
40
+ </Fieldset>
41
+ </SplitterPanel>
42
+ <SplitterPanel :size="75">
43
+ <GraphPanelWidget :plots="plots" />
44
+ </SplitterPanel>
45
+ </Splitter>
46
+ </SplitterPanel>
47
+ <SplitterPanel v-if="!simulationOnly" :size="11">
48
+ <Editor :id="editorId" class="border-none h-full" :readonly="true" v-model="consoleContents" />
49
+ </SplitterPanel>
50
+ </Splitter>
51
+ </div>
52
+ </template>
53
+
54
+ <script setup lang="ts">
55
+ import * as vueusecore from '@vueuse/core'
56
+
57
+ import * as vue from 'vue'
58
+
59
+ import * as common from '../../common/common'
60
+ import * as locCommon from '../../common/locCommon'
61
+ import * as vueCommon from '../../common/vueCommon'
62
+ import * as locApi from '../../libopencor/locApi'
63
+
64
+ import { type IGraphPanelPlot } from '../widgets/GraphPanelWidget.vue'
65
+
66
+ const props = defineProps<{
67
+ file: locApi.File
68
+ isActiveFile: boolean
69
+ simulationOnly?: boolean
70
+ }>()
71
+
72
+ const toolbarId = `simulationExperimentToolbar_${props.file.path()}`
73
+ const editorId = `simulationExperimentEditor_${props.file.path()}`
74
+ const instance = props.file.instance()
75
+ const instanceTask = instance.task(0)
76
+
77
+ const parameters = vue.ref<string[]>([])
78
+ const xParameter = vue.ref(instanceTask.voiName())
79
+ const yParameter = vue.ref(instanceTask.stateName(0))
80
+ const plots = vue.ref<IGraphPanelPlot[]>([])
81
+ const consoleContents = vue.ref<string>(`<b>${props.file.path()}</b>`)
82
+
83
+ function addParameter(param: string): void {
84
+ parameters.value.push(param)
85
+ }
86
+
87
+ addParameter(instanceTask.voiName())
88
+
89
+ for (let i = 0; i < instanceTask.stateCount(); i++) {
90
+ addParameter(instanceTask.stateName(i))
91
+ }
92
+
93
+ for (let i = 0; i < instanceTask.rateCount(); i++) {
94
+ addParameter(instanceTask.rateName(i))
95
+ }
96
+
97
+ for (let i = 0; i < instanceTask.constantCount(); i++) {
98
+ addParameter(instanceTask.constantName(i))
99
+ }
100
+
101
+ for (let i = 0; i < instanceTask.computedConstantCount(); i++) {
102
+ addParameter(instanceTask.computedConstantName(i))
103
+ }
104
+
105
+ for (let i = 0; i < instanceTask.algebraicCount(); i++) {
106
+ addParameter(instanceTask.algebraicName(i))
107
+ }
108
+
109
+ function onRun(): void {
110
+ // Run the instance, output the simulation time to the console, and update the plot.
111
+
112
+ const simulationTime = instance.run()
113
+
114
+ consoleContents.value += `<br/>&nbsp;&nbsp;<b>Simulation time:</b> ${common.formatTime(simulationTime)}`
115
+
116
+ void vue.nextTick().then(() => {
117
+ const consoleElement = document.getElementById(editorId)?.getElementsByClassName('ql-editor')[0]
118
+
119
+ if (consoleElement !== undefined) {
120
+ consoleElement.scrollTop = consoleElement.scrollHeight
121
+ }
122
+ })
123
+
124
+ updatePlot()
125
+ }
126
+
127
+ const xInfo = vue.computed(() => locCommon.simulationDataInfo(instanceTask, xParameter.value))
128
+ const yInfo = vue.computed(() => locCommon.simulationDataInfo(instanceTask, yParameter.value))
129
+
130
+ function updatePlot() {
131
+ plots.value = [
132
+ {
133
+ x: {
134
+ data: locCommon.simulationData(instanceTask, xInfo.value)
135
+ },
136
+ y: {
137
+ data: locCommon.simulationData(instanceTask, yInfo.value)
138
+ }
139
+ }
140
+ ]
141
+ }
142
+
143
+ // "Initialise" our plot.
144
+
145
+ vue.onMounted(() => {
146
+ updatePlot()
147
+ })
148
+
149
+ // Track the height of our file tablist toolbar.
150
+
151
+ vueCommon.trackElementHeight(toolbarId)
152
+
153
+ // Keyboard shortcuts.
154
+
155
+ if (!common.isMobile()) {
156
+ vueusecore.onKeyStroke((event: KeyboardEvent) => {
157
+ if (props.isActiveFile && !event.ctrlKey && !event.shiftKey && !event.metaKey && event.code === 'F9') {
158
+ event.preventDefault()
159
+
160
+ onRun()
161
+ }
162
+ })
163
+ }
164
+ </script>
165
+
166
+ <style scoped>
167
+ :deep(.p-button) {
168
+ transition: none;
169
+ }
170
+
171
+ :deep(.p-button-icon) {
172
+ font-size: 1.5rem;
173
+ }
174
+
175
+ :deep(.p-button-icon-only) {
176
+ width: 2rem;
177
+ }
178
+
179
+ :deep(.p-button-label) {
180
+ height: 0;
181
+ }
182
+
183
+ :deep(.p-editor-content) {
184
+ border: none !important;
185
+ }
186
+
187
+ :deep(.p-editor-toolbar) {
188
+ display: none;
189
+ }
190
+
191
+ .p-toolbar {
192
+ border: none;
193
+ border-radius: 0;
194
+ border-bottom: 1px solid var(--p-content-border-color);
195
+ }
196
+
197
+ :deep(.ql-editor) {
198
+ padding: 0.25rem 0.5rem;
199
+ }
200
+
201
+ :deep(.ql-editor > *) {
202
+ cursor: default;
203
+ }
204
+
205
+ .simulation-experiment-only {
206
+ height: calc(100vh - var(--simulation-experiment-toolbar-height));
207
+ }
208
+
209
+ .simulation-experiment {
210
+ height: calc(
211
+ 100vh - var(--main-menu-height) - var(--file-tablist-height) - var(--simulation-experiment-toolbar-height)
212
+ );
213
+ }
214
+ </style>