.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/ACRNContext.jsx b/misc/config_tools/configurator/src/ACRNContext.jsx
new file mode 100644
index 000000000..c77737cc1
--- /dev/null
+++ b/misc/config_tools/configurator/src/ACRNContext.jsx
@@ -0,0 +1,37 @@
+import React, {createContext} from 'react'
+import {Helper, TauriLocalFSBackend} from "./lib/helper";
+import {Configurator} from "./lib/acrn";
+
+// 1. Use React createContext API to create ACRN Context
+export const ACRNContext = createContext({
+ helper: () => {
+ },
+ configurator: () => {
+ }
+})
+
+// 2. Create Context Provider
+export class ACRNProvider extends React.Component {
+ constructor(props) {
+ super(props);
+ let fsBackend = new TauriLocalFSBackend()
+ let helper = new Helper(fsBackend, fsBackend)
+ let configurator = new Configurator(helper)
+ this.state = {
+ helper: helper,
+ configurator: configurator
+ }
+ }
+
+ render() {
+ console.log(this.state)
+ return (
+
+ {this.props.children}
+
+ )
+ }
+}
+
+// 3. export Consumer
+export const ACRNConsumer = ACRNContext.Consumer
diff --git a/misc/config_tools/configurator/src/App.css b/misc/config_tools/configurator/src/App.css
new file mode 100644
index 000000000..126662e34
--- /dev/null
+++ b/misc/config_tools/configurator/src/App.css
@@ -0,0 +1,97 @@
+a {
+ /* Browse for folder… */
+ font-family: Roboto, serif;
+ font-style: normal;
+ font-weight: bold;
+ font-size: 16px;
+ line-height: 19px;
+
+ color: #2A958D;
+ text-decoration: auto;
+}
+
+@media (min-width: 576px) {
+ .border-end-sm {
+ border-right: 1px solid #dee2e6 !important;
+ }
+}
+
+.btn-lg {
+ min-height: 50px;
+ border-radius: 30px;
+ font-family: Roboto, serif;
+ font-style: normal;
+ font-weight: bold;
+ line-height: 23px;
+ text-align: center;
+ text-transform: capitalize;
+
+ color: #FFFFFF;
+}
+
+.btn-primary {
+ background-color: #007B81;
+ border-color: #007B81;
+}
+
+.btn-primary:hover {
+ background-color: #004B50;
+ border-color: #004B50;
+}
+
+select {
+ height: 49px;
+ background-image: url("assets/images/dropdown_arrow.svg") !important;
+ background-repeat: no-repeat;
+ background-position: right 0.75rem center;
+ background-size: 16px 12px;
+ border: 1px solid #ced4da;
+ border-radius: 0.25rem;
+ transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ appearance: none;
+}
+
+select:focus {
+ border: 1px solid #69BFAD;
+ box-sizing: border-box;
+ box-shadow: 0 0 6px rgba(105, 191, 173, 0.37);
+ border-radius: 5px;
+}
+
+
+.banner-text {
+ max-width: 594px;
+
+ font-family: Roboto, serif;
+ font-style: normal;
+ font-weight: normal;
+ font-size: 28px;
+ line-height: 37px;
+ letter-spacing: -0.175px;
+
+ color: #FFFFFF;
+}
+
+.bg-gray {
+ background: #F5F5F5;
+}
+
+.text-right {
+ text-align: right;
+}
+
+table {
+ width: 100%;
+}
+
+.descInfoBtn {
+ color: gray;
+}
+
+.descInfoBtn:hover {
+ color: lightgray;
+}
+
+.rst-paragraph:last-child{
+ margin-bottom: 0;
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/App.jsx b/misc/config_tools/configurator/src/App.jsx
new file mode 100644
index 000000000..f6adc1ff3
--- /dev/null
+++ b/misc/config_tools/configurator/src/App.jsx
@@ -0,0 +1,25 @@
+import {ACRNProvider} from "./ACRNContext";
+import {HashRouter, Routes, Route} from "react-router-dom";
+import './App.css'
+import Welcome from "./pages/Welcome/Welcome";
+import Error from "./pages/Error/Error";
+import Config from "./pages/Config/Config";
+
+import "bootstrap/dist/js/bootstrap"
+
+function App() {
+ return (
+
+
+
+ }/>
+ }/>
+ }/>
+
+
+
+ )
+}
+
+
+export default App
diff --git a/misc/config_tools/configurator/src/assets/fonts/Roboto-Regular.woff b/misc/config_tools/configurator/src/assets/fonts/Roboto-Regular.woff
new file mode 100644
index 000000000..b867bd559
Binary files /dev/null and b/misc/config_tools/configurator/src/assets/fonts/Roboto-Regular.woff differ
diff --git a/misc/config_tools/configurator/src/assets/fonts/Roboto-Regular.woff2 b/misc/config_tools/configurator/src/assets/fonts/Roboto-Regular.woff2
new file mode 100644
index 000000000..c63fb57ca
Binary files /dev/null and b/misc/config_tools/configurator/src/assets/fonts/Roboto-Regular.woff2 differ
diff --git a/misc/config_tools/configurator/src/assets/fonts/Roboto.css b/misc/config_tools/configurator/src/assets/fonts/Roboto.css
new file mode 100644
index 000000000..528a25e6b
--- /dev/null
+++ b/misc/config_tools/configurator/src/assets/fonts/Roboto.css
@@ -0,0 +1,9 @@
+@font-face {
+ font-family: 'Roboto';
+ src: url('Roboto-Regular.woff2') format('woff2'),
+ url('Roboto-Regular.woff') format('woff');
+ font-weight: normal;
+ font-style: normal;
+ font-display: swap;
+}
+
diff --git a/misc/config_tools/configurator/src/assets/images/back_arrow_icon.svg b/misc/config_tools/configurator/src/assets/images/back_arrow_icon.svg
new file mode 100644
index 000000000..9b8f0f5ea
--- /dev/null
+++ b/misc/config_tools/configurator/src/assets/images/back_arrow_icon.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/misc/config_tools/configurator/src/assets/images/dropdown_arrow.svg b/misc/config_tools/configurator/src/assets/images/dropdown_arrow.svg
new file mode 100644
index 000000000..def5bc0ad
--- /dev/null
+++ b/misc/config_tools/configurator/src/assets/images/dropdown_arrow.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/misc/config_tools/configurator/src/assets/schema/dynamicScenario.py b/misc/config_tools/configurator/src/assets/schema/dynamicScenario.py
new file mode 100644
index 000000000..091e38e88
--- /dev/null
+++ b/misc/config_tools/configurator/src/assets/schema/dynamicScenario.py
@@ -0,0 +1,78 @@
+"""
+
+public var board_xml
+public function get_enum(source, selector)
+
+enum = {
+ 'type': 'dynamicEnum',
+ 'function': 'get_enum',
+ 'source': 'board_xml',
+ 'selector': element['xs:annotation']['@acrn:options'],
+ 'sorted': element['xs:annotation'].get('@acrn:options-sorted-by', None)
+}
+
+params defined before this document string in js side like
+```js
+let params = JSON.stringify({board_xml: boardXMLText, scenario_json: JSON.stringify(scenario_json)})
+params = Base64.encode(params);
+return runPyCode(`
+params="${params}"
+${dynamicScenario}
+`)
+```
+"""
+
+import json
+from base64 import b64decode
+
+import elementpath
+import lxml.etree as etree
+
+# local test var set
+if 'params' not in globals():
+ # params = b64encode(open(r"board.xml",'rb').read())
+ raise NotImplementedError
+
+# Main flow
+# noinspection PyUnresolvedReferences,PyUnboundLocalVariable
+params: str = b64decode(params).decode('utf-8')
+params: dict = json.loads(params)
+board_xml = etree.XML(params["board_xml"])
+scenario_json = params['scenario_json']
+
+
+def get_enum(source, options):
+ elements = [str(x) for x in elementpath.select(source, options) if x]
+ elements = list(set(elements))
+ if not elements:
+ elements = ['']
+ return elements
+
+
+def dynamic_enum(**kwargs):
+ # value from env
+ function, source = [globals()[kwargs[key]] for key in ['function', 'source']]
+ # value from given
+ selector, sorted_func = [kwargs[key] for key in ['selector', 'sorted']]
+
+ # get enum data
+ enum = function(source, selector)
+ if sorted_func:
+ enum = sorted(enum, key=eval(sorted_func))
+ return enum
+
+
+def dynamic_enum_apply(obj):
+ # get json schema enum obj
+ if 'enum' in obj and isinstance(obj['enum'], dict):
+ enum_setting = obj['enum']
+ # check enum obj type
+ if enum_setting['type'] == 'dynamicEnum':
+ del enum_setting['type']
+ # replace json schema obj enum field data
+ obj['enum'] = dynamic_enum(**enum_setting)
+ return obj
+
+
+data = json.loads(scenario_json, object_hook=dynamic_enum_apply)
+json.dumps(data)
diff --git a/misc/config_tools/configurator/src/assets/schema/scenario.json b/misc/config_tools/configurator/src/assets/schema/scenario.json
new file mode 100644
index 000000000..28526e926
--- /dev/null
+++ b/misc/config_tools/configurator/src/assets/schema/scenario.json
@@ -0,0 +1,2392 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema",
+ "type": "object",
+ "required": [
+ "acrn-config"
+ ],
+ "properties": {
+ "acrn-config": {
+ "$ref": "#/definitions/ACRNConfigType"
+ }
+ },
+ "additionalProperties": true,
+ "definitions": {
+ "Boolean": {
+ "type": "string",
+ "enum": [
+ "y",
+ "n"
+ ]
+ },
+ "EnablementType": {
+ "type": "string",
+ "enum": [
+ "Enable",
+ "Disable"
+ ]
+ },
+ "HexFormat": {
+ "type": "string",
+ "pattern": "^0[Xx][0-9A-Fa-f]+|0$"
+ },
+ "None": {
+ "type": "string",
+ "pattern": "^[*]{0}$"
+ },
+ "BuildType": {
+ "type": "string",
+ "enum": [
+ "release",
+ "debug"
+ ]
+ },
+ "KernLoadAddr": {
+ "anyOf": [
+ {
+ "title": "None",
+ "type": "string",
+ "pattern": "^[*]{0}$"
+ },
+ {
+ "title": "HexFormat",
+ "type": "string",
+ "pattern": "^0[Xx][0-9A-Fa-f]+|0$"
+ }
+ ]
+ },
+ "KernEntryAddr": {
+ "anyOf": [
+ {
+ "title": "None",
+ "type": "string",
+ "pattern": "^[*]{0}$"
+ },
+ {
+ "title": "HexFormat",
+ "type": "string",
+ "pattern": "^0[Xx][0-9A-Fa-f]+|0$"
+ }
+ ]
+ },
+ "MaxMsixTableNumType": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 2048
+ },
+ "MaxMsixTableSizeType": {
+ "anyOf": [
+ {
+ "title": "None",
+ "type": "string",
+ "pattern": "^[*]{0}$"
+ },
+ {
+ "title": "MaxMsixTableNumType",
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 2048
+ }
+ ]
+ },
+ "MemorySizeType": {
+ "anyOf": [
+ {
+ "title": "HexFormat",
+ "type": "string",
+ "pattern": "^0[Xx][0-9A-Fa-f]+|0$"
+ }
+ ]
+ },
+ "LogLevelType": {
+ "type": "string",
+ "enum": [
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5"
+ ],
+ "enumNames": [
+ "0: None",
+ "1: Critical",
+ "2: Error",
+ "3: Warning",
+ "4: Information",
+ "5: Debug"
+ ]
+ },
+ "SchedulerType": {
+ "type": "string",
+ "enum": [
+ "SCHED_NOOP",
+ "SCHED_IORR",
+ "SCHED_BVT",
+ "SCHED_PRIO"
+ ]
+ },
+ "PriorityType": {
+ "type": "string",
+ "enum": [
+ "PRIO_LOW",
+ "PRIO_HIGH"
+ ]
+ },
+ "SerialConsoleType": {
+ "type": "string",
+ "pattern": "^.*ttyS[\\d]+$"
+ },
+ "SerialConsoleOptions": {
+ "anyOf": [
+ {
+ "title": "None",
+ "type": "string",
+ "pattern": "^[*]{0}$"
+ },
+ {
+ "title": "SerialConsoleType",
+ "type": "string",
+ "pattern": "^.*ttyS[\\d]+$"
+ }
+ ]
+ },
+ "VMNameType": {
+ "type": "string",
+ "pattern": "^([a-zA-Z0-9_\\-]){1,15}$"
+ },
+ "VBDFType": {
+ "type": "string",
+ "pattern": "^[0-9A-Fa-f]{1,2}:[0-1][0-9A-Fa-f].[0-7]$"
+ },
+ "ProviderType": {
+ "type": "string",
+ "enum": [
+ "Hypervisor",
+ "Device Model"
+ ]
+ },
+ "IVSHMEMSize": {
+ "type": "integer",
+ "enum": [
+ 2,
+ 4,
+ 8,
+ 16,
+ 32,
+ 64,
+ 128,
+ 256,
+ 512
+ ]
+ },
+ "LoadOrderType": {
+ "type": "string",
+ "enum": [
+ "SERVICE_VM",
+ "PRE_LAUNCHED_VM",
+ "POST_LAUNCHED_VM"
+ ]
+ },
+ "VMType": {
+ "type": "string",
+ "enum": [
+ "RTVM",
+ "STANDARD_VM",
+ "TEE_VM",
+ "REE_VM"
+ ]
+ },
+ "VMKernelType": {
+ "type": "string",
+ "enum": [
+ "KERNEL_BZIMAGE",
+ "KERNEL_RAWIMAGE",
+ "KERNEL_ELF"
+ ]
+ },
+ "ConsoleVuartConfiguration": {
+ "type": "string",
+ "enum": [
+ "None",
+ "COM Port 1",
+ "COM Port 2",
+ "COM Port 3",
+ "COM Port 4",
+ "PCI"
+ ]
+ },
+ "VuartType": {
+ "type": "string",
+ "enum": [
+ "legacy",
+ "pci"
+ ]
+ },
+ "OSType": {
+ "type": "string",
+ "enum": [
+ "Non-Windows OS",
+ "Windows OS"
+ ]
+ },
+ "BasicVMType": {
+ "type": "string",
+ "enum": [
+ "RTVM",
+ "STANDARD_VM"
+ ]
+ },
+ "IVSHMEMVM": {
+ "type": "object",
+ "required": [
+ "VM_NAME",
+ "VBDF"
+ ],
+ "properties": {
+ "VM_NAME": {
+ "$ref": "#/definitions/VMNameType",
+ "title": "Shared VMS",
+ "description": "\n
\n Name of the VM which use this IVSHMEM.\n
\n
\n"
+ },
+ "VBDF": {
+ "$ref": "#/definitions/VBDFType",
+ "title": "VBDF",
+ "description": "\n
\n Bus, Device and function of the virtual\ndevice in VM . Set in hex.\n
\n
\n"
+ }
+ }
+ },
+ "IVSHMEMVMS": {
+ "type": "object",
+ "required": [
+ "IVSHMEM_VM"
+ ],
+ "properties": {
+ "IVSHMEM_VM": {
+ "items": {
+ "$ref": "#/definitions/IVSHMEMVM"
+ },
+ "type": "array",
+ "title": "IVSHMEM_VM",
+ "description": "\n
\n Set each VM which use this IVSHMEM.\n
\n
\n"
+ }
+ }
+ },
+ "IVSHMEMRegionType": {
+ "type": "object",
+ "required": [
+ "NAME",
+ "PROVIDED_BY",
+ "IVSHMEM_SIZE",
+ "IVSHMEM_VMS"
+ ],
+ "properties": {
+ "NAME": {
+ "type": "string",
+ "pattern": "^\\w+$",
+ "title": "Name",
+ "description": "\n
\n Name of the shared memory region.\n
\n
\n"
+ },
+ "PROVIDED_BY": {
+ "$ref": "#/definitions/ProviderType",
+ "default": "Hypervisor",
+ "title": "Emulated by",
+ "description": "\n
\n Whether the shared memory region is emulated by the hypervisor or Device Model.\n
\n
\n"
+ },
+ "IVSHMEM_SIZE": {
+ "$ref": "#/definitions/IVSHMEMSize",
+ "default": 2,
+ "title": "Size (MB)",
+ "description": "\n
\n Select the size of the shared memory region\nin megabytes. The value should be a power of 2\nand no more than 512.\n
\n
\n"
+ },
+ "IVSHMEM_VMS": {
+ "$ref": "#/definitions/IVSHMEMVMS",
+ "title": "Shared VMs",
+ "description": "\n
\n Select all VMs that use the shared memory region.\n
\n
\n"
+ }
+ }
+ },
+ "IVSHMEMInfo": {
+ "type": "object",
+ "properties": {
+ "IVSHMEM_REGION": {
+ "items": {
+ "$ref": "#/definitions/IVSHMEMRegionType"
+ },
+ "type": "array",
+ "title": "IVSHMEM_REGION",
+ "description": "\n
\n Set a inter-VM shared memory.\n
\n
\n"
+ }
+ }
+ },
+ "RDTType": {
+ "type": "object",
+ "required": [
+ "RDT_ENABLED",
+ "CDP_ENABLED",
+ "VCAT_ENABLED"
+ ],
+ "properties": {
+ "RDT_ENABLED": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Intel Resource Director Technology",
+ "description": "\n
\n Enable the Intel Resource Director Technology (RDT)\nallocation feature. If the board hardware does not support\nRDT, setting this option to\n \n y\n \n is ignored.\n
\n
\n"
+ },
+ "CDP_ENABLED": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Code and Data Prioritization",
+ "description": "\n
\n Enable Code and Data Prioritization (CDP). CDP provides control over code and data placement in cache to improve an application's real-time performance.\n
\n
\n"
+ },
+ "VCAT_ENABLED": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "VCAT_ENABLED",
+ "description": "\n
\n Enable virtualization of the Cache Allocation Technology (CAT) feature in RDT. CAT enables you to allocate cache to VMs, providing isolation to avoid performance interference from other VMs.\n
\n
\n"
+ },
+ "CLOS_MASK": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "title": "CLOS_MASK",
+ "description": "\n
\n Specify the cache capacity bitmask for the CLOS; only continuous '1' bits\nare allowed. The value will be ignored when hardware does not support RDT.\nThis option takes effect only if\n \n hv.FEATURES.RDT.RDT_ENABLED\n \n is set to\n \n y\n \n .\nAs\n \n vm.clos.vcpu_clos\n \n specifies the index of the CLOS to be associated with the given vCPU,\n \n hv.FEATURES.RDT.CLOS_MASK\n \n of that CLOS would impact the performance of the given vCPU.\n
\n
\n"
+ },
+ "MBA_DELAY": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "title": "MBA_DELAY",
+ "description": "\n
\n Memory Bandwidth Allocation delay value.\n
\n
\n"
+ }
+ }
+ },
+ "SSRAMInfo": {
+ "type": "object",
+ "required": [
+ "SSRAM_ENABLED"
+ ],
+ "properties": {
+ "SSRAM_ENABLED": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Software SRAM (for real-time apps)",
+ "description": "\n
\n Enable Software SRAM. This feature reserves memory buffers as always-cached memory to improve an application's real-time performance.\n
\n
\n"
+ }
+ }
+ },
+ "CPUAffinityConfiguration": {
+ "type": "object",
+ "required": [
+ "pcpu_id"
+ ],
+ "properties": {
+ "pcpu_id": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "default": 2
+ },
+ "title": "pcpu_id",
+ "description": "\n
\n A pCPU that this VM's vCPU is allowed to pin\nto.\n
\n
\n"
+ }
+ }
+ },
+ "CLOSConfiguration": {
+ "type": "object",
+ "required": [
+ "vcpu_clos"
+ ],
+ "properties": {
+ "vcpu_clos": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "default": 0
+ },
+ "title": "vcpu_clos",
+ "description": "\n
\n By default (\n \n virtual_cat_support\n \n is not specified):\nvcpu_clos is per-CPU and it configures each CPU in VMs to a desired CLOS ID in the\n \n VM\n \n section of the\nscenario file. Follow\n \n RDT Detection and Resource Capabilities\n \n to identify the maximum supported CLOS ID that can be used.\n
\n
\n If\n \n virtual_cat_support\n \n is specified:\nvcpu_clos is not per-CPU anymore, just a list of physical CLOSIDs (minimum 2) that are assigned to VMs\nfor vCAT use. Each vcpu_clos will be mapped to a virtual CLOSID, the first vcpu_clos is mapped to virtual\nCLOSID 0 and the second is mapped to virtual CLOSID 1, etc.\n
\n
\n"
+ }
+ }
+ },
+ "EPCSection": {
+ "type": "object",
+ "required": [
+ "base",
+ "size"
+ ],
+ "properties": {
+ "base": {
+ "$ref": "#/definitions/HexFormat",
+ "default": 0,
+ "title": "Intel Software Guard Extensions (SGX) EPC section base",
+ "description": "\n
\n Specify the enclave page cache (EPC) section base for Intel Software Guard Extensions (SGX). Must be page aligned.\n
\n
\n"
+ },
+ "size": {
+ "$ref": "#/definitions/HexFormat",
+ "default": 0,
+ "title": "Intel Software Guard Extensions (SGX) EPC section size (bytes)",
+ "description": "\n
\n Specify the enclave page cache (EPC) section size in bytes for Intel Software Guard Extensions (SGX). Must be page aligned.\n
\n
\n"
+ }
+ }
+ },
+ "MemoryInfo": {
+ "type": "object",
+ "required": [
+ "whole",
+ "start_hpa"
+ ],
+ "properties": {
+ "whole": {
+ "type": "integer",
+ "default": 256,
+ "title": "VM physical memory allocation (MB)",
+ "description": "\n
\n Specify the physical memory allocated to this VM in Megabytes.\n
\n
\n"
+ },
+ "start_hpa": {
+ "$ref": "#/definitions/HexFormat",
+ "default": "0x100000000",
+ "title": "start_hpa",
+ "description": "\n
\n The starting physical address in host for the VM.\n
\n
\n"
+ },
+ "size": {
+ "$ref": "#/definitions/MemorySizeType",
+ "default": "0x20000000",
+ "title": "size",
+ "description": "\n
\n The memory size in bytes for the VM. Default value is\n \n 0x200000000\n \n .\n
\n
\n"
+ },
+ "start_hpa2": {
+ "$ref": "#/definitions/HexFormat",
+ "default": "0x0",
+ "title": "start_hpa2",
+ "description": "\n
\n Start of second HPA for non-contiguous allocations in host for the VM.\n
\n
\n"
+ },
+ "size_hpa2": {
+ "$ref": "#/definitions/MemorySizeType",
+ "default": "0x0",
+ "title": "size_hpa2",
+ "description": "\n
\n Memory size of second HPA for non-contiguous allocations in Bytes for the VM.\n
\n
\n"
+ }
+ }
+ },
+ "OSConfigurations": {
+ "type": "object",
+ "required": [
+ "kern_type",
+ "kern_mod"
+ ],
+ "properties": {
+ "kern_type": {
+ "$ref": "#/definitions/VMKernelType",
+ "title": "Kernel image type",
+ "description": "\n
\n Select the kernel image type so that the hypervisor can load it correctly.\n
\n
\n"
+ },
+ "kern_mod": {
+ "type": "string",
+ "title": "Kernel module tag",
+ "description": "\n
\n Specify the tag for the kernel image that is used as a multiboot module. The tag's spelling must exactly match the module tag in the GRUB multiboot cmdline.\n
\n
\n"
+ },
+ "ramdisk_mod": {
+ "type": "string",
+ "title": "RAMdisk module tag",
+ "description": "\n
\n Specify the tag for the RAMdisk image that is used as a multiboot module. The tag's spelling must exactly match the module tag in the GRUB multiboot cmdline.\n
\n
\n"
+ },
+ "bootargs": {
+ "type": "string",
+ "title": "Kernel command-line parameters",
+ "description": "\n
\n Specify the command-line parameters that will be used to boot the kernel for this VM. See\n \n Linux documentation\n \n for a list of parameters.\n
\n
\n"
+ },
+ "kern_load_addr": {
+ "$ref": "#/definitions/KernLoadAddr",
+ "title": "kern_load_addr",
+ "description": "\n
\n The loading address in host memory for the VM kernel.\n
\n
\n"
+ },
+ "kern_entry_addr": {
+ "$ref": "#/definitions/KernEntryAddr",
+ "title": "kern_entry_addr",
+ "description": "\n
\n The entry address in host memory for the VM kernel.\n
\n
\n"
+ }
+ }
+ },
+ "VuartEndpointType": {
+ "type": "object",
+ "required": [
+ "vm_name",
+ "io_port"
+ ],
+ "properties": {
+ "vm_name": {
+ "type": "string",
+ "title": "Virtual UART port",
+ "description": "\n
\n Virtual UART port\n
\n
\n"
+ },
+ "io_port": {
+ "$ref": "#/definitions/HexFormat",
+ "default": "0x3F",
+ "title": "Virtual I/O address",
+ "description": "\n
\n Virtual I/O address\n
\n
\n"
+ },
+ "vbdf": {
+ "items": {
+ "$ref": "#/definitions/VBDFType"
+ },
+ "type": "array",
+ "maxItems": 1
+ }
+ }
+ },
+ "VuartConnectionType": {
+ "type": "object",
+ "required": [
+ "name",
+ "type",
+ "endpoint"
+ ],
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "type": {
+ "$ref": "#/definitions/VuartType",
+ "default": "legacy"
+ },
+ "endpoint": {
+ "minItems": 2,
+ "items": {
+ "$ref": "#/definitions/VuartEndpointType"
+ },
+ "type": "array",
+ "maxItems": 2
+ }
+ }
+ },
+ "VuartConnectionsType": {
+ "type": "object",
+ "properties": {
+ "vuart_connection": {
+ "items": {
+ "$ref": "#/definitions/VuartConnectionType"
+ },
+ "type": "array"
+ }
+ }
+ },
+ "MMIOResourcesConfiguration": {
+ "type": "object",
+ "properties": {
+ "TPM2": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Trusted platform module (TPM2) passthrough",
+ "description": "\n
\n Pass through the trusted platform module (TPM2) device to this VM.\n
\n
\n"
+ },
+ "p2sb": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Primary-to-Sideband bridge passthrough",
+ "description": "\n
\n Pass through the Primary-to-Sideband (P2SB) bridge register access BAR to this VM.\n
\n
\n"
+ }
+ }
+ },
+ "PCIDevsConfiguration": {
+ "type": "object",
+ "properties": {
+ "pci_dev": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "title": "PCI device assignment",
+ "description": "\n
\n Select the PCI devices you want to assign to this virtual machine.\n
\n
\n",
+ "enum": {
+ "type": "dynamicEnum",
+ "function": "get_enum",
+ "source": "board_xml",
+ "selector": "//device[class]/@description",
+ "sorted": "lambda s: (s.split(' ', maxsplit=1)[-1].split(':')[0], s.split(' ')[0])"
+ }
+ }
+ }
+ },
+ "DebugOptionsType": {
+ "type": "object",
+ "required": [
+ "BUILD_TYPE",
+ "SERIAL_CONSOLE",
+ "MEM_LOGLEVEL",
+ "NPK_LOGLEVEL",
+ "CONSOLE_LOGLEVEL"
+ ],
+ "properties": {
+ "BUILD_TYPE": {
+ "$ref": "#/definitions/BuildType",
+ "default": "debug",
+ "title": "Build type",
+ "description": "\n
\n Select the build type. Debug mode enables debug shell, prints, and logs. Release mode optimizes the ACRN binary for deployment and turns off all debug infrastructure. These settings can only be changed at build time.\n
\n
\n"
+ },
+ "SERIAL_CONSOLE": {
+ "$ref": "#/definitions/SerialConsoleOptions",
+ "title": "Serial console port",
+ "description": "\n
\n Select the host serial device used for hypervisor debugging.\n
\n
\n",
+ "enum": {
+ "type": "dynamicEnum",
+ "function": "get_enum",
+ "source": "board_xml",
+ "selector": "//ttys/serial[type != '0']/dev_path/text()",
+ "sorted": null
+ }
+ },
+ "MEM_LOGLEVEL": {
+ "$ref": "#/definitions/LogLevelType",
+ "default": 0,
+ "title": "ACRN log level",
+ "description": "\n
\n Select the default log level for log messages stored in memory. Value can be changed at runtime. Log messages with the selected value or lower are displayed.\n
\n
\n"
+ },
+ "NPK_LOGLEVEL": {
+ "$ref": "#/definitions/LogLevelType",
+ "default": 0,
+ "title": "Intel Trace Hub log level",
+ "description": "\n
\n Select the default log level for the hypervisor via Intel Trace Hub log. Use the Intel Trace Hub's memory to record log messages. Value can be changed at runtime. Log messages with the selected value or lower are displayed.\n
\n
\n"
+ },
+ "CONSOLE_LOGLEVEL": {
+ "$ref": "#/definitions/LogLevelType",
+ "default": 0,
+ "title": "Serial console log level",
+ "description": "\n
\n Select the default log level for log messages written to the serial console. Log messages with the selected value or lower are displayed.\n
\n
\n"
+ }
+ }
+ },
+ "FeatureOptionsType": {
+ "type": "object",
+ "required": [
+ "RELOC",
+ "SCHEDULER",
+ "MULTIBOOT2",
+ "ENFORCE_TURNOFF_AC",
+ "ENFORCE_TURNOFF_GP",
+ "SECURITY_VM_FIXUP",
+ "KEEP_IRQ_DISABLED",
+ "RDT",
+ "HYPERV_ENABLED",
+ "IOMMU_ENFORCE_SNP",
+ "ACPI_PARSE_ENABLED",
+ "L1D_VMENTRY_ENABLED",
+ "MCE_ON_PSC_DISABLED",
+ "IVSHMEM"
+ ],
+ "properties": {
+ "RELOC": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Hypervisor relocation in memory",
+ "description": "\n
\n Enable hypervisor relocation in memory. The bootloader may need to change the location of the hypervisor because of other firmware.\n
\n
\n"
+ },
+ "SCHEDULER": {
+ "$ref": "#/definitions/SchedulerType",
+ "default": "SCHED_BVT",
+ "title": "Virtual CPU scheduler",
+ "description": "\n
\n Select the scheduling algorithm used to determine which User VM runs on a shared virtual CPU.\n
\n
\n"
+ },
+ "MULTIBOOT2": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Multiboot2",
+ "description": "\n
\n Enable multiboot2 protocol support and multiboot1 downward compatibility. Disable this feature if multiboot1 meets your requirements and to reduce lines of code.\n
\n
\n"
+ },
+ "ENFORCE_TURNOFF_AC": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Split lock detection",
+ "description": "\n
\n Enable detection of split locks, which can negatively affect an application's real-time performance. If a lock is detected, an alignment check exception #AC occurs.\n
\n
\n"
+ },
+ "ENFORCE_TURNOFF_GP": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Uncacheable-memory lock detection",
+ "description": "\n
\n Enable detection of uncacheable-memory locks, which can negatively affect an application's real-time performance. If a lock is detected, a general-protection exception #GP occurs.\n
\n
\n"
+ },
+ "SECURITY_VM_FIXUP": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "SECURITY_VM_FIXUP",
+ "description": "\n
\n Enable to do fixup for TPM2 and SMBIOS for Security VM. If no Security VM, setting this option to\n \n n\n \n
\n
\n"
+ },
+ "KEEP_IRQ_DISABLED": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "KEEP_IRQ_DISABLED",
+ "description": "\n
\n If\n \n y\n \n , permanently disables all interrupts in HV root mode.\n
\n
\n"
+ },
+ "RDT": {
+ "$ref": "#/definitions/RDTType",
+ "title": "Intel Resource Director Technology (RDT)",
+ "description": "\n
\n Intel Resource Director Technology (RDT) provides cache and memory bandwidth allocation features. The features can be used to improve an application's real-time performance.\n
\n
\n"
+ },
+ "HYPERV_ENABLED": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Hyper-V virtualization technology",
+ "description": "\n
\n Enable Microsoft Hyper-V Hypervisor Top-Level Functional Specification (TFLS) for Windows User VMs.\n
\n
\n"
+ },
+ "IOMMU_ENFORCE_SNP": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "IOMMU_ENFORCE_SNP",
+ "description": "\n
\n Specify if the IOMMU enforces snoop behavior of DMA operations.\n
\n
\n"
+ },
+ "ACPI_PARSE_ENABLED": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Parse ACPI tables at runtime",
+ "description": "\n
\n Enable ACPI runtime parsing to get DMAR (DMA remapping) configuration data from the APCI tables. Otherwise, use existing, static information from the associated board configuration file.\n
\n
\n"
+ },
+ "L1D_VMENTRY_ENABLED": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Mitigate L1 terminal fault",
+ "description": "\n
\n Enable L1 cache flush before VM entry to prevent L1 terminal fault. L1 terminal fault is a hardware vulnerability that allows unauthorized disclosure of information residing in the L1 data cache.\n
\n
\n"
+ },
+ "MCE_ON_PSC_DISABLED": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Machine Check Error (MCE) workaround",
+ "description": "\n
\n Enable the software workaround for Machine Check Error on Page Size Change (hardware bug in some processor families).\n
\n
\n"
+ },
+ "IVSHMEM": {
+ "$ref": "#/definitions/IVSHMEMInfo",
+ "title": "Inter-VM shared memory",
+ "description": ""
+ },
+ "SSRAM": {
+ "$ref": "#/definitions/SSRAMInfo",
+ "title": "Software SRAM (for real-time apps)",
+ "description": ""
+ }
+ }
+ },
+ "MemoryOptionsType": {
+ "type": "object",
+ "required": [
+ "STACK_SIZE",
+ "HV_RAM_START"
+ ],
+ "properties": {
+ "STACK_SIZE": {
+ "$ref": "#/definitions/HexFormat",
+ "default": "0x2000",
+ "title": "CPU memory stack size (bytes per CPU)",
+ "description": "\n
\n Specify the size of the memory stack in bytes for each physical CPU. For example, if you specify 8 kilobytes, each CPU will get its own 8-kilobyte stack.\n
\n
\n"
+ },
+ "HV_RAM_START": {
+ "$ref": "#/definitions/HexFormat",
+ "default": "0x00400000",
+ "title": "HV_RAM_START",
+ "description": "\n
\n The 2MB-aligned starting physical address of the RAM region used by the hypervisor.\n
\n
\n"
+ }
+ }
+ },
+ "CapacitiesOptionsType": {
+ "type": "object",
+ "required": [
+ "MAX_VM_NUM",
+ "MAX_IOAPIC_NUM",
+ "MAX_PCI_DEV_NUM",
+ "MAX_IOAPIC_LINES",
+ "MAX_PT_IRQ_ENTRIES",
+ "MAX_MSIX_TABLE_NUM",
+ "MAX_EMULATED_MMIO"
+ ],
+ "properties": {
+ "MAX_VM_NUM": {
+ "type": "integer",
+ "default": 16,
+ "title": "MAX_VM_NUM",
+ "description": "\n
\n Maximum number of User VMs allowed.\n
\n
\n"
+ },
+ "MAX_IOAPIC_NUM": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 10,
+ "default": 1,
+ "title": "MAX_IOAPIC_NUM",
+ "description": "\n
\n Maximum number of IOAPICs.\n
\n
\n"
+ },
+ "MAX_PCI_DEV_NUM": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 1024,
+ "default": 96,
+ "title": "Maximum number of PCI devices",
+ "description": "\n
\n Specify the maximum number of PCI devices. This impacts the amount of memory used to maintain information about these PCI devices. The default value is calculated from the board configuration file. If you have PCI devices that were not detected by the Board Inspector, you may need to change this maximum value.\n
\n
\n"
+ },
+ "MAX_IOAPIC_LINES": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 120,
+ "default": 120,
+ "title": "MAX_IOAPIC_LINES",
+ "description": "\n
\n Maximum number of interrupt lines per IOAPIC.\n
\n
\n"
+ },
+ "MAX_PT_IRQ_ENTRIES": {
+ "type": "integer",
+ "default": 256,
+ "title": "Maximum number of IRQ entries for passthrough devices",
+ "description": "\n
\n Specify the maximum number of interrupt request (IRQ) entries from all passthrough devices.\n
\n
\n"
+ },
+ "MAX_MSIX_TABLE_NUM": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 2048,
+ "default": 64,
+ "title": "Maximum number of MSI-X tables per device",
+ "description": "\n
\n Specify the maximum number of Message Signaled Interrupt MSI-X tables per device. The default value is calculated from the board configuration file.\n
\n
\n"
+ },
+ "MAX_EMULATED_MMIO": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 128,
+ "default": 16,
+ "title": "Maximum number of emulated MMIO regions",
+ "description": "\n
\n Specify the maximum number of emulated MMIO regions for device virtualization. The default value is calculated from the board configuration file.\n
\n
\n"
+ }
+ }
+ },
+ "MiscCfgOptionsType": {
+ "type": "object",
+ "required": [
+ "GPU_SBDF"
+ ],
+ "properties": {
+ "GPU_SBDF": {
+ "$ref": "#/definitions/HexFormat",
+ "default": "0x00000010",
+ "title": "GPU_SBDF",
+ "description": "\n
\n Segment, Bus, Device, and function of the GPU.\n
\n
\n"
+ }
+ }
+ },
+ "HVConfigType": {
+ "type": "object",
+ "required": [
+ "DEBUG_OPTIONS",
+ "FEATURES",
+ "MEMORY",
+ "CAPACITIES",
+ "MISC_CFG",
+ "vuart_connections"
+ ],
+ "properties": {
+ "DEBUG_OPTIONS": {
+ "$ref": "#/definitions/DebugOptionsType",
+ "title": "Debug options",
+ "description": "\n
\n Configure the debug facilities.\n
\n
\n"
+ },
+ "FEATURES": {
+ "$ref": "#/definitions/FeatureOptionsType",
+ "title": "Hypervisor features",
+ "description": "\n
\n Enable hypervisor features.\n
\n
\n"
+ },
+ "MEMORY": {
+ "$ref": "#/definitions/MemoryOptionsType",
+ "title": "Memory options",
+ "description": "\n
\n Configure memory used by the hypervisor.\n
\n
\n"
+ },
+ "CAPACITIES": {
+ "$ref": "#/definitions/CapacitiesOptionsType",
+ "title": "Hypervisor capacities",
+ "description": "\n
\n Configure the capacities of the hypervisor.\n
\n
\n"
+ },
+ "MISC_CFG": {
+ "$ref": "#/definitions/MiscCfgOptionsType",
+ "title": "MISC_CFG",
+ "description": "\n
\n Miscellaneous options for workarounds.\n
\n
\n"
+ },
+ "vuart_connections": {
+ "$ref": "#/definitions/VuartConnectionsType",
+ "title": "Virtual UART connection",
+ "description": "\n"
+ }
+ }
+ },
+ "VMConfigType": {
+ "type": "object",
+ "required": [
+ "load_order",
+ "vm_type",
+ "name",
+ "clos",
+ "memory",
+ "priority",
+ "companion_vmid",
+ "console_vuart",
+ "os_type",
+ "vbootloader",
+ "vuart0",
+ "virtio_devices"
+ ],
+ "properties": {
+ "load_order": {
+ "$ref": "#/definitions/LoadOrderType",
+ "title": "Load order",
+ "description": "\n
\n Specify the load_order.\n
\n
\n"
+ },
+ "vm_type": {
+ "$ref": "#/definitions/VMType",
+ "title": "VM type",
+ "description": "\n
\n Select the VM type. A standard VM (\n \n STANDARD_VM\n \n ) is for general-purpose applications, such as human-machine interface (HMI). A real-time VM (\n \n RTVM\n \n ) offers special features for time-sensitive applications.\n
\n
\n"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 15,
+ "pattern": "^\\S+$",
+ "title": "VM name",
+ "description": "\n
\n Specify the name used to identify this VM. The VM name will be shown in the hypervisor console vm_list command.\n
\n
\n"
+ },
+ "lapic_passthrough": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "LAPIC passthrough",
+ "description": "\n
\n Enable LAPIC passthrough for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "io_completion_polling": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "I/O completion polling",
+ "description": "\n
\n Enable polling mode for I/O completion for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "nested_virtualization_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Nested virtualization",
+ "description": "\n
\n Enable nested virtualization for KVM.\n
\n
\n"
+ },
+ "virtual_cat_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Virtual Cache Allocation Technology (vCAT)",
+ "description": "\n
\n Enable virtualization of the Cache Allocation Technology (CAT) feature in RDT. CAT enables you to allocate cache to VMs, providing isolation to avoid performance interference from other VMs.\n
\n
\n"
+ },
+ "secure_world_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "secure_world_support",
+ "description": "\n
\n Specify secure world support for trustry OS.\n
\n
\n"
+ },
+ "hide_mtrr_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "hide_mtrr_support",
+ "description": "\n
\n Specify MTRR capability to hide for VM.\n
\n
\n"
+ },
+ "security_vm": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "security_vm",
+ "description": "\n
\n Specify TPM2 FIXUP for VM.\n
\n
\n"
+ },
+ "cpu_affinity": {
+ "$ref": "#/definitions/CPUAffinityConfiguration",
+ "title": "Physical CPU affinity",
+ "description": "\n
\n Select a subset of physical CPUs that this VM can use. More than one can be selected.\n
\n
\n"
+ },
+ "clos": {
+ "$ref": "#/definitions/CLOSConfiguration",
+ "title": "clos",
+ "description": "\n
\n Class of Service for Cache Allocation Technology.\nRefer SDM 17.19.2 for details, and use with caution.\n
\n
\n"
+ },
+ "epc_section": {
+ "$ref": "#/definitions/EPCSection",
+ "title": "epc_section",
+ "description": "\n
\n Specify the Intel Software Guard Extensions (SGX) enclave page cache (EPC) section settings.\n
\n
\n"
+ },
+ "memory": {
+ "$ref": "#/definitions/MemoryInfo",
+ "title": "memory",
+ "description": "\n
\n Specify memory information for Service and User VMs.\n
\n
\n"
+ },
+ "priority": {
+ "$ref": "#/definitions/PriorityType",
+ "default": "PRIO_LOW",
+ "title": "priority",
+ "description": "\n
\n Specify the VM vCPU priority for scheduling.\n
\n
\n"
+ },
+ "companion_vmid": {
+ "type": "integer",
+ "default": 65535,
+ "title": "companion_vmid",
+ "description": "\n
\n Specify the companion VM id of this VM.\n
\n
\n"
+ },
+ "os_config": {
+ "$ref": "#/definitions/OSConfigurations",
+ "title": "os_config",
+ "description": "\n
\n General information for host kernel, boot\nargument and memory.\n
\n
\n"
+ },
+ "console_vuart": {
+ "$ref": "#/definitions/ConsoleVuartConfiguration",
+ "default": "None",
+ "title": "Console virtual UART type",
+ "description": "\n
\n Select the console virtual UART (vUART) type. Add the console settings to the kernel command line by typing them in the \"Linux kernel command-line parameters\" text box (for example, console=ttyS0 for COM port 1).\n
\n
\n"
+ },
+ "mmio_resources": {
+ "$ref": "#/definitions/MMIOResourcesConfiguration",
+ "title": "mmio_resources",
+ "description": "\n
\n MMIO resources to passthrough.\n
\n
\n"
+ },
+ "pt_intx": {
+ "type": "string",
+ "title": "pt_intx",
+ "description": "\n
\n Specify the pre-launched VM owned IOAPIC pins and the corresponding mapping between physical GSI and virtual GSI.\n
\n
\n"
+ },
+ "pci_devs": {
+ "$ref": "#/definitions/PCIDevsConfiguration",
+ "title": "PCI device assignment",
+ "description": ""
+ },
+ "PTM": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Precision Time Measurement",
+ "description": "\n
\n Enable virtualization of PCIe Precision Time Measurement (PTM) mechanism for devices with PTM capability and for real-time application. The hypervisor provides PCIe root port emulation instead of host bridge emulation for the VM. PTM coordinates timing between the device and root port with the device's local timebases without relying on software.\n
\n
\n"
+ },
+ "os_type": {
+ "$ref": "#/definitions/OSType",
+ "default": "Non-Windows OS",
+ "title": "OS type",
+ "description": "\n
\n Select the OS type for this VM. This is required to run Windows in a User VM. See\n \n Device Model Parameters\n \n for how to include this in the Device Model arguments.\n
\n
\n"
+ },
+ "vbootloader": {
+ "$ref": "#/definitions/EnablementType",
+ "default": "Enable",
+ "title": "OVMF (Open Virtual Machine Firmware)",
+ "description": "\n
\n Use virtual bootloader OVMF (Open Virtual Machine Firmware) to boot this VM.\n
\n
\n"
+ },
+ "vuart0": {
+ "$ref": "#/definitions/EnablementType",
+ "default": "Enable",
+ "title": "Emulate COM1 as stdio I/O in Device Model",
+ "description": "\n
\n Enable the ACRN Device Model to emulate COM1 as a User VM stdio I/O. Hypervisor global emulation will take priority over this VM setting.\n
\n
\n"
+ },
+ "usb_xhci": {
+ "type": "string",
+ "pattern": "^([\\d]+-[\\d]+){0,1}(:[\\d]+-[\\d]+)*$",
+ "title": "Virtual USB host controller interface",
+ "description": "\n
\n Select the USB physical bus and port number that will be emulated by the ACRN Device Model for this VM. USB 3.0, 2.0, and 1.0 are supported.\n
\n
\n"
+ },
+ "virtio_devices": {
+ "type": "object",
+ "properties": {
+ "console": {
+ "type": "string",
+ "title": "console",
+ "description": "\n
\n \n The virtio console device setting.Input format:\n \n \n \n \n \n [@]stdio|tty|pty|sock:portname[=portpath][,[@]stdio|tty|pty:portname[=portpath]]\n \n \n
\n \n \n
\n"
+ },
+ "network": {
+ "type": "string",
+ "title": "network",
+ "description": "\n
\n \n The virtio network device setting.\n \n \n \n Input format:\n \n \n device_name[,vhost][,mac=XX:XX:XX:XX:XX:XX]\n \n \n .\nThe\n \n device_name\n \n is the name of the TAP (or MacVTap) device.\nIt must include the keyword\n \n tap\n \n .\n \n vhost\n \n specifies the\nvhost backend; otherwise, the VBSU backend is used. The\n \n mac\n \n address is optional.\n
\n \n \n
\n"
+ },
+ "input": {
+ "type": "string",
+ "title": "input",
+ "description": "\n
\n The virtio input device setting.\n
\n
\n"
+ },
+ "block": {
+ "type": "string",
+ "title": "block",
+ "description": "\n
\n \n The virtio block device setting.\n \n \n \n Format: [blk partition:][img path] e.g.: /dev/sda3:./a/b.img\n
\n \n \n
\n"
+ }
+ },
+ "title": "Virt-IO devices",
+ "description": "\n
\n Enable virt-IO devices in post-launched VMs.\n
\n
\n"
+ }
+ }
+ },
+ "ACRNConfigType": {
+ "type": "object",
+ "required": [
+ "hv"
+ ],
+ "properties": {
+ "hv": {
+ "$ref": "#/definitions/HVConfigType",
+ "title": "hv",
+ "description": "\n
\n The hypervisor configuration defines a working scenario and target\nboard by configuring the hypervisor image features and capabilities such as\nsetting up the log and the serial port.\n
\n
\n"
+ }
+ }
+ },
+ "PreLaunchedMemoryInfo": {
+ "type": "object",
+ "required": [
+ "start_hpa"
+ ],
+ "properties": {
+ "start_hpa": {
+ "$ref": "#/definitions/HexFormat",
+ "default": "0x100000000",
+ "title": "start_hpa",
+ "description": "\n
\n The starting physical address in host for the VM.\n
\n
\n"
+ },
+ "size": {
+ "$ref": "#/definitions/MemorySizeType",
+ "default": "0x20000000",
+ "title": "size",
+ "description": "\n
\n The memory size in bytes for the VM. Default value is\n \n 0x200000000\n \n .\n
\n
\n"
+ },
+ "start_hpa2": {
+ "$ref": "#/definitions/HexFormat",
+ "default": "0x0",
+ "title": "start_hpa2",
+ "description": "\n
\n Start of second HPA for non-contiguous allocations in host for the VM.\n
\n
\n"
+ },
+ "size_hpa2": {
+ "$ref": "#/definitions/MemorySizeType",
+ "default": "0x0",
+ "title": "size_hpa2",
+ "description": "\n
\n Memory size of second HPA for non-contiguous allocations in Bytes for the VM.\n
\n
\n"
+ }
+ }
+ },
+ "PreLaunchedVMConfigType": {
+ "type": "object",
+ "required": [
+ "load_order",
+ "vm_type",
+ "name",
+ "clos",
+ "memory",
+ "priority",
+ "companion_vmid",
+ "console_vuart"
+ ],
+ "properties": {
+ "load_order": {
+ "$ref": "#/definitions/LoadOrderType",
+ "title": "Load order",
+ "description": "\n
\n Specify the load_order.\n
\n
\n"
+ },
+ "vm_type": {
+ "$ref": "#/definitions/VMType",
+ "title": "VM type",
+ "description": "\n
\n Select the VM type. A standard VM (\n \n STANDARD_VM\n \n ) is for general-purpose applications, such as human-machine interface (HMI). A real-time VM (\n \n RTVM\n \n ) offers special features for time-sensitive applications.\n
\n
\n"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 15,
+ "pattern": "^\\S+$",
+ "title": "VM name",
+ "description": "\n
\n Specify the name used to identify this VM. The VM name will be shown in the hypervisor console vm_list command.\n
\n
\n"
+ },
+ "lapic_passthrough": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "LAPIC passthrough",
+ "description": "\n
\n Enable LAPIC passthrough for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "io_completion_polling": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "I/O completion polling",
+ "description": "\n
\n Enable polling mode for I/O completion for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "nested_virtualization_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Nested virtualization",
+ "description": "\n
\n Enable nested virtualization for KVM.\n
\n
\n"
+ },
+ "virtual_cat_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Virtual Cache Allocation Technology (vCAT)",
+ "description": "\n
\n Enable virtualization of the Cache Allocation Technology (CAT) feature in RDT. CAT enables you to allocate cache to VMs, providing isolation to avoid performance interference from other VMs.\n
\n
\n"
+ },
+ "secure_world_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "secure_world_support",
+ "description": "\n
\n Specify secure world support for trustry OS.\n
\n
\n"
+ },
+ "hide_mtrr_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "hide_mtrr_support",
+ "description": "\n
\n Specify MTRR capability to hide for VM.\n
\n
\n"
+ },
+ "security_vm": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "security_vm",
+ "description": "\n
\n Specify TPM2 FIXUP for VM.\n
\n
\n"
+ },
+ "cpu_affinity": {
+ "$ref": "#/definitions/CPUAffinityConfiguration",
+ "title": "Physical CPU affinity",
+ "description": "\n
\n Select a subset of physical CPUs that this VM can use. More than one can be selected.\n
\n
\n"
+ },
+ "clos": {
+ "$ref": "#/definitions/CLOSConfiguration",
+ "title": "clos",
+ "description": "\n
\n Class of Service for Cache Allocation Technology.\nRefer SDM 17.19.2 for details, and use with caution.\n
\n
\n"
+ },
+ "epc_section": {
+ "$ref": "#/definitions/EPCSection",
+ "title": "epc_section",
+ "description": "\n
\n Specify the Intel Software Guard Extensions (SGX) enclave page cache (EPC) section settings.\n
\n
\n"
+ },
+ "memory": {
+ "$ref": "#/definitions/PreLaunchedMemoryInfo",
+ "title": "memory",
+ "description": "\n
\n Specify memory information for Service and User VMs.\n
\n
\n"
+ },
+ "priority": {
+ "$ref": "#/definitions/PriorityType",
+ "default": "PRIO_LOW",
+ "title": "priority",
+ "description": "\n
\n Specify the VM vCPU priority for scheduling.\n
\n
\n"
+ },
+ "companion_vmid": {
+ "type": "integer",
+ "default": 65535,
+ "title": "companion_vmid",
+ "description": "\n
\n Specify the companion VM id of this VM.\n
\n
\n"
+ },
+ "os_config": {
+ "$ref": "#/definitions/OSConfigurations",
+ "title": "os_config",
+ "description": "\n
\n General information for host kernel, boot\nargument and memory.\n
\n
\n"
+ },
+ "console_vuart": {
+ "$ref": "#/definitions/ConsoleVuartConfiguration",
+ "default": "None",
+ "title": "Console virtual UART type",
+ "description": "\n
\n Select the console virtual UART (vUART) type. Add the console settings to the kernel command line by typing them in the \"Linux kernel command-line parameters\" text box (for example, console=ttyS0 for COM port 1).\n
\n
\n"
+ },
+ "mmio_resources": {
+ "$ref": "#/definitions/MMIOResourcesConfiguration",
+ "title": "mmio_resources",
+ "description": "\n
\n MMIO resources to passthrough.\n
\n
\n"
+ },
+ "pt_intx": {
+ "type": "string",
+ "title": "pt_intx",
+ "description": "\n
\n Specify the pre-launched VM owned IOAPIC pins and the corresponding mapping between physical GSI and virtual GSI.\n
\n
\n"
+ },
+ "pci_devs": {
+ "$ref": "#/definitions/PCIDevsConfiguration",
+ "title": "PCI device assignment",
+ "description": ""
+ },
+ "PTM": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Precision Time Measurement",
+ "description": "\n
\n Enable virtualization of PCIe Precision Time Measurement (PTM) mechanism for devices with PTM capability and for real-time application. The hypervisor provides PCIe root port emulation instead of host bridge emulation for the VM. PTM coordinates timing between the device and root port with the device's local timebases without relying on software.\n
\n
\n"
+ }
+ }
+ },
+ "ServiceMemoryInfo": {
+ "type": "object",
+ "required": [
+ "start_hpa"
+ ],
+ "properties": {
+ "start_hpa": {
+ "$ref": "#/definitions/HexFormat",
+ "default": "0x100000000",
+ "title": "start_hpa",
+ "description": "\n
\n The starting physical address in host for the VM.\n
\n
\n"
+ },
+ "size": {
+ "$ref": "#/definitions/MemorySizeType",
+ "default": "0x20000000",
+ "title": "size",
+ "description": "\n
\n The memory size in bytes for the VM. Default value is\n \n 0x200000000\n \n .\n
\n
\n"
+ },
+ "start_hpa2": {
+ "$ref": "#/definitions/HexFormat",
+ "default": "0x0",
+ "title": "start_hpa2",
+ "description": "\n
\n Start of second HPA for non-contiguous allocations in host for the VM.\n
\n
\n"
+ },
+ "size_hpa2": {
+ "$ref": "#/definitions/MemorySizeType",
+ "default": "0x0",
+ "title": "size_hpa2",
+ "description": "\n
\n Memory size of second HPA for non-contiguous allocations in Bytes for the VM.\n
\n
\n"
+ }
+ }
+ },
+ "ServiceVMConfigType": {
+ "type": "object",
+ "required": [
+ "load_order",
+ "vm_type",
+ "name",
+ "clos",
+ "memory",
+ "priority",
+ "companion_vmid",
+ "console_vuart"
+ ],
+ "properties": {
+ "load_order": {
+ "$ref": "#/definitions/LoadOrderType",
+ "title": "Load order",
+ "description": "\n
\n Specify the load_order.\n
\n
\n"
+ },
+ "vm_type": {
+ "$ref": "#/definitions/VMType",
+ "title": "VM type",
+ "description": "\n
\n Select the VM type. A standard VM (\n \n STANDARD_VM\n \n ) is for general-purpose applications, such as human-machine interface (HMI). A real-time VM (\n \n RTVM\n \n ) offers special features for time-sensitive applications.\n
\n
\n"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 15,
+ "pattern": "^\\S+$",
+ "title": "VM name",
+ "description": "\n
\n Specify the name used to identify this VM. The VM name will be shown in the hypervisor console vm_list command.\n
\n
\n"
+ },
+ "lapic_passthrough": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "LAPIC passthrough",
+ "description": "\n
\n Enable LAPIC passthrough for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "io_completion_polling": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "I/O completion polling",
+ "description": "\n
\n Enable polling mode for I/O completion for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "nested_virtualization_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Nested virtualization",
+ "description": "\n
\n Enable nested virtualization for KVM.\n
\n
\n"
+ },
+ "virtual_cat_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Virtual Cache Allocation Technology (vCAT)",
+ "description": "\n
\n Enable virtualization of the Cache Allocation Technology (CAT) feature in RDT. CAT enables you to allocate cache to VMs, providing isolation to avoid performance interference from other VMs.\n
\n
\n"
+ },
+ "secure_world_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "secure_world_support",
+ "description": "\n
\n Specify secure world support for trustry OS.\n
\n
\n"
+ },
+ "hide_mtrr_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "hide_mtrr_support",
+ "description": "\n
\n Specify MTRR capability to hide for VM.\n
\n
\n"
+ },
+ "security_vm": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "security_vm",
+ "description": "\n
\n Specify TPM2 FIXUP for VM.\n
\n
\n"
+ },
+ "cpu_affinity": {
+ "$ref": "#/definitions/CPUAffinityConfiguration",
+ "title": "Physical CPU affinity",
+ "description": "\n
\n Select a subset of physical CPUs that this VM can use. More than one can be selected.\n
\n
\n"
+ },
+ "clos": {
+ "$ref": "#/definitions/CLOSConfiguration",
+ "title": "clos",
+ "description": "\n
\n Class of Service for Cache Allocation Technology.\nRefer SDM 17.19.2 for details, and use with caution.\n
\n
\n"
+ },
+ "memory": {
+ "$ref": "#/definitions/ServiceMemoryInfo",
+ "title": "memory",
+ "description": "\n
\n Specify memory information for Service and User VMs.\n
\n
\n"
+ },
+ "priority": {
+ "$ref": "#/definitions/PriorityType",
+ "default": "PRIO_LOW",
+ "title": "priority",
+ "description": "\n
\n Specify the VM vCPU priority for scheduling.\n
\n
\n"
+ },
+ "companion_vmid": {
+ "type": "integer",
+ "default": 65535,
+ "title": "companion_vmid",
+ "description": "\n
\n Specify the companion VM id of this VM.\n
\n
\n"
+ },
+ "os_config": {
+ "$ref": "#/definitions/OSConfigurations",
+ "title": "os_config",
+ "description": "\n
\n General information for host kernel, boot\nargument and memory.\n
\n
\n"
+ },
+ "console_vuart": {
+ "$ref": "#/definitions/ConsoleVuartConfiguration",
+ "default": "None",
+ "title": "Console virtual UART type",
+ "description": "\n
\n Select the console virtual UART (vUART) type. Add the console settings to the kernel command line by typing them in the \"Linux kernel command-line parameters\" text box (for example, console=ttyS0 for COM port 1).\n
\n
\n"
+ }
+ }
+ },
+ "PostLaunchedMemoryInfo": {
+ "type": "object",
+ "required": [
+ "whole"
+ ],
+ "properties": {
+ "whole": {
+ "type": "integer",
+ "default": 256,
+ "title": "VM physical memory allocation (MB)",
+ "description": "\n
\n Specify the physical memory allocated to this VM in Megabytes.\n
\n
\n"
+ }
+ }
+ },
+ "PostLaunchedMMIOResourcesConfiguration": {
+ "type": "object",
+ "properties": {
+ "TPM2": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Trusted platform module (TPM2) passthrough",
+ "description": "\n
\n Pass through the trusted platform module (TPM2) device to this VM.\n
\n
\n"
+ }
+ }
+ },
+ "PostLaunchedVMConfigType": {
+ "type": "object",
+ "required": [
+ "load_order",
+ "vm_type",
+ "name",
+ "clos",
+ "memory",
+ "priority",
+ "companion_vmid",
+ "console_vuart",
+ "os_type",
+ "vbootloader",
+ "vuart0",
+ "virtio_devices"
+ ],
+ "properties": {
+ "load_order": {
+ "$ref": "#/definitions/LoadOrderType",
+ "title": "Load order",
+ "description": "\n
\n Specify the load_order.\n
\n
\n"
+ },
+ "vm_type": {
+ "$ref": "#/definitions/VMType",
+ "title": "VM type",
+ "description": "\n
\n Select the VM type. A standard VM (\n \n STANDARD_VM\n \n ) is for general-purpose applications, such as human-machine interface (HMI). A real-time VM (\n \n RTVM\n \n ) offers special features for time-sensitive applications.\n
\n
\n"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 15,
+ "pattern": "^\\S+$",
+ "title": "VM name",
+ "description": "\n
\n Specify the name used to identify this VM. The VM name will be shown in the hypervisor console vm_list command.\n
\n
\n"
+ },
+ "lapic_passthrough": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "LAPIC passthrough",
+ "description": "\n
\n Enable LAPIC passthrough for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "io_completion_polling": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "I/O completion polling",
+ "description": "\n
\n Enable polling mode for I/O completion for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "nested_virtualization_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Nested virtualization",
+ "description": "\n
\n Enable nested virtualization for KVM.\n
\n
\n"
+ },
+ "virtual_cat_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Virtual Cache Allocation Technology (vCAT)",
+ "description": "\n
\n Enable virtualization of the Cache Allocation Technology (CAT) feature in RDT. CAT enables you to allocate cache to VMs, providing isolation to avoid performance interference from other VMs.\n
\n
\n"
+ },
+ "secure_world_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "secure_world_support",
+ "description": "\n
\n Specify secure world support for trustry OS.\n
\n
\n"
+ },
+ "hide_mtrr_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "hide_mtrr_support",
+ "description": "\n
\n Specify MTRR capability to hide for VM.\n
\n
\n"
+ },
+ "security_vm": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "security_vm",
+ "description": "\n
\n Specify TPM2 FIXUP for VM.\n
\n
\n"
+ },
+ "cpu_affinity": {
+ "$ref": "#/definitions/CPUAffinityConfiguration",
+ "title": "Physical CPU affinity",
+ "description": "\n
\n Select a subset of physical CPUs that this VM can use. More than one can be selected.\n
\n
\n"
+ },
+ "clos": {
+ "$ref": "#/definitions/CLOSConfiguration",
+ "title": "clos",
+ "description": "\n
\n Class of Service for Cache Allocation Technology.\nRefer SDM 17.19.2 for details, and use with caution.\n
\n
\n"
+ },
+ "memory": {
+ "$ref": "#/definitions/PostLaunchedMemoryInfo",
+ "title": "memory",
+ "description": "\n
\n Specify memory information for Service and User VMs.\n
\n
\n"
+ },
+ "priority": {
+ "$ref": "#/definitions/PriorityType",
+ "default": "PRIO_LOW",
+ "title": "priority",
+ "description": "\n
\n Specify the VM vCPU priority for scheduling.\n
\n
\n"
+ },
+ "companion_vmid": {
+ "type": "integer",
+ "default": 65535,
+ "title": "companion_vmid",
+ "description": "\n
\n Specify the companion VM id of this VM.\n
\n
\n"
+ },
+ "console_vuart": {
+ "$ref": "#/definitions/ConsoleVuartConfiguration",
+ "default": "None",
+ "title": "Console virtual UART type",
+ "description": "\n
\n Select the console virtual UART (vUART) type. Add the console settings to the kernel command line by typing them in the \"Linux kernel command-line parameters\" text box (for example, console=ttyS0 for COM port 1).\n
\n
\n"
+ },
+ "mmio_resources": {
+ "$ref": "#/definitions/PostLaunchedMMIOResourcesConfiguration",
+ "title": "mmio_resources",
+ "description": "\n
\n MMIO resources to passthrough.\n
\n
\n"
+ },
+ "pci_devs": {
+ "$ref": "#/definitions/PCIDevsConfiguration",
+ "title": "PCI device assignment",
+ "description": ""
+ },
+ "PTM": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Precision Time Measurement",
+ "description": "\n
\n Enable virtualization of PCIe Precision Time Measurement (PTM) mechanism for devices with PTM capability and for real-time application. The hypervisor provides PCIe root port emulation instead of host bridge emulation for the VM. PTM coordinates timing between the device and root port with the device's local timebases without relying on software.\n
\n
\n"
+ },
+ "os_type": {
+ "$ref": "#/definitions/OSType",
+ "default": "Non-Windows OS",
+ "title": "OS type",
+ "description": "\n
\n Select the OS type for this VM. This is required to run Windows in a User VM. See\n \n Device Model Parameters\n \n for how to include this in the Device Model arguments.\n
\n
\n"
+ },
+ "vbootloader": {
+ "$ref": "#/definitions/EnablementType",
+ "default": "Enable",
+ "title": "OVMF (Open Virtual Machine Firmware)",
+ "description": "\n
\n Use virtual bootloader OVMF (Open Virtual Machine Firmware) to boot this VM.\n
\n
\n"
+ },
+ "vuart0": {
+ "$ref": "#/definitions/EnablementType",
+ "default": "Enable",
+ "title": "Emulate COM1 as stdio I/O in Device Model",
+ "description": "\n
\n Enable the ACRN Device Model to emulate COM1 as a User VM stdio I/O. Hypervisor global emulation will take priority over this VM setting.\n
\n
\n"
+ },
+ "usb_xhci": {
+ "type": "string",
+ "pattern": "^([\\d]+-[\\d]+){0,1}(:[\\d]+-[\\d]+)*$",
+ "title": "Virtual USB host controller interface",
+ "description": "\n
\n Select the USB physical bus and port number that will be emulated by the ACRN Device Model for this VM. USB 3.0, 2.0, and 1.0 are supported.\n
\n
\n"
+ },
+ "virtio_devices": {
+ "type": "object",
+ "properties": {
+ "console": {
+ "type": "string",
+ "title": "console",
+ "description": "\n
\n \n The virtio console device setting.Input format:\n \n \n \n \n \n [@]stdio|tty|pty|sock:portname[=portpath][,[@]stdio|tty|pty:portname[=portpath]]\n \n \n
\n \n \n
\n"
+ },
+ "network": {
+ "type": "string",
+ "title": "network",
+ "description": "\n
\n \n The virtio network device setting.\n \n \n \n Input format:\n \n \n device_name[,vhost][,mac=XX:XX:XX:XX:XX:XX]\n \n \n .\nThe\n \n device_name\n \n is the name of the TAP (or MacVTap) device.\nIt must include the keyword\n \n tap\n \n .\n \n vhost\n \n specifies the\nvhost backend; otherwise, the VBSU backend is used. The\n \n mac\n \n address is optional.\n
\n \n \n
\n"
+ },
+ "input": {
+ "type": "string",
+ "title": "input",
+ "description": "\n
\n The virtio input device setting.\n
\n
\n"
+ },
+ "block": {
+ "type": "string",
+ "title": "block",
+ "description": "\n
\n \n The virtio block device setting.\n \n \n \n Format: [blk partition:][img path] e.g.: /dev/sda3:./a/b.img\n
\n \n \n
\n"
+ }
+ },
+ "title": "Virt-IO devices",
+ "description": "\n
\n Enable virt-IO devices in post-launched VMs.\n
\n
\n"
+ }
+ }
+ },
+ "BasicMemoryInfo": {
+ "type": "object",
+ "required": [
+ "whole"
+ ],
+ "properties": {
+ "whole": {
+ "type": "integer",
+ "default": 256,
+ "title": "VM physical memory allocation (MB)",
+ "description": "\n
\n Specify the physical memory allocated to this VM in Megabytes.\n
\n
\n"
+ }
+ }
+ },
+ "VMBasicConfigType": {
+ "type": "object",
+ "required": [
+ "vm_type",
+ "name",
+ "memory",
+ "console_vuart",
+ "os_type",
+ "vbootloader",
+ "vuart0",
+ "virtio_devices"
+ ],
+ "properties": {
+ "vm_type": {
+ "$ref": "#/definitions/BasicVMType",
+ "title": "VM type",
+ "description": "\n
\n Select the VM type. A standard VM (\n \n STANDARD_VM\n \n ) is for general-purpose applications, such as human-machine interface (HMI). A real-time VM (\n \n RTVM\n \n ) offers special features for time-sensitive applications.\n
\n
\n"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 15,
+ "pattern": "^\\S+$",
+ "title": "VM name",
+ "description": "\n
\n Specify the name used to identify this VM. The VM name will be shown in the hypervisor console vm_list command.\n
\n
\n"
+ },
+ "cpu_affinity": {
+ "$ref": "#/definitions/CPUAffinityConfiguration",
+ "title": "Physical CPU affinity",
+ "description": "\n
\n Select a subset of physical CPUs that this VM can use. More than one can be selected.\n
\n
\n"
+ },
+ "memory": {
+ "$ref": "#/definitions/BasicMemoryInfo",
+ "title": "memory",
+ "description": "\n
\n Specify memory information for Service and User VMs.\n
\n
\n"
+ },
+ "os_config": {
+ "$ref": "#/definitions/OSConfigurations",
+ "title": "os_config",
+ "description": "\n
\n General information for host kernel, boot\nargument and memory.\n
\n
\n"
+ },
+ "console_vuart": {
+ "$ref": "#/definitions/ConsoleVuartConfiguration",
+ "default": "None",
+ "title": "Console virtual UART type",
+ "description": "\n
\n Select the console virtual UART (vUART) type. Add the console settings to the kernel command line by typing them in the \"Linux kernel command-line parameters\" text box (for example, console=ttyS0 for COM port 1).\n
\n
\n"
+ },
+ "mmio_resources": {
+ "$ref": "#/definitions/MMIOResourcesConfiguration",
+ "title": "mmio_resources",
+ "description": "\n
\n MMIO resources to passthrough.\n
\n
\n"
+ },
+ "pci_devs": {
+ "$ref": "#/definitions/PCIDevsConfiguration",
+ "title": "PCI device assignment",
+ "description": ""
+ },
+ "os_type": {
+ "$ref": "#/definitions/OSType",
+ "default": "Non-Windows OS",
+ "title": "OS type",
+ "description": "\n
\n Select the OS type for this VM. This is required to run Windows in a User VM. See\n \n Device Model Parameters\n \n for how to include this in the Device Model arguments.\n
\n
\n"
+ },
+ "vbootloader": {
+ "$ref": "#/definitions/EnablementType",
+ "default": "Enable",
+ "title": "OVMF (Open Virtual Machine Firmware)",
+ "description": "\n
\n Use virtual bootloader OVMF (Open Virtual Machine Firmware) to boot this VM.\n
\n
\n"
+ },
+ "vuart0": {
+ "$ref": "#/definitions/EnablementType",
+ "default": "Enable",
+ "title": "Emulate COM1 as stdio I/O in Device Model",
+ "description": "\n
\n Enable the ACRN Device Model to emulate COM1 as a User VM stdio I/O. Hypervisor global emulation will take priority over this VM setting.\n
\n
\n"
+ },
+ "usb_xhci": {
+ "type": "string",
+ "pattern": "^([\\d]+-[\\d]+){0,1}(:[\\d]+-[\\d]+)*$",
+ "title": "Virtual USB host controller interface",
+ "description": "\n
\n Select the USB physical bus and port number that will be emulated by the ACRN Device Model for this VM. USB 3.0, 2.0, and 1.0 are supported.\n
\n
\n"
+ },
+ "virtio_devices": {
+ "type": "object",
+ "properties": {
+ "console": {
+ "type": "string",
+ "title": "console",
+ "description": "\n
\n \n The virtio console device setting.Input format:\n \n \n \n \n \n [@]stdio|tty|pty|sock:portname[=portpath][,[@]stdio|tty|pty:portname[=portpath]]\n \n \n
\n \n \n
\n"
+ },
+ "network": {
+ "type": "string",
+ "title": "network",
+ "description": "\n
\n \n The virtio network device setting.\n \n \n \n Input format:\n \n \n device_name[,vhost][,mac=XX:XX:XX:XX:XX:XX]\n \n \n .\nThe\n \n device_name\n \n is the name of the TAP (or MacVTap) device.\nIt must include the keyword\n \n tap\n \n .\n \n vhost\n \n specifies the\nvhost backend; otherwise, the VBSU backend is used. The\n \n mac\n \n address is optional.\n
\n \n \n
\n"
+ },
+ "input": {
+ "type": "string",
+ "title": "input",
+ "description": "\n
\n The virtio input device setting.\n
\n
\n"
+ },
+ "block": {
+ "type": "string",
+ "title": "block",
+ "description": "\n
\n \n The virtio block device setting.\n \n \n \n Format: [blk partition:][img path] e.g.: /dev/sda3:./a/b.img\n
\n \n \n
\n"
+ }
+ },
+ "title": "Virt-IO devices",
+ "description": "\n
\n Enable virt-IO devices in post-launched VMs.\n
\n
\n"
+ }
+ }
+ },
+ "PreLaunchedVMBasicConfigType": {
+ "type": "object",
+ "required": [
+ "vm_type",
+ "name",
+ "console_vuart"
+ ],
+ "properties": {
+ "vm_type": {
+ "$ref": "#/definitions/BasicVMType",
+ "title": "VM type",
+ "description": "\n
\n Select the VM type. A standard VM (\n \n STANDARD_VM\n \n ) is for general-purpose applications, such as human-machine interface (HMI). A real-time VM (\n \n RTVM\n \n ) offers special features for time-sensitive applications.\n
\n
\n"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 15,
+ "pattern": "^\\S+$",
+ "title": "VM name",
+ "description": "\n
\n Specify the name used to identify this VM. The VM name will be shown in the hypervisor console vm_list command.\n
\n
\n"
+ },
+ "cpu_affinity": {
+ "$ref": "#/definitions/CPUAffinityConfiguration",
+ "title": "Physical CPU affinity",
+ "description": "\n
\n Select a subset of physical CPUs that this VM can use. More than one can be selected.\n
\n
\n"
+ },
+ "os_config": {
+ "$ref": "#/definitions/OSConfigurations",
+ "title": "os_config",
+ "description": "\n
\n General information for host kernel, boot\nargument and memory.\n
\n
\n"
+ },
+ "console_vuart": {
+ "$ref": "#/definitions/ConsoleVuartConfiguration",
+ "default": "None",
+ "title": "Console virtual UART type",
+ "description": "\n
\n Select the console virtual UART (vUART) type. Add the console settings to the kernel command line by typing them in the \"Linux kernel command-line parameters\" text box (for example, console=ttyS0 for COM port 1).\n
\n
\n"
+ },
+ "mmio_resources": {
+ "$ref": "#/definitions/MMIOResourcesConfiguration",
+ "title": "mmio_resources",
+ "description": "\n
\n MMIO resources to passthrough.\n
\n
\n"
+ },
+ "pci_devs": {
+ "$ref": "#/definitions/PCIDevsConfiguration",
+ "title": "PCI device assignment",
+ "description": ""
+ }
+ }
+ },
+ "ServiceVMBasicConfigType": {
+ "type": "object",
+ "required": [
+ "vm_type",
+ "name",
+ "console_vuart"
+ ],
+ "properties": {
+ "vm_type": {
+ "$ref": "#/definitions/BasicVMType",
+ "title": "VM type",
+ "description": "\n
\n Select the VM type. A standard VM (\n \n STANDARD_VM\n \n ) is for general-purpose applications, such as human-machine interface (HMI). A real-time VM (\n \n RTVM\n \n ) offers special features for time-sensitive applications.\n
\n
\n"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 15,
+ "pattern": "^\\S+$",
+ "title": "VM name",
+ "description": "\n
\n Specify the name used to identify this VM. The VM name will be shown in the hypervisor console vm_list command.\n
\n
\n"
+ },
+ "cpu_affinity": {
+ "$ref": "#/definitions/CPUAffinityConfiguration",
+ "title": "Physical CPU affinity",
+ "description": "\n
\n Select a subset of physical CPUs that this VM can use. More than one can be selected.\n
\n
\n"
+ },
+ "os_config": {
+ "$ref": "#/definitions/OSConfigurations",
+ "title": "os_config",
+ "description": "\n
\n General information for host kernel, boot\nargument and memory.\n
\n
\n"
+ },
+ "console_vuart": {
+ "$ref": "#/definitions/ConsoleVuartConfiguration",
+ "default": "None",
+ "title": "Console virtual UART type",
+ "description": "\n
\n Select the console virtual UART (vUART) type. Add the console settings to the kernel command line by typing them in the \"Linux kernel command-line parameters\" text box (for example, console=ttyS0 for COM port 1).\n
\n
\n"
+ }
+ }
+ },
+ "PostLaunchedVMBasicConfigType": {
+ "type": "object",
+ "required": [
+ "vm_type",
+ "name",
+ "memory",
+ "console_vuart",
+ "os_type",
+ "vbootloader",
+ "vuart0",
+ "virtio_devices"
+ ],
+ "properties": {
+ "vm_type": {
+ "$ref": "#/definitions/BasicVMType",
+ "title": "VM type",
+ "description": "\n
\n Select the VM type. A standard VM (\n \n STANDARD_VM\n \n ) is for general-purpose applications, such as human-machine interface (HMI). A real-time VM (\n \n RTVM\n \n ) offers special features for time-sensitive applications.\n
\n
\n"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 15,
+ "pattern": "^\\S+$",
+ "title": "VM name",
+ "description": "\n
\n Specify the name used to identify this VM. The VM name will be shown in the hypervisor console vm_list command.\n
\n
\n"
+ },
+ "cpu_affinity": {
+ "$ref": "#/definitions/CPUAffinityConfiguration",
+ "title": "Physical CPU affinity",
+ "description": "\n
\n Select a subset of physical CPUs that this VM can use. More than one can be selected.\n
\n
\n"
+ },
+ "memory": {
+ "$ref": "#/definitions/PostLaunchedMemoryInfo",
+ "title": "memory",
+ "description": "\n
\n Specify memory information for Service and User VMs.\n
\n
\n"
+ },
+ "console_vuart": {
+ "$ref": "#/definitions/ConsoleVuartConfiguration",
+ "default": "None",
+ "title": "Console virtual UART type",
+ "description": "\n
\n Select the console virtual UART (vUART) type. Add the console settings to the kernel command line by typing them in the \"Linux kernel command-line parameters\" text box (for example, console=ttyS0 for COM port 1).\n
\n
\n"
+ },
+ "mmio_resources": {
+ "$ref": "#/definitions/PostLaunchedMMIOResourcesConfiguration",
+ "title": "mmio_resources",
+ "description": "\n
\n MMIO resources to passthrough.\n
\n
\n"
+ },
+ "pci_devs": {
+ "$ref": "#/definitions/PCIDevsConfiguration",
+ "title": "PCI device assignment",
+ "description": ""
+ },
+ "os_type": {
+ "$ref": "#/definitions/OSType",
+ "default": "Non-Windows OS",
+ "title": "OS type",
+ "description": "\n
\n Select the OS type for this VM. This is required to run Windows in a User VM. See\n \n Device Model Parameters\n \n for how to include this in the Device Model arguments.\n
\n
\n"
+ },
+ "vbootloader": {
+ "$ref": "#/definitions/EnablementType",
+ "default": "Enable",
+ "title": "OVMF (Open Virtual Machine Firmware)",
+ "description": "\n
\n Use virtual bootloader OVMF (Open Virtual Machine Firmware) to boot this VM.\n
\n
\n"
+ },
+ "vuart0": {
+ "$ref": "#/definitions/EnablementType",
+ "default": "Enable",
+ "title": "Emulate COM1 as stdio I/O in Device Model",
+ "description": "\n
\n Enable the ACRN Device Model to emulate COM1 as a User VM stdio I/O. Hypervisor global emulation will take priority over this VM setting.\n
\n
\n"
+ },
+ "usb_xhci": {
+ "type": "string",
+ "pattern": "^([\\d]+-[\\d]+){0,1}(:[\\d]+-[\\d]+)*$",
+ "title": "Virtual USB host controller interface",
+ "description": "\n
\n Select the USB physical bus and port number that will be emulated by the ACRN Device Model for this VM. USB 3.0, 2.0, and 1.0 are supported.\n
\n
\n"
+ },
+ "virtio_devices": {
+ "type": "object",
+ "properties": {
+ "console": {
+ "type": "string",
+ "title": "console",
+ "description": "\n
\n \n The virtio console device setting.Input format:\n \n \n \n \n \n [@]stdio|tty|pty|sock:portname[=portpath][,[@]stdio|tty|pty:portname[=portpath]]\n \n \n
\n \n \n
\n"
+ },
+ "network": {
+ "type": "string",
+ "title": "network",
+ "description": "\n
\n \n The virtio network device setting.\n \n \n \n Input format:\n \n \n device_name[,vhost][,mac=XX:XX:XX:XX:XX:XX]\n \n \n .\nThe\n \n device_name\n \n is the name of the TAP (or MacVTap) device.\nIt must include the keyword\n \n tap\n \n .\n \n vhost\n \n specifies the\nvhost backend; otherwise, the VBSU backend is used. The\n \n mac\n \n address is optional.\n
\n \n \n
\n"
+ },
+ "input": {
+ "type": "string",
+ "title": "input",
+ "description": "\n
\n The virtio input device setting.\n
\n
\n"
+ },
+ "block": {
+ "type": "string",
+ "title": "block",
+ "description": "\n
\n \n The virtio block device setting.\n \n \n \n Format: [blk partition:][img path] e.g.: /dev/sda3:./a/b.img\n
\n \n \n
\n"
+ }
+ },
+ "title": "Virt-IO devices",
+ "description": "\n
\n Enable virt-IO devices in post-launched VMs.\n
\n
\n"
+ }
+ }
+ },
+ "BasicFeatureOptionsType": {
+ "type": "object",
+ "required": [
+ "IVSHMEM"
+ ],
+ "properties": {
+ "IVSHMEM": {
+ "$ref": "#/definitions/IVSHMEMInfo",
+ "title": "Inter-VM shared memory",
+ "description": ""
+ }
+ }
+ },
+ "HVBasicConfigType": {
+ "type": "object",
+ "required": [
+ "DEBUG_OPTIONS",
+ "FEATURES",
+ "vuart_connections"
+ ],
+ "properties": {
+ "DEBUG_OPTIONS": {
+ "$ref": "#/definitions/DebugOptionsType",
+ "title": "Debug options",
+ "description": "\n
\n Configure the debug facilities.\n
\n
\n"
+ },
+ "FEATURES": {
+ "$ref": "#/definitions/BasicFeatureOptionsType",
+ "title": "Hypervisor features",
+ "description": "\n
\n Enable hypervisor features.\n
\n
\n"
+ },
+ "vuart_connections": {
+ "$ref": "#/definitions/VuartConnectionsType",
+ "title": "Virtual UART connection",
+ "description": "\n"
+ }
+ }
+ },
+ "AdvancedMemoryInfo": {
+ "type": "object",
+ "required": [
+ "start_hpa"
+ ],
+ "properties": {
+ "start_hpa": {
+ "$ref": "#/definitions/HexFormat",
+ "default": "0x100000000",
+ "title": "start_hpa",
+ "description": "\n
\n The starting physical address in host for the VM.\n
\n
\n"
+ },
+ "size": {
+ "$ref": "#/definitions/MemorySizeType",
+ "default": "0x20000000",
+ "title": "size",
+ "description": "\n
\n The memory size in bytes for the VM. Default value is\n \n 0x200000000\n \n .\n
\n
\n"
+ },
+ "start_hpa2": {
+ "$ref": "#/definitions/HexFormat",
+ "default": "0x0",
+ "title": "start_hpa2",
+ "description": "\n
\n Start of second HPA for non-contiguous allocations in host for the VM.\n
\n
\n"
+ },
+ "size_hpa2": {
+ "$ref": "#/definitions/MemorySizeType",
+ "default": "0x0",
+ "title": "size_hpa2",
+ "description": "\n
\n Memory size of second HPA for non-contiguous allocations in Bytes for the VM.\n
\n
\n"
+ }
+ }
+ },
+ "VMAdvancedConfigType": {
+ "type": "object",
+ "required": [
+ "clos",
+ "memory"
+ ],
+ "properties": {
+ "lapic_passthrough": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "LAPIC passthrough",
+ "description": "\n
\n Enable LAPIC passthrough for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "io_completion_polling": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "I/O completion polling",
+ "description": "\n
\n Enable polling mode for I/O completion for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "nested_virtualization_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Nested virtualization",
+ "description": "\n
\n Enable nested virtualization for KVM.\n
\n
\n"
+ },
+ "virtual_cat_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Virtual Cache Allocation Technology (vCAT)",
+ "description": "\n
\n Enable virtualization of the Cache Allocation Technology (CAT) feature in RDT. CAT enables you to allocate cache to VMs, providing isolation to avoid performance interference from other VMs.\n
\n
\n"
+ },
+ "clos": {
+ "$ref": "#/definitions/CLOSConfiguration",
+ "title": "clos",
+ "description": "\n
\n Class of Service for Cache Allocation Technology.\nRefer SDM 17.19.2 for details, and use with caution.\n
\n
\n"
+ },
+ "epc_section": {
+ "$ref": "#/definitions/EPCSection",
+ "title": "epc_section",
+ "description": "\n
\n Specify the Intel Software Guard Extensions (SGX) enclave page cache (EPC) section settings.\n
\n
\n"
+ },
+ "memory": {
+ "$ref": "#/definitions/AdvancedMemoryInfo",
+ "title": "memory",
+ "description": "\n
\n Specify memory information for Service and User VMs.\n
\n
\n"
+ },
+ "pt_intx": {
+ "type": "string",
+ "title": "pt_intx",
+ "description": "\n
\n Specify the pre-launched VM owned IOAPIC pins and the corresponding mapping between physical GSI and virtual GSI.\n
\n
\n"
+ },
+ "PTM": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Precision Time Measurement",
+ "description": "\n
\n Enable virtualization of PCIe Precision Time Measurement (PTM) mechanism for devices with PTM capability and for real-time application. The hypervisor provides PCIe root port emulation instead of host bridge emulation for the VM. PTM coordinates timing between the device and root port with the device's local timebases without relying on software.\n
\n
\n"
+ }
+ }
+ },
+ "PreLaunchedVMAdvancedConfigType": {
+ "type": "object",
+ "required": [
+ "clos",
+ "memory"
+ ],
+ "properties": {
+ "lapic_passthrough": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "LAPIC passthrough",
+ "description": "\n
\n Enable LAPIC passthrough for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "io_completion_polling": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "I/O completion polling",
+ "description": "\n
\n Enable polling mode for I/O completion for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "nested_virtualization_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Nested virtualization",
+ "description": "\n
\n Enable nested virtualization for KVM.\n
\n
\n"
+ },
+ "virtual_cat_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Virtual Cache Allocation Technology (vCAT)",
+ "description": "\n
\n Enable virtualization of the Cache Allocation Technology (CAT) feature in RDT. CAT enables you to allocate cache to VMs, providing isolation to avoid performance interference from other VMs.\n
\n
\n"
+ },
+ "clos": {
+ "$ref": "#/definitions/CLOSConfiguration",
+ "title": "clos",
+ "description": "\n
\n Class of Service for Cache Allocation Technology.\nRefer SDM 17.19.2 for details, and use with caution.\n
\n
\n"
+ },
+ "epc_section": {
+ "$ref": "#/definitions/EPCSection",
+ "title": "epc_section",
+ "description": "\n
\n Specify the Intel Software Guard Extensions (SGX) enclave page cache (EPC) section settings.\n
\n
\n"
+ },
+ "memory": {
+ "$ref": "#/definitions/PreLaunchedMemoryInfo",
+ "title": "memory",
+ "description": "\n
\n Specify memory information for Service and User VMs.\n
\n
\n"
+ },
+ "pt_intx": {
+ "type": "string",
+ "title": "pt_intx",
+ "description": "\n
\n Specify the pre-launched VM owned IOAPIC pins and the corresponding mapping between physical GSI and virtual GSI.\n
\n
\n"
+ },
+ "PTM": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Precision Time Measurement",
+ "description": "\n
\n Enable virtualization of PCIe Precision Time Measurement (PTM) mechanism for devices with PTM capability and for real-time application. The hypervisor provides PCIe root port emulation instead of host bridge emulation for the VM. PTM coordinates timing between the device and root port with the device's local timebases without relying on software.\n
\n
\n"
+ }
+ }
+ },
+ "ServiceVMAdvancedConfigType": {
+ "type": "object",
+ "required": [
+ "clos",
+ "memory"
+ ],
+ "properties": {
+ "lapic_passthrough": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "LAPIC passthrough",
+ "description": "\n
\n Enable LAPIC passthrough for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "io_completion_polling": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "I/O completion polling",
+ "description": "\n
\n Enable polling mode for I/O completion for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "nested_virtualization_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Nested virtualization",
+ "description": "\n
\n Enable nested virtualization for KVM.\n
\n
\n"
+ },
+ "virtual_cat_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Virtual Cache Allocation Technology (vCAT)",
+ "description": "\n
\n Enable virtualization of the Cache Allocation Technology (CAT) feature in RDT. CAT enables you to allocate cache to VMs, providing isolation to avoid performance interference from other VMs.\n
\n
\n"
+ },
+ "clos": {
+ "$ref": "#/definitions/CLOSConfiguration",
+ "title": "clos",
+ "description": "\n
\n Class of Service for Cache Allocation Technology.\nRefer SDM 17.19.2 for details, and use with caution.\n
\n
\n"
+ },
+ "memory": {
+ "$ref": "#/definitions/ServiceMemoryInfo",
+ "title": "memory",
+ "description": "\n
\n Specify memory information for Service and User VMs.\n
\n
\n"
+ }
+ }
+ },
+ "PostLaunchedVMAdvancedConfigType": {
+ "type": "object",
+ "required": [
+ "clos"
+ ],
+ "properties": {
+ "lapic_passthrough": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "LAPIC passthrough",
+ "description": "\n
\n Enable LAPIC passthrough for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "io_completion_polling": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "I/O completion polling",
+ "description": "\n
\n Enable polling mode for I/O completion for this VM. This feature is required for VMs with stringent real-time performance needs.\n
\n
\n"
+ },
+ "nested_virtualization_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Nested virtualization",
+ "description": "\n
\n Enable nested virtualization for KVM.\n
\n
\n"
+ },
+ "virtual_cat_support": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Virtual Cache Allocation Technology (vCAT)",
+ "description": "\n
\n Enable virtualization of the Cache Allocation Technology (CAT) feature in RDT. CAT enables you to allocate cache to VMs, providing isolation to avoid performance interference from other VMs.\n
\n
\n"
+ },
+ "clos": {
+ "$ref": "#/definitions/CLOSConfiguration",
+ "title": "clos",
+ "description": "\n
\n Class of Service for Cache Allocation Technology.\nRefer SDM 17.19.2 for details, and use with caution.\n
\n
\n"
+ },
+ "PTM": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Precision Time Measurement",
+ "description": "\n
\n Enable virtualization of PCIe Precision Time Measurement (PTM) mechanism for devices with PTM capability and for real-time application. The hypervisor provides PCIe root port emulation instead of host bridge emulation for the VM. PTM coordinates timing between the device and root port with the device's local timebases without relying on software.\n
\n
\n"
+ }
+ }
+ },
+ "AdvancedFeatureOptionsType": {
+ "type": "object",
+ "required": [
+ "RELOC",
+ "SCHEDULER",
+ "MULTIBOOT2",
+ "ENFORCE_TURNOFF_AC",
+ "ENFORCE_TURNOFF_GP",
+ "RDT",
+ "HYPERV_ENABLED",
+ "ACPI_PARSE_ENABLED",
+ "L1D_VMENTRY_ENABLED",
+ "MCE_ON_PSC_DISABLED"
+ ],
+ "properties": {
+ "RELOC": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Hypervisor relocation in memory",
+ "description": "\n
\n Enable hypervisor relocation in memory. The bootloader may need to change the location of the hypervisor because of other firmware.\n
\n
\n"
+ },
+ "SCHEDULER": {
+ "$ref": "#/definitions/SchedulerType",
+ "default": "SCHED_BVT",
+ "title": "Virtual CPU scheduler",
+ "description": "\n
\n Select the scheduling algorithm used to determine which User VM runs on a shared virtual CPU.\n
\n
\n"
+ },
+ "MULTIBOOT2": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Multiboot2",
+ "description": "\n
\n Enable multiboot2 protocol support and multiboot1 downward compatibility. Disable this feature if multiboot1 meets your requirements and to reduce lines of code.\n
\n
\n"
+ },
+ "ENFORCE_TURNOFF_AC": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Split lock detection",
+ "description": "\n
\n Enable detection of split locks, which can negatively affect an application's real-time performance. If a lock is detected, an alignment check exception #AC occurs.\n
\n
\n"
+ },
+ "ENFORCE_TURNOFF_GP": {
+ "$ref": "#/definitions/Boolean",
+ "default": "n",
+ "title": "Uncacheable-memory lock detection",
+ "description": "\n
\n Enable detection of uncacheable-memory locks, which can negatively affect an application's real-time performance. If a lock is detected, a general-protection exception #GP occurs.\n
\n
\n"
+ },
+ "RDT": {
+ "$ref": "#/definitions/RDTType",
+ "title": "Intel Resource Director Technology (RDT)",
+ "description": "\n
\n Intel Resource Director Technology (RDT) provides cache and memory bandwidth allocation features. The features can be used to improve an application's real-time performance.\n
\n
\n"
+ },
+ "HYPERV_ENABLED": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Hyper-V virtualization technology",
+ "description": "\n
\n Enable Microsoft Hyper-V Hypervisor Top-Level Functional Specification (TFLS) for Windows User VMs.\n
\n
\n"
+ },
+ "ACPI_PARSE_ENABLED": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Parse ACPI tables at runtime",
+ "description": "\n
\n Enable ACPI runtime parsing to get DMAR (DMA remapping) configuration data from the APCI tables. Otherwise, use existing, static information from the associated board configuration file.\n
\n
\n"
+ },
+ "L1D_VMENTRY_ENABLED": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Mitigate L1 terminal fault",
+ "description": "\n
\n Enable L1 cache flush before VM entry to prevent L1 terminal fault. L1 terminal fault is a hardware vulnerability that allows unauthorized disclosure of information residing in the L1 data cache.\n
\n
\n"
+ },
+ "MCE_ON_PSC_DISABLED": {
+ "$ref": "#/definitions/Boolean",
+ "default": "y",
+ "title": "Machine Check Error (MCE) workaround",
+ "description": "\n
\n Enable the software workaround for Machine Check Error on Page Size Change (hardware bug in some processor families).\n
\n
\n"
+ },
+ "SSRAM": {
+ "$ref": "#/definitions/SSRAMInfo",
+ "title": "Software SRAM (for real-time apps)",
+ "description": ""
+ }
+ }
+ },
+ "AdvancedMemoryOptionsType": {
+ "type": "object",
+ "required": [
+ "STACK_SIZE"
+ ],
+ "properties": {
+ "STACK_SIZE": {
+ "$ref": "#/definitions/HexFormat",
+ "default": "0x2000",
+ "title": "CPU memory stack size (bytes per CPU)",
+ "description": "\n
\n Specify the size of the memory stack in bytes for each physical CPU. For example, if you specify 8 kilobytes, each CPU will get its own 8-kilobyte stack.\n
\n
\n"
+ }
+ }
+ },
+ "AdvancedCapacitiesOptionsType": {
+ "type": "object",
+ "required": [
+ "MAX_PCI_DEV_NUM",
+ "MAX_PT_IRQ_ENTRIES",
+ "MAX_MSIX_TABLE_NUM",
+ "MAX_EMULATED_MMIO"
+ ],
+ "properties": {
+ "MAX_PCI_DEV_NUM": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 1024,
+ "default": 96,
+ "title": "Maximum number of PCI devices",
+ "description": "\n
\n Specify the maximum number of PCI devices. This impacts the amount of memory used to maintain information about these PCI devices. The default value is calculated from the board configuration file. If you have PCI devices that were not detected by the Board Inspector, you may need to change this maximum value.\n
\n
\n"
+ },
+ "MAX_PT_IRQ_ENTRIES": {
+ "type": "integer",
+ "default": 256,
+ "title": "Maximum number of IRQ entries for passthrough devices",
+ "description": "\n
\n Specify the maximum number of interrupt request (IRQ) entries from all passthrough devices.\n
\n
\n"
+ },
+ "MAX_MSIX_TABLE_NUM": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 2048,
+ "default": 64,
+ "title": "Maximum number of MSI-X tables per device",
+ "description": "\n
\n Specify the maximum number of Message Signaled Interrupt MSI-X tables per device. The default value is calculated from the board configuration file.\n
\n
\n"
+ },
+ "MAX_EMULATED_MMIO": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 128,
+ "default": 16,
+ "title": "Maximum number of emulated MMIO regions",
+ "description": "\n
\n Specify the maximum number of emulated MMIO regions for device virtualization. The default value is calculated from the board configuration file.\n
\n
\n"
+ }
+ }
+ },
+ "HVAdvancedConfigType": {
+ "type": "object",
+ "required": [
+ "FEATURES",
+ "MEMORY",
+ "CAPACITIES"
+ ],
+ "properties": {
+ "FEATURES": {
+ "$ref": "#/definitions/AdvancedFeatureOptionsType",
+ "title": "Hypervisor features",
+ "description": "\n
\n Enable hypervisor features.\n
\n
\n"
+ },
+ "MEMORY": {
+ "$ref": "#/definitions/AdvancedMemoryOptionsType",
+ "title": "Memory options",
+ "description": "\n
\n Configure memory used by the hypervisor.\n
\n
\n"
+ },
+ "CAPACITIES": {
+ "$ref": "#/definitions/AdvancedCapacitiesOptionsType",
+ "title": "Hypervisor capacities",
+ "description": "\n
\n Configure the capacities of the hypervisor.\n
\n
\n"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/components/Banner/Banner.css b/misc/config_tools/configurator/src/components/Banner/Banner.css
new file mode 100644
index 000000000..c21fe9872
--- /dev/null
+++ b/misc/config_tools/configurator/src/components/Banner/Banner.css
@@ -0,0 +1,6 @@
+.banner {
+ min-height: 50px;
+ background-image: url("images/top_pattern.png"), linear-gradient(90.29deg, #242357 0.08%, #6ABFAE 85.19%, #69BFAD 99.72%);
+ background-repeat: no-repeat;
+ background-position: 50%;
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/components/Banner/Banner.jsx b/misc/config_tools/configurator/src/components/Banner/Banner.jsx
new file mode 100644
index 000000000..1b7c1105b
--- /dev/null
+++ b/misc/config_tools/configurator/src/components/Banner/Banner.jsx
@@ -0,0 +1,9 @@
+import React from "react";
+
+export class Banner extends React.Component {
+ render() {
+ return (
+ {this.props.children}
+ );
+ }
+}
diff --git a/misc/config_tools/configurator/src/components/Banner/images/top_pattern.png b/misc/config_tools/configurator/src/components/Banner/images/top_pattern.png
new file mode 100644
index 000000000..4626e9173
Binary files /dev/null and b/misc/config_tools/configurator/src/components/Banner/images/top_pattern.png differ
diff --git a/misc/config_tools/configurator/src/components/Banner/index.jsx b/misc/config_tools/configurator/src/components/Banner/index.jsx
new file mode 100644
index 000000000..c698d227a
--- /dev/null
+++ b/misc/config_tools/configurator/src/components/Banner/index.jsx
@@ -0,0 +1,5 @@
+import './Banner.css'
+import {Banner} from "./Banner";
+
+
+export default Banner;
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/components/Confirm/Confirm.jsx b/misc/config_tools/configurator/src/components/Confirm/Confirm.jsx
new file mode 100644
index 000000000..35af8d11e
--- /dev/null
+++ b/misc/config_tools/configurator/src/components/Confirm/Confirm.jsx
@@ -0,0 +1,45 @@
+import {Button, Modal} from "react-bootstrap";
+import {useState} from "react";
+
+export default function Confirm(props) {
+ const [show, setShow] = useState(false);
+
+ const handleClose = (choice) => {
+ setShow(false);
+ props.callback(choice)
+ }
+
+ const handleShow = (e) => {
+ setShow(true);
+ e.preventDefault()
+ }
+
+ return (
+ handleClose('cancel')} size="lg">
+
+ {props.title}
+
+
+
+ {props.content}
+
+
+
+ handleShow('cancel')}
+ size="lg" style={{width: '137px'}}>
+ Cancel
+
+ handleShow('no')} size="lg"
+ style={{width: '137px'}}>
+ {props.no ? props.no : "No"}
+
+ {
+ props.callback('yes')
+ }} size="lg" style={{width: '137px'}}>
+ {props.yes ? props.yes : "Yes"}
+
+
+
+ );
+}
+
diff --git a/misc/config_tools/configurator/src/components/Confirm/index.jsx b/misc/config_tools/configurator/src/components/Confirm/index.jsx
new file mode 100644
index 000000000..8b5ecbaee
--- /dev/null
+++ b/misc/config_tools/configurator/src/components/Confirm/index.jsx
@@ -0,0 +1,3 @@
+import Confirm from "./Confirm";
+
+export default Confirm
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/components/Footer/footer.jsx b/misc/config_tools/configurator/src/components/Footer/footer.jsx
new file mode 100644
index 000000000..0f6bac9bb
--- /dev/null
+++ b/misc/config_tools/configurator/src/components/Footer/footer.jsx
@@ -0,0 +1,25 @@
+import {Component} from "react";
+import {getVersion} from "@tauri-apps/api/app";
+
+
+export default class Footer extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ version: "0.1.0"
+ }
+ }
+
+ componentDidMount = () => {
+ getVersion().then((version) => {
+ this.setState({version: version})
+ })
+ }
+
+ render = () => {
+ return
+
© Copyright Project ACRN™, a Series of LF Projects, LLC.
+ - Version {this.state.version}
+
+ }
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/components/Footer/index.jsx b/misc/config_tools/configurator/src/components/Footer/index.jsx
new file mode 100644
index 000000000..c6c8f167f
--- /dev/null
+++ b/misc/config_tools/configurator/src/components/Footer/index.jsx
@@ -0,0 +1,3 @@
+import Footer from "./footer";
+
+export default Footer
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/components/Navbar/Navbar.css b/misc/config_tools/configurator/src/components/Navbar/Navbar.css
new file mode 100644
index 000000000..8936059ac
--- /dev/null
+++ b/misc/config_tools/configurator/src/components/Navbar/Navbar.css
@@ -0,0 +1,44 @@
+body {
+ -webkit-app-region: no-drag
+}
+
+.bg-navbar {
+ height: 80px;
+ background: #007B81;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
+ -webkit-user-select: none;
+ -webkit-app-region: drag;
+}
+
+.logo-text {
+ cursor: default;
+ font-family: Roboto, serif;
+ font-style: normal;
+ font-weight: bold;
+ font-size: 26px;
+ line-height: 30px;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+
+ color: #9ADFD1;
+}
+
+.controlButtons {
+ margin-right: 30px;
+ width: 130px;
+ filter: drop-shadow(3px 3px 2px rgb(0 0 0 / 0.4));
+}
+
+
+.wmb {
+ cursor: pointer;
+ -webkit-app-region: no-drag;
+}
+
+.wmb:hover {
+ opacity: 0.7;
+}
+
+.btn-close {
+ -webkit-app-region: no-drag;
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/components/Navbar/Navbar.jsx b/misc/config_tools/configurator/src/components/Navbar/Navbar.jsx
new file mode 100644
index 000000000..8e19300fd
--- /dev/null
+++ b/misc/config_tools/configurator/src/components/Navbar/Navbar.jsx
@@ -0,0 +1,39 @@
+import React from "react";
+import {Container} from "react-bootstrap";
+import {Navbar as BootstrapNavbar} from "react-bootstrap";
+import logo from "./images/ACRN_Logo.svg";
+import {faWindowMaximize} from "@fortawesome/free-regular-svg-icons";
+import {faClose, faMinus} from "@fortawesome/free-solid-svg-icons"
+import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
+
+import {windowHelper} from "../../lib/platform/tauri/tauri";
+
+
+export class Navbar extends React.Component {
+ render() {
+ return (
+
+
+
+
+
+ Configurator
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/misc/config_tools/configurator/src/components/Navbar/images/ACRN_Logo.svg b/misc/config_tools/configurator/src/components/Navbar/images/ACRN_Logo.svg
new file mode 100644
index 000000000..e52cd4bfa
--- /dev/null
+++ b/misc/config_tools/configurator/src/components/Navbar/images/ACRN_Logo.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/misc/config_tools/configurator/src/components/Navbar/index.jsx b/misc/config_tools/configurator/src/components/Navbar/index.jsx
new file mode 100644
index 000000000..12075d91d
--- /dev/null
+++ b/misc/config_tools/configurator/src/components/Navbar/index.jsx
@@ -0,0 +1,5 @@
+import './Navbar.css'
+import {Navbar} from "./Navbar";
+
+
+export default Navbar;
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/favicon.svg b/misc/config_tools/configurator/src/favicon.svg
new file mode 100644
index 000000000..de4aeddc1
--- /dev/null
+++ b/misc/config_tools/configurator/src/favicon.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/misc/config_tools/configurator/src/index.css b/misc/config_tools/configurator/src/index.css
new file mode 100644
index 000000000..ec2585e8c
--- /dev/null
+++ b/misc/config_tools/configurator/src/index.css
@@ -0,0 +1,13 @@
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+}
diff --git a/misc/config_tools/configurator/src/index.scss b/misc/config_tools/configurator/src/index.scss
new file mode 100644
index 000000000..b24d45b1c
--- /dev/null
+++ b/misc/config_tools/configurator/src/index.scss
@@ -0,0 +1,36 @@
+// global
+$primary: #007B81;
+
+// grid padding
+$grid-gutter-width: 0;
+
+// a
+$link-decoration: auto;
+$link-shade-percentage: 39%;
+
+// text box fontsize
+$input-font-size-lg: 1rem;
+
+@import "../node_modules/bootstrap/scss/bootstrap";
+
+.text-pre-line {
+ white-space: pre-line;
+}
+
+.btn.btn-outline-primary {
+ color: #007B81;
+ border-color: #007B81;
+ background-color: white;
+}
+
+.btn-outline-primary:hover {
+ color: #004B50;
+ border-color: #004B50;
+ background-color: white;
+}
+
+.btn-close-white {
+ opacity: 1;
+ filter: none;
+ background-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M11.5195 8.99331L17.4644 3.04835C18.1607 2.3521 18.1607 1.21845 17.4644 0.522192C16.7682 -0.174064 15.6345 -0.174064 14.9383 0.522192L8.99331 6.46715L3.04835 0.531118C2.3521 -0.165138 1.21845 -0.165138 0.522192 0.531118C-0.174064 1.22737 -0.174064 2.36102 0.522192 3.05728L6.46715 9.00223L0.522192 14.9472C-0.174064 15.6434 -0.174064 16.7771 0.522192 17.4733C0.87032 17.8215 1.32556 18 1.78081 18C2.23605 18 2.6913 17.8215 3.03942 17.4733L8.98438 11.5284L14.9293 17.4733C15.2775 17.8215 15.7327 18 16.1879 18C16.6432 18 17.0984 17.8215 17.4466 17.4733C18.1428 16.7771 18.1428 15.6434 17.4466 14.9472L11.5195 8.99331Z' fill='white'/%3E%3C/svg%3E%0A");
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/lib/.gitignore b/misc/config_tools/configurator/src/lib/.gitignore
new file mode 100644
index 000000000..b2a7018ad
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/.gitignore
@@ -0,0 +1,2 @@
+pyodide
+/pyodide.tar.bz2
diff --git a/misc/config_tools/configurator/src/lib/acrn.jsx b/misc/config_tools/configurator/src/lib/acrn.jsx
new file mode 100644
index 000000000..e425e5532
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/acrn.jsx
@@ -0,0 +1,476 @@
+import _ from "lodash";
+
+import React, {Component} from "react";
+import {path} from "@tauri-apps/api";
+import scenario from '../assets/schema/scenario.json'
+import {createSearchParams} from "react-router-dom";
+import queryString from 'query-string'
+import {invoke} from "@tauri-apps/api/tauri";
+import {getNewSchema} from "./runpy";
+
+
+function ThrowError(errMsg) {
+ alert(errMsg)
+ throw new Error(errMsg)
+}
+
+function NameTrans(name) {
+ return {
+ "recentlyWorkingFolders": "WorkingFolder",
+ "board": "Board",
+ "scenario": "Scenario",
+ }[name.replace("History", "")]
+}
+
+class EventBase extends Component {
+ constructor(props) {
+ super(props)
+ this.funRegister = {}
+ this.funRegisterID = 0
+ this.eventName = {
+ scenarioDataUpdate: 'scenarioDataUpdate'
+ }
+ }
+
+ register = (eventName, fun) => {
+ if (this.funRegister.hasOwnProperty(eventName)) {
+ this.funRegisterID++
+ this.funRegister[eventName][this.funRegisterID] = fun
+ return this.funRegisterID
+ }
+ }
+
+ unregister = (eventName, funRegisterID) => {
+ if (this.funRegister.hasOwnProperty(eventName)) {
+ if (this.funRegister[eventName].hasOwnProperty(funRegisterID)) {
+ delete this.funRegister[eventName][funRegisterID]
+ }
+ }
+ }
+
+}
+
+
+export class XMLLayer extends EventBase {
+ constructor(helper) {
+ super();
+ this.helper = helper
+ this.funRegister = {
+ onScenarioLoad: []
+ }
+ }
+
+ #parseXML = (XMLText) => {
+ return (new DOMParser()).parseFromString(XMLText, "text/xml")
+ }
+
+ #validateBoardXMLText = boardXMLText => {
+ // call inside
+ try {
+ let boardXML = this.#parseXML(boardXMLText, "text/xml");
+ return !!boardXML.querySelector("BIOS_INFO")
+ } catch (e) {
+ return false
+ }
+ }
+
+ #validateScenarioXMLText = (scenarioXMLText) => {
+ // call inside
+ try {
+ let scenarioXML = this.#parseXML(scenarioXMLText);
+ return !!scenarioXML.querySelector("acrn-config")
+ } catch (e) {
+ return false
+ }
+ }
+
+ loadBoard = async (boardXMLPath) => {
+ // call by program
+ let boardXMLText = await this.helper.read(await this.helper.resolveHome(boardXMLPath))
+ if (this.#validateBoardXMLText(boardXMLText)) {
+ let PCIDevices = this.getPCIDevice(boardXMLText)
+ return {boardXMLText, PCIDevices}
+ } else {
+ alert('Board XML Error!')
+ return false
+ }
+ }
+
+ loadScenario = async (scenarioXMLPath) => {
+ // call by program
+ // load scenario data from xml file
+ let scenarioXMLText = await this.helper.read(await this.helper.resolveHome(scenarioXMLPath))
+ if (!this.#validateScenarioXMLText(scenarioXMLText)) {
+ ThrowError('Scenario XML Error!')
+ }
+
+ return this.helper.convertXMLTextToObj(scenarioXMLText)['acrn-config']
+ }
+
+ getPCIDevice = (boardXMLText) => {
+ let pci_devices = this.#parseXML(boardXMLText).querySelector("PCI_DEVICE").textContent
+ // Remove Region line
+ pci_devices = pci_devices.replace(/Region.+\s+/g, '\n')
+ // Remove Space
+ pci_devices = pci_devices.replace(/[\n\t]+/g, '\n')
+ // Split by \n
+ pci_devices = pci_devices.split('\n')
+
+ return _.uniq(pci_devices)
+ }
+
+ saveBoard = (boardFileWritePath, boardData) => {
+ this.helper.save(boardFileWritePath, boardData)
+ }
+
+ saveScenario = (scenarioWritePath, scenarioData) => {
+ // call by program
+ console.log(scenarioData)
+ const scenarioXML = this.helper.convertObjToXML({'acrn-config': scenarioData})
+ console.log(scenarioXML)
+ // debugger
+ this.helper.save(scenarioWritePath, scenarioXML)
+ }
+}
+
+
+export class ProgramLayer extends EventBase {
+ constructor(helper, instanceOfXMLLayer) {
+ super()
+ this.helper = helper
+ this.vmID = 0
+ this.scenarioData = {}
+ this.initScenario()
+ this.xmlLayer = instanceOfXMLLayer
+ this.funRegister = {
+ scenarioDataUpdate: []
+ }
+ }
+
+ initScenario = () => {
+ this.vmID = 0
+ this.scenarioData = {
+ hv: {},
+ vm: {
+ PRE_LAUNCHED_VM: [],
+ SERVICE_VM: [],
+ POST_LAUNCHED_VM: [],
+ }
+ }
+ }
+
+ onScenarioDataUpdateEvent() {
+ this.funRegister.scenarioDataUpdate.map((f) => f())
+ }
+
+ newScenario = (preLaunchedVM, serviceVM, postLaunchedVM) => {
+ // call by view
+ this.initScenario()
+ for (let i = 0; i < preLaunchedVM; i++) {
+ this.addVM('PRE_LAUNCHED_VM')
+ }
+ for (let i = 0; i < serviceVM; i++) {
+ this.addVM('SERVICE_VM')
+ }
+ for (let i = 0; i < postLaunchedVM; i++) {
+ this.addVM('POST_LAUNCHED_VM')
+ }
+ this.onScenarioDataUpdateEvent()
+ }
+
+
+ addVM = (VMType, vmData = {}) => {
+ // call by inside and view
+ // if provide VMType and vmData at same time
+ // function will set VMType to vmData
+ let vm = vmData
+ let usedVMID = false;
+
+ // check VMType
+ let vmType = VMType;
+ if (!_.isString(vmType) || _.isEmpty(vmType)) {
+ // vmType is empty or not correct
+ if (vm.hasOwnProperty('load_order')) {
+ vmType = vm.load_order;
+ } else {
+ ThrowError('No VMType Set')
+ }
+ } else {
+ if (vm.hasOwnProperty('load_order') && vm.load_order !== vmType) {
+ console.warn("vmType and vmData provide load_order at same time, set vmData's load_order to vmType now")
+ }
+ vm.load_order = vmType
+ }
+
+
+ // check and validate vm name
+ if (!vm.hasOwnProperty('name') || !_.isString(vm.name) || _.isEmpty(vm.name)) {
+ vm.name = 'VM' + this.vmID
+ usedVMID = true
+ }
+
+ vm['@id'] = this.vmID
+ this.vmID++
+
+ // add to config
+ this.scenarioData.vm[vmType].push(vm)
+ this.onScenarioDataUpdateEvent()
+ }
+
+ deleteVM = (vmID) => {
+ // call by view
+ for (let vmType in this.scenarioData.vm) {
+ this.scenarioData.vm[vmType].map((vmConfig, vmIndex) => {
+ if (vmConfig['@id'] === vmID) {
+ this.scenarioData.vm[vmType].splice(vmIndex, 1)
+ }
+ })
+ }
+ this.onScenarioDataUpdateEvent()
+ }
+
+ loadBoard = async (WorkingFolder, boardXMLPath) => {
+ // call by view
+ let boardData = await this.xmlLayer.loadBoard(boardXMLPath)
+ if (boardData === false) {
+ return false
+ }
+ let {boardXMLText, PCIDevices} = boardData
+
+ // get new board file name
+ let newBoardFileName = await path.basename(boardXMLPath)
+ let cut = 0
+ if (_.endsWith(newBoardFileName.toLowerCase(), '.xml')) {
+ cut = '.xml'.length
+ }
+ if (_.endsWith(newBoardFileName.toLowerCase(), '.board.xml')) {
+ cut = '.board.xml'.length
+ }
+ newBoardFileName = newBoardFileName.slice(0, newBoardFileName.length - cut)
+ newBoardFileName = newBoardFileName + '.board.xml'
+
+ // new board file save path
+ const boardFileWritePath = await path.join(await this.helper.resolveHome(WorkingFolder), newBoardFileName)
+
+ // remove current working folder old Board File first
+ await this.removeOldBoardFile(WorkingFolder)
+
+ // save board file to working director
+ this.xmlLayer.saveBoard(boardFileWritePath, boardXMLText)
+ // get shownName
+ let shownName = await path.join(WorkingFolder, newBoardFileName)
+ console.log({shownName, boardXMLText, PCIDevices})
+ return {shownName, boardXMLText, PCIDevices}
+ }
+
+ loadScenario = async (scenarioXMLPath) => {
+ // call by view
+ let scenarioConfig = await this.xmlLayer.loadScenario(scenarioXMLPath)
+ this.initScenario()
+ this.scenarioData.hv = scenarioConfig.hv;
+ scenarioConfig.vm.map((vmConfig, index) => {
+ let vmType = vmConfig.load_order
+ if (!this.scenarioData.vm.hasOwnProperty(vmType)) {
+ try {
+ ThrowError('VM @id=' + index + ' VMType Does Not Exist')
+ } catch (e) {
+
+ }
+ } else {
+ //fix pci_devs is object issue
+ if (vmConfig.hasOwnProperty("pci_devs") && vmConfig.pci_devs.hasOwnProperty("pci_dev") && _.isString(vmConfig.pci_devs.pci_dev)) {
+ vmConfig.pci_devs.pci_dev = [vmConfig.pci_devs.pci_dev]
+ }
+ this.addVM(vmType, vmConfig)
+ }
+ })
+ this.onScenarioDataUpdateEvent()
+ }
+
+ getOriginScenarioData = () => {
+ // call by inside
+ let originScenario = _.cloneDeep(this.scenarioData)
+ originScenario.vm = originScenario.vm.PRE_LAUNCHED_VM.concat(
+ originScenario.vm.SERVICE_VM,
+ originScenario.vm.POST_LAUNCHED_VM
+ )
+ return originScenario
+ }
+
+ async removeOldBoardFile(WorkingFolder) {
+ let files = await this.helper.list(await this.helper.resolveHome(WorkingFolder))
+ files.map((filename) => {
+ if (_.endsWith(filename, '.board.xml')) {
+ this.helper.remove(filename)
+ }
+ })
+ }
+
+
+ saveScenario = async (WorkingFolder) => {
+ // call by view
+ let originScenarioData = this.getOriginScenarioData()
+ let filename = 'scenario.xml'
+ let scenarioWritePath = await path.join(await this.helper.resolveHome(WorkingFolder), filename)
+ this.xmlLayer.saveScenario(scenarioWritePath, originScenarioData)
+ // noinspection UnnecessaryLocalVariableJS
+ let shownPath = await path.join(WorkingFolder, filename)
+ return shownPath
+ }
+
+}
+
+export class Configurator extends EventBase {
+ // get data from Program
+ // convert it to view data
+ constructor(helper) {
+ super()
+
+ this.WorkingFolder = this.#getURLParam('WorkingFolder')
+
+ this.helper = helper
+ this.XMLLayer = new XMLLayer(this.helper)
+ this.programLayer = new ProgramLayer(this.helper, this.XMLLayer)
+
+ this.vmSchemas = this.Schemas()
+ this.hvSchema = this.vmSchemas.HV
+ delete this.vmSchemas.HV
+
+ this.updateSchema()
+ }
+
+ #getURLParam(key) {
+ let hash = location.hash
+ let params = hash.substring(hash.indexOf('?'))
+ params = queryString.parse(params)
+ return params[key]
+ }
+
+ #buildPageParams(url, queryParams = {}) {
+ let data = {pathname: url}
+ if (queryParams) {
+ data.search = createSearchParams(queryParams).toString()
+ }
+ return data;
+ }
+
+ settingWorkingFolder = (WorkingFolder) => {
+ this.WorkingFolder = WorkingFolder
+ return this.#buildPageParams('./config', {WorkingFolder})
+ }
+
+ updateSchema = () => {
+ let listingFunctions = [this.ivshmemEnum]
+ listingFunctions.forEach((func) => {
+ func()
+ this.programLayer.register("scenarioDataUpdate", func)
+ })
+ }
+
+ ivshmemEnum = () => {
+ let odata = this.programLayer.getOriginScenarioData()
+ let vmNames = odata.vm.map((vmData) => {
+ return vmData.name
+ })
+ if (vmNames.length === 0) {
+ vmNames = ['']
+ }
+ this.hvSchema.basic.definitions.VMNameType.enum = vmNames
+ }
+
+
+ loadBoard = async (boardXMLPath, callback) => {
+ let boardData = await this.programLayer.loadBoard(this.WorkingFolder, boardXMLPath)
+ if (boardData === false) {
+ await this.removeHistory('board', boardXMLPath)
+ return
+ }
+ let {shownName, boardXMLText, PCIDevices} = boardData
+ scenario.definitions.PCIDevsConfiguration.properties.pci_dev.items.enum = PCIDevices
+ Object.keys(this.vmSchemas).map((VMTypeKey) => {
+ Object.keys(this.vmSchemas[VMTypeKey]).map((configLevelKey) => {
+ this.vmSchemas[VMTypeKey][configLevelKey].definitions.PCIDevsConfiguration.properties.pci_dev.items.enum = PCIDevices
+ })
+ })
+ try {
+ let new_scenario = getNewSchema(boardXMLText)
+ scenario.definitions = new_scenario.definitions;
+ Object.keys(this.vmSchemas).map((VMTypeKey) => {
+ Object.keys(this.vmSchemas[VMTypeKey]).map((configLevelKey) => {
+ this.vmSchemas[VMTypeKey][configLevelKey].definitions = new_scenario.definitions
+ })
+ })
+ Object.keys(this.hvSchema).map((configLevelKey) => {
+ this.hvSchema[configLevelKey].definitions = new_scenario.definitions;
+ })
+ } catch (e) {
+
+ }
+
+ callback(shownName, boardXMLText)
+ }
+
+ async getHistory(key) {
+ let p = await invoke("get_history", {historyType: NameTrans(key)})
+ console.log("p", p);
+ return JSON.parse(p)
+ }
+
+ async addHistory(key, historyPath) {
+ await invoke("add_history", {historyType: NameTrans(key), path: historyPath})
+ return Promise.resolve()
+ }
+
+ async setHistory(key, history) {
+ return Promise.resolve()
+ }
+
+ async removeHistory(key, historyPath) {
+ let history = await this.getHistory(key)
+ let index = history.indexOf(historyPath);
+ if (index > -1) {
+ history.splice(index, 1);
+ }
+ return this.setHistory(key, history)
+ }
+
+
+ saveScenario = async () => {
+ let shownPath = await this.programLayer.saveScenario(this.WorkingFolder)
+ return this.addHistory('scenario', shownPath)
+ }
+
+
+ static #getSchema(prefix) {
+ let bo = scenario.definitions[prefix + "BasicConfigType"]
+ let basic = _.cloneDeep(scenario);
+ basic.type = bo.type;
+ basic.required = bo.required;
+ basic.properties = bo.properties;
+
+ let ao = scenario.definitions[prefix + "AdvancedConfigType"]
+ let advanced = _.cloneDeep(scenario);
+ advanced.type = ao.type;
+ advanced.required = ao.required;
+ advanced.properties = ao.properties;
+
+ return {basic, advanced}
+ }
+
+ Schemas() {
+ let prefixData = {
+ HV: 'HV',
+ PRE_LAUNCHED_VM: 'PreLaunchedVM',
+ SERVICE_VM: 'ServiceVM',
+ POST_LAUNCHED_VM: 'PostLaunchedVM'
+ }
+ for (let key in prefixData) {
+ prefixData[key] = Configurator.#getSchema(prefixData[key])
+ }
+ return prefixData
+ }
+
+ log() {
+ this.helper.log(...arguments)
+ }
+}
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/.gitignore b/misc/config_tools/configurator/src/lib/bs4rjsf/.gitignore
new file mode 100644
index 000000000..4e57eef88
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/.gitignore
@@ -0,0 +1,2 @@
+*.js
+*.js.map
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/AddButton/AddButton.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/AddButton/AddButton.tsx
new file mode 100644
index 000000000..cc3ad1086
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/AddButton/AddButton.tsx
@@ -0,0 +1,13 @@
+import React from "react";
+
+import {AddButtonProps} from "@rjsf/core";
+import Button from "react-bootstrap/Button";
+import {BsPlus} from "react-icons/bs";
+
+const AddButton: React.FC = props => (
+
+
+
+);
+
+export default AddButton;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/AddButton/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/AddButton/index.ts
new file mode 100644
index 000000000..d8452a62d
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/AddButton/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./AddButton";
+export * from "./AddButton";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/ArrayFieldTemplate/ArrayFieldTemplate.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/ArrayFieldTemplate/ArrayFieldTemplate.tsx
new file mode 100644
index 000000000..fbe9b2ed7
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/ArrayFieldTemplate/ArrayFieldTemplate.tsx
@@ -0,0 +1,210 @@
+import React from "react";
+import {utils} from "@rjsf/core";
+import Row from "react-bootstrap/Row";
+import Col from "react-bootstrap/Col";
+import Container from "react-bootstrap/Container";
+import {ArrayFieldTemplateProps, IdSchema} from "@rjsf/core";
+
+import AddButton from "../AddButton/AddButton";
+import IconButton from "../IconButton/IconButton";
+
+const {isMultiSelect, getDefaultRegistry} = utils;
+
+const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
+ const {schema, registry = getDefaultRegistry()} = props;
+
+ if (isMultiSelect(schema, registry.rootSchema)) {
+ return ;
+ } else {
+ return ;
+ }
+};
+
+type ArrayFieldTitleProps = {
+ TitleField: any;
+ idSchema: IdSchema;
+ title: string;
+ required: boolean;
+};
+
+const ArrayFieldTitle = ({
+ TitleField,
+ idSchema,
+ title,
+ required,
+ }: ArrayFieldTitleProps) => {
+ if (!title) {
+ return null;
+ }
+
+ const id = `${idSchema.$id}__title`;
+ return ;
+};
+
+type ArrayFieldDescriptionProps = {
+ DescriptionField: any;
+ idSchema: IdSchema;
+ description: string;
+};
+
+const ArrayFieldDescription = ({
+ DescriptionField,
+ idSchema,
+ description,
+ }: ArrayFieldDescriptionProps) => {
+ if (!description) {
+ return null;
+ }
+
+ const id = `${idSchema.$id}__description`;
+ return ;
+};
+
+// Used in the two templates
+const DefaultArrayItem = (props: any) => {
+ const btnStyle = {
+ flex: 1,
+ paddingLeft: 6,
+ paddingRight: 6,
+ fontWeight: "bold",
+ };
+ return (
+
+
+ {props.children}
+
+
+ {props.hasToolbar && (
+
+ {(props.hasMoveUp || props.hasMoveDown) && (
+
+
+
+ )}
+
+ {(props.hasMoveUp || props.hasMoveDown) && (
+
+
+
+ )}
+
+ {props.hasRemove && (
+
+
+
+ )}
+
+ )}
+
+
+
+ );
+};
+
+const DefaultFixedArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
+ return (
+
+
+
+ {(props.uiSchema["ui:description"] || props.schema.description) && (
+
+ {props.uiSchema["ui:description"] || props.schema.description}
+
+ )}
+
+
+ {props.items && props.items.map(DefaultArrayItem)}
+
+
+ {props.canAdd && (
+
+ )}
+
+ );
+};
+
+const DefaultNormalArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
+ return (
+
+
+
+
+
+ {(props.uiSchema["ui:description"] || props.schema.description) && (
+
+ )}
+
+
+ {props.items && props.items.map(p => DefaultArrayItem(p))}
+
+ {props.canAdd && (
+
+
+
+
+
+
+
+ )}
+
+
+
+
+ );
+};
+
+export default ArrayFieldTemplate;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/ArrayFieldTemplate/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/ArrayFieldTemplate/index.ts
new file mode 100644
index 000000000..a4dd3bbba
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/ArrayFieldTemplate/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./ArrayFieldTemplate";
+export * from "./ArrayFieldTemplate";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/CheckboxWidget/CheckboxWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/CheckboxWidget/CheckboxWidget.tsx
new file mode 100644
index 000000000..aa79b1664
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/CheckboxWidget/CheckboxWidget.tsx
@@ -0,0 +1,50 @@
+import React from "react";
+
+import {WidgetProps} from "@rjsf/core";
+import Form from "react-bootstrap/Form";
+
+const CheckboxWidget = (props: WidgetProps) => {
+ const {
+ id,
+ value,
+ required,
+ disabled,
+ readonly,
+ label,
+ schema,
+ autofocus,
+ onChange,
+ onBlur,
+ onFocus,
+ } = props;
+
+ const _onChange = ({
+ target: {checked},
+ }: React.FocusEvent) => onChange(checked);
+ const _onBlur = ({
+ target: {checked},
+ }: React.FocusEvent) => onBlur(id, checked);
+ const _onFocus = ({
+ target: {checked},
+ }: React.FocusEvent) => onFocus(id, checked);
+
+ const desc = label || schema.description;
+ return (
+
+
+
+ );
+};
+
+export default CheckboxWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/CheckboxWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/CheckboxWidget/index.ts
new file mode 100644
index 000000000..1090bf22c
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/CheckboxWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./CheckboxWidget";
+export * from "./CheckboxWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/CheckboxesWidget/CheckboxesWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/CheckboxesWidget/CheckboxesWidget.tsx
new file mode 100644
index 000000000..2b34bf3f8
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/CheckboxesWidget/CheckboxesWidget.tsx
@@ -0,0 +1,103 @@
+import React from "react";
+import Form from "react-bootstrap/Form";
+import {WidgetProps} from "@rjsf/core";
+
+const selectValue = (value: any, selected: any, all: any) => {
+ const at = all.indexOf(value);
+ const updated = selected.slice(0, at).concat(value, selected.slice(at));
+
+ // As inserting values at predefined index positions doesn't work with empty
+ // arrays, we need to reorder the updated selection to match the initial order
+ return updated.sort((a: any, b: any) => all.indexOf(a) > all.indexOf(b));
+};
+
+const deselectValue = (value: any, selected: any) => {
+ return selected.filter((v: any) => v !== value);
+};
+
+const CheckboxesWidget = ({
+ schema,
+ label,
+ id,
+ disabled,
+ options,
+ value,
+ autofocus,
+ readonly,
+ required,
+ onChange,
+ onBlur,
+ onFocus,
+ }: WidgetProps) => {
+ const {enumOptions, enumDisabled, inline} = options;
+
+ const _onChange = (option: any) => ({
+ target: {checked},
+ }: React.ChangeEvent) => {
+ const all = (enumOptions as any).map(({value}: any) => value);
+
+ if (checked) {
+ onChange(selectValue(option.value, value, all));
+ } else {
+ onChange(deselectValue(option.value, value));
+ }
+ };
+
+ const _onBlur = ({target: {value}}: React.FocusEvent) =>
+ onBlur(id, value);
+ const _onFocus = ({
+ target: {value},
+ }: React.FocusEvent) => onFocus(id, value);
+
+ return (
+ <>
+ {label || schema.title}
+
+ {(enumOptions as any).map((option: any, index: number) => {
+ const checked = value.indexOf(option.value) !== -1;
+ const itemDisabled =
+ enumDisabled && (enumDisabled as any).indexOf(option.value) != -1;
+
+ return inline ? (
+
+
+ ) : (
+
+
+ );
+ })}
+
+ >
+ );
+};
+
+export default CheckboxesWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/CheckboxesWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/CheckboxesWidget/index.ts
new file mode 100644
index 000000000..0a330f7ce
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/CheckboxesWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./CheckboxesWidget";
+export * from "./CheckboxesWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/ColorWidget/ColorWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/ColorWidget/ColorWidget.tsx
new file mode 100644
index 000000000..3cca6122e
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/ColorWidget/ColorWidget.tsx
@@ -0,0 +1,10 @@
+import React from "react";
+import {WidgetProps} from "@rjsf/core";
+
+const ColorWidget = (props: WidgetProps) => {
+ const {registry} = props;
+ const {TextWidget} = registry.widgets;
+ return ;
+};
+
+export default ColorWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/ColorWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/ColorWidget/index.ts
new file mode 100644
index 000000000..eecf91373
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/ColorWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./ColorWidget";
+export * from "./ColorWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/DateTimeWidget/DateTimeWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/DateTimeWidget/DateTimeWidget.tsx
new file mode 100644
index 000000000..74087e299
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/DateTimeWidget/DateTimeWidget.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import {utils, WidgetProps} from "@rjsf/core";
+
+const {localToUTC, utcToLocal} = utils;
+
+const DateTimeWidget = (props: WidgetProps) => {
+ const {registry} = props;
+ const {TextWidget} = registry.widgets;
+ const value = utcToLocal(props.value);
+ const onChange = (value: any) => {
+ props.onChange(localToUTC(value));
+ };
+
+ return (
+
+ );
+};
+
+export default DateTimeWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/DateTimeWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/DateTimeWidget/index.ts
new file mode 100644
index 000000000..f412f0cb8
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/DateTimeWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./DateTimeWidget";
+export * from "./DateTimeWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/DateWidget/DateWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/DateWidget/DateWidget.tsx
new file mode 100644
index 000000000..d8b99a62c
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/DateWidget/DateWidget.tsx
@@ -0,0 +1,15 @@
+import React from "react";
+import {WidgetProps} from "@rjsf/core";
+
+const DateWidget = (props: WidgetProps) => {
+ const {registry} = props;
+ const {TextWidget} = registry.widgets;
+ return (
+
+ );
+};
+
+export default DateWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/DateWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/DateWidget/index.ts
new file mode 100644
index 000000000..3260f890b
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/DateWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./DateWidget";
+export * from "./DateWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/DescriptionField/DescriptionField.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/DescriptionField/DescriptionField.tsx
new file mode 100644
index 000000000..1fca34ecf
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/DescriptionField/DescriptionField.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import {FieldProps} from "@rjsf/core";
+
+export interface DescriptionFieldProps extends Partial {
+ description?: string;
+}
+
+const DescriptionField = ({description}: Partial) => {
+ if (description) {
+ return ;
+ }
+
+ return null;
+};
+
+export default DescriptionField;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/DescriptionField/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/DescriptionField/index.ts
new file mode 100644
index 000000000..e12b66a3b
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/DescriptionField/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./DescriptionField";
+export * from "./DescriptionField";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/EmailWidget/EmailWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/EmailWidget/EmailWidget.tsx
new file mode 100644
index 000000000..581ce00c3
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/EmailWidget/EmailWidget.tsx
@@ -0,0 +1,10 @@
+import React from "react";
+import {WidgetProps} from "@rjsf/core";
+
+const EmailWidget = (props: WidgetProps) => {
+ const {registry} = props;
+ const {TextWidget} = registry.widgets;
+ return ;
+};
+
+export default EmailWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/EmailWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/EmailWidget/index.ts
new file mode 100644
index 000000000..4537a21aa
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/EmailWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./EmailWidget";
+export * from "./EmailWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/ErrorList/ErrorList.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/ErrorList/ErrorList.tsx
new file mode 100644
index 000000000..6a9a1bf04
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/ErrorList/ErrorList.tsx
@@ -0,0 +1,25 @@
+import React from "react";
+
+import Card from "react-bootstrap/Card";
+import ListGroup from "react-bootstrap/ListGroup";
+
+import {ErrorListProps} from "@rjsf/core";
+
+const ErrorList = ({errors}: ErrorListProps) => (
+
+ Errors
+
+
+ {errors.map((error, i: number) => {
+ return (
+
+ {error.stack}
+
+ );
+ })}
+
+
+
+);
+
+export default ErrorList;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/ErrorList/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/ErrorList/index.ts
new file mode 100644
index 000000000..7626610c6
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/ErrorList/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./ErrorList";
+export * from "./ErrorList";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/FieldTemplate/FieldTemplate.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/FieldTemplate/FieldTemplate.tsx
new file mode 100644
index 000000000..bd318d9f6
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/FieldTemplate/FieldTemplate.tsx
@@ -0,0 +1,171 @@
+import React from "react";
+
+import {FieldTemplateProps} from "@rjsf/core";
+
+import Form from "react-bootstrap/Form";
+import ListGroup from "react-bootstrap/ListGroup";
+
+import WrapIfAdditional from "./WrapIfAdditional";
+
+import {OverlayTrigger, Popover} from "react-bootstrap";
+import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
+import {faCircleInfo, faCircleExclamation} from "@fortawesome/free-solid-svg-icons";
+import _ from "lodash";
+
+const FieldTemplate = (
+ {
+ id,
+ children,
+ displayLabel,
+ rawErrors = [],
+ rawHelp,
+ rawDescription,
+ classNames,
+ disabled,
+ label,
+ onDropPropertyClick,
+ onKeyChange,
+ readonly,
+ required,
+ schema,
+ uiSchema
+ }: FieldTemplateProps) => {
+
+ let descLabel = (uiSchema.hasOwnProperty("ui:descLabel") && uiSchema["ui:descLabel"] === true)
+ let descWithChildren
+ let showLabel = _.endsWith(id, 'IVSHMEM_VM_0_VBDF') || _.endsWith(id, 'IVSHMEM_VM_0_VM_NAME') || (
+ id.indexOf('vuart_connection') > 0 && (_.endsWith(id, 'vm_name') || _.endsWith(id, 'io_port'))
+ )
+ let dlva = uiSchema.hasOwnProperty("ui:descLabelAli") && uiSchema["ui:descLabelAli"] === 'V'
+ if (displayLabel && rawDescription) {
+ let desc
+ const icon = rawErrors.length > 0 ? faCircleExclamation : faCircleInfo;
+ if (descLabel) {
+ if (dlva) {
+ desc =
+
+ 0 ? "text-danger" : "text-muted"}
+ dangerouslySetInnerHTML={{__html: rawDescription}}/>
+
+
+ }>
+
+
0 ? "text-danger" : "")}>
+ {uiSchema["ui:title"] || schema.title || label}
+ {(label || uiSchema["ui:title"] || schema.title) && required ? "*" : null}
+
+
+
+ descWithChildren =
+ {desc}
+
+ {children}
+
+
+ } else {
+ desc =
+
+ 0 ? "text-danger" : "text-muted"}
+ dangerouslySetInnerHTML={{__html: rawDescription}}/>
+
+
+ }>
+
+
0 ? "text-danger" : "")}>
+ {uiSchema["ui:title"] || schema.title || label}
+ {(label || uiSchema["ui:title"] || schema.title) && required ? "*" : null}
+
+
+
+ descWithChildren =
+ {desc}
+
+ {children}
+
+
+ }
+ } else {
+ desc =
+
+ 0 ? "text-danger" : "text-muted"}
+ dangerouslySetInnerHTML={{__html: rawDescription}}/>
+
+
+ }>
+
+ 0 ? "red" : ""}
+ />
+
+
+ descWithChildren =
+ {desc}
+
+ {children}
+
+
+ }
+
+
+ } else {
+ descWithChildren = children
+ }
+ return (
+
+
+ {descWithChildren}
+ {rawErrors.length > 0 && (
+
+ {rawErrors.map((error: string) => {
+ return (
+
+
+ {error}
+
+
+ );
+ })}
+
+ )}
+ {rawHelp && (
+ 0 ? "text-danger" : "text-muted"}
+ id={id}>
+ {rawHelp}
+
+ )}
+
+
+ );
+};
+
+export default FieldTemplate;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/FieldTemplate/WrapIfAdditional.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/FieldTemplate/WrapIfAdditional.tsx
new file mode 100644
index 000000000..fb2310da9
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/FieldTemplate/WrapIfAdditional.tsx
@@ -0,0 +1,83 @@
+import React from "react";
+
+import {utils} from "@rjsf/core";
+import {JSONSchema7} from "json-schema";
+
+import Row from "react-bootstrap/Row";
+import Col from "react-bootstrap/Col";
+import Form from "react-bootstrap/Form";
+
+import IconButton from "../IconButton/IconButton";
+
+const {ADDITIONAL_PROPERTY_FLAG} = utils;
+
+type WrapIfAdditionalProps = {
+ children: React.ReactElement;
+ classNames: string;
+ disabled: boolean;
+ id: string;
+ label: string;
+ onDropPropertyClick: (index: string) => (event?: any) => void;
+ onKeyChange: (index: string) => (event?: any) => void;
+ readonly: boolean;
+ required: boolean;
+ schema: JSONSchema7;
+};
+
+const WrapIfAdditional = ({
+ children,
+ disabled,
+ id,
+ label,
+ onDropPropertyClick,
+ onKeyChange,
+ readonly,
+ required,
+ schema,
+ }: WrapIfAdditionalProps) => {
+ const keyLabel = `${label} Key`; // i18n ?
+ const additional = schema.hasOwnProperty(ADDITIONAL_PROPERTY_FLAG);
+
+ if (!additional) {
+ return children;
+ }
+
+ const handleBlur = ({target}: React.FocusEvent) =>
+ onKeyChange(target.value);
+
+ return (
+
+
+
+ {keyLabel}
+
+
+
+
+ {children}
+
+
+
+
+
+ );
+};
+
+export default WrapIfAdditional;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/FieldTemplate/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/FieldTemplate/index.ts
new file mode 100644
index 000000000..8d6d06eb6
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/FieldTemplate/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./FieldTemplate";
+export * from "./FieldTemplate";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/Fields/Fields.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/Fields/Fields.ts
new file mode 100644
index 000000000..adb32d9f2
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/Fields/Fields.ts
@@ -0,0 +1,7 @@
+import DescriptionField from "../DescriptionField/DescriptionField";
+import TitleField from "../TitleField/TitleField";
+
+export default {
+ DescriptionField,
+ TitleField,
+};
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/Fields/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/Fields/index.ts
new file mode 100644
index 000000000..faa3dcbc3
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/Fields/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./Fields";
+export * from "./Fields";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/FileWidget/FileWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/FileWidget/FileWidget.tsx
new file mode 100644
index 000000000..511c28e44
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/FileWidget/FileWidget.tsx
@@ -0,0 +1,10 @@
+import React from "react";
+import {WidgetProps} from "@rjsf/core";
+
+const FileWidget = (props: WidgetProps) => {
+ const {registry} = props;
+ const {TextWidget} = registry.widgets;
+ return ;
+};
+
+export default FileWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/FileWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/FileWidget/index.ts
new file mode 100644
index 000000000..558890e80
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/FileWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./FileWidget";
+export * from "./FileWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/Form/Form.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/Form/Form.tsx
new file mode 100644
index 000000000..971c6b565
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/Form/Form.tsx
@@ -0,0 +1,10 @@
+import {withTheme, FormProps} from "@rjsf/core";
+
+import Theme from "../Theme";
+import {StatelessComponent} from "react";
+
+const Form:
+ | React.ComponentClass>
+ | StatelessComponent> = withTheme(Theme);
+
+export default Form;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/Form/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/Form/index.ts
new file mode 100644
index 000000000..243aa7b6d
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/Form/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./Form";
+export * from "./Form";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/IconButton/IconButton.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/IconButton/IconButton.tsx
new file mode 100644
index 000000000..062708166
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/IconButton/IconButton.tsx
@@ -0,0 +1,33 @@
+import React from "react";
+import Button, {ButtonProps} from "react-bootstrap/Button";
+import {IoIosRemove} from "react-icons/io";
+import {GrAdd} from "react-icons/gr";
+import {AiOutlineArrowUp, AiOutlineArrowDown} from "react-icons/ai";
+
+const mappings: any = {
+ remove: ,
+ plus: ,
+ "arrow-up": ,
+ "arrow-down": ,
+};
+
+type IconButtonProps = ButtonProps & {
+ icon: string;
+ variant?: ButtonProps["variant"];
+ className?: string;
+ tabIndex?: number;
+ style?: any;
+ disabled?: any;
+ onClick?: any;
+};
+
+const IconButton = (props: IconButtonProps) => {
+ const {icon, className, ...otherProps} = props;
+ return (
+
+ {mappings[icon]}
+
+ );
+};
+
+export default IconButton;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/IconButton/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/IconButton/index.ts
new file mode 100644
index 000000000..36cd49eb5
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/IconButton/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./IconButton";
+export * from "./IconButton";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/ObjectFieldTemplate/ObjectFieldTemplate.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/ObjectFieldTemplate/ObjectFieldTemplate.tsx
new file mode 100644
index 000000000..8f99c2465
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/ObjectFieldTemplate/ObjectFieldTemplate.tsx
@@ -0,0 +1,81 @@
+import React from "react";
+
+import Row from "react-bootstrap/Row";
+import Col from "react-bootstrap/Col";
+import Container from "react-bootstrap/Container";
+
+import {ObjectFieldTemplateProps} from "@rjsf/core";
+import {utils} from "@rjsf/core";
+
+import AddButton from "../AddButton/AddButton";
+
+const {canExpand} = utils;
+
+const ObjectFieldTemplate = (
+ {
+ DescriptionField,
+ description,
+ TitleField,
+ title,
+ properties,
+ required,
+ uiSchema,
+ idSchema,
+ schema,
+ formData,
+ onAddClick,
+ disabled,
+ readonly,
+ }: ObjectFieldTemplateProps) => {
+ let content = properties.map((element: any, index: number) => {
+ return (
+
+ {element.content}
+
+ );
+ })
+ let expand = canExpand(schema, uiSchema, formData) ? (
+
+
+
+
+
+ ) : null
+
+ let container =
+ {content}
+ {expand}
+
+
+ return (
+ <>
+ {(uiSchema["ui:title"] || title) && (
+
+ )}
+ {description && (
+
+ )}
+ {container}
+ >
+ );
+};
+
+export default ObjectFieldTemplate;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/ObjectFieldTemplate/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/ObjectFieldTemplate/index.ts
new file mode 100644
index 000000000..1c56e5023
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/ObjectFieldTemplate/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./ObjectFieldTemplate";
+export * from "./ObjectFieldTemplate";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/PasswordWidget/PasswordWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/PasswordWidget/PasswordWidget.tsx
new file mode 100644
index 000000000..ad187fb55
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/PasswordWidget/PasswordWidget.tsx
@@ -0,0 +1,55 @@
+import React from "react";
+
+import Form from "react-bootstrap/Form";
+
+import {WidgetProps} from "@rjsf/core";
+
+const PasswordWidget = ({
+ id,
+ required,
+ readonly,
+ disabled,
+ value,
+ label,
+ onFocus,
+ onBlur,
+ onChange,
+ options,
+ autofocus,
+ schema,
+ rawErrors = [],
+ }: WidgetProps) => {
+ const _onChange = ({
+ target: {value},
+ }: React.ChangeEvent) =>
+ onChange(value === "" ? options.emptyValue : value);
+ const _onBlur = ({target: {value}}: React.FocusEvent) =>
+ onBlur(id, value);
+ const _onFocus = ({
+ target: {value},
+ }: React.FocusEvent) => onFocus(id, value);
+
+ return (
+
+ 0 ? "text-danger" : ""}>
+ {label || schema.title}
+ {(label || schema.title) && required ? "*" : null}
+
+ 0 ? "is-invalid" : ""}
+ required={required}
+ disabled={disabled}
+ readOnly={readonly}
+ type="password"
+ value={value ? value : ""}
+ onFocus={_onFocus}
+ onBlur={_onBlur}
+ onChange={_onChange}
+ />
+
+ );
+};
+
+export default PasswordWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/PasswordWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/PasswordWidget/index.ts
new file mode 100644
index 000000000..ad9455bc9
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/PasswordWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./PasswordWidget";
+export * from "./PasswordWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/RadioWidget/RadioWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/RadioWidget/RadioWidget.tsx
new file mode 100644
index 000000000..80fa2594e
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/RadioWidget/RadioWidget.tsx
@@ -0,0 +1,70 @@
+import React from "react";
+
+import Form from "react-bootstrap/Form";
+
+import {WidgetProps} from "@rjsf/core";
+
+const RadioWidget = (
+ {
+ id,
+ schema,
+ options,
+ value,
+ required,
+ disabled,
+ readonly,
+ label,
+ onChange,
+ onBlur,
+ onFocus,
+ uiSchema,
+ }: WidgetProps) => {
+ const {enumOptions, enumDisabled} = options;
+
+ const _onChange = ({target: {value},}: React.ChangeEvent) =>
+ onChange(schema.type == "boolean" ? value !== "false" : value);
+ const _onBlur = ({target: {value}}: React.FocusEvent) =>
+ onBlur(id, value);
+ const _onFocus = ({target: {value},}: React.FocusEvent) => onFocus(id, value);
+
+ const inline = Boolean(options && options.inline);
+
+ return (
+
+
+ {uiSchema["ui:title"] || schema.title || label}
+ {(label || uiSchema["ui:title"] || schema.title) && required ? "*" : null}
+
+
+ {(enumOptions as any).map((option: any, i: number) => {
+ const itemDisabled =
+ Array.isArray(enumDisabled) &&
+ enumDisabled.indexOf(option.value) !== -1;
+ const checked = option.value == value;
+
+ // @ts-ignore
+ const radio = (
+
+ );
+ return radio;
+ })}
+
+
+ );
+};
+
+export default RadioWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/RadioWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/RadioWidget/index.ts
new file mode 100644
index 000000000..d461a901c
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/RadioWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./RadioWidget";
+export * from "./RadioWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/RangeWidget/RangeWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/RangeWidget/RangeWidget.tsx
new file mode 100644
index 000000000..9eeb38122
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/RangeWidget/RangeWidget.tsx
@@ -0,0 +1,57 @@
+import React from "react";
+
+import Form from "react-bootstrap/Form";
+
+import {utils} from "@rjsf/core";
+import {WidgetProps} from "@rjsf/core";
+
+const {rangeSpec} = utils;
+
+const RangeWidget = ({
+ value,
+ readonly,
+ disabled,
+ onBlur,
+ onFocus,
+ options,
+ schema,
+ onChange,
+ required,
+ label,
+ id,
+ uiSchema,
+ }: WidgetProps) => {
+ let sliderProps = {value, label, id, ...rangeSpec(schema)};
+
+ const _onChange = ({
+ target: {value},
+ }: React.ChangeEvent) =>
+ onChange(value === "" ? options.emptyValue : value);
+ const _onBlur = ({target: {value}}: React.FocusEvent) =>
+ onBlur(id, value);
+ const _onFocus = ({
+ target: {value},
+ }: React.FocusEvent) => onFocus(id, value);
+
+ return (
+
+
+ {uiSchema["ui:title"] || schema.title || label}
+ {(label || uiSchema["ui:title"] || schema.title) && required ? "*" : null}
+
+
+ {value}
+
+ );
+};
+
+export default RangeWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/RangeWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/RangeWidget/index.ts
new file mode 100644
index 000000000..f43ff7464
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/RangeWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./RangeWidget";
+export * from "./RangeWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/SelectWidget/SelectWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/SelectWidget/SelectWidget.tsx
new file mode 100644
index 000000000..5a9c7b813
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/SelectWidget/SelectWidget.tsx
@@ -0,0 +1,132 @@
+import React from "react";
+
+import Form from "react-bootstrap/Form";
+
+import {WidgetProps} from "@rjsf/core";
+import {utils} from "@rjsf/core";
+
+const {asNumber, guessType} = utils;
+
+const nums = new Set(["number", "integer"]);
+
+/**
+ * This is a silly limitation in the DOM where option change event values are
+ * always retrieved as strings.
+ */
+const processValue = (schema: any, value: any) => {
+ // "enum" is a reserved word, so only "type" and "items" can be destructured
+ const {type, items} = schema;
+ if (value === "") {
+ return undefined;
+ } else if (type === "array" && items && nums.has(items.type)) {
+ return value.map(asNumber);
+ } else if (type === "boolean") {
+ return value === "true";
+ } else if (type === "number") {
+ return asNumber(value);
+ }
+
+ // If type is undefined, but an enum is present, try and infer the type from
+ // the enum values
+ if (schema.enum) {
+ if (schema.enum.every((x: any) => guessType(x) === "number")) {
+ return asNumber(value);
+ } else if (schema.enum.every((x: any) => guessType(x) === "boolean")) {
+ return value === "true";
+ }
+ }
+
+ return value;
+};
+
+const SelectWidget = (
+ {
+ schema,
+ id,
+ options,
+ label,
+ required,
+ disabled,
+ readonly,
+ value,
+ multiple,
+ autofocus,
+ onChange,
+ onBlur,
+ onFocus,
+ placeholder,
+ rawErrors = [],
+ }: WidgetProps) => {
+ const {enumOptions, enumDisabled} = options;
+
+ const emptyValue = multiple ? [] : "";
+
+ function getValue(
+ event: React.FocusEvent | React.ChangeEvent | any,
+ multiple: Boolean,
+ ) {
+ if (multiple) {
+ return [].slice
+ .call(event.target.options as any)
+ .filter((o: any) => o.selected)
+ .map((o: any) => o.value);
+ } else {
+ return event.target.value;
+ }
+ }
+
+ return (
+
+ 0 ? "text-danger" : "")}>
+ {label || schema.title}
+ {(label || schema.title) && required ? "*" : null}
+
+
+
0 ? "is-invalid" : "")}
+ onBlur={
+ onBlur &&
+ ((event: React.FocusEvent) => {
+ const newValue = getValue(event, multiple);
+ onBlur(id, processValue(schema, newValue));
+ })
+ }
+ onFocus={
+ onFocus &&
+ ((event: React.FocusEvent) => {
+ const newValue = getValue(event, multiple);
+ onFocus(id, processValue(schema, newValue));
+ })
+ }
+ onChange={(event: React.ChangeEvent) => {
+ const newValue = getValue(event, multiple);
+ onChange(processValue(schema, newValue));
+ }}>
+ {!multiple && schema.default === undefined && (
+ {placeholder}
+ )}
+ {(enumOptions as any).map(({value, label}: any, i: number) => {
+ const disabled: any =
+ Array.isArray(enumDisabled) &&
+ (enumDisabled as any).indexOf(value) != -1;
+ return (
+
+ {label}
+
+ );
+ })}
+
+
+
+ );
+};
+
+export default SelectWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/SelectWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/SelectWidget/index.ts
new file mode 100644
index 000000000..6e104f2ea
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/SelectWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./SelectWidget";
+export * from "./SelectWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/TextWidget/TextWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/TextWidget/TextWidget.tsx
new file mode 100644
index 000000000..ea491c296
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/TextWidget/TextWidget.tsx
@@ -0,0 +1,74 @@
+import React from "react";
+
+import Form from "react-bootstrap/Form";
+
+import {WidgetProps} from "@rjsf/core";
+
+const TextWidget = (
+ {
+ id,
+ placeholder,
+ required,
+ readonly,
+ disabled,
+ type,
+ label,
+ value,
+ onChange,
+ onBlur,
+ onFocus,
+ autofocus,
+ options,
+ schema,
+ rawErrors = [],
+ uiSchema,
+ }: WidgetProps) => {
+ const _onChange = ({
+ target: {value},
+ }: React.ChangeEvent) =>
+ onChange(value === "" ? options.emptyValue : value);
+ const _onBlur = ({target: {value}}: React.FocusEvent) =>
+ onBlur(id, value);
+ const _onFocus = ({
+ target: {value},
+ }: React.FocusEvent) => onFocus(id, value);
+ const inputType = (type || schema.type) === "string" ? "text" : `${type || schema.type}`;
+
+ // const classNames = [rawErrors.length > 0 ? "is-invalid" : "", type === 'file' ? 'custom-file-label': ""]
+ return (
+
+ 0 ? "text-danger" : "")}>
+ {uiSchema["ui:title"] || schema.title || label}
+ {(label || uiSchema["ui:title"] || schema.title) && required ? "*" : null}
+
+
+
0 ? "is-invalid" : ""}
+ list={schema.examples ? `examples_${id}` : undefined}
+ type={inputType}
+ value={value || value === 0 ? value : ""}
+ onChange={_onChange}
+ onBlur={_onBlur}
+ onFocus={_onFocus}
+ />
+
+ {schema.examples ? (
+
+ {(schema.examples as string[])
+ .concat(schema.default ? ([schema.default] as string[]) : [])
+ .map((example: any) => {
+ return ;
+ })}
+
+ ) : null}
+
+ );
+};
+
+export default TextWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/TextWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/TextWidget/index.ts
new file mode 100644
index 000000000..e7e838908
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/TextWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./TextWidget";
+export * from "./TextWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/TextareaWidget/TextareaWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/TextareaWidget/TextareaWidget.tsx
new file mode 100644
index 000000000..763efb975
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/TextareaWidget/TextareaWidget.tsx
@@ -0,0 +1,71 @@
+import React from "react";
+
+import {WidgetProps} from "@rjsf/core";
+import FormControl from "react-bootstrap/FormControl";
+import InputGroup from "react-bootstrap/InputGroup";
+
+type CustomWidgetProps = WidgetProps & {
+ options: any;
+};
+
+const TextareaWidget = ({
+ id,
+ placeholder,
+ value,
+ required,
+ disabled,
+ autofocus,
+ label,
+ readonly,
+ onBlur,
+ onFocus,
+ onChange,
+ options,
+ schema,
+ rawErrors = [],
+ uiSchema,
+ }: CustomWidgetProps) => {
+ const _onChange = ({
+ target: {value},
+ }: React.ChangeEvent) =>
+ onChange(value === "" ? options.emptyValue : value);
+ const _onBlur = ({
+ target: {value},
+ }: React.FocusEvent) => onBlur(id, value);
+ const _onFocus = ({
+ target: {value},
+ }: React.FocusEvent) => onFocus(id, value);
+
+ return (
+ <>
+
+ {uiSchema["ui:title"] || schema.title || label}
+ {required && (
+ 0 ? "text-danger ml-1" : "ml-1"}>
+ {"*"}
+
+ )}
+
+
+
+
+ >
+ );
+};
+
+export default TextareaWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/TextareaWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/TextareaWidget/index.ts
new file mode 100644
index 000000000..cbcf0b8f3
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/TextareaWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./TextareaWidget";
+export * from "./TextareaWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/Theme/Theme.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/Theme/Theme.tsx
new file mode 100644
index 000000000..b3b0fc179
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/Theme/Theme.tsx
@@ -0,0 +1,37 @@
+import React from "react";
+
+import Button from "react-bootstrap/Button";
+
+import ArrayFieldTemplate from "../ArrayFieldTemplate";
+import ErrorList from "../ErrorList";
+import Fields from "../Fields";
+import FieldTemplate from "../FieldTemplate";
+import ObjectFieldTemplate from "../ObjectFieldTemplate";
+import Widgets from "../Widgets";
+
+import {ThemeProps} from "@rjsf/core";
+import {utils} from "@rjsf/core";
+
+const {getDefaultRegistry} = utils;
+
+const {fields, widgets} = getDefaultRegistry();
+
+const DefaultChildren = () => (
+
+
+ Submit
+
+
+);
+
+const Theme: ThemeProps = {
+ children: ,
+ ArrayFieldTemplate,
+ fields: {...fields, ...Fields},
+ FieldTemplate,
+ ObjectFieldTemplate,
+ widgets: {...widgets, ...Widgets},
+ ErrorList,
+};
+
+export default Theme;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/Theme/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/Theme/index.ts
new file mode 100644
index 000000000..2398c7502
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/Theme/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./Theme";
+export * from "./Theme";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/TitleField/TitleField.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/TitleField/TitleField.tsx
new file mode 100644
index 000000000..ee6067da2
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/TitleField/TitleField.tsx
@@ -0,0 +1,17 @@
+import React from "react";
+import {FieldProps} from "@rjsf/core";
+
+export interface TitleFieldProps extends Partial {
+ title: string;
+}
+
+const TitleField = ({title, uiSchema}: Partial) => (
+ <>
+
+
{(uiSchema && uiSchema["ui:title"]) || title}
+
+
+ >
+);
+
+export default TitleField;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/TitleField/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/TitleField/index.ts
new file mode 100644
index 000000000..51ee5dd17
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/TitleField/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./TitleField";
+export * from "./TitleField";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/URLWidget/URLWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/URLWidget/URLWidget.tsx
new file mode 100644
index 000000000..ff7972a63
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/URLWidget/URLWidget.tsx
@@ -0,0 +1,10 @@
+import React from "react";
+import {WidgetProps} from "@rjsf/core";
+
+const URLWidget = (props: WidgetProps) => {
+ const {registry} = props;
+ const {TextWidget} = registry.widgets;
+ return ;
+};
+
+export default URLWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/URLWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/URLWidget/index.ts
new file mode 100644
index 000000000..6aaa0378e
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/URLWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./URLWidget";
+export * from "./URLWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/UpDownWidget/UpDownWidget.tsx b/misc/config_tools/configurator/src/lib/bs4rjsf/UpDownWidget/UpDownWidget.tsx
new file mode 100644
index 000000000..8a732e1c2
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/UpDownWidget/UpDownWidget.tsx
@@ -0,0 +1,52 @@
+import React from "react";
+
+import Form from "react-bootstrap/Form";
+
+import {WidgetProps} from "@rjsf/core";
+
+const UpDownWidget = ({
+ id,
+ required,
+ readonly,
+ disabled,
+ label,
+ value,
+ onChange,
+ onBlur,
+ onFocus,
+ autofocus,
+ schema,
+ uiSchema,
+ }: WidgetProps) => {
+ const _onChange = ({
+ target: {value},
+ }: React.ChangeEvent) => onChange(value);
+ const _onBlur = ({target: {value}}: React.FocusEvent) =>
+ onBlur(id, value);
+ const _onFocus = ({
+ target: {value},
+ }: React.FocusEvent) => onFocus(id, value);
+
+ return (
+
+
+ {uiSchema["ui:title"] || schema.title || label}
+ {(label || uiSchema["ui:title"] || schema.title) && required ? "*" : null}
+
+
+
+ );
+};
+
+export default UpDownWidget;
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/UpDownWidget/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/UpDownWidget/index.ts
new file mode 100644
index 000000000..4097027b7
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/UpDownWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./UpDownWidget";
+export * from "./UpDownWidget";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/Widgets/Widgets.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/Widgets/Widgets.ts
new file mode 100644
index 000000000..cfd1a5b54
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/Widgets/Widgets.ts
@@ -0,0 +1,33 @@
+import CheckboxWidget from "../CheckboxWidget/CheckboxWidget";
+import CheckboxesWidget from "../CheckboxesWidget/CheckboxesWidget";
+import ColorWidget from "../ColorWidget/ColorWidget";
+import DateWidget from "../DateWidget/DateWidget";
+import DateTimeWidget from "../DateTimeWidget/DateTimeWidget";
+import EmailWidget from "../EmailWidget/EmailWidget";
+import PasswordWidget from "../PasswordWidget/PasswordWidget";
+import RadioWidget from "../RadioWidget/RadioWidget";
+import RangeWidget from "../RangeWidget/RangeWidget";
+import SelectWidget from "../SelectWidget/SelectWidget";
+import TextareaWidget from "../TextareaWidget/TextareaWidget";
+import TextWidget from "../TextWidget/TextWidget";
+import UpDownWidget from "../UpDownWidget/UpDownWidget";
+import URLWidget from "../URLWidget/URLWidget";
+import FileWidget from "../FileWidget/FileWidget";
+
+export default {
+ CheckboxWidget,
+ CheckboxesWidget,
+ ColorWidget,
+ DateWidget,
+ DateTimeWidget,
+ EmailWidget,
+ PasswordWidget,
+ RadioWidget,
+ RangeWidget,
+ SelectWidget,
+ TextareaWidget,
+ TextWidget,
+ UpDownWidget,
+ URLWidget,
+ FileWidget,
+};
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/Widgets/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/Widgets/index.ts
new file mode 100644
index 000000000..91b5756bc
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/Widgets/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./Widgets";
+export * from "./Widgets";
diff --git a/misc/config_tools/configurator/src/lib/bs4rjsf/index.ts b/misc/config_tools/configurator/src/lib/bs4rjsf/index.ts
new file mode 100644
index 000000000..2e1e8faf8
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/bs4rjsf/index.ts
@@ -0,0 +1,10 @@
+import Form from "./Form/Form";
+
+export {default as Fields} from "./Fields";
+export {default as FieldTemplate} from "./FieldTemplate";
+export {default as Form} from "./Form";
+export {default as ObjectFieldTemplate} from "./ObjectFieldTemplate";
+export {default as Theme} from "./Theme";
+export {default as Widgets} from "./Widgets";
+
+export default Form;
diff --git a/misc/config_tools/configurator/src/lib/common.js b/misc/config_tools/configurator/src/lib/common.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/misc/config_tools/configurator/src/lib/helper.jsx b/misc/config_tools/configurator/src/lib/helper.jsx
new file mode 100644
index 000000000..65e507862
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/helper.jsx
@@ -0,0 +1,221 @@
+import _ from "lodash";
+import xml2json, {parseXml} from "./xml2json";
+import JSON2XML from "./json2xml";
+
+import {fs, path} from "@tauri-apps/api"
+
+
+// noinspection JSUnresolvedFunction,JSUnresolvedVariable
+class BackendBase {
+ listConfig = async () => {
+ await this.init
+
+ let config = await this.read(this.configPath)
+ if (config == null) {
+ return {}
+ }
+ try {
+ config = JSON.parse(config)
+ } catch (e) {
+ return {}
+ }
+ return config
+ }
+
+ getConfig = async (key, defaultValue) => {
+ let config = await this.listConfig()
+ console.log(key, config, defaultValue)
+ if (config.hasOwnProperty(key)) {
+ return config[key]
+ }
+ return defaultValue
+ }
+
+ setConfig = async (key, value) => {
+ let config = await this.listConfig()
+ config[key] = value
+ config = JSON.stringify(config)
+ await this.write(this.configPath, config)
+ }
+
+ read = async () => {
+ throw new Error('Not Impl')
+ }
+ write = async () => {
+ throw new Error('Not Impl')
+ }
+ list = async () => {
+ throw new Error('Not Impl')
+ }
+ remove = async () => {
+ throw new Error('Not Impl')
+ }
+}
+
+export class LocalStorageBackend extends BackendBase {
+ constructor() {
+ super()
+ this.rootFSName = 'acrn-configurator'
+ this.configPath = 'config.json'
+ }
+
+ _readRootFS = () => {
+ let localStorageFS = localStorage.getItem(this.rootFSName)
+ if (localStorageFS == null) {
+ return {}
+ }
+ localStorageFS = JSON.parse(localStorageFS)
+ return localStorageFS
+ }
+
+ _writeRootFS = (fs) => {
+ localStorage.setItem(this.rootFSName, JSON.stringify(fs))
+ }
+
+ read = async (filePath) => {
+ let localStorageFS = this._readRootFS()
+ if (localStorageFS.hasOwnProperty(filePath)) {
+ return localStorageFS[filePath]
+ }
+ return null
+ }
+
+ write = async (filePath, value) => {
+ let localStorageFS = this._readRootFS()
+ localStorageFS[filePath] = value
+ this._writeRootFS(localStorageFS)
+ }
+ list = async (dirPath) => {
+ let localStorageFS = this._readRootFS()
+ let dirFiles = []
+ for (let filepath in localStorageFS) {
+ if (_.startsWith(filepath, dirPath)) {
+ dirFiles.push(filepath)
+ }
+ }
+ return dirFiles
+ }
+ remove = async (filename) => {
+ let localStorageFS = this._readRootFS()
+ if (localStorageFS.hasOwnProperty(filename)) {
+ delete localStorageFS[filename]
+ }
+ }
+}
+
+export class TauriLocalFSBackend extends BackendBase {
+ constructor() {
+ super()
+ this.init = path.homeDir()
+ .then(async (dirPath) => {
+ this.configDir = await path.join(dirPath, '.acrn-configurator')
+ this.configPath = await path.join(this.configDir, 'config.json')
+ return fs.readDir(this.configDir)
+ .catch(async () => {
+ fs.createDir(this.configDir, {recursive: true})
+ .then(() => this.write(this.configPath, "{}"))
+ .catch((reason) => {
+ alert("Create config dir failed. Error:" + reason)
+ })
+ })
+ })
+ }
+
+
+ read = async (filePath) => {
+ await this.init
+ return await fs.readTextFile(filePath)
+ .catch(reason => {
+ // Todo: add isFile function to check perm
+ // alert("Read file error! Error: " + reason);
+ return null
+ })
+ }
+
+ write = async (filePath, value) => {
+ await this.init
+ return await fs.writeFile({
+ path: filePath,
+ contents: value
+ }).catch((reason => alert("Write file error! Error: " + reason)))
+ }
+
+ list = async (dirPath) => {
+ await this.init
+ return (await fs.readDir(dirPath))
+ .filter((file) => file.children == null)
+ .map((file) => file.path)
+ }
+
+ remove = async (filePath) => {
+ await this.init
+ return await fs.removeFile(filePath)
+ }
+
+}
+
+export class Helper {
+ constructor(configBackend, fsBackend) {
+ this.configBackend = configBackend
+ this.fsBackend = fsBackend
+ this.color = {
+ blue: "#1475b2",
+ green: "#42c02e",
+ deepGray: "#606060"
+ }
+ }
+
+ log(title, content, backgroundColor = this.color.green) {
+ console.log(
+ "%c ".concat(title, " %c ").concat(content, " "),
+ "padding: 1px; border-radius: 3px 0 0 3px; color: #fff; background: ".concat(this.color.deepGray, ";"),
+ "padding: 1px; border-radius: 0 3px 3px 0; color: #fff; background: ".concat(backgroundColor, ";")
+ )
+ }
+
+ resolveHome = async (filepath) => {
+ if (filepath[0] === '~') {
+ return await path.join(await path.homeDir(), filepath.slice(1))
+ }
+ return filepath;
+ }
+
+ convertXMLTextToObj = (XMLText) => {
+ let jsonText = xml2json(parseXml(XMLText))
+ // console.log(scenarioData)
+ return JSON.parse(jsonText)
+ }
+
+ convertObjToXML = (o) => {
+ // convert our js object to xml
+ let json2xml = new JSON2XML()
+ // noinspection UnnecessaryLocalVariableJS
+ let xml = json2xml.convert(o)
+ return xml
+ }
+
+ read = async filePath => {
+ return await this.fsBackend.read(filePath)
+ }
+
+ save = async (filePath, text) => {
+ await this.fsBackend.write(filePath, text)
+ };
+
+ list = async dirPath => {
+ return await this.fsBackend.list(dirPath)
+ };
+
+ remove = async filePath => {
+ await this.fsBackend.remove(filePath).catch((reason => alert("Remove file failed! Error:" + reason)))
+ }
+
+ getConfig = async (configKeyName, defaultValue) => {
+ return await this.configBackend.getConfig(configKeyName, defaultValue)
+ }
+
+ setConfig = async (configKeyName, value) => {
+ return await this.configBackend.setConfig(configKeyName, value)
+ };
+
+}
diff --git a/misc/config_tools/configurator/src/lib/json2xml.jsx b/misc/config_tools/configurator/src/lib/json2xml.jsx
new file mode 100644
index 000000000..526ed5b84
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/json2xml.jsx
@@ -0,0 +1,88 @@
+import _ from 'lodash'
+
+export default class JSON2XML {
+ constructor(indent = ' ', newline = '\n') {
+ this.xml = ''
+ this.indent = indent
+ this.newline = newline
+ }
+
+ convert = (jsObject) => {
+ return this.handleConvert(jsObject)
+ }
+
+ handleConvert(jsObject) {
+ if (!(_.isObject(jsObject) || _.isArray(jsObject))) {
+ throw new Error('Not js object')
+ }
+ this.xml = ''
+ for (const jsObjectKey in jsObject) {
+ this.#convertObject(0, jsObjectKey, jsObject[jsObjectKey])
+ }
+ return this.xml
+ }
+
+ #getAttrText(jso) {
+ let attrs = {}
+ Object.keys(jso).map((childKey) => {
+ if (_.startsWith(childKey, '@')) {
+ attrs[childKey.substr(1)] = jso[childKey]
+ delete jso[childKey]
+ }
+ }
+ )
+ let attrText = ''
+ for (const attrsKey in attrs) {
+ attrText += ` ${attrsKey}="${attrs[attrsKey]}"`
+ }
+ return attrText
+ }
+
+ addNewlineAndIndent(deepth) {
+ this.xml += this.newline + _.repeat(this.indent, deepth)
+ }
+
+ #convertObject(deepth, selfName, jsObjectElement) {
+ // this.#convertObject('acrn-config',jso['acrn-config'])
+ // let example = {
+ // 'acrn-config': {
+ // '@board': 'whl-ipc-i7',
+ // hv: {},
+ // vm: [
+ // {'@id': 1},
+ // {'@id': 2},
+ // {'@id': 3}
+ // ]
+ // }
+ // }
+
+ if (_.isString(jsObjectElement) || _.isNumber(jsObjectElement)) {
+ // this.#convertObject('boot_args', 'default')
+ this.addNewlineAndIndent(deepth)
+ let val = _.escape(`${jsObjectElement}`)
+ this.xml += `<${selfName}>${val}${selfName}>`
+ } else if (_.isArray(jsObjectElement)) {
+ // this.#convertObject('cpuid', [1,2,3])
+ // this.#convertObject('vm', [{},{},{}])
+ for (const index in jsObjectElement) {
+ this.#convertObject(deepth, selfName, jsObjectElement[index])
+ }
+ } else if (_.isObject(jsObjectElement)) {
+ // this.#convertObject('acrn-config',jso['acrn-config'])
+ let attrText = this.#getAttrText(jsObjectElement)
+ this.addNewlineAndIndent(deepth)
+ this.xml += `<${selfName}${attrText}>`
+ for (const key in jsObjectElement) {
+ this.#convertObject(deepth + 1, key, jsObjectElement[key])
+ }
+ this.addNewlineAndIndent(deepth)
+ this.xml += `${selfName}>`
+ } else if (jsObjectElement == null) {
+ //pass
+ } else {
+ console.log(jsObjectElement)
+ debugger;
+ throw new Error("Unknown Object")
+ }
+ }
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/lib/platform/tauri/tauri.jsx b/misc/config_tools/configurator/src/lib/platform/tauri/tauri.jsx
new file mode 100644
index 000000000..51895534a
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/platform/tauri/tauri.jsx
@@ -0,0 +1,22 @@
+import {appWindow} from "@tauri-apps/api/window";
+
+class WindowHelper {
+ minimal = (cb = null) => {
+ appWindow.minimize().then(() => {
+ cb ? cb() : false
+ })
+ }
+
+ maxmal = (cb = null) => {
+ appWindow.toggleMaximize().then(() => {
+ cb ? cb() : false
+ })
+ }
+
+ close = () => {
+ appWindow.close().then()
+ }
+}
+
+
+export const windowHelper = new WindowHelper()
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/lib/runpy.jsx b/misc/config_tools/configurator/src/lib/runpy.jsx
new file mode 100644
index 000000000..e568764b9
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/runpy.jsx
@@ -0,0 +1,58 @@
+import dynamicScenario from "../assets/schema/dynamicScenario.py"
+import scenario_json from "../assets/schema/scenario.json"
+import {Base64} from 'js-base64';
+
+
+function toJSONString(obj) {
+ if (_.isString(obj)) {
+ return JSON.stringify(obj)
+ }
+ return JSON.stringify(JSON.stringify(obj))
+}
+
+
+async function initPyodide() {
+ await pyodide.runPythonAsync(
+ `import micropip
+await micropip.install(['elementpath', 'xmlschema'])
+`)
+ return pyodide
+}
+
+initPyodide()
+
+function runPyCode(pythonCode) {
+ pyodide.loadPackagesFromImports(pythonCode)
+ return pyodide.runPython(pythonCode)
+}
+
+function writeFile(filename, content) {
+ let file_content = JSON.stringify(content)
+ return runPyCode(
+ `import json; open(${toJSONString(filename)},'w',encoding='utf-8').write(json.loads(${toJSONString(file_content)}))`
+ )
+}
+
+function readFile(filename) {
+ return runPyCode(
+ `open(${toJSONString(filename)},'r',encoding='utf-8').read()`
+ )
+}
+
+function loadLibrary(libraryName, content) {
+ return writeFile(`/lib/python3.9/${libraryName}.py`, content)
+}
+
+function getNewSchema(boardXMLText) {
+ let params = JSON.stringify({board_xml: boardXMLText, scenario_json: JSON.stringify(scenario_json)})
+ params = Base64.encode(params);
+ let scenario_text = runPyCode(`
+params="${params}"
+${dynamicScenario}
+`)
+ let new_scenario_json = JSON.parse(scenario_text)
+ console.log(new_scenario_json)
+ return new_scenario_json
+}
+
+export {runPyCode, readFile, writeFile, getNewSchema}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/lib/xml2json.js b/misc/config_tools/configurator/src/lib/xml2json.js
new file mode 100644
index 000000000..f7f933cc6
--- /dev/null
+++ b/misc/config_tools/configurator/src/lib/xml2json.js
@@ -0,0 +1,191 @@
+export function parseXml(xml) {
+ let dom = null;
+ if (window.DOMParser) {
+ try {
+ dom = (new DOMParser()).parseFromString(xml, "text/xml");
+ } catch (e) {
+ dom = null;
+ }
+ } else if (window.ActiveXObject) {
+ try {
+ dom = new ActiveXObject('Microsoft.XMLDOM');
+ dom.async = false;
+ if (!dom.loadXML(xml)) {
+ // parse error ..
+ window.alert(dom.parseError.reason + dom.parseError.srcText);
+ }
+ } catch (e) {
+ dom = null;
+ }
+ } else {
+ alert("cannot parse xml string!");
+ }
+ return dom;
+}
+
+export default function xml2json(xml, tab = "\t") {
+ let X = {
+ toObj: function (xml) {
+ let o = {};
+ if (xml.nodeType === 1) {
+ // element node ..
+ if (xml.attributes.length) {
+ // element with attributes ..
+ for (let i = 0; i < xml.attributes.length; i++) {
+ o["@" + xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue || "").toString();
+ }
+ }
+ if (xml.firstChild) {
+ // element has child nodes ..
+ let textChild = 0, cdataChild = 0, hasElementChild = false;
+ for (let n = xml.firstChild; n; n = n.nextSibling) {
+ if (n.nodeType === 1) {
+ hasElementChild = true;
+ } else if (n.nodeType === 3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) {
+ // non-whitespace text
+ textChild++;
+ } else if (n.nodeType === 4) {
+ // cdata section node
+ cdataChild++;
+ }
+ }
+ if (hasElementChild) {
+ if (textChild < 2 && cdataChild < 2) {
+ // structured element with evtl. a single text or/and cdata node ..
+ X.removeWhite(xml);
+ for (let n = xml.firstChild; n; n = n.nextSibling) {
+ if (n.nodeType === 3) {
+ // text node
+ o["#text"] = X.escape(n.nodeValue);
+ } else if (n.nodeType === 4) {
+ // cdata node
+ o["#cdata"] = X.escape(n.nodeValue);
+ } else if (o[n.nodeName]) {
+ // multiple occurence of element ..
+ if (o[n.nodeName] instanceof Array) {
+ o[n.nodeName][o[n.nodeName].length] = X.toObj(n);
+ } else {
+ o[n.nodeName] = [o[n.nodeName], X.toObj(n)];
+ }
+
+ } else {
+ // first occurence of element..
+ o[n.nodeName] = X.toObj(n);
+ }
+ }
+ } else {
+ // mixed content
+ if (!xml.attributes.length) {
+ o = X.escape(X.innerXml(xml));
+ } else {
+ o["#text"] = X.escape(X.innerXml(xml));
+ }
+ }
+ } else if (textChild) {
+ // pure text
+ if (!xml.attributes.length)
+ o = X.escape(X.innerXml(xml));
+ else
+ o["#text"] = X.escape(X.innerXml(xml));
+ } else if (cdataChild) {
+ // cdata
+ if (cdataChild > 1)
+ o = X.escape(X.innerXml(xml));
+ else {
+ for (let n = xml.firstChild; n; n = n.nextSibling)
+ o["#cdata"] = X.escape(n.nodeValue);
+ }
+ }
+ }
+ if (!xml.attributes.length && !xml.firstChild) o = null;
+ } else if (xml.nodeType === 9) {
+ // document.node
+ o = X.toObj(xml.documentElement);
+ } else
+ alert("unhandled node type: " + xml.nodeType);
+ return o;
+ },
+ toJson: function (o, name, ind) {
+ let json = name ? ("\"" + name + "\"") : "";
+ if (o instanceof Array) {
+ for (let i = 0, n = o.length; i < n; i++)
+ o[i] = X.toJson(o[i], "", ind + "\t");
+ json += (name ? ":[" : "[") + (o.length > 1 ? ("\n" + ind + "\t" + o.join(",\n" + ind + "\t") + "\n" + ind) : o.join("")) + "]";
+ } else if (o == null)
+ json += (name && ":") + "null";
+ else if (typeof (o) == "object") {
+ let arr = [];
+ for (let m in o)
+ arr[arr.length] = X.toJson(o[m], m, ind + "\t");
+ json += (name ? ":{" : "{") + (arr.length > 1 ? ("\n" + ind + "\t" + arr.join(",\n" + ind + "\t") + "\n" + ind) : arr.join("")) + "}";
+ } else if (typeof (o) == "string")
+ json += (name && ":") + "\"" + o.toString() + "\"";
+ else
+ json += (name && ":") + o.toString();
+ return json;
+ },
+ innerXml: function (node) {
+ let s = ""
+ if ("innerHTML" in node)
+ s = node.innerHTML;
+ else {
+ let asXml = function (n) {
+ let s = "";
+ if (n.nodeType === 1) {
+ s += "<" + n.nodeName;
+ for (let i = 0; i < n.attributes.length; i++)
+ s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue || "").toString() + "\"";
+ if (n.firstChild) {
+ s += ">";
+ for (let c = n.firstChild; c; c = c.nextSibling)
+ s += asXml(c);
+ s += "" + n.nodeName + ">";
+ } else
+ s += "/>";
+ } else if (n.nodeType === 3)
+ s += n.nodeValue;
+ else if (n.nodeType === 4)
+ s += "";
+ return s;
+ };
+ for (let c = node.firstChild; c; c = c.nextSibling)
+ s += asXml(c);
+ }
+ return s;
+ },
+ escape: function (txt) {
+ return txt.replace(/[\\]/g, "\\\\")
+ .replace(/["]/g, '\\"')
+ .replace(/[\n]/g, '\\n')
+ .replace(/[\r]/g, '\\r');
+ },
+ removeWhite: function (e) {
+ e.normalize();
+ for (let n = e.firstChild; n;) {
+ if (n.nodeType === 3) { // text node
+ if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) {
+ // pure whitespace text node
+ let nxt = n.nextSibling;
+ e.removeChild(n);
+ n = nxt;
+ } else
+ n = n.nextSibling;
+ } else if (n.nodeType === 1) {
+ // element node
+ X.removeWhite(n);
+ n = n.nextSibling;
+ } else {
+ // any other node
+ n = n.nextSibling;
+ }
+ }
+ return e;
+ }
+ };
+ if (xml.nodeType === 9) {
+ // document node
+ xml = xml.documentElement;
+ }
+ let json = X.toJson(X.toObj(X.removeWhite(xml)), xml.nodeName, "\t");
+ return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/[\t\n]/g, "")) + "\n}";
+}
diff --git a/misc/config_tools/configurator/src/logo.svg b/misc/config_tools/configurator/src/logo.svg
new file mode 100644
index 000000000..6b60c1042
--- /dev/null
+++ b/misc/config_tools/configurator/src/logo.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/misc/config_tools/configurator/src/main.jsx b/misc/config_tools/configurator/src/main.jsx
new file mode 100644
index 000000000..e878a4b86
--- /dev/null
+++ b/misc/config_tools/configurator/src/main.jsx
@@ -0,0 +1,25 @@
+import VConsole from 'vconsole';
+
+const vConsole = new VConsole();
+import React from 'react'
+import ReactDOM from 'react-dom'
+
+import './index.scss'
+import './assets/fonts/Roboto.css'
+
+import App from './App'
+import Navbar from "./components/Navbar";
+import MyErrorBoundary from "./pages/Error/MyErrorBoundary";
+import {Container} from "react-bootstrap";
+
+ReactDOM.render(
+
+
+
+
+
+
+
+ ,
+ document.getElementById('root')
+)
diff --git a/misc/config_tools/configurator/src/pages/Config/Config.css b/misc/config_tools/configurator/src/pages/Config/Config.css
new file mode 100644
index 000000000..52c3dbd87
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/Config.css
@@ -0,0 +1,8 @@
+.card, .card-header {
+ background-color: #F5F5F5;
+}
+
+.configBody {
+ height: calc(100vh - 80px);
+ overflow: auto;
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/Config.jsx b/misc/config_tools/configurator/src/pages/Config/Config.jsx
new file mode 100644
index 000000000..106fdea36
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/Config.jsx
@@ -0,0 +1,67 @@
+import React from "react";
+import {Accordion, Container} from "react-bootstrap";
+
+import "./Config.css"
+
+import Banner from "../../components/Banner";
+
+import backArrow from '../../assets/images/back_arrow_icon.svg'
+
+
+// import ExitACRNConfigurationModal from "./ExitACRNConfigurationModal/ExitACRNConfigurationModal";
+import ImportABoardConfigurationFile from "./ImportABoardConfigurationFile";
+import CreateNewOrImportAnExistingScenario from "./CreateNewOrImportAnExistingScenario";
+import ConfigureSettingsForScenario from "./ConfigureSettingsForScenario";
+
+import Footer from "../../components/Footer";
+import {ACRNContext} from "../../ACRNContext";
+
+class Config extends React.Component {
+ constructor(props, context) {
+ super(props);
+ let {configurator} = context
+ this.state = {
+ WorkingFolder: configurator.WorkingFolder
+ }
+ }
+
+ render = () => {
+ return (
+
+
+
+
{
+ document.location.href = '#'
+ }}/> Working folder: {this.state.WorkingFolder}
+
+
+
+
+
+
+ {/**/}
+
+
+
+
+ {/*/!**!/*/}
+
+
+
+
+ {/**/}
+
+
+
+
+
+
)
+ };
+
+
+}
+
+Config.contextType = ACRNContext
+
+export default Config
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/ConfigForm.css b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/ConfigForm.css
new file mode 100644
index 000000000..bd8a5eab6
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/ConfigForm.css
@@ -0,0 +1,10 @@
+.form-area {
+ mix-blend-mode: normal;
+ border: 1px solid #373A77;
+ box-sizing: border-box;
+ border-radius: 5px;
+}
+
+.object-property-expand {
+ display: none;
+}
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/ConfigForm.jsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/ConfigForm.jsx
new file mode 100644
index 000000000..96e7532f9
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/ConfigForm.jsx
@@ -0,0 +1,150 @@
+import React, {Component} from "react"
+import Form from "../../../../lib/bs4rjsf"
+import {ACRNContext} from "../../../../ACRNContext";
+import SelectWidget from "./IVSHMEM_VM/SelectWidget";
+import TextWidget from "./IVSHMEM_VM/TextWidget";
+
+// import CustomTemplateField from "./CustomTemplateField/CustomTemplateField";
+
+export class ConfigForm extends Component {
+ constructor(props) {
+ super(props);
+ }
+
+ setFormData = (data) => {
+ let {configurator} = this.context
+ let VMID = data['@id']
+
+ if (VMID == null) {
+ configurator.programLayer.scenarioData.hv = data
+ return
+ }
+
+ let load_order = data['load_order']
+ for (let index = 0; index < configurator.programLayer.scenarioData.vm[load_order].length; index++) {
+ if (configurator.programLayer.scenarioData.vm[load_order][index]['@id'] === VMID) {
+ configurator.programLayer.scenarioData.vm[load_order][index] = data
+ }
+ }
+
+ }
+
+
+ getParams = (VMID, mode) => {
+ let {configurator} = this.context
+ let schema, formData;
+ if (VMID === -1) {
+ schema = configurator.hvSchema[mode]
+ formData = configurator.programLayer.scenarioData.hv
+ } else {
+ let VMData = null;
+ configurator.programLayer.getOriginScenarioData().vm.map((vmConfig) => {
+ if (vmConfig['@id'] === VMID) {
+ VMData = vmConfig
+ }
+ })
+ schema = configurator.vmSchemas[VMData.load_order][mode]
+ formData = VMData
+ }
+
+ return {schema, formData}
+ }
+
+
+ render = () => {
+ let VMID = this.props.VMID
+ let mode = this.props.mode
+
+ let params = this.getParams(VMID, mode)
+ let uiSchema = {
+ basic: {
+ DEBUG_OPTIONS: {
+ BUILD_TYPE: {
+ "ui:widget": "radio"
+ }
+ },
+ FEATURES: {
+ IVSHMEM: {
+ IVSHMEM_REGION: {
+ items: {
+ IVSHMEM_VMS: {
+ IVSHMEM_VM: {
+ "ui:style": {
+ border: "1px solid gray",
+ padding: "1rem",
+ borderRadius: "7px"
+ },
+ items: {
+ VM_NAME: {
+ "ui:grid": 7,
+ "ui:widget": 'VM_NAME',
+ "ui:descLabel": true,
+ "ui:descLabelAli": 'H',
+ "ui:descLabelMT": true
+ },
+ VBDF: {
+ "ui:grid": 5,
+ "ui:widget": 'VBDF',
+ "ui:descLabel": true,
+ "ui:descLabelAli": 'V',
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ vuart_connections: {
+ vuart_connection: {
+ items: {
+ endpoint: {
+ items: {
+ vm_name: {
+ "ui:grid": 6,
+ "ui:widget": 'VBDF',
+ "ui:descLabel": true,
+ "ui:descLabelAli": 'V',
+ },
+ io_port: {
+ "ui:grid": 6,
+ "ui:widget": 'VBDF',
+ "ui:descLabel": true,
+ "ui:descLabelAli": 'V',
+ },
+ }
+ }
+ }
+ }
+ }
+ },
+ additionalProperties: {
+ "ui:widget": "hidden"
+ }
+ }
+
+ let widgets = {
+ VM_NAME: SelectWidget,
+ VBDF: TextWidget
+ }
+
+
+ return
+
* are required fields
+
+ }
+}
+
+ConfigForm.contextType = ACRNContext
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/CustomTemplateField.js b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/CustomTemplateField.js
new file mode 100644
index 000000000..5eea0cd9f
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/CustomTemplateField.js
@@ -0,0 +1,30 @@
+import React from "react";
+import Form from "react-bootstrap/Form";
+import ListGroup from "react-bootstrap/ListGroup";
+import WrapIfAdditional from "./WrapIfAdditional";
+// @ts-ignore
+import rst2html from "rst2html";
+import { OverlayTrigger, Popover } from "react-bootstrap";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faCircleExclamation } from "@fortawesome/free-solid-svg-icons";
+const CustomTemplateField = ({ id, children, displayLabel, rawErrors = [], rawHelp, rawDescription, classNames, disabled, label, onDropPropertyClick, onKeyChange, readonly, required, schema }) => {
+ let descHtml = "";
+ if (rawDescription) {
+ descHtml = rst2html(rawDescription);
+ }
+ return (React.createElement(WrapIfAdditional, { classNames: classNames, disabled: disabled, id: id, label: label, onDropPropertyClick: onDropPropertyClick, onKeyChange: onKeyChange, readonly: readonly, required: required, schema: schema },
+ React.createElement(Form.Group, null,
+ displayLabel && rawDescription && (React.createElement(OverlayTrigger, { trigger: ["hover", "focus"], key: "top", placement: "top", overlay: React.createElement(Popover, { id: `popover-positioned-top` },
+ React.createElement(Popover.Body, null,
+ React.createElement(Form.Text, { className: rawErrors.length > 0 ? "text-danger" : "text-muted", dangerouslySetInnerHTML: { __html: descHtml } }))) },
+ React.createElement("span", null,
+ React.createElement(FontAwesomeIcon, { icon: faCircleExclamation, color: rawErrors.length > 0 ? "red" : "gray" })))),
+ children,
+ rawErrors.length > 0 && (React.createElement(ListGroup, { as: "ul" }, rawErrors.map((error) => {
+ return (React.createElement(ListGroup.Item, { as: "li", key: error, className: "border-0 m-0 p-0" },
+ React.createElement("small", { className: "m-0 text-danger" }, error)));
+ }))),
+ rawHelp && (React.createElement(Form.Text, { className: rawErrors.length > 0 ? "text-danger" : "text-muted", id: id }, rawHelp)))));
+};
+export default CustomTemplateField;
+//# sourceMappingURL=CustomTemplateField.js.map
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/CustomTemplateField.js.map b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/CustomTemplateField.js.map
new file mode 100644
index 000000000..dc6dce9de
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/CustomTemplateField.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"CustomTemplateField.js","sourceRoot":"","sources":["CustomTemplateField.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,IAAI,MAAM,sBAAsB,CAAC;AACxC,OAAO,SAAS,MAAM,2BAA2B,CAAC;AAClD,OAAO,gBAAgB,MAAM,oBAAoB,CAAA;AACjD,aAAa;AACb,OAAO,QAAQ,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAC,cAAc,EAAE,OAAO,EAAC,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAC,eAAe,EAAC,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAC,mBAAmB,EAAC,MAAM,mCAAmC,CAAC;AAEtE,MAAM,mBAAmB,GAAG,CACxB,EACI,EAAE,EACF,QAAQ,EACR,YAAY,EACZ,SAAS,GAAG,EAAE,EACd,OAAO,EACP,cAAc,EACd,UAAU,EACV,QAAQ,EACR,KAAK,EACL,mBAAmB,EACnB,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,MAAM,EACW,EAAE,EAAE;IAEzB,IAAI,QAAQ,GAAG,EAAE,CAAA;IACjB,IAAI,cAAc,EAAE;QAChB,QAAQ,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC;KACvC;IAED,OAAO,CACH,oBAAC,gBAAgB,IACb,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,QAAQ,EAClB,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,KAAK,EACZ,mBAAmB,EAAE,mBAAmB,EACxC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM;QAEd,oBAAC,IAAI,CAAC,KAAK;YACN,YAAY,IAAI,cAAc,IAAI,CAC/B,oBAAC,cAAc,IACX,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,EAC3B,GAAG,EAAC,KAAK,EACT,SAAS,EAAC,KAAK,EACf,OAAO,EACH,oBAAC,OAAO,IAAC,EAAE,EAAE,wBAAwB;oBACjC,oBAAC,OAAO,CAAC,IAAI;wBACT,oBAAC,IAAI,CAAC,IAAI,IAAC,SAAS,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,EAC9D,uBAAuB,EAAE,EAAC,MAAM,EAAE,QAAQ,EAAC,GAAG,CAC9C,CACT;gBAEd;oBACI,oBAAC,eAAe,IACZ,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,GAC9C,CACC,CACM,CACpB;YACA,QAAQ;YACR,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,CACrB,oBAAC,SAAS,IAAC,EAAE,EAAC,IAAI,IACb,SAAS,CAAC,GAAG,CAAC,CAAC,KAAa,EAAE,EAAE;gBAC7B,OAAO,CACH,oBAAC,SAAS,CAAC,IAAI,IAAC,EAAE,EAAC,IAAI,EAAC,GAAG,EAAE,KAAK,EAAE,SAAS,EAAC,kBAAkB;oBAC5D,+BAAO,SAAS,EAAC,iBAAiB,IAC7B,KAAK,CACF,CACK,CACpB,CAAC;YACN,CAAC,CAAC,CACM,CACf;YACA,OAAO,IAAI,CACR,oBAAC,IAAI,CAAC,IAAI,IACN,SAAS,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,EAC9D,EAAE,EAAE,EAAE,IACL,OAAO,CACA,CACf,CACQ,CACE,CACtB,CAAC;AACN,CAAC,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/CustomTemplateField.tsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/CustomTemplateField.tsx
new file mode 100644
index 000000000..dc404e39c
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/CustomTemplateField.tsx
@@ -0,0 +1,97 @@
+import React from "react";
+
+import {FieldTemplateProps} from "@rjsf/core";
+
+import Form from "react-bootstrap/Form";
+import ListGroup from "react-bootstrap/ListGroup";
+import WrapIfAdditional from "./WrapIfAdditional"
+// @ts-ignore
+import rst2html from "rst2html"
+import {OverlayTrigger, Popover} from "react-bootstrap";
+import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
+import {faCircleExclamation} from "@fortawesome/free-solid-svg-icons";
+
+const CustomTemplateField = (
+ {
+ id,
+ children,
+ displayLabel,
+ rawErrors = [],
+ rawHelp,
+ rawDescription,
+ classNames,
+ disabled,
+ label,
+ onDropPropertyClick,
+ onKeyChange,
+ readonly,
+ required,
+ schema
+ }: FieldTemplateProps) => {
+
+ let descHtml = ""
+ if (rawDescription) {
+ descHtml = rst2html(rawDescription);
+ }
+
+ return (
+
+
+ {displayLabel && rawDescription && (
+
+
+ 0 ? "text-danger" : "text-muted"}
+ dangerouslySetInnerHTML={{__html: descHtml}}/>
+
+
+ }>
+
+ 0 ? "red" : "gray"}
+ />
+
+
+ )}
+ {children}
+ {rawErrors.length > 0 && (
+
+ {rawErrors.map((error: string) => {
+ return (
+
+
+ {error}
+
+
+ );
+ })}
+
+ )}
+ {rawHelp && (
+ 0 ? "text-danger" : "text-muted"}
+ id={id}>
+ {rawHelp}
+
+ )}
+
+
+ );
+};
+
+export default CustomTemplateField;
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/WrapIfAdditional.js b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/WrapIfAdditional.js
new file mode 100644
index 000000000..1845c5985
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/WrapIfAdditional.js
@@ -0,0 +1,30 @@
+import React from "react";
+import { utils } from "@rjsf/core";
+import Row from "react-bootstrap/Row";
+import Col from "react-bootstrap/Col";
+import Form from "react-bootstrap/Form";
+import IconButton from "@rjsf/bootstrap-4";
+const { ADDITIONAL_PROPERTY_FLAG } = utils;
+const WrapIfAdditional = ({ children, disabled, id, label, onDropPropertyClick, onKeyChange, readonly, required, schema, }) => {
+ const keyLabel = `${label} Key`; // i18n ?
+ const additional = schema.hasOwnProperty(ADDITIONAL_PROPERTY_FLAG);
+ if (!additional) {
+ return children;
+ }
+ const handleBlur = ({ target }) => onKeyChange(target.value);
+ // @ts-ignore
+ return (React.createElement(Row, { key: `${id}-key` },
+ React.createElement(Col, { xs: 5 },
+ React.createElement(Form.Group, null,
+ React.createElement(Form.Label, null, keyLabel),
+ React.createElement(Form.Control, { required: required, defaultValue: label, disabled: disabled || readonly, id: `${id}-key`, name: `${id}-key`, onBlur: !readonly ? handleBlur : undefined, type: "text" }))),
+ React.createElement(Col, { xs: 5 }, children),
+ React.createElement(Col, { xs: 2, className: "py-4" },
+ React.createElement(IconButton
+ // @ts-ignore
+ , {
+ // @ts-ignore
+ block: true, className: "w-100", variant: "danger", icon: "remove", tabIndex: -1, disabled: disabled || readonly, onClick: onDropPropertyClick(label) }))));
+};
+export default WrapIfAdditional;
+//# sourceMappingURL=WrapIfAdditional.js.map
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/WrapIfAdditional.js.map b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/WrapIfAdditional.js.map
new file mode 100644
index 000000000..cd823ef9d
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/WrapIfAdditional.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"WrapIfAdditional.js","sourceRoot":"","sources":["WrapIfAdditional.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAC,KAAK,EAAC,MAAM,YAAY,CAAC;AAGjC,OAAO,GAAG,MAAM,qBAAqB,CAAC;AACtC,OAAO,GAAG,MAAM,qBAAqB,CAAC;AACtC,OAAO,IAAI,MAAM,sBAAsB,CAAC;AAExC,OAAO,UAAU,MAAM,mBAAmB,CAAC;AAE3C,MAAM,EAAC,wBAAwB,EAAC,GAAG,KAAK,CAAC;AAezC,MAAM,gBAAgB,GAAG,CAAC,EACI,QAAQ,EACR,QAAQ,EACR,EAAE,EACF,KAAK,EACL,mBAAmB,EACnB,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,MAAM,GACc,EAAE,EAAE;IAClD,MAAM,QAAQ,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC,SAAS;IAC1C,MAAM,UAAU,GAAG,MAAM,CAAC,cAAc,CAAC,wBAAwB,CAAC,CAAC;IAEnE,IAAI,CAAC,UAAU,EAAE;QACb,OAAO,QAAQ,CAAC;KACnB;IAED,MAAM,UAAU,GAAG,CAAC,EAAC,MAAM,EAAqC,EAAE,EAAE,CAChE,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAG9B,aAAa;IACb,OAAO,CACH,oBAAC,GAAG,IAAC,GAAG,EAAE,GAAG,EAAE,MAAM;QACjB,oBAAC,GAAG,IAAC,EAAE,EAAE,CAAC;YACN,oBAAC,IAAI,CAAC,KAAK;gBACP,oBAAC,IAAI,CAAC,KAAK,QAAE,QAAQ,CAAc;gBACnC,oBAAC,IAAI,CAAC,OAAO,IACT,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,KAAK,EACnB,QAAQ,EAAE,QAAQ,IAAI,QAAQ,EAC9B,EAAE,EAAE,GAAG,EAAE,MAAM,EACf,IAAI,EAAE,GAAG,EAAE,MAAM,EACjB,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EAC1C,IAAI,EAAC,MAAM,GACb,CACO,CACX;QACN,oBAAC,GAAG,IAAC,EAAE,EAAE,CAAC,IACL,QAAQ,CACP;QACN,oBAAC,GAAG,IAAC,EAAE,EAAE,CAAC,EAAE,SAAS,EAAC,MAAM;YACxB,oBAAC,UAAU;YACP,aAAa;;gBAAb,aAAa;gBACb,KAAK,EAAE,IAAI,EACX,SAAS,EAAC,OAAO,EACjB,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,CAAC,CAAC,EACZ,QAAQ,EAAE,QAAQ,IAAI,QAAQ,EAC9B,OAAO,EAAE,mBAAmB,CAAC,KAAK,CAAC,GACrC,CACA,CACJ,CACT,CAAC;AACN,CAAC,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/WrapIfAdditional.tsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/WrapIfAdditional.tsx
new file mode 100644
index 000000000..56b606bd2
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/CustomTemplateField/WrapIfAdditional.tsx
@@ -0,0 +1,85 @@
+import React from "react";
+
+import {utils} from "@rjsf/core";
+import {JSONSchema7} from "json-schema";
+
+import Row from "react-bootstrap/Row";
+import Col from "react-bootstrap/Col";
+import Form from "react-bootstrap/Form";
+
+import IconButton from "@rjsf/bootstrap-4";
+
+const {ADDITIONAL_PROPERTY_FLAG} = utils;
+
+type WrapIfAdditionalProps = {
+ children: React.ReactElement;
+ classNames: string;
+ disabled: boolean;
+ id: string;
+ label: string;
+ onDropPropertyClick: (index: string) => (event?: any) => void;
+ onKeyChange: (index: string) => (event?: any) => void;
+ readonly: boolean;
+ required: boolean;
+ schema: JSONSchema7;
+};
+
+const WrapIfAdditional = ({
+ children,
+ disabled,
+ id,
+ label,
+ onDropPropertyClick,
+ onKeyChange,
+ readonly,
+ required,
+ schema,
+ }: WrapIfAdditionalProps) => {
+ const keyLabel = `${label} Key`; // i18n ?
+ const additional = schema.hasOwnProperty(ADDITIONAL_PROPERTY_FLAG);
+
+ if (!additional) {
+ return children;
+ }
+
+ const handleBlur = ({target}: React.FocusEvent) =>
+ onKeyChange(target.value);
+
+
+ // @ts-ignore
+ return (
+
+
+
+ {keyLabel}
+
+
+
+
+ {children}
+
+
+
+
+
+ );
+};
+
+export default WrapIfAdditional;
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/IVSHMEM_VM/SelectWidget/SelectWidget.tsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/IVSHMEM_VM/SelectWidget/SelectWidget.tsx
new file mode 100644
index 000000000..0c5eef43a
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/IVSHMEM_VM/SelectWidget/SelectWidget.tsx
@@ -0,0 +1,130 @@
+import React from "react";
+
+import Form from "react-bootstrap/Form";
+
+import {WidgetProps} from "@rjsf/core";
+import {utils} from "@rjsf/core";
+import _ from "lodash";
+
+const {asNumber, guessType} = utils;
+
+const nums = new Set(["number", "integer"]);
+
+/**
+ * This is a silly limitation in the DOM where option change event values are
+ * always retrieved as strings.
+ */
+const processValue = (schema: any, value: any) => {
+ // "enum" is a reserved word, so only "type" and "items" can be destructured
+ const {type, items} = schema;
+ if (value === "") {
+ return undefined;
+ } else if (type === "array" && items && nums.has(items.type)) {
+ return value.map(asNumber);
+ } else if (type === "boolean") {
+ return value === "true";
+ } else if (type === "number") {
+ return asNumber(value);
+ }
+
+ // If type is undefined, but an enum is present, try and infer the type from
+ // the enum values
+ if (schema.enum) {
+ if (schema.enum.every((x: any) => guessType(x) === "number")) {
+ return asNumber(value);
+ } else if (schema.enum.every((x: any) => guessType(x) === "boolean")) {
+ return value === "true";
+ }
+ }
+
+ return value;
+};
+
+const SelectWidget = (
+ {
+ schema,
+ id,
+ options,
+ label,
+ required,
+ disabled,
+ readonly,
+ value,
+ multiple,
+ autofocus,
+ onChange,
+ onBlur,
+ onFocus,
+ placeholder,
+ rawErrors = [],
+ }: WidgetProps) => {
+ const {enumOptions, enumDisabled} = options;
+
+ const emptyValue = multiple ? [] : "";
+
+ function getValue(
+ event: React.FocusEvent | React.ChangeEvent | any,
+ multiple: Boolean,
+ ) {
+ if (multiple) {
+ return [].slice
+ .call(event.target.options as any)
+ .filter((o: any) => o.selected)
+ .map((o: any) => o.value);
+ } else {
+ return event.target.value;
+ }
+ }
+
+ let isFirst = _.endsWith(id, 'IVSHMEM_VM_0_VM_NAME')
+ return (
+
+
+
0 ? "is-invalid" : "")}
+ onBlur={
+ onBlur &&
+ ((event: React.FocusEvent) => {
+ const newValue = getValue(event, multiple);
+ onBlur(id, processValue(schema, newValue));
+ })
+ }
+ onFocus={
+ onFocus &&
+ ((event: React.FocusEvent) => {
+ const newValue = getValue(event, multiple);
+ onFocus(id, processValue(schema, newValue));
+ })
+ }
+ onChange={(event: React.ChangeEvent) => {
+ const newValue = getValue(event, multiple);
+ onChange(processValue(schema, newValue));
+ }}>
+ {!multiple && schema.default === undefined && (
+ {placeholder}
+ )}
+ {(enumOptions as any).map(({value, label}: any, i: number) => {
+ const disabled: any =
+ Array.isArray(enumDisabled) &&
+ (enumDisabled as any).indexOf(value) != -1;
+ return (
+
+ {label}
+
+ );
+ })}
+
+
+
+ );
+};
+
+export default SelectWidget;
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/IVSHMEM_VM/SelectWidget/index.ts b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/IVSHMEM_VM/SelectWidget/index.ts
new file mode 100644
index 000000000..6e104f2ea
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/IVSHMEM_VM/SelectWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./SelectWidget";
+export * from "./SelectWidget";
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/IVSHMEM_VM/TextWidget/TextWidget.tsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/IVSHMEM_VM/TextWidget/TextWidget.tsx
new file mode 100644
index 000000000..23e0ca0e5
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/IVSHMEM_VM/TextWidget/TextWidget.tsx
@@ -0,0 +1,72 @@
+import React from "react";
+
+import Form from "react-bootstrap/Form";
+
+import {WidgetProps} from "@rjsf/core";
+import _ from "lodash";
+
+const TextWidget = (
+ {
+ id,
+ placeholder,
+ required,
+ readonly,
+ disabled,
+ type,
+ label,
+ value,
+ onChange,
+ onBlur,
+ onFocus,
+ autofocus,
+ options,
+ schema,
+ rawErrors = [],
+ uiSchema,
+ }: WidgetProps) => {
+ const _onChange = ({
+ target: {value},
+ }: React.ChangeEvent) =>
+ onChange(value === "" ? options.emptyValue : value);
+ const _onBlur = ({target: {value}}: React.FocusEvent) =>
+ onBlur(id, value);
+ const _onFocus = ({
+ target: {value},
+ }: React.FocusEvent) => onFocus(id, value);
+ const inputType = (type || schema.type) === "string" ? "text" : `${type || schema.type}`;
+
+ // const classNames = [rawErrors.length > 0 ? "is-invalid" : "", type === 'file' ? 'custom-file-label': ""]
+ // let isFirst = _.endsWith(id, 'IVSHMEM_VM_0_VBDF')
+ return (
+
+
+
0 ? "is-invalid" : ""}
+ list={schema.examples ? `examples_${id}` : undefined}
+ type={inputType}
+ value={value || value === 0 ? value : ""}
+ onChange={_onChange}
+ onBlur={_onBlur}
+ onFocus={_onFocus}
+ />
+
+ {schema.examples ? (
+
+ {(schema.examples as string[])
+ .concat(schema.default ? ([schema.default] as string[]) : [])
+ .map((example: any) => {
+ return ;
+ })}
+
+ ) : null}
+
+ );
+};
+
+export default TextWidget;
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/IVSHMEM_VM/TextWidget/index.ts b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/IVSHMEM_VM/TextWidget/index.ts
new file mode 100644
index 000000000..e7e838908
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/IVSHMEM_VM/TextWidget/index.ts
@@ -0,0 +1,2 @@
+export {default} from "./TextWidget";
+export * from "./TextWidget";
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/index.jsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/index.jsx
new file mode 100644
index 000000000..d3ebf86f2
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigForm/index.jsx
@@ -0,0 +1,5 @@
+import './ConfigForm.css'
+import {ConfigForm} from "./ConfigForm";
+
+
+export default ConfigForm;
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/ConfigTabBar.css b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/ConfigTabBar.css
new file mode 100644
index 000000000..b59ad88c4
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/ConfigTabBar.css
@@ -0,0 +1,12 @@
+.TabBox {
+ display: flex;
+ user-select: none;
+ border: 1px solid #007B81;
+ box-sizing: border-box;
+ border-radius: 5px;
+}
+
+.rjsf button[type='submit'] {
+ display: none;
+}
+
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/ConfigTabBar.jsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/ConfigTabBar.jsx
new file mode 100644
index 000000000..5ac542d10
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/ConfigTabBar.jsx
@@ -0,0 +1,84 @@
+import React, {Component} from "react";
+import "./ConfigTabBar.css"
+import Tab from "./Tab";
+import TabSplitter from "./TabSplitter";
+import TabAdd from "./TabAdd";
+import {ACRNContext} from "../../../../ACRNContext";
+
+export default class ConfigTabBar extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ activeVMID: -1
+ }
+ }
+
+ componentDidMount = () => {
+ let {configurator} = this.context
+ this.funRegisterID = configurator.programLayer.register(
+ configurator.eventName.scenarioDataUpdate, () => {
+ this.forceUpdate()
+ }
+ )
+ }
+
+ componentWillUnmount = () => {
+ let {configurator} = this.context
+ configurator.programLayer.unregister(this.funRegisterID)
+ }
+
+ active = (activeVMID = null) => {
+ if (activeVMID == null) {
+ return this.state.activeVMID
+ }
+ this.props.callback ? this.props.callback(activeVMID) : null;
+ return this.setState({activeVMID: activeVMID})
+ }
+
+ tab = (vmConfig, desc) => {
+ console.log(vmConfig)
+ return
+ }
+
+
+ render = () => {
+ let {configurator} = this.context
+ let scenarioData = configurator.programLayer.scenarioData
+
+ let PRE_LAUNCHED_VM = scenarioData.vm.PRE_LAUNCHED_VM.map((vmConfig) => {
+ return this.tab(vmConfig, 'Pre-Launched')
+ })
+ let SERVICE_VM = scenarioData.vm.SERVICE_VM.map((vmConfig) => {
+ return this.tab(vmConfig, 'ServiceVM')
+ })
+ let POST_LAUNCHED_VM = scenarioData.vm.POST_LAUNCHED_VM.map((vmConfig) => {
+ return this.tab(vmConfig, 'Post-Launched')
+ })
+
+ return (
+
+
+
+ {PRE_LAUNCHED_VM}
+ {
+ configurator.programLayer.addVM('PRE_LAUNCHED_VM')
+ }}/>
+ {SERVICE_VM}
+ {POST_LAUNCHED_VM}
+ {
+ if (scenarioData.vm.SERVICE_VM.length === 0) {
+ configurator.programLayer.addVM('SERVICE_VM')
+ }
+ configurator.programLayer.addVM('POST_LAUNCHED_VM')
+ }}/>
+
+ )
+ }
+}
+ConfigTabBar.contextType = ACRNContext
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/Tab/Tab.css b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/Tab/Tab.css
new file mode 100644
index 000000000..e22c4815b
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/Tab/Tab.css
@@ -0,0 +1,17 @@
+div.TabBox{
+ overflow-x: auto;
+}
+.TabBox .Tab {
+ background: #E1F2EF;
+ border-radius: 5px;
+ color: #007B81;
+ border: 1px solid #007B81;
+ margin: 0 0.5rem 0 0;
+ cursor: pointer;
+ white-space: nowrap;
+}
+
+.TabBox .Tab.Active {
+ background: #373A77;
+ color: white;
+}
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/Tab/Tab.jsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/Tab/Tab.jsx
new file mode 100644
index 000000000..58ee1a84e
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/Tab/Tab.jsx
@@ -0,0 +1,22 @@
+import React, {Component} from "react";
+import './Tab.css'
+
+export default class Tab extends Component {
+ constructor(props) {
+ super(props);
+ }
+
+ setActive = () => {
+ this.props.active(this.props.VMID)
+ }
+
+ render() {
+ let active = this.props.active() === this.props.VMID ? "Active" : ''
+ return (
+
+
{this.props.title}
+
{this.props.desc}
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/Tab/index.jsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/Tab/index.jsx
new file mode 100644
index 000000000..d559af4d7
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/Tab/index.jsx
@@ -0,0 +1,3 @@
+import Tab from "./Tab";
+
+export default Tab
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabAdd/TabAdd.css b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabAdd/TabAdd.css
new file mode 100644
index 000000000..0fbb5a74c
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabAdd/TabAdd.css
@@ -0,0 +1,4 @@
+.TabAdd {
+ cursor: pointer;
+ white-space: nowrap;
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabAdd/TabAdd.jsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabAdd/TabAdd.jsx
new file mode 100644
index 000000000..5d6a18b40
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabAdd/TabAdd.jsx
@@ -0,0 +1,12 @@
+import React, {Component} from "react";
+import addIcon from './assets/Plus.svg'
+import './TabAdd.css'
+
+export default class TabAdd extends Component {
+ render() {
+ return ()
+ }
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabAdd/assets/Plus.svg b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabAdd/assets/Plus.svg
new file mode 100644
index 000000000..a3c91700f
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabAdd/assets/Plus.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabAdd/index.jsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabAdd/index.jsx
new file mode 100644
index 000000000..17a69f7d8
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabAdd/index.jsx
@@ -0,0 +1,3 @@
+import TabAdd from "./TabAdd";
+
+export default TabAdd
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabSplitter/TabSplitter.css b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabSplitter/TabSplitter.css
new file mode 100644
index 000000000..a64298309
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabSplitter/TabSplitter.css
@@ -0,0 +1,6 @@
+.TabSplitter {
+ height: 74px;
+ width: 0;
+ border-right: 1px solid #C4C4C4;
+ margin: 0 0.5rem;
+}
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabSplitter/TabSplitter.jsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabSplitter/TabSplitter.jsx
new file mode 100644
index 000000000..6751502a8
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabSplitter/TabSplitter.jsx
@@ -0,0 +1,10 @@
+import {Component} from "react";
+import './TabSplitter.css'
+
+export default class TabSplitter extends Component {
+ render() {
+ return (
+ {" "}
+ )
+ }
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabSplitter/index.jsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabSplitter/index.jsx
new file mode 100644
index 000000000..bfc0be382
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/TabSplitter/index.jsx
@@ -0,0 +1,3 @@
+import TabSplitter from "./TabSplitter";
+
+export default TabSplitter
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/index.jsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigTabBar/index.jsx
new file mode 100644
index 000000000..e69de29bb
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigureSettingsForScenario.css b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigureSettingsForScenario.css
new file mode 100644
index 000000000..5abc12ca6
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigureSettingsForScenario.css
@@ -0,0 +1,55 @@
+.nav-tabs .nav-link {
+ font-family: Roboto, serif;
+ font-style: normal;
+ font-weight: 500;
+ font-size: 22px;
+ line-height: 26px;
+
+ background: #E1F2EF;
+ border: 1px solid #007B81;
+ box-sizing: border-box;
+ box-shadow: inset 0 -1px 0 #D7D7D7;
+ border-radius: 5px 5px 0 0;
+ border-bottom: none;
+
+ color: #007B81;
+ margin-right: 4px;
+ padding: 1rem 1.5rem;
+}
+
+.nav-tabs .nav-link.active {
+ mix-blend-mode: normal;
+ border: 1px solid #373A77;
+ border-bottom: none;
+ box-sizing: border-box;
+ border-radius: 5px 5px 0 0;
+
+
+ font-weight: 600;
+
+ color: #373A77;
+}
+
+.nav-tabs .nav-link.active:after {
+ display: block;
+ content: "";
+ height: 3px;
+ background: white;
+ position: relative;
+ top: calc(1rem + 1px);
+ left: -1.5rem;
+ width: calc(100% + 3rem + 1px);
+}
+
+.tab-content {
+ margin-top: -2px;
+}
+.nav-tabs{
+ border-bottom: none;
+}
+
+.deleteVM {
+ height: 52px;
+ max-width: 210px;
+ width: 100%;
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigureSettingsForScenario.jsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigureSettingsForScenario.jsx
new file mode 100644
index 000000000..214c91f10
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/ConfigureSettingsForScenario.jsx
@@ -0,0 +1,93 @@
+import React, {Component} from "react";
+import "./ConfigureSettingsForScenario.css"
+import {Accordion, Button} from "react-bootstrap";
+import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
+import {faMinus} from "@fortawesome/free-solid-svg-icons";
+import ConfigTabBar from "./ConfigTabBar/ConfigTabBar";
+import ConfigForm from "./ConfigForm";
+import {ACRNContext} from "../../../ACRNContext";
+
+export default class ConfigureSettingsForScenario extends Component {
+ constructor(props) {
+ super(props);
+ // VMID possible value
+ // [-1 0 1 2 3]
+ this.state = {
+ VMID: -1,
+ tabMode: 'basic'
+ }
+ }
+
+ tabSwitch = (VMID) => {
+ this.setState({VMID: VMID})
+ document.querySelector('#BasicFormTab').click()
+ this.forceUpdate()
+ }
+
+ tab = (tabID, title, isActive) => {
+ let active = isActive ? 'active' : ''
+ return (
+
+ {title}
+
+
+ )
+ }
+
+ tabsContent = (mode, VMID) => {
+ let tabID = mode + 'Form'
+
+ return (
+
+
)
+ }
+
+ render = () => {
+ let {configurator} = this.context
+ return (
+
+
+
3. Configure settings for scenario and launch scripts
+
{
+ configurator.saveScenario()
+ .then(() => {
+ alert('Save successful!')
+ })
+ e.preventDefault();
+ e.stopPropagation();
+ }}>Save Scenario and Launch Scripts
+
+
+
+
+
+
+
+
+
+
+ {this.tab('BasicForm', 'Basic Parameters', true)}
+ {this.tab('AdvancedForm', 'Advanced Parameters', false)}
+
+
{
+ configurator.programLayer.deleteVM(this.state.VMID)
+ }}>
+ Delete VM
+
+
+
+ {this.tabsContent('Basic', this.state.VMID)}
+ {this.tabsContent('Advanced', this.state.VMID)}
+
+
+
+
+ )
+ }
+}
+ConfigureSettingsForScenario.contextType = ACRNContext
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/index.jsx b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/index.jsx
new file mode 100644
index 000000000..42a2b4174
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ConfigureSettingsForScenario/index.jsx
@@ -0,0 +1,3 @@
+import ConfigureSettingsForScenario from "./ConfigureSettingsForScenario";
+
+export default ConfigureSettingsForScenario
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/CreateNewOrImportAnExistingScenario.jsx b/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/CreateNewOrImportAnExistingScenario.jsx
new file mode 100644
index 000000000..5698f939b
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/CreateNewOrImportAnExistingScenario.jsx
@@ -0,0 +1,126 @@
+import React, {Component} from "react";
+import {Accordion, Button, Col, Form, Row} from "react-bootstrap";
+
+import CreateScenarioModal from "./CreateScenarioModal/CreateScenarioModal";
+import Banner from "../../../components/Banner";
+import {dialog} from "@tauri-apps/api";
+import {ACRNContext} from "../../../ACRNContext";
+
+export default class CreateNewOrImportAnExistingScenario extends Component {
+ constructor(props) {
+ super(props);
+ this.scenarioXMLSelect = React.createRef()
+ this.scenarioXMLFileInput = React.createRef()
+ this.state = {
+ scenarioConfigFiles: [],
+ stage: props.stage,
+ scenarioName: '',
+ scenarioConfig: {},
+ selected: null
+ }
+ }
+
+ componentDidMount() {
+ this.scenarioHistoryUpdate()
+ }
+
+ scenarioHistoryUpdate() {
+ let {configurator} = this.context
+ return configurator.getHistory('scenario').then((scenarioHistory) => {
+ this.setState({scenarioConfigFiles: scenarioHistory})
+ })
+ }
+
+
+ openFileDialog = () => {
+ dialog.open({directory: false, multiple: false}).then(this.scenarioChange)
+ }
+
+ scenarioChange = (filepath) => {
+ console.log(filepath)
+ let {configurator} = this.context
+ configurator.addHistory('scenario', filepath).then(() => {
+ this.scenarioHistoryUpdate().then(() => {
+ this.scenarioXMLSelect.current.value = filepath
+ })
+ })
+ }
+
+ importScenario = () => {
+ let {configurator} = this.context
+ configurator.programLayer.loadScenario(this.scenarioXMLSelect.current.value)
+ .then(() => {
+ this.setState({selected: configurator.WorkingFolder + '/scenario.xml'})
+ })
+ .catch((reason) => {
+ console.log(reason)
+ alert(reason)
+ })
+ };
+
+ render = () => {
+ let {configurator} = this.context
+ let scenarioHistorySelect = this.state.scenarioConfigFiles.map((optionValue, index) => {
+ return ({optionValue} )
+ })
+ return (
+
+
+
+ 2. Create new or import an existing scenario
+
+
+
+
+
+ Current scenario:
+ {this.state.selected ? this.state.selected : "none selected"}
+
+ {
+ this.setState({selected: configurator.WorkingFolder + '/scenario.xml'})
+ }}/>
+
+
+
+
+
+
+
+
+
+ )
+ }
+}
+CreateNewOrImportAnExistingScenario.contextType = ACRNContext
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/CreateScenarioModal/CreateScenarioModal.css b/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/CreateScenarioModal/CreateScenarioModal.css
new file mode 100644
index 000000000..27786294e
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/CreateScenarioModal/CreateScenarioModal.css
@@ -0,0 +1,17 @@
+.vmNum input[type="number"] {
+ max-width: 69px;
+ text-align: center;
+}
+.vmNum b{
+ padding: 0 0;
+}
+
+.vmNum b, .vmNum input {
+ flex: 0 1 50%;
+}
+
+.modal-header .btn-close{
+ margin-right: 15px;
+ color: white;
+ font-weight: bolder;
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/CreateScenarioModal/CreateScenarioModal.jsx b/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/CreateScenarioModal/CreateScenarioModal.jsx
new file mode 100644
index 000000000..bd9438fc7
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/CreateScenarioModal/CreateScenarioModal.jsx
@@ -0,0 +1,128 @@
+import {Button, Form, Modal} from "react-bootstrap";
+import {Component} from "react";
+import "./CreateScenarioModal.css"
+import {ACRNContext} from "../../../../ACRNContext";
+
+export default class CreateScenarioModal extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ show: false,
+ mode: 'POST_LAUNCHED_VM'
+ }
+ }
+
+
+ handelScenarioCreateEvent = () => {
+ function getVal(id) {
+ // @ts-ignore
+ return document.querySelector('#' + id + 'Inp').value
+ }
+
+ function getInt(id) {
+ return parseInt(getVal(id))
+ }
+
+ const vmNum = {
+ PRE_LAUNCHED_VM: this.state.mode === 'POST_LAUNCHED_VM' ? 0 : getInt('PRE_LAUNCHED_VM'),
+ SERVICE_VM: this.state.mode === 'PRE_LAUNCHED_VM' ? 0 : getInt('SERVICE_VM'),
+ POST_LAUNCHED_VM: this.state.mode === 'PRE_LAUNCHED_VM' ? 0 : getInt('POST_LAUNCHED_VM'),
+ }
+ let {configurator} = this.context
+
+ configurator.programLayer.newScenario(vmNum.PRE_LAUNCHED_VM, vmNum.SERVICE_VM, vmNum.POST_LAUNCHED_VM)
+ this.props.cb()
+ this.setState({show: false})
+ }
+
+
+ render = () => {
+
+ const handleClose = () => this.setState({show: false});
+ const handleShow = () => this.setState({show: true});
+ const handleScenarioModeChange = (event) => this.setState({mode: event.target.id})
+
+ return (
+ <>
+
+ Create Scenario…
+
+
+
+
+ Create a new Scenario
+
+
+
+
+
+
+ Cancel
+
+
+ Create
+
+
+
+ >
+ )
+ }
+}
+
+CreateScenarioModal.contextType = ACRNContext
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/CreateScenarioModal/index.jsx b/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/CreateScenarioModal/index.jsx
new file mode 100644
index 000000000..f70751faf
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/CreateScenarioModal/index.jsx
@@ -0,0 +1,3 @@
+import CreateScenarioModal from "./CreateScenarioModal";
+
+export default CreateScenarioModal
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/index.jsx b/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/index.jsx
new file mode 100644
index 000000000..9a5af579d
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/CreateNewOrImportAnExistingScenario/index.jsx
@@ -0,0 +1,3 @@
+import CreateNewOrImportAnExistingScenario from "./CreateNewOrImportAnExistingScenario";
+
+export default CreateNewOrImportAnExistingScenario
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ExitACRNConfigurationModal/ExitACRNConfigurationModal.jsx b/misc/config_tools/configurator/src/pages/Config/ExitACRNConfigurationModal/ExitACRNConfigurationModal.jsx
new file mode 100644
index 000000000..84792f416
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ExitACRNConfigurationModal/ExitACRNConfigurationModal.jsx
@@ -0,0 +1,63 @@
+import {CloseButton, Button, Modal} from "react-bootstrap";
+import {Component, useState} from "react";
+import {ACRNContext} from "../../../ACRNContext";
+
+export default class ExitACRNConfigurationModal extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ show: false
+ }
+ }
+
+ render() {
+ let {configurator} = this.context
+
+ const handleClose = () => this.setState({show: false});
+ const handleShow = (e) => {
+ this.setState({show: true})
+ e.preventDefault()
+ }
+
+ return (
+ <>
+
+
+
+
+ Exit ACRN Configuration
+
+
+
+
ACRN Configuration files will be saved in the working folder:
+
+ {configurator.WorkingFolder}
+
+
+
+
+
+ Cancel
+
+ {
+ console.log('exit without save')
+ window.close()
+ }} size="lg" style={{width: '237px'}}>
+ Discard Changes
+
+ {
+
+ configurator.saveScenario().then(() => {
+ window.close()
+ })
+ }} size="lg" style={{width: '137px'}}>
+ Save
+
+
+
+ >
+ );
+ }
+}
+ExitACRNConfigurationModal.contextType = ACRNContext
diff --git a/misc/config_tools/configurator/src/pages/Config/ImportABoardConfigurationFile/ImportABoardConfigurationFile.jsx b/misc/config_tools/configurator/src/pages/Config/ImportABoardConfigurationFile/ImportABoardConfigurationFile.jsx
new file mode 100644
index 000000000..27f45d9bd
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ImportABoardConfigurationFile/ImportABoardConfigurationFile.jsx
@@ -0,0 +1,165 @@
+import React, {Component} from "react";
+import {Accordion, Button, Col, Form, Row} from "react-bootstrap";
+import Banner from "../../../components/Banner";
+import {dialog} from "@tauri-apps/api";
+import {ACRNContext} from "../../../ACRNContext";
+
+export default class ImportABoardConfigurationFile extends Component {
+ constructor(props) {
+ super(props);
+ this.boardXMLSelect = React.createRef()
+ this.boardXMLFileInput = React.createRef()
+ this.state = {
+ disableImport: true,
+ boardFiles: [],
+ boardName: '',
+ boardXML: '',
+ BIOS_INFO: '',
+ BASE_BOARD_INFO: ''
+ }
+ }
+
+ componentDidMount() {
+ let {configurator} = this.context
+ configurator.getHistory('board').then((boardFiles) => {
+ let disableImport = boardFiles.length === 0
+ this.setState({boardFiles, disableImport})
+ })
+ }
+
+
+ openFileDialog = () => {
+ //this.boardXMLFileInput.current.click()
+ dialog.open({directory: false, multiple: false}).then((filepath) => {
+ this.boardChange(filepath)
+ })
+ };
+
+ boardChange = (filepath) => {
+ let {configurator} = this.context
+ configurator.addHistory('board', filepath).then(() => {
+ return configurator.getHistory('board')
+ }).then((boardFiles) => {
+ console.log(boardFiles)
+ this.setState({
+ disableImport: false,
+ boardFiles
+ })
+ }).then(() => {
+ this.boardXMLSelect.current.value = filepath
+ })
+ }
+
+ importBoard = () => {
+ let {configurator} = this.context
+
+ if (!this.state.boardXML) {
+ configurator.loadBoard(this.boardXMLSelect.current.value, this.updateBoardInfo)
+ } else {
+ this.openFileDialog()
+ }
+ }
+
+ getNodeTextContent(boardXML, nodeSelector) {
+ return boardXML.querySelector(nodeSelector).textContent;
+ }
+
+ updateBoardInfo = (boardName, boardXMLText) => {
+ const boardXML = (new DOMParser()).parseFromString(boardXMLText, 'text/xml')
+ let BIOS_INFO = this.getNodeTextContent(boardXML, "BIOS_INFO").replace(/\s+BIOS Information\s+/, '')
+ let BASE_BOARD_INFO = this.getNodeTextContent(boardXML, "BASE_BOARD_INFO").replace(/\s+Base Board Information\s+/, '')
+ this.setState({
+ boardName: boardName,
+ boardXML: boardXML,
+ BIOS_INFO: BIOS_INFO,
+ BASE_BOARD_INFO: BASE_BOARD_INFO
+ })
+ this.boardXMLSelect.current.disabled = true
+ }
+
+ render() {
+ let boardHistorySelect = this.state.boardFiles.map((optionValue, index) => {
+ return (
+ {optionValue}
+ )
+ })
+ return (
+
+
+
+ 1. Import a board configuration file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {this.state.boardXML ? "Current Board: " + this.state.boardName : "No board information has been imported yet."}
+
+
+
+
+
+ {this.state.BASE_BOARD_INFO}
+
+
+ {this.state.BIOS_INFO}
+
+
+
+
+
+
+
+
+
+ )
+ }
+}
+ImportABoardConfigurationFile.contextType = ACRNContext
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Config/ImportABoardConfigurationFile/index.jsx b/misc/config_tools/configurator/src/pages/Config/ImportABoardConfigurationFile/index.jsx
new file mode 100644
index 000000000..57c43eeea
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Config/ImportABoardConfigurationFile/index.jsx
@@ -0,0 +1,3 @@
+import ImportABoardConfigurationFile from "./ImportABoardConfigurationFile";
+
+export default ImportABoardConfigurationFile
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Error/Error.jsx b/misc/config_tools/configurator/src/pages/Error/Error.jsx
new file mode 100644
index 000000000..19763d6f4
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Error/Error.jsx
@@ -0,0 +1,15 @@
+import React from 'react';
+
+export class Error extends React.Component {
+ render() {
+ return (
+
+ There's nothing here!
+ Your URL: {document.location.href}
+
+ );
+ }
+}
+
+
+export default Error;
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Error/MyErrorBoundary.jsx b/misc/config_tools/configurator/src/pages/Error/MyErrorBoundary.jsx
new file mode 100644
index 000000000..e0bf6c260
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Error/MyErrorBoundary.jsx
@@ -0,0 +1,23 @@
+import {Component} from "react";
+
+export default class MyErrorBoundary extends Component {
+ state = {
+ hasError: false,
+ };
+
+ static getDerivedStateFromError(error) {
+ return {hasError: true};
+ };
+
+ componentDidCatch(error, errorInfo) {
+ // A custom error logging function
+ console.log(error, errorInfo);
+ };
+
+ render() {
+ return this.state.hasError ? <>
+ Error detect, you can see error log in vconsole at right bottom.
+ Please report error log to me, https://github.com/Weiyi-Feng/acrn-hypervisor/issue .
+ > : this.props.children;
+ }
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Welcome/ExitACRNConfigurationModal/ExitACRNConfigurationModal.jsx b/misc/config_tools/configurator/src/pages/Welcome/ExitACRNConfigurationModal/ExitACRNConfigurationModal.jsx
new file mode 100644
index 000000000..25ceb1654
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Welcome/ExitACRNConfigurationModal/ExitACRNConfigurationModal.jsx
@@ -0,0 +1,41 @@
+import {CloseButton, Button, Modal} from "react-bootstrap";
+import {useState} from "react";
+
+export default function ExitACRNConfigurationModal(props) {
+ const [show, setShow] = useState(false);
+
+ const handleClose = () => setShow(false);
+ const handleShow = (e) => {
+ setShow(true);
+ e.preventDefault()
+ }
+
+ return (
+ <>
+
+
+
+
+ Exit ACRN Configuration
+
+
+
+ Do you want exit ACRN Configuration?
+
+
+
+
+ Cancel
+
+ {
+ window.close()
+ }} size="lg" style={{width: '137px'}}>
+ Yes
+
+
+
+ >
+ );
+}
+
diff --git a/misc/config_tools/configurator/src/pages/Welcome/StartNewConfigurationPanel/StartNewConfigurationPanel.jsx b/misc/config_tools/configurator/src/pages/Welcome/StartNewConfigurationPanel/StartNewConfigurationPanel.jsx
new file mode 100644
index 000000000..f50b6618d
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Welcome/StartNewConfigurationPanel/StartNewConfigurationPanel.jsx
@@ -0,0 +1,138 @@
+import React from "react";
+import {useNavigate} from "react-router";
+import {Button, Form} from "react-bootstrap";
+
+import Confirm from "../../../components/Confirm/Confirm";
+import {dialog, fs} from "@tauri-apps/api";
+import {ACRNContext} from "../../../ACRNContext";
+import {invoke} from "@tauri-apps/api/tauri";
+
+class StartNewConfigurationPanel extends React.Component {
+ constructor(props, context) {
+ super(props);
+ const {navigate} = this.props;
+ this.navigate = navigate
+ this.pathInput = React.createRef()
+
+ this.state = {defaultPath: ""}
+ }
+
+ componentDidMount = () => {
+ invoke("get_home",).then(
+ (path) => {
+ let ps = "/";
+ if (path.indexOf("\\") > 0) {
+ ps = "\\"
+ }
+ path = [path, "acrn-work", "MyConfiguration"].join(ps) + ps
+ console.log(path)
+ this.setState({defaultPath: path})
+
+ }
+ )
+ }
+
+ addRecentDir = async (dirPath) => {
+ let {configurator} = this.context;
+ await configurator.addHistory("recentlyWorkingFolders", dirPath);
+ }
+
+ nextPage = (WorkingFolder) => {
+ this.addRecentDir(WorkingFolder)
+ .then(() => {
+ let {configurator} = this.context;
+ let params = configurator.settingWorkingFolder(WorkingFolder)
+ this.navigate(params);
+ })
+ }
+
+ openDir = () => {
+ dialog.open({
+ title: "Start new configurator",
+ directory: true,
+ multiple: false
+ }).then((filePath) => {
+ this.pathInput.current.value = filePath
+ }).catch()
+ }
+
+ useFolder = async () => {
+ let {helper} = this.context
+ let folderPath = this.pathInput.current.value ? this.pathInput.current.value : this.pathInput.current.placeholder;
+ helper.resolveHome(folderPath).then((homeResolvePath) => {
+ fs.readDir(homeResolvePath)
+ .then((files) => {
+ console.log("Directory exists.", files)
+ if (files.length > 0) {
+ confirm("Directory exists, overwrite it?")
+ .then((r) => {
+ if (r) this.nextPage(folderPath)
+ })
+ } else {
+ this.nextPage(folderPath)
+ }
+ })
+ .catch(() => {
+ fs.createDir(homeResolvePath, {recursive: true})
+ .then(() => {
+ console.log('Directory created successfully!');
+ this.nextPage(folderPath)
+ }).catch((err) => {
+ return console.error(err);
+ })
+ })
+ })
+ }
+
+
+ handleConfirm = (params) => {
+ console.log(params)
+ }
+
+
+ render = () => {
+ return ()
+ }
+}
+
+StartNewConfigurationPanel.contextType = ACRNContext;
+
+export default function (props) {
+ const navigate = useNavigate();
+
+ return ;
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Welcome/StartNewConfigurationPanel/index.jsx b/misc/config_tools/configurator/src/pages/Welcome/StartNewConfigurationPanel/index.jsx
new file mode 100644
index 000000000..02eed6cdb
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Welcome/StartNewConfigurationPanel/index.jsx
@@ -0,0 +1,3 @@
+import StartNewConfigurationPanel from "./StartNewConfigurationPanel";
+
+export default StartNewConfigurationPanel
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Welcome/UseAnExistingConfigurationPanel/UseAnExistingConfigurationPanel.jsx b/misc/config_tools/configurator/src/pages/Welcome/UseAnExistingConfigurationPanel/UseAnExistingConfigurationPanel.jsx
new file mode 100644
index 000000000..1e2217bf0
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Welcome/UseAnExistingConfigurationPanel/UseAnExistingConfigurationPanel.jsx
@@ -0,0 +1,123 @@
+import {useNavigate} from "react-router";
+import React from "react";
+import {Button, Form} from "react-bootstrap";
+import {dialog} from "@tauri-apps/api";
+import {ACRNContext} from "../../../ACRNContext";
+
+
+class UseAnExistingConfigurationPanel extends React.Component {
+ constructor(props) {
+ super(props);
+ const {navigate} = this.props;
+ this.navigate = navigate
+ this.pathSelect = React.createRef()
+ this.state = {
+ recentDirs: []
+ }
+ }
+
+ componentDidMount() {
+ this.updateHistory()
+ }
+
+ updateHistory = () => {
+ let {configurator} = this.context
+ return configurator.getHistory("recentlyWorkingFolders")
+ .then((recentDirs) => {
+ this.setState({recentDirs})
+ })
+ }
+
+ recentDir = () => {
+ let recent = this.state.recentDirs;
+ let result = []
+ for (let i = 0; i < recent.length; i++) {
+ let dirPath = recent[i];
+ result.push(
+ {dirPath}
+ )
+ }
+
+ return result;
+ }
+
+ addRecentDir = (dirPath) => {
+ let {config, configurator} = this.context
+ return configurator.addHistory("recentlyWorkingFolders", dirPath)
+ }
+
+ nextPage = (WorkingFolder) => {
+ let {configurator} = this.context
+ this.addRecentDir(WorkingFolder).then(() => {
+ let params = configurator.settingWorkingFolder(WorkingFolder)
+ this.navigate(params);
+ })
+ }
+
+ openDir = async () => {
+ await dialog.open({
+ title: 'Open Working Folder',
+ directory: true,
+ multiple: false
+ }).then(async (existDir) => {
+ await this.addRecentDir(existDir)
+ await this.updateHistory()
+ this.pathSelect.current.value = existDir
+ }).catch()
+ }
+
+ useFolder = () => {
+ let folderPath = this.pathSelect.current.value;
+ if (!folderPath) {
+ alert("Please select existing configuration folder!")
+ return
+ }
+ this.nextPage(folderPath)
+ }
+
+ render() {
+ let recent_dir = this.recentDir();
+ return (
+
+ )
+ }
+}
+
+UseAnExistingConfigurationPanel.contextType = ACRNContext
+
+export default function (props) {
+ const navigate = useNavigate();
+
+ return ;
+}
diff --git a/misc/config_tools/configurator/src/pages/Welcome/UseAnExistingConfigurationPanel/index.jsx b/misc/config_tools/configurator/src/pages/Welcome/UseAnExistingConfigurationPanel/index.jsx
new file mode 100644
index 000000000..8a1258bda
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Welcome/UseAnExistingConfigurationPanel/index.jsx
@@ -0,0 +1,3 @@
+import UseAnExistingConfigurationPanel from "./UseAnExistingConfigurationPanel";
+
+export default UseAnExistingConfigurationPanel
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/pages/Welcome/Welcome.css b/misc/config_tools/configurator/src/pages/Welcome/Welcome.css
new file mode 100644
index 000000000..8f25f2239
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Welcome/Welcome.css
@@ -0,0 +1,8 @@
+.wel-btn {
+ max-width: 221px;
+ width: 100%;
+}
+
+.banner.welcome {
+ min-height: 164px;
+}
diff --git a/misc/config_tools/configurator/src/pages/Welcome/Welcome.jsx b/misc/config_tools/configurator/src/pages/Welcome/Welcome.jsx
new file mode 100644
index 000000000..61c49c1cd
--- /dev/null
+++ b/misc/config_tools/configurator/src/pages/Welcome/Welcome.jsx
@@ -0,0 +1,53 @@
+import React from "react";
+import {Col, Container, Row} from "react-bootstrap";
+
+import './Welcome.css'
+
+import Banner from "../../components/Banner";
+
+import StartNewConfigurationPanel from "./StartNewConfigurationPanel";
+import UseAnExistingConfigurationPanel from "./UseAnExistingConfigurationPanel";
+import Footer from "../../components/Footer";
+
+class Welcome extends React.Component {
+ render() {
+ return (
+
+
+
+
+ ACRN Configurator helps you set up and customize your ACRN hypervisor and VMs.
+
+
+
+
This is a preview version, please be careful.
+ If you find a bug, please report it to
+
+ https://github.com/Weiyi-Feng/acrn-hypervisor
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ }
+}
+
+export default Welcome
diff --git a/misc/config_tools/configurator/src/plugin/tauri-plugin.ts b/misc/config_tools/configurator/src/plugin/tauri-plugin.ts
new file mode 100644
index 000000000..4cb4ce9e7
--- /dev/null
+++ b/misc/config_tools/configurator/src/plugin/tauri-plugin.ts
@@ -0,0 +1,71 @@
+const fs = require('fs')
+const path = require('path')
+
+import type {ConfigEnv, Plugin, ResolvedConfig} from "vite";
+import replace from "@rollup/plugin-replace";
+
+import cli from "@tauri-apps/cli"
+import Config from "../../src-tauri/types/config"
+
+import tauriConf from "../../src-tauri/tauri.json";
+
+
+interface Options {
+ config?: (c: Config, e: ConfigEnv) => Config;
+}
+
+
+export default (options?: Options): Plugin => {
+ let tauriConfig: Config = {...tauriConf};
+ let viteConfig: ResolvedConfig;
+
+ const tauri = (mode: "dev" | "build"): Promise => {
+ // Generate `tauri.conf.json` by `tauri.json`.
+ console.log("Generate `tauri.conf.json` by `tauri.json`.")
+ let filePath = path.resolve(__dirname, '..', '..', 'src-tauri', 'tauri.conf.json')
+ let config = JSON.stringify(tauriConfig)
+ try {
+ fs.writeFileSync(filePath, config)
+ return cli.run([mode], 'tauri')
+ } catch (err) {
+ console.error(err)
+ return Promise.reject(err)
+ }
+ }
+
+ return {
+ ...replace({
+ "process.env.IS_TAURI": "true",
+ preventAssignment: false
+ }),
+ name: "tauri-plugin",
+ configureServer(server) {
+ server?.httpServer?.on("listening", () => {
+ if (!process.env.TAURI_SERVE) {
+ process.env.TAURI_SERVE = "true";
+ delete tauriConfig["$schema"]
+ tauri('dev').finally()
+ }
+ });
+ },
+ closeBundle() {
+ if (!process.env.TAURI_BUILD) {
+ process.env.TAURI_BUILD = "true";
+ delete tauriConfig["$schema"]
+ tauri('build').finally()
+ }
+ },
+ config(viteConfig, env) {
+ process.env.IS_TAURI = "true";
+ if (options && options.config) {
+ options.config(tauriConfig, env);
+ }
+ if (env.command === "build") {
+ viteConfig.base = "/";
+ }
+ },
+ configResolved(resolvedConfig) {
+ viteConfig = resolvedConfig;
+ },
+ };
+};
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/plugin/text-plugin.js b/misc/config_tools/configurator/src/plugin/text-plugin.js
new file mode 100644
index 000000000..2ebdfa92e
--- /dev/null
+++ b/misc/config_tools/configurator/src/plugin/text-plugin.js
@@ -0,0 +1,17 @@
+export default function textFileResolver(fileRegex) {
+ function compileFileToJS(src) {
+ return src;
+ }
+ return {
+ name: 'transform-xsd-file',
+ transform(src, id) {
+ if (fileRegex.test(id)) {
+ return {
+ code: compileFileToJS(src),
+ map: null // 如果可行将提供 source map
+ };
+ }
+ }
+ };
+}
+//# sourceMappingURL=text-plugin.js.map
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/plugin/text-plugin.js.map b/misc/config_tools/configurator/src/plugin/text-plugin.js.map
new file mode 100644
index 000000000..53d2a40a1
--- /dev/null
+++ b/misc/config_tools/configurator/src/plugin/text-plugin.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"text-plugin.js","sourceRoot":"","sources":["text-plugin.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,SAAiB;IACtD,SAAS,eAAe,CAAC,GAAW;QAChC,OAAO,GAAG,CAAA;IACd,CAAC;IAED,OAAO;QACH,IAAI,EAAE,oBAAoB;QAC1B,SAAS,CAAC,GAAW,EAAE,EAAU;YAC7B,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;gBACpB,OAAO;oBACH,IAAI,EAAE,eAAe,CAAC,GAAG,CAAC;oBAC1B,GAAG,EAAE,IAAI,CAAC,qBAAqB;iBAClC,CAAA;aACJ;QACL,CAAC;KACJ,CAAA;AACL,CAAC"}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/src/plugin/text-plugin.ts b/misc/config_tools/configurator/src/plugin/text-plugin.ts
new file mode 100644
index 000000000..eab74d1e8
--- /dev/null
+++ b/misc/config_tools/configurator/src/plugin/text-plugin.ts
@@ -0,0 +1,24 @@
+import _ from "lodash";
+import * as fs from "fs";
+
+export default function textFileResolver(fileRegex: RegExp) {
+ function compileFileToJS(id: string, src: string) {
+ if (_.endsWith(id, '.txt')) {
+ let src = fs.readFileSync(id, {encoding: "utf-8"})
+ return "export default " + JSON.stringify(src)
+ }
+ return "export default " + JSON.stringify(src)
+ }
+
+ return {
+ name: 'text-file-resolver',
+ transform(src: string, id: string) {
+ if (fileRegex.test(id)) {
+ return {
+ code: compileFileToJS(id, src),
+ map: null // 如果可行将提供 source map
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/tsconfig.json b/misc/config_tools/configurator/tsconfig.json
new file mode 100644
index 000000000..9450bff21
--- /dev/null
+++ b/misc/config_tools/configurator/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "target": "esnext",
+ "module": "esnext",
+ "strict": true,
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "sourceMap": true,
+ "esModuleInterop": true,
+ "lib": [
+ "esnext",
+ "dom"
+ ],
+ "types": [
+ "vite/client"
+ ],
+ "jsx": "react"
+ }
+}
\ No newline at end of file
diff --git a/misc/config_tools/configurator/vite.config.js b/misc/config_tools/configurator/vite.config.js
new file mode 100644
index 000000000..5435694b0
--- /dev/null
+++ b/misc/config_tools/configurator/vite.config.js
@@ -0,0 +1,17 @@
+import path from "path";
+
+import {defineConfig} from 'vite'
+
+import react from '@vitejs/plugin-react'
+import tauri from './src/plugin/tauri-plugin'
+import textFileResolver from './src/plugin/text-plugin'
+
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ base: './',
+ plugins: [react(), tauri(), textFileResolver(/\.(xsd|py|txt)$/)],
+ build: {
+ outDir: path.resolve(__dirname, 'build')
+ }
+})
diff --git a/misc/config_tools/configurator/yarn.lock b/misc/config_tools/configurator/yarn.lock
new file mode 100644
index 000000000..5616c8184
--- /dev/null
+++ b/misc/config_tools/configurator/yarn.lock
@@ -0,0 +1,1399 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@ampproject/remapping@^2.1.0":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34"
+ integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==
+ dependencies:
+ "@jridgewell/trace-mapping" "^0.3.0"
+
+"@babel/code-frame@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789"
+ integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==
+ dependencies:
+ "@babel/highlight" "^7.16.7"
+
+"@babel/compat-data@^7.16.4":
+ version "7.17.0"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.0.tgz#86850b8597ea6962089770952075dcaabb8dba34"
+ integrity sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==
+
+"@babel/core@^7.16.12":
+ version "7.17.5"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.5.tgz#6cd2e836058c28f06a4ca8ee7ed955bbf37c8225"
+ integrity sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA==
+ dependencies:
+ "@ampproject/remapping" "^2.1.0"
+ "@babel/code-frame" "^7.16.7"
+ "@babel/generator" "^7.17.3"
+ "@babel/helper-compilation-targets" "^7.16.7"
+ "@babel/helper-module-transforms" "^7.16.7"
+ "@babel/helpers" "^7.17.2"
+ "@babel/parser" "^7.17.3"
+ "@babel/template" "^7.16.7"
+ "@babel/traverse" "^7.17.3"
+ "@babel/types" "^7.17.0"
+ convert-source-map "^1.7.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.2"
+ json5 "^2.1.2"
+ semver "^6.3.0"
+
+"@babel/generator@^7.17.3":
+ version "7.17.3"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.3.tgz#a2c30b0c4f89858cb87050c3ffdfd36bdf443200"
+ integrity sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==
+ dependencies:
+ "@babel/types" "^7.17.0"
+ jsesc "^2.5.1"
+ source-map "^0.5.0"
+
+"@babel/helper-annotate-as-pure@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862"
+ integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-compilation-targets@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b"
+ integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==
+ dependencies:
+ "@babel/compat-data" "^7.16.4"
+ "@babel/helper-validator-option" "^7.16.7"
+ browserslist "^4.17.5"
+ semver "^6.3.0"
+
+"@babel/helper-environment-visitor@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7"
+ integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-function-name@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f"
+ integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==
+ dependencies:
+ "@babel/helper-get-function-arity" "^7.16.7"
+ "@babel/template" "^7.16.7"
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-get-function-arity@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419"
+ integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-hoist-variables@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246"
+ integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-module-imports@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437"
+ integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-module-transforms@^7.16.7":
+ version "7.17.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.6.tgz#3c3b03cc6617e33d68ef5a27a67419ac5199ccd0"
+ integrity sha512-2ULmRdqoOMpdvkbT8jONrZML/XALfzxlb052bldftkicAUy8AxSCkD5trDPQcwHNmolcl7wP6ehNqMlyUw6AaA==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.16.7"
+ "@babel/helper-module-imports" "^7.16.7"
+ "@babel/helper-simple-access" "^7.16.7"
+ "@babel/helper-split-export-declaration" "^7.16.7"
+ "@babel/helper-validator-identifier" "^7.16.7"
+ "@babel/template" "^7.16.7"
+ "@babel/traverse" "^7.17.3"
+ "@babel/types" "^7.17.0"
+
+"@babel/helper-plugin-utils@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5"
+ integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==
+
+"@babel/helper-simple-access@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7"
+ integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-split-export-declaration@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b"
+ integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-validator-identifier@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
+ integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
+
+"@babel/helper-validator-option@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23"
+ integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==
+
+"@babel/helpers@^7.17.2":
+ version "7.17.2"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.2.tgz#23f0a0746c8e287773ccd27c14be428891f63417"
+ integrity sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==
+ dependencies:
+ "@babel/template" "^7.16.7"
+ "@babel/traverse" "^7.17.0"
+ "@babel/types" "^7.17.0"
+
+"@babel/highlight@^7.16.7":
+ version "7.16.10"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88"
+ integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.16.7"
+ chalk "^2.0.0"
+ js-tokens "^4.0.0"
+
+"@babel/parser@^7.16.7", "@babel/parser@^7.17.3":
+ version "7.17.3"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.3.tgz#b07702b982990bf6fdc1da5049a23fece4c5c3d0"
+ integrity sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA==
+
+"@babel/plugin-syntax-jsx@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665"
+ integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-react-jsx-development@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz#43a00724a3ed2557ed3f276a01a929e6686ac7b8"
+ integrity sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==
+ dependencies:
+ "@babel/plugin-transform-react-jsx" "^7.16.7"
+
+"@babel/plugin-transform-react-jsx-self@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.16.7.tgz#f432ad0cba14c4a1faf44f0076c69e42a4d4479e"
+ integrity sha512-oe5VuWs7J9ilH3BCCApGoYjHoSO48vkjX2CbA5bFVhIuO2HKxA3vyF7rleA4o6/4rTDbk6r8hBW7Ul8E+UZrpA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-react-jsx-source@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.16.7.tgz#1879c3f23629d287cc6186a6c683154509ec70c0"
+ integrity sha512-rONFiQz9vgbsnaMtQlZCjIRwhJvlrPET8TabIUK2hzlXw9B9s2Ieaxte1SCOOXMbWRHodbKixNf3BLcWVOQ8Bw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-react-jsx@^7.16.7":
+ version "7.17.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz#eac1565da176ccb1a715dae0b4609858808008c1"
+ integrity sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.16.7"
+ "@babel/helper-module-imports" "^7.16.7"
+ "@babel/helper-plugin-utils" "^7.16.7"
+ "@babel/plugin-syntax-jsx" "^7.16.7"
+ "@babel/types" "^7.17.0"
+
+"@babel/runtime@^7.13.16", "@babel/runtime@^7.14.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.7":
+ version "7.17.2"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941"
+ integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
+"@babel/runtime@^7.17.2":
+ version "7.17.8"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.8.tgz#3e56e4aff81befa55ac3ac6a0967349fd1c5bca2"
+ integrity sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
+"@babel/template@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
+ integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==
+ dependencies:
+ "@babel/code-frame" "^7.16.7"
+ "@babel/parser" "^7.16.7"
+ "@babel/types" "^7.16.7"
+
+"@babel/traverse@^7.17.0", "@babel/traverse@^7.17.3":
+ version "7.17.3"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57"
+ integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==
+ dependencies:
+ "@babel/code-frame" "^7.16.7"
+ "@babel/generator" "^7.17.3"
+ "@babel/helper-environment-visitor" "^7.16.7"
+ "@babel/helper-function-name" "^7.16.7"
+ "@babel/helper-hoist-variables" "^7.16.7"
+ "@babel/helper-split-export-declaration" "^7.16.7"
+ "@babel/parser" "^7.17.3"
+ "@babel/types" "^7.17.0"
+ debug "^4.1.0"
+ globals "^11.1.0"
+
+"@babel/types@^7.16.7", "@babel/types@^7.17.0":
+ version "7.17.0"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b"
+ integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.16.7"
+ to-fast-properties "^2.0.0"
+
+"@fortawesome/fontawesome-common-types@^0.3.0":
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.3.0.tgz#949995a05c0d8801be7e0a594f775f1dbaa0d893"
+ integrity sha512-CA3MAZBTxVsF6SkfkHXDerkhcQs0QPofy43eFdbWJJkZiq3SfiaH1msOkac59rQaqto5EqWnASboY1dBuKen5w==
+
+"@fortawesome/fontawesome-svg-core@^1.3.0":
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.3.0.tgz#343fac91fa87daa630d26420bfedfba560f85885"
+ integrity sha512-UIL6crBWhjTNQcONt96ExjUnKt1D68foe3xjEensLDclqQ6YagwCRYVQdrp/hW0ALRp/5Fv/VKw+MqTUWYYvPg==
+ dependencies:
+ "@fortawesome/fontawesome-common-types" "^0.3.0"
+
+"@fortawesome/free-regular-svg-icons@^6.0.0":
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.0.0.tgz#f3cb91dac643472fe8138024b93fbfbdf05675cc"
+ integrity sha512-lYK6oyQL8HwZUAVWGqF7TGuwQBVfphNBVTdvPSD3h4gmQfGazm/xcwg3kmtcRycu3y6QspOC7hPXSoJbVqSYCw==
+ dependencies:
+ "@fortawesome/fontawesome-common-types" "^0.3.0"
+
+"@fortawesome/free-solid-svg-icons@^6.0.0":
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.0.0.tgz#bed4a501b631c6cfa35c09830f7cb63ffca1589d"
+ integrity sha512-o4FZ1XbndcgeWNb8Wh0y+Hgf73CjmyOQowUSaqQCtgIIdS+XliSBSOwCl330wER+I6CGYE96hT27bHBPmzX2Gg==
+ dependencies:
+ "@fortawesome/fontawesome-common-types" "^0.3.0"
+
+"@fortawesome/react-fontawesome@^0.1.17":
+ version "0.1.17"
+ resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.17.tgz#06fc06cb1a721e38e5b50b4a1cb851e9b9c77d7a"
+ integrity sha512-dX43Z5IvMaW7fwzU8farosYjKNGfRb2HB/DgjVBHeJZ/NSnuuaujPPx0YOdcAq+n3mqn70tyCde2HM1mqbhiuw==
+ dependencies:
+ prop-types "^15.8.1"
+
+"@jridgewell/resolve-uri@^3.0.3":
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c"
+ integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==
+
+"@jridgewell/sourcemap-codec@^1.4.10":
+ version "1.4.11"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec"
+ integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==
+
+"@jridgewell/trace-mapping@^0.3.0":
+ version "0.3.4"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3"
+ integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@popperjs/core@^2.10.1", "@popperjs/core@^2.11.2":
+ version "2.11.2"
+ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.2.tgz#830beaec4b4091a9e9398ac50f865ddea52186b9"
+ integrity sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==
+
+"@react-aria/ssr@^3.0.1":
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.1.2.tgz#665a6fd56385068c7417922af2d0d71b0618e52d"
+ integrity sha512-amXY11ImpokvkTMeKRHjsSsG7v1yzzs6yeqArCyBIk60J3Yhgxwx9Cah+Uu/804ATFwqzN22AXIo7SdtIaMP+g==
+ dependencies:
+ "@babel/runtime" "^7.6.2"
+
+"@restart/hooks@^0.4.0", "@restart/hooks@^0.4.5":
+ version "0.4.5"
+ resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.5.tgz#e7acbea237bfc9e479970500cf87538b41a1ed02"
+ integrity sha512-tLGtY0aHeIfT7aPwUkvQuhIy3+q3w4iqmUzFLPlOAf/vNUacLaBt1j/S//jv/dQhenRh8jvswyMojCwmLvJw8A==
+ dependencies:
+ dequal "^2.0.2"
+
+"@restart/ui@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@restart/ui/-/ui-1.0.1.tgz#379f8340ab14adc18522731a1be6e32eaa10ef92"
+ integrity sha512-hLAqltcAjQYtjGuHBHKyPpR3ScTxzdkSYNvniwBfN7rUDbYiHu/UZiI1hvV2idJeUvktRnz29l7W9BnNLHrG6Q==
+ dependencies:
+ "@babel/runtime" "^7.13.16"
+ "@popperjs/core" "^2.10.1"
+ "@react-aria/ssr" "^3.0.1"
+ "@restart/hooks" "^0.4.0"
+ "@types/warning" "^3.0.0"
+ dequal "^2.0.2"
+ dom-helpers "^5.2.0"
+ prop-types "^15.7.2"
+ uncontrollable "^7.2.1"
+ warning "^4.0.3"
+
+"@rjsf/core@^4.0.1":
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/@rjsf/core/-/core-4.0.1.tgz#3363aadbaf40f7086f8669c4a645ed0cddd6c2da"
+ integrity sha512-3HZ5Udy/Yvtixjn/dOcVm4IBoF/HM/LmUAPZt98elSAISga3vhzGc3zv7Fi3IbDNrvL6ZdJbfIj+BtR0q4Rnrg==
+ dependencies:
+ "@types/json-schema" "^7.0.7"
+ ajv "^6.7.0"
+ core-js-pure "^3.6.5"
+ json-schema-merge-allof "^0.6.0"
+ jsonpointer "^5.0.0"
+ lodash "^4.17.15"
+ nanoid "^3.1.23"
+ prop-types "^15.7.2"
+ react-is "^16.9.0"
+
+"@rollup/plugin-replace@^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-4.0.0.tgz#e34c457d6a285f0213359740b43f39d969b38a67"
+ integrity sha512-+rumQFiaNac9y64OHtkHGmdjm7us9bo1PlbgQfdihQtuNxzjpaB064HbRnewUOggLQxVCCyINfStkgmBeQpv1g==
+ dependencies:
+ "@rollup/pluginutils" "^3.1.0"
+ magic-string "^0.25.7"
+
+"@rollup/pluginutils@^3.1.0":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
+ integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
+ dependencies:
+ "@types/estree" "0.0.39"
+ estree-walker "^1.0.1"
+ picomatch "^2.2.2"
+
+"@rollup/pluginutils@^4.1.2":
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.2.tgz#ed5821c15e5e05e32816f5fb9ec607cdf5a75751"
+ integrity sha512-ROn4qvkxP9SyPeHaf7uQC/GPFY6L/OWy9+bd9AwcjOAWQwxRscoEyAUD8qCY5o5iL4jqQwoLk2kaTKJPb/HwzQ==
+ dependencies:
+ estree-walker "^2.0.1"
+ picomatch "^2.2.2"
+
+"@tauri-apps/api@^1.0.0-rc.1":
+ version "1.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-1.0.0-rc.1.tgz#ce26a0fe30d8980a4682d50015eec28ed4a23c1e"
+ integrity sha512-VBUOmfT8ea02JB/Qr+FHeaLnug5BRA7ro2pX47q0GZCbZsU9b+iPnOXl0ShJwT0melR7B6iamyhDwkHStMVfQA==
+ dependencies:
+ type-fest "2.11.2"
+
+"@tauri-apps/cli-darwin-arm64@1.0.0-rc.5":
+ version "1.0.0-rc.5"
+ resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.0.0-rc.5.tgz#9374be8c96afa1cf77b5647bc81fe1a05006757d"
+ integrity sha512-X+3EIAUGfoL8uE6PBADZC8FcUISe4JPQCxXgaVv6ehoZGoCh/pFJF7AvBGTQxbnvngqM7Xce4Lmh63Io2/5ggw==
+
+"@tauri-apps/cli-darwin-x64@1.0.0-rc.5":
+ version "1.0.0-rc.5"
+ resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.0.0-rc.5.tgz#4db1dd56a228bfafe5b3f64c7661abbe5606e804"
+ integrity sha512-fEpgOdAvKdq9C5/yip8RnwP1VS+nRrtKdzzplu4jY6njDVH/Vom8mg+EfXkCY5RveCaoskJMFgUvt10IGeZHBA==
+
+"@tauri-apps/cli-linux-arm-gnueabihf@1.0.0-rc.5":
+ version "1.0.0-rc.5"
+ resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.0.0-rc.5.tgz#0f1ff84c534908a54fbe63b45004abc6d4f03f4d"
+ integrity sha512-V7sWSBrpLyvkQxpkHIM8JltYqQhiTpThySDjO8POtrTqkRwM5BXORcCYhxTAKCedecfYK/RNUJ6Q0t7+3jS6DQ==
+
+"@tauri-apps/cli-linux-arm64-gnu@1.0.0-rc.5":
+ version "1.0.0-rc.5"
+ resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.0.0-rc.5.tgz#c5d2f1dd962d80a1958261229778b410b86254d4"
+ integrity sha512-HhM+2FInxtUAI/41LF4fDEzmhLQUq6DOoo7fLN94vgWlhsPyWZoDGP9pA043XbO86+4OX5JZUW1SnTVXMwEaMA==
+
+"@tauri-apps/cli-linux-arm64-musl@1.0.0-rc.5":
+ version "1.0.0-rc.5"
+ resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.0.0-rc.5.tgz#f485db9ca5402f5d2c4585801a3aaa4519bd67e8"
+ integrity sha512-DhHdKOhf3+peA/sM0c9CpxK89cp8GVwOB5osFW55fxBZbD0mJFeL2SzjkgfGFqFu6Ci/ZiibQGfEp8XTC8OsYA==
+
+"@tauri-apps/cli-linux-x64-gnu@1.0.0-rc.5":
+ version "1.0.0-rc.5"
+ resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.0.0-rc.5.tgz#e468939585bc98e80ec827d0a3d8ba06a7cc38e8"
+ integrity sha512-pZzsOHRGG/mdcn7fF/yOIOdeVzGxZUtZqvlGSd90ZM9Ps3//uYGCBHoNTbeSwp/V6+D0KVDaSCbm9lYlHoXcdA==
+
+"@tauri-apps/cli-linux-x64-musl@1.0.0-rc.5":
+ version "1.0.0-rc.5"
+ resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.0.0-rc.5.tgz#a1308e93850555f2b6c97b5d7cdbbb9afaa803e2"
+ integrity sha512-COwWCbOhEjBlzGRGTa0ESO4/AiC0cBZ2UEPExRn++S+kWSPJ2vsyMdCLu3hiMy1ABSIRcQ4Vc68M1iVkLhOHHw==
+
+"@tauri-apps/cli-win32-x64-msvc@1.0.0-rc.5":
+ version "1.0.0-rc.5"
+ resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.0.0-rc.5.tgz#d15c95143d9123d10c510e28ba90df5349b68ecd"
+ integrity sha512-zzizUmPWvnWjj+IUCk36kVjS9j1Bg5JmnwOW5QdX26+a64q5vocmVimCgrfZ5STw3sDFXE++S55ghpzhhH5o+g==
+
+"@tauri-apps/cli@^1.0.0-rc.5":
+ version "1.0.0-rc.5"
+ resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-1.0.0-rc.5.tgz#db8ca22c9f55f5400d1c6addd78ee1df5dc15a29"
+ integrity sha512-Q3D0R5YdZRA5EcL206hwwKCyXpet2mRDcfaRTU/IXwF73bS4AMja+JdAGfO2cyHuSvXd+b//Cgbei2zCG8M6hw==
+ optionalDependencies:
+ "@tauri-apps/cli-darwin-arm64" "1.0.0-rc.5"
+ "@tauri-apps/cli-darwin-x64" "1.0.0-rc.5"
+ "@tauri-apps/cli-linux-arm-gnueabihf" "1.0.0-rc.5"
+ "@tauri-apps/cli-linux-arm64-gnu" "1.0.0-rc.5"
+ "@tauri-apps/cli-linux-arm64-musl" "1.0.0-rc.5"
+ "@tauri-apps/cli-linux-x64-gnu" "1.0.0-rc.5"
+ "@tauri-apps/cli-linux-x64-musl" "1.0.0-rc.5"
+ "@tauri-apps/cli-win32-x64-msvc" "1.0.0-rc.5"
+
+"@types/estree@0.0.39":
+ version "0.0.39"
+ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
+ integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
+
+"@types/invariant@^2.2.33":
+ version "2.2.35"
+ resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.35.tgz#cd3ebf581a6557452735688d8daba6cf0bd5a3be"
+ integrity sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==
+
+"@types/json-schema@^7.0.7":
+ version "7.0.9"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
+ integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
+
+"@types/lodash@^4.14.179":
+ version "4.14.179"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.179.tgz#490ec3288088c91295780237d2497a3aa9dfb5c5"
+ integrity sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==
+
+"@types/node@^17.0.21":
+ version "17.0.21"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644"
+ integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==
+
+"@types/prop-types@*", "@types/prop-types@^15.7.3":
+ version "15.7.4"
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
+ integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
+
+"@types/react-transition-group@^4.4.1":
+ version "4.4.4"
+ resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.4.tgz#acd4cceaa2be6b757db61ed7b432e103242d163e"
+ integrity sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==
+ dependencies:
+ "@types/react" "*"
+
+"@types/react@*", "@types/react@>=16.14.8", "@types/react@>=16.9.11":
+ version "17.0.39"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.39.tgz#d0f4cde092502a6db00a1cded6e6bf2abb7633ce"
+ integrity sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
+"@types/scheduler@*":
+ version "0.16.2"
+ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
+ integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
+
+"@types/warning@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52"
+ integrity sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=
+
+"@vitejs/plugin-react@^1.0.7":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-1.2.0.tgz#4cfb4c0475e93885e56d66ff15e12ef4c34b0af0"
+ integrity sha512-Rywwt0IXXg6yQ0hv3cMT3mtdDcGIw31mGaa+MMMAT651LhoXLF2yFy4LrakiTs7UKs7RPBo9eNgaS8pgl2A6Qw==
+ dependencies:
+ "@babel/core" "^7.16.12"
+ "@babel/plugin-transform-react-jsx" "^7.16.7"
+ "@babel/plugin-transform-react-jsx-development" "^7.16.7"
+ "@babel/plugin-transform-react-jsx-self" "^7.16.7"
+ "@babel/plugin-transform-react-jsx-source" "^7.16.7"
+ "@rollup/pluginutils" "^4.1.2"
+ react-refresh "^0.11.0"
+ resolve "^1.22.0"
+
+ajv@^6.7.0:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ansi-styles@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+ integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+ dependencies:
+ color-convert "^1.9.0"
+
+anymatch@~3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
+ integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
+ dependencies:
+ normalize-path "^3.0.0"
+ picomatch "^2.0.4"
+
+binary-extensions@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
+ integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
+
+bootstrap@^5.1.3:
+ version "5.1.3"
+ resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.1.3.tgz#ba081b0c130f810fa70900acbc1c6d3c28fa8f34"
+ integrity sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==
+
+braces@~3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+ integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+ dependencies:
+ fill-range "^7.0.1"
+
+browserslist@^4.17.5:
+ version "4.19.3"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.3.tgz#29b7caad327ecf2859485f696f9604214bedd383"
+ integrity sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==
+ dependencies:
+ caniuse-lite "^1.0.30001312"
+ electron-to-chromium "^1.4.71"
+ escalade "^3.1.1"
+ node-releases "^2.0.2"
+ picocolors "^1.0.0"
+
+caniuse-lite@^1.0.30001312:
+ version "1.0.30001312"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz#e11eba4b87e24d22697dae05455d5aea28550d5f"
+ integrity sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==
+
+chalk@^2.0.0:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+ integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+ dependencies:
+ ansi-styles "^3.2.1"
+ escape-string-regexp "^1.0.5"
+ supports-color "^5.3.0"
+
+"chokidar@>=3.0.0 <4.0.0":
+ version "3.5.3"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
+ integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
+ dependencies:
+ anymatch "~3.1.2"
+ braces "~3.0.2"
+ glob-parent "~5.1.2"
+ is-binary-path "~2.1.0"
+ is-glob "~4.0.1"
+ normalize-path "~3.0.0"
+ readdirp "~3.6.0"
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+classnames@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
+ integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
+
+color-convert@^1.9.0:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+ dependencies:
+ color-name "1.1.3"
+
+color-name@1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+ integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
+compute-gcd@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/compute-gcd/-/compute-gcd-1.2.1.tgz#34d639f3825625e1357ce81f0e456a6249d8c77f"
+ integrity sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg==
+ dependencies:
+ validate.io-array "^1.0.3"
+ validate.io-function "^1.0.2"
+ validate.io-integer-array "^1.0.0"
+
+compute-lcm@^1.1.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/compute-lcm/-/compute-lcm-1.1.2.tgz#9107c66b9dca28cefb22b4ab4545caac4034af23"
+ integrity sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ==
+ dependencies:
+ compute-gcd "^1.2.1"
+ validate.io-array "^1.0.3"
+ validate.io-function "^1.0.2"
+ validate.io-integer-array "^1.0.0"
+
+convert-source-map@^1.7.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
+ integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
+ dependencies:
+ safe-buffer "~5.1.1"
+
+copy-text-to-clipboard@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz#8cbf8f90e0a47f12e4a24743736265d157bce69c"
+ integrity sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==
+
+core-js-pure@^3.6.5:
+ version "3.21.1"
+ resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.21.1.tgz#8c4d1e78839f5f46208de7230cebfb72bc3bdb51"
+ integrity sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==
+
+core-js@^3.11.0:
+ version "3.21.1"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.1.tgz#f2e0ddc1fc43da6f904706e8e955bc19d06a0d94"
+ integrity sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==
+
+csstype@^3.0.2:
+ version "3.0.10"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5"
+ integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==
+
+debug@^4.1.0:
+ version "4.3.3"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
+ integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
+ dependencies:
+ ms "2.1.2"
+
+decode-uri-component@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+ integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
+
+dequal@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d"
+ integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==
+
+dom-helpers@^5.0.1, dom-helpers@^5.2.0, dom-helpers@^5.2.1:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
+ integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
+ dependencies:
+ "@babel/runtime" "^7.8.7"
+ csstype "^3.0.2"
+
+electron-to-chromium@^1.4.71:
+ version "1.4.73"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.73.tgz#422f6f514315bcace9615903e4a9b6b9fa283137"
+ integrity sha512-RlCffXkE/LliqfA5m29+dVDPB2r72y2D2egMMfIy3Le8ODrxjuZNVo4NIC2yPL01N4xb4nZQLwzi6Z5tGIGLnA==
+
+esbuild-android-64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.24.tgz#d826ac29a9b983dc200a0ca407c34c64d2480b38"
+ integrity sha512-mbhO8NepmUZ84cP/axGR8IzH1Trth+uknEJzz36cZl8FfMA3ooaiBsMyzJ35s70QEAreiEt1XzltZ4pcfOsVUA==
+
+esbuild-android-arm64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.24.tgz#f8aaf28dbd4d7b80d30b0323c7a645e3a12b5de9"
+ integrity sha512-wM3iuLZjaA9BhlMOH6mWvTGXwPJsLOuAbMkGiczSY+NLeG2WF1ouCcuhFz2jZCbnw9lnI30QWgzebNBQi9K8SA==
+
+esbuild-darwin-64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.24.tgz#9f6e4b94066e9c5e521b19176a85edd7f94ac074"
+ integrity sha512-GDaCV5e9mdrJkrGT91W8WCqQ/+fvB/nsULIu4l7Ik7dlQd5uB4qeKRcFFl5Vz5ODK/C/UWZmKmMQWokZsLNWLQ==
+
+esbuild-darwin-arm64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.24.tgz#6b8ce7f2a3e1210cc672c73d658669e5f88b8efb"
+ integrity sha512-reU7/vEdXsg+zZWxKL/gaHsJkGMCC49Y4pqbsaBmx0YAF00K0+V7w5BHBF+iY5jvtJ1ZCYRHTN/iAbYVOnoV0w==
+
+esbuild-freebsd-64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.24.tgz#b586d8aa290bfcdf0aa73fdfcf64c092d49795f2"
+ integrity sha512-Mp35Rz/XoixG7Uka6l54hU/XUxAEwQozgKoHPusJzX+Fu1vANil0Ypos0RJkidu7skSkd0xisNIT+gtD36BxpA==
+
+esbuild-freebsd-arm64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.24.tgz#4e7098de07791089b6ef6b4f17a8ce6124da7bec"
+ integrity sha512-+tf4a4zYaHP1XXPt286mxOc2bmj13K57GZYjqYz/G3c3sgNXa0JBkcPlUATIj96WfXhWM115n3nHe9wF88+ZGQ==
+
+esbuild-linux-32@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.24.tgz#afe7345f3e8ee58eede36c0254e128924680b305"
+ integrity sha512-8jMZErn5aLnlSQqzK365yoWRr67ZkGNcoTmk1CK5Bk1EB9g7uwCfdZsmWcclLWPGkIhMbdk4OvzQ+Wp0popwWA==
+
+esbuild-linux-64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.24.tgz#ce7b95e8ed96076dcb329d1d34e4b9ef084e02cf"
+ integrity sha512-D/JCsk9OY2IZj+fkU74pKD4rD2pjeiYUbze1cS5D6+U0pz8j71GjZY5UkfwHhBBbNyPe6DPCyex97txQUlHwWw==
+
+esbuild-linux-arm64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.24.tgz#f8aee4eb78309fd3c064e86393a698e33908e8ec"
+ integrity sha512-DypWEDQLE+PoHGMa4FLcmKvS+yQLsYlsN03R496rTpDOiVQGrRdo0LbYtf+uHpDxa1KRrHZsQim6n8m3VBHP6g==
+
+esbuild-linux-arm@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.24.tgz#eaccc0a4d7d17e7290e5b20c383e6969c6769bf7"
+ integrity sha512-N+vvGpJAUWv3j+YZGOMEtrHwrrSG582TuAThBwoRE7d2N4zFE2WQBCiSYaAVckMQhvMOPqnCdqeTDUse5nlKTw==
+
+esbuild-linux-mips64le@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.24.tgz#9db94b62bbcbaad6229087ea27a6d7dc3fc878d6"
+ integrity sha512-eMk9pEHba1yd5bOuPZUJfFucigvysdcE2d/wV4M0eUdb/VjyH9fcGqz8byvSjmYSOt3WCn/V4jLVI+pwDSHWYw==
+
+esbuild-linux-ppc64le@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.24.tgz#194d59a5ebfcdbfc7e710d13893e6c62216baa77"
+ integrity sha512-4vQ/Y6EV5Z2BjO7RdpEyTCv702WmOPc95d2CbUcFvg78FpGQAmrbIrHXu/yX4+rdUU6vMNBn3M+7M7/lxmxjjg==
+
+esbuild-linux-riscv64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.24.tgz#0d8cbbcd2f3b7cc4365ebc8fac8d00fdc95691d2"
+ integrity sha512-pAN9/+NZ487Wo9PmlOM6Ra95SrhG8JQw7fCgi3z7dUufwTApTNTPGs5UOMD4Bmorju+DeGb0f0GddLaeabvqDg==
+
+esbuild-linux-s390x@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.24.tgz#460e726f5d827f1171f9eac8c9bbcad8e6574ec5"
+ integrity sha512-ZR+VMHP2WS3022x2sK/85cBfKGgPalIZzpquDWjra9nUb+WdEzuK9i9bRsstLmjIPs3uIkGfe6xXUh/7PNLllw==
+
+esbuild-netbsd-64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.24.tgz#8f6f4c68b8e84e1e011330096f118013cc275601"
+ integrity sha512-1PzXU++e0PEaSuGpkhrVb+fDUw9mSp4laY9KRsjJkAuXPDj0rHz7KxK7CAbzY/ucufeIR9Ca8/oMpdVyWdaOGw==
+
+esbuild-openbsd-64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.24.tgz#6fbfc089103d154d74d4ea5a3aefc2844189094a"
+ integrity sha512-PvXh7JJAFM1kR87XDWbRrUkaOGVMS6Dq/IRXE2E02maio21JELk/jNRijTe81ztr8v+8K9osB3rG9zKqIdTxhQ==
+
+esbuild-sunos-64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.24.tgz#09931dbbc610f88feda1c071f2844e7d8867f46e"
+ integrity sha512-5iYi76kGQdyCqvSUknqjTZ0T19KvQD6hiklPAY6kVoQ1YoDUGCGILRI9eM/3zLNLG1bUFgcdJ2ktaBxwyXuHyA==
+
+esbuild-windows-32@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.24.tgz#3c8dddffaec6dce9a3ac8ef2bec939ec5af17e68"
+ integrity sha512-oDxcNu4P1FWTk2ompKB0uKHfxYw1QuubH189+PlfrrWT9tVu+mxT9dSwJu2erfUDz5dnr6h8rgkg95NGboeJxg==
+
+esbuild-windows-64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.24.tgz#e97081ee16d51c5a210cd1de15454689ac09fc8b"
+ integrity sha512-0uxXF1yLcGEM2es0OMDgQYQGZXQEEIdq8cG3IWhY2GGfFRLXpMgic1iUE+SKCh+b82t1ftUVoyG0zIFRn5NOIA==
+
+esbuild-windows-arm64@0.14.24:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.24.tgz#0fcff2b58c772874f8dc52638557f817609ed699"
+ integrity sha512-unwaYRaIK/4OaZm0jnM3pLKMPEjaQqmT5teTciSZ86VYaiYZF27Ki7BW7R5ngk27gIw0ovIfUcn9DhJgp7qAlw==
+
+esbuild@^0.14.14:
+ version "0.14.24"
+ resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.24.tgz#543899cf9ff0ebe206dc3de2ac18b9122d9f5697"
+ integrity sha512-NjfmycVQqY0+iKXoHXsvMAKx4XF/bD/dDm8pK6C/3aJO/i/uby+7AR4z8vu0qkiihkB5Y43+9BjdY2MGnswC/A==
+ optionalDependencies:
+ esbuild-android-64 "0.14.24"
+ esbuild-android-arm64 "0.14.24"
+ esbuild-darwin-64 "0.14.24"
+ esbuild-darwin-arm64 "0.14.24"
+ esbuild-freebsd-64 "0.14.24"
+ esbuild-freebsd-arm64 "0.14.24"
+ esbuild-linux-32 "0.14.24"
+ esbuild-linux-64 "0.14.24"
+ esbuild-linux-arm "0.14.24"
+ esbuild-linux-arm64 "0.14.24"
+ esbuild-linux-mips64le "0.14.24"
+ esbuild-linux-ppc64le "0.14.24"
+ esbuild-linux-riscv64 "0.14.24"
+ esbuild-linux-s390x "0.14.24"
+ esbuild-netbsd-64 "0.14.24"
+ esbuild-openbsd-64 "0.14.24"
+ esbuild-sunos-64 "0.14.24"
+ esbuild-windows-32 "0.14.24"
+ esbuild-windows-64 "0.14.24"
+ esbuild-windows-arm64 "0.14.24"
+
+escalade@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+ integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+
+escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+estree-walker@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
+ integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
+
+estree-walker@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
+ integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
+
+fast-deep-equal@^3.1.1:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fill-range@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+ integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+ dependencies:
+ to-regex-range "^5.0.1"
+
+filter-obj@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b"
+ integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs=
+
+fsevents@~2.3.2:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+ integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+
+function-bind@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+ integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+gensync@^1.0.0-beta.2:
+ version "1.0.0-beta.2"
+ resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
+ integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
+
+glob-parent@~5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+ dependencies:
+ is-glob "^4.0.1"
+
+globals@^11.1.0:
+ version "11.12.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+ integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+has-flag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+ integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+
+has@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+ integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+ dependencies:
+ function-bind "^1.1.1"
+
+history@^5.2.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b"
+ integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==
+ dependencies:
+ "@babel/runtime" "^7.7.6"
+
+immutable@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23"
+ integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==
+
+invariant@^2.2.4:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+ integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
+ dependencies:
+ loose-envify "^1.0.0"
+
+is-binary-path@~2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
+ integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
+ dependencies:
+ binary-extensions "^2.0.0"
+
+is-core-module@^2.8.1:
+ version "2.8.1"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
+ integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
+ dependencies:
+ has "^1.0.3"
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-glob@^4.0.1, is-glob@~4.0.1:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-number@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+ integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+js-base64@^3.7.2:
+ version "3.7.2"
+ resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.2.tgz#816d11d81a8aff241603d19ce5761e13e41d7745"
+ integrity sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+jsesc@^2.5.1:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+ integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
+json-schema-compare@^0.2.2:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/json-schema-compare/-/json-schema-compare-0.2.2.tgz#dd601508335a90c7f4cfadb6b2e397225c908e56"
+ integrity sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==
+ dependencies:
+ lodash "^4.17.4"
+
+json-schema-merge-allof@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/json-schema-merge-allof/-/json-schema-merge-allof-0.6.0.tgz#64d48820fec26b228db837475ce3338936bf59a5"
+ integrity sha512-LEw4VMQVRceOPLuGRWcxW5orTTiR9ZAtqTAe4rQUjNADTeR81bezBVFa0MqIwp0YmHIM1KkhSjZM7o+IQhaPbQ==
+ dependencies:
+ compute-lcm "^1.1.0"
+ json-schema-compare "^0.2.2"
+ lodash "^4.17.4"
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json5@^2.1.2:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
+ integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
+ dependencies:
+ minimist "^1.2.5"
+
+jsonpointer@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.0.tgz#f802669a524ec4805fa7389eadbc9921d5dc8072"
+ integrity sha512-PNYZIdMjVIvVgDSYKTT63Y+KZ6IZvGRNNWcxwD+GNnUz1MKPfv30J8ueCjdwcN0nDx2SlshgyB7Oy0epAzVRRg==
+
+lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+ integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+ dependencies:
+ js-tokens "^3.0.0 || ^4.0.0"
+
+magic-string@^0.25.7:
+ version "0.25.7"
+ resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
+ integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==
+ dependencies:
+ sourcemap-codec "^1.4.4"
+
+minimist@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
+ integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+
+ms@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+mutation-observer@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/mutation-observer/-/mutation-observer-1.0.3.tgz#42e9222b101bca82e5ba9d5a7acf4a14c0f263d0"
+ integrity sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA==
+
+nanoid@^3.1.23, nanoid@^3.3.1:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
+ integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
+
+node-fetch@2:
+ version "2.6.7"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
+ integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
+ dependencies:
+ whatwg-url "^5.0.0"
+
+node-releases@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01"
+ integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==
+
+normalize-path@^3.0.0, normalize-path@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+ integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+object-assign@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+path-parse@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+picocolors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+ integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+postcss@^8.4.6:
+ version "8.4.7"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.7.tgz#f99862069ec4541de386bf57f5660a6c7a0875a8"
+ integrity sha512-L9Ye3r6hkkCeOETQX6iOaWZgjp3LL6Lpqm6EtgbKrgqGGteRMNb9vzBfRL96YOSu8o7x3MfIH9Mo5cPJFGrW6A==
+ dependencies:
+ nanoid "^3.3.1"
+ picocolors "^1.0.0"
+ source-map-js "^1.0.2"
+
+prop-types-extra@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.1.tgz#58c3b74cbfbb95d304625975aa2f0848329a010b"
+ integrity sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==
+ dependencies:
+ react-is "^16.3.2"
+ warning "^4.0.0"
+
+prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
+ version "15.8.1"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
+ integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
+ dependencies:
+ loose-envify "^1.4.0"
+ object-assign "^4.1.1"
+ react-is "^16.13.1"
+
+punycode@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+query-string@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.1.tgz#754620669db978625a90f635f12617c271a088e1"
+ integrity sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w==
+ dependencies:
+ decode-uri-component "^0.2.0"
+ filter-obj "^1.1.0"
+ split-on-first "^1.0.0"
+ strict-uri-encode "^2.0.0"
+
+react-bootstrap@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-2.1.2.tgz#a81161821c351d5e0eaed5bb85d50659736f2aa4"
+ integrity sha512-E7PR13cVsEW70gw08BWplENwn6PHTshskOsQygZqyc65jQlsnr9MsmuW/lgzAN2OiMBnc0KaNpuZ/FohL7dchw==
+ dependencies:
+ "@babel/runtime" "^7.14.0"
+ "@restart/hooks" "^0.4.5"
+ "@restart/ui" "^1.0.1"
+ "@types/invariant" "^2.2.33"
+ "@types/prop-types" "^15.7.3"
+ "@types/react" ">=16.14.8"
+ "@types/react-transition-group" "^4.4.1"
+ "@types/warning" "^3.0.0"
+ classnames "^2.3.1"
+ dom-helpers "^5.2.1"
+ invariant "^2.2.4"
+ prop-types "^15.7.2"
+ prop-types-extra "^1.1.0"
+ react-transition-group "^4.4.1"
+ uncontrollable "^7.2.1"
+ warning "^4.0.3"
+
+react-dom@^17.0.2:
+ version "17.0.2"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
+ integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
+ dependencies:
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+ scheduler "^0.20.2"
+
+react-icons@^4.3.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.3.1.tgz#2fa92aebbbc71f43d2db2ed1aed07361124e91ca"
+ integrity sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==
+
+react-is@^16.13.1, react-is@^16.3.2, react-is@^16.9.0:
+ version "16.13.1"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+ integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+
+react-lifecycles-compat@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
+ integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
+
+react-refresh@^0.11.0:
+ version "0.11.0"
+ resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
+ integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
+
+react-router-dom@^6.2.1:
+ version "6.2.2"
+ resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.2.2.tgz#f1a2c88365593c76b9612ae80154a13fcb72e442"
+ integrity sha512-AtYEsAST7bDD4dLSQHDnk/qxWLJdad5t1HFa1qJyUrCeGgEuCSw0VB/27ARbF9Fi/W5598ujvJOm3ujUCVzuYQ==
+ dependencies:
+ history "^5.2.0"
+ react-router "6.2.2"
+
+react-router@6.2.2, react-router@^6.2.1:
+ version "6.2.2"
+ resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.2.2.tgz#495e683a0c04461eeb3d705fe445d6cf42f0c249"
+ integrity sha512-/MbxyLzd7Q7amp4gDOGaYvXwhEojkJD5BtExkuKmj39VEE0m3l/zipf6h2WIB2jyAO0lI6NGETh4RDcktRm4AQ==
+ dependencies:
+ history "^5.2.0"
+
+react-transition-group@^4.4.1:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
+ integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ dom-helpers "^5.0.1"
+ loose-envify "^1.4.0"
+ prop-types "^15.6.2"
+
+react@^17.0.2:
+ version "17.0.2"
+ resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
+ integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
+ dependencies:
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+
+readdirp@~3.6.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
+ integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
+ dependencies:
+ picomatch "^2.2.1"
+
+regenerator-runtime@^0.13.4:
+ version "0.13.9"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
+ integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
+
+resolve@^1.22.0:
+ version "1.22.0"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
+ integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
+ dependencies:
+ is-core-module "^2.8.1"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
+rollup@^2.59.0:
+ version "2.69.0"
+ resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.69.0.tgz#82aa86682a45e9760146b736c1643bf435506156"
+ integrity sha512-kjER91tHyek8gAkuz7+558vSnTQ+pITEok1P0aNOS45ZXyngaqPsXJmSel4QPQnJo7EJMjXUU1/GErWkWiKORg==
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+safe-buffer@~5.1.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+sass@^1.49.9:
+ version "1.49.9"
+ resolved "https://registry.yarnpkg.com/sass/-/sass-1.49.9.tgz#b15a189ecb0ca9e24634bae5d1ebc191809712f9"
+ integrity sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==
+ dependencies:
+ chokidar ">=3.0.0 <4.0.0"
+ immutable "^4.0.0"
+ source-map-js ">=0.6.2 <2.0.0"
+
+scheduler@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
+ integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
+ dependencies:
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+
+semver@^6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+ integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
+"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
+ integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+
+source-map@^0.5.0:
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+ integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
+
+sourcemap-codec@^1.4.4:
+ version "1.4.8"
+ resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
+ integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
+
+split-on-first@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
+ integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
+
+strict-uri-encode@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
+ integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
+
+supports-color@^5.3.0:
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+ integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+ dependencies:
+ has-flag "^3.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+to-fast-properties@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+ integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
+
+to-regex-range@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+ integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+ dependencies:
+ is-number "^7.0.0"
+
+tr46@~0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+ integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
+
+type-fest@2.11.2:
+ version "2.11.2"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.11.2.tgz#5534a919858bc517492cd3a53a673835a76d2e71"
+ integrity sha512-reW2Y2Mpn0QNA/5fvtm5doROLwDPu2zOm5RtY7xQQS05Q7xgC8MOZ3yPNaP9m/s/sNjjFQtHo7VCNqYW2iI+Ig==
+
+uncontrollable@^7.2.1:
+ version "7.2.1"
+ resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-7.2.1.tgz#1fa70ba0c57a14d5f78905d533cf63916dc75738"
+ integrity sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==
+ dependencies:
+ "@babel/runtime" "^7.6.3"
+ "@types/react" ">=16.9.11"
+ invariant "^2.2.4"
+ react-lifecycles-compat "^3.0.4"
+
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
+validate.io-array@^1.0.3:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/validate.io-array/-/validate.io-array-1.0.6.tgz#5b5a2cafd8f8b85abb2f886ba153f2d93a27774d"
+ integrity sha1-W1osr9j4uFq7L4hroVPy2Tond00=
+
+validate.io-function@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/validate.io-function/-/validate.io-function-1.0.2.tgz#343a19802ed3b1968269c780e558e93411c0bad7"
+ integrity sha1-NDoZgC7TsZaCaceA5VjpNBHAutc=
+
+validate.io-integer-array@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/validate.io-integer-array/-/validate.io-integer-array-1.0.0.tgz#2cabde033293a6bcbe063feafe91eaf46b13a089"
+ integrity sha1-LKveAzKTpry+Bj/q/pHq9GsToIk=
+ dependencies:
+ validate.io-array "^1.0.3"
+ validate.io-integer "^1.0.4"
+
+validate.io-integer@^1.0.4:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/validate.io-integer/-/validate.io-integer-1.0.5.tgz#168496480b95be2247ec443f2233de4f89878068"
+ integrity sha1-FoSWSAuVviJH7EQ/IjPeT4mHgGg=
+ dependencies:
+ validate.io-number "^1.0.3"
+
+validate.io-number@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/validate.io-number/-/validate.io-number-1.0.3.tgz#f63ffeda248bf28a67a8d48e0e3b461a1665baf8"
+ integrity sha1-9j/+2iSL8opnqNSODjtGGhZluvg=
+
+vconsole@^3.14.3:
+ version "3.14.3"
+ resolved "https://registry.yarnpkg.com/vconsole/-/vconsole-3.14.3.tgz#a452867f03b2eb1f57a3ce4b8ff2b0e067e92040"
+ integrity sha512-Je26lm4AzS8uGRVLvHRmCK2MoSDviM/z/kpM4RLGrbDzDB36stlMjmtOa3Vh9IQDQG/aw/gqQGtpSHIGEP7/og==
+ dependencies:
+ "@babel/runtime" "^7.17.2"
+ copy-text-to-clipboard "^3.0.1"
+ core-js "^3.11.0"
+ mutation-observer "^1.0.3"
+
+vite@^2.8.0:
+ version "2.8.6"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-2.8.6.tgz#32d50e23c99ca31b26b8ccdc78b1d72d4d7323d3"
+ integrity sha512-e4H0QpludOVKkmOsRyqQ7LTcMUDF3mcgyNU4lmi0B5JUbe0ZxeBBl8VoZ8Y6Rfn9eFKYtdXNPcYK97ZwH+K2ug==
+ dependencies:
+ esbuild "^0.14.14"
+ postcss "^8.4.6"
+ resolve "^1.22.0"
+ rollup "^2.59.0"
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+warning@^4.0.0, warning@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
+ integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
+ dependencies:
+ loose-envify "^1.0.0"
+
+webidl-conversions@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+ integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
+
+whatwg-url@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
+ integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
+ dependencies:
+ tr46 "~0.0.3"
+ webidl-conversions "^3.0.0"
diff --git a/misc/config_tools/scenario_config/jsonschema/__init__.py b/misc/config_tools/scenario_config/jsonschema/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/misc/config_tools/scenario_config/jsonschema/converter.py b/misc/config_tools/scenario_config/jsonschema/converter.py
new file mode 100644
index 000000000..ed8c999cf
--- /dev/null
+++ b/misc/config_tools/scenario_config/jsonschema/converter.py
@@ -0,0 +1,350 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 Intel Corporation.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+"""
+# XS2JS
+
+Convert XML Schema To JSON Schema.
+
+# Example Code
+
+```python
+import os
+import json
+from xs2js import XS2JS
+
+schema_file = os.path.join('schema', 'config.xsd')
+json_schema = XS2JS(schema_file).get_json_schema()
+json_schema = json.dumps(json_schema, indent='\t')
+output_file = 'schema.json'
+open(output_file, 'w', encoding='utf-8').write(json_schema)
+```
+
+"""
+import os
+import json
+
+from collections import OrderedDict
+from pathlib import Path
+
+import xmltodict
+
+try:
+ from .document import ACRNDocumentStringConvertor
+except ImportError:
+ from document import ACRNDocumentStringConvertor
+
+
+class XSTypes:
+ def __init__(self, schema_dict):
+ self.simple = self.load_type(schema_dict.get('xs:simpleType', []))
+ self.complex = self.load_type(schema_dict.get('xs:complexType', []))
+
+ @staticmethod
+ def load_type(type_list):
+ return {type_info['@name']: type_info for type_info in type_list}
+
+ def get_type(self, type_name):
+ if type_name in self.simple:
+ return self.simple[type_name]
+ elif type_name in self.complex:
+ return self.complex[type_name]
+ print(type_name)
+ raise NotImplementedError
+
+
+class XS:
+ def __init__(self, schema_filename):
+ self.schema = self.load_file(schema_filename)
+ self.types = XSTypes(self.schema)
+
+ @staticmethod
+ def load_file(schema_filename):
+ """load xml schema file and convert it to json dict"""
+ schema = open(schema_filename, encoding='utf-8').read()
+
+ # load schema_content
+ schema_content = xmltodict.parse(schema)
+ schema_content = schema_content['xs:schema']
+
+ # handle xinclude element
+ XS._handle_include(schema_filename, schema_content)
+
+ return schema_content
+
+ @staticmethod
+ def _handle_include(schema_filename, schema_content):
+ """parse xsd document xi:include tag and inject their content to origin document content"""
+ if 'xi:include' in schema_content:
+ for include in reversed(schema_content['xi:include']):
+ source_path = os.path.join(os.path.dirname(schema_filename), include['@href'])
+ include_content = XS.load_file(source_path)
+
+ # marge data
+ for attr in ['xs:simpleType', 'xs:complexType']:
+ if attr in include_content:
+ if attr not in schema_content:
+ schema_content[attr] = []
+
+ schema_content[attr] = [
+ *include_content[attr],
+ *schema_content[attr]
+ ]
+
+
+class XS2JS:
+ xst2jst_mapping = {
+ 'xs:string': 'string',
+ 'xs:integer': 'integer'
+ }
+ xsa2jsa_mapping = {
+ 'xs:minLength': ('minLength', int),
+ 'xs:maxLength': ('maxLength', int),
+ 'xs:pattern': ('pattern', lambda x: f"^{x}$"),
+ 'xs:minInclusive': ('minimum', int),
+ 'xs:maxInclusive': ('maximum', int),
+ }
+
+ def __init__(self, schema_filename):
+ self.xs = XS(schema_filename)
+ self.desc_conv = ACRNDocumentStringConvertor()
+
+ def _get_definitions(self):
+ """convert xml schema types to json schema definitions"""
+ definitions = OrderedDict()
+
+ # simple types
+ for type_name, simple_type in self.xs.types.simple.items():
+ definitions[type_name] = self.xso2jso(simple_type)
+
+ # complex types
+ for type_name, complex_type in self.xs.types.complex.items():
+ definitions[type_name] = self.xse2jse(complex_type)
+
+ return definitions
+
+ def get_json_schema(self):
+ json_schema = self.xse2jse(self.xs.schema)
+ json_schema["additionalProperties"] = True
+ json_schema['$schema'] = "http://json-schema.org/draft-07/schema"
+ json_schema.move_to_end('$schema', False)
+ json_schema["definitions"] = self._get_definitions()
+ return json_schema
+
+ def xst2jst(self, type_name) -> str:
+ """convert xml schema type name to json schema type name"""
+ if type_name in self.xst2jst_mapping:
+ return self.xst2jst_mapping[type_name]
+ print(type_name)
+ raise NotImplementedError
+
+ def xsa2jsa(self, restriction):
+ """convert xml schema object attrs to json schema object attrs"""
+ result = {}
+ for key in restriction:
+ if key in self.xsa2jsa_mapping:
+ js_key, js_type = self.xsa2jsa_mapping[key]
+ result[js_key] = js_type(restriction[key]['@value'])
+ return result
+
+ def xso2jso(self, obj, show_type_name=False) -> OrderedDict:
+ """convert xml schema object to json schema object"""
+ if 'xs:restriction' in obj:
+ restriction = obj['xs:restriction']
+ js_st = OrderedDict({"type": self.xst2jst(restriction['@base'])})
+ if show_type_name:
+ js_st['title'] = obj['@name']
+ js_st.move_to_end('title', False)
+ if 'xs:enumeration' in restriction:
+ type_func = {"string": str, "integer": int}.get(js_st['type'], str)
+ # enum
+ enum = []
+ for enum_element in restriction['xs:enumeration']:
+ enum.append(type_func(enum_element['@value']))
+ js_st["enum"] = enum
+
+ # enumNames
+ if enum and '@acrn:title' in restriction['xs:enumeration'][0].get('xs:annotation', {}):
+ enum_names = []
+ for enum_element in restriction['xs:enumeration']:
+ enum_names.append(enum_element['xs:annotation']['@acrn:title'])
+ js_st["enumNames"] = enum_names
+
+ js_st.update(self.xsa2jsa(restriction))
+ return js_st
+ elif 'xs:union' in obj:
+ member_types = obj['xs:union']['@memberTypes'].split(' ')
+ member_js_objects = []
+ for type_name in member_types:
+ member_type = self.xs.types.get_type(type_name)
+ member_js_objects.append(self.xso2jso(member_type, True))
+ # Todo: union type refactor
+ return OrderedDict({"anyOf": member_js_objects})
+ print(obj)
+ raise NotImplementedError
+
+ def get_element_define(self, element):
+ basic_define = {}
+ if 'xs:simpleType' in element:
+ basic_define = self.xso2jso(element['xs:simpleType'])
+ elif '@type' in element:
+ element_type = element['@type']
+ if element_type in self.xst2jst_mapping:
+ basic_define['type'] = self.xst2jst_mapping[element_type]
+ else:
+ basic_define["$ref"] = "#/definitions/%s" % element_type
+ elif 'xs:complexType' in element:
+ basic_define = self.xse2jse(element['xs:complexType'])
+ else:
+ print(json.dumps(element, indent=2))
+ raise NotImplementedError
+ return basic_define
+
+ def xse2jse(self, obj) -> OrderedDict:
+ """convert xml schema elements to json schema elements"""
+ properties = OrderedDict()
+ required = []
+
+ # get elements
+ all_elements = self.get_elements(obj)
+
+ for element in all_elements:
+ name = element['@name']
+
+ # get element basic define (basic/simple type? $ref?)
+ try:
+ basic_define = self.get_element_define(element)
+ except NotImplementedError:
+ print(f"{name} not translated")
+ continue
+
+ # build element json schema
+ js_ele = OrderedDict(basic_define)
+
+ # get default
+ if '@default' in element:
+ default = element['@default']
+ if default.isdigit():
+ default = int(default)
+
+ js_ele['default'] = default
+
+ # is this element is required ?
+ if '@minOccurs' in element:
+ min_items = int(element['@minOccurs'])
+ if min_items > 0:
+ required.append(name)
+ if min_items > 1:
+ js_ele['minItems'] = min_items
+ else:
+ # by default, field is required
+ required.append(name)
+
+ if '@maxOccurs' in element:
+ if 'type' in js_ele:
+ js_ele['items'] = {'type': js_ele['type']}
+ elif '$ref' in js_ele:
+ js_ele['items'] = {'$ref': js_ele['$ref']}
+ del js_ele['$ref']
+ else:
+ raise NotImplementedError
+
+ if element['@maxOccurs'] == "unbounded":
+ # unlimited, only set type = array
+ js_ele['type'] = 'array'
+ else:
+ js_ele['type'] = 'array'
+ js_ele['maxItems'] = int(element['@maxOccurs'])
+
+ if 'default' in js_ele:
+ js_ele['items']['default'] = js_ele['default']
+ del js_ele['default']
+
+ # get description
+ if 'xs:annotation' in element:
+ # title
+ js_ele['title'] = element['xs:annotation'].get('@acrn:title', name)
+
+ # documentation
+ documentation: str = element['xs:annotation'].get('xs:documentation', None)
+ if documentation is None or documentation.strip() == '':
+ documentation = ''
+ if documentation:
+ documentation = self.desc_conv.convert(documentation)
+ js_ele['description'] = documentation
+
+ # dynamic enum
+ if '@acrn:options' in element['xs:annotation']:
+ dynamic_enum = {
+ 'type': 'dynamicEnum',
+ 'function': 'get_enum',
+ 'source': 'board_xml',
+ 'selector': element['xs:annotation']['@acrn:options'],
+ 'sorted': element['xs:annotation'].get('@acrn:options-sorted-by', None)
+ }
+ js_ele['enum'] = dynamic_enum
+
+ properties[name] = js_ele
+
+ # build result
+ result = OrderedDict({"type": "object"})
+
+ if required:
+ result['required'] = required
+
+ if properties:
+ result["properties"] = properties
+
+ return result
+
+ @staticmethod
+ def get_elements(obj):
+ """get elements from xml schema object"""
+ all_elements = []
+ if 'xs:element' in obj:
+ elements = obj['xs:element']
+ if not isinstance(elements, list):
+ elements = [elements]
+ all_elements.extend(elements)
+ for attr in ['xs:sequence', 'xs:all']:
+ if attr in obj:
+ elements = obj[attr]['xs:element']
+ if not isinstance(elements, list):
+ elements = [elements]
+ all_elements.extend(elements)
+ return all_elements
+
+
+def main(manual_call=False):
+ """
+ if you call this function in your
+ module must set params `manual_call=True`
+ else this function will not run
+ """
+ # for pyodide run check.
+ if __name__ != '__main__' and not manual_call:
+ return
+
+ # find acrn-hypervisor/misc/config_tools folder
+ config_tools = Path(__file__).absolute()
+ while config_tools.name != "config_tools":
+ config_tools = config_tools.parent
+
+ schema_file = config_tools / 'configurator' / 'build' / 'sliced.xsd'
+ json_schema_file = config_tools / 'configurator' / 'src' / 'assets' / 'schema' / 'scenario.json'
+
+ # Convert XSD to JSON Schema
+ json_schema = XS2JS(schema_file).get_json_schema()
+ json_schema = json.dumps(json_schema, indent='\t')
+
+ # Write file and print successful message
+ open(json_schema_file, 'w', encoding='utf-8').write(json_schema)
+ print("File %s Convert Success. JSON Schema Write To: %s" % (repr(schema_file), repr(json_schema_file)))
+
+
+# for pyodide
+main()
diff --git a/misc/config_tools/scenario_config/jsonschema/document.py b/misc/config_tools/scenario_config/jsonschema/document.py
new file mode 100644
index 000000000..2218777c5
--- /dev/null
+++ b/misc/config_tools/scenario_config/jsonschema/document.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 Intel Corporation.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+"""
+This program support run in pyodide env.
+In js side you can pass params by add follow code before this script.
+```js
+params = "${Base64.encode(params)}"
+```
+"""
+
+import re
+import sys
+import json
+import base64
+
+from urllib.parse import urljoin
+from subprocess import check_output
+
+from typing import Optional
+
+try:
+ import locale # module missing in Jython
+
+ locale.setlocale(locale.LC_ALL, '')
+except locale.Error:
+ pass
+
+from docutils.core import publish_string
+from sphinx.ext.intersphinx import fetch_inventory
+from bs4 import BeautifulSoup
+
+
+class RSTNormalizer:
+ def __init__(self, url_base, objects_inv: [dict, str]):
+ self.url_base = url_base
+
+ if isinstance(objects_inv, dict):
+ # parsed data
+ self.data = objects_inv
+ elif isinstance(objects_inv, str):
+ self.data = self.__convert_inv(objects_inv)
+ else:
+ raise NotImplementedError
+
+ self.fake_roles = [
+ self.fake_role('option', 'std:cmdoption'),
+ self.fake_role('ref', 'std:label')
+ ]
+
+ @staticmethod
+ def __convert_inv(url):
+ try:
+ inv_data = RSTNormalizer.__prase_inv_file(url)
+ except Exception as e:
+ print(e)
+ print('Download inv data failed, document link will link to search page.')
+ inv_data = None
+ return inv_data
+
+ @staticmethod
+ def __prase_inv_file(filename):
+ class MockConfig:
+ intersphinx_timeout: int = None
+ tls_verify = False
+ user_agent = None
+
+ class MockApp:
+ srcdir = ''
+ config = MockConfig()
+
+ def warn(self, msg: str) -> None:
+ print(msg, file=sys.stderr)
+
+ invdata = fetch_inventory(MockApp(), '', filename) # type: ignore
+ result = {}
+ for key in sorted(invdata or {}):
+ result[key] = {}
+ data = result[key]
+ for entry, einfo in sorted(invdata[key].items()):
+ data[entry] = {"title": einfo[3] if einfo[3] != '-' else '', "link": einfo[2]}
+ return result
+
+ def normalize(self, rest_text):
+ result = re.sub(r'\.\. option::[ \t]+(\S+)', lambda x: x.group(1) + f'\n{len(x.group(1)) * "-"}', rest_text)
+ for fake_role_handle in self.fake_roles:
+ result = fake_role_handle(result)
+
+ return result
+
+ def url(self, key, data):
+ title = data["title"] if data["title"] else key
+ url = urljoin(self.url_base, data['link'])
+ return f'`{title} <{url}>`_'
+
+ def fake_role(self, rest_key, json_key):
+ def handel_role(rest_text):
+ def re_sub(match):
+ key = match.group(1)
+ if self.data is None or json_key not in self.data or key not in self.data[json_key]:
+ return self.url(key, {'title': '', 'link': f'search.html?q={key}&check_keywords=yes&area=default'})
+ return self.url(key, self.data[json_key][key])
+
+ return re.sub(f':{rest_key}:' + r'`(.+?)`', re_sub, rest_text)
+
+ return handel_role
+
+
+class ACRNDocumentStringConvertor:
+ def __init__(self, objects_inv: Optional[dict] = None):
+ self.version = self.get_acrn_document_version()
+ self.url_base = 'https://projectacrn.github.io/{}/'.format(self.version)
+
+ self.objects_inv = objects_inv
+ if self.objects_inv is None:
+ self.objects_inv = urljoin(self.url_base, 'objects.inv')
+
+ self.rst_normalizer = RSTNormalizer(self.url_base, self.objects_inv)
+
+ @staticmethod
+ def get_acrn_document_version(default_version='latest'):
+ version = default_version
+ try:
+ branch_name = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).decode()
+ version = re.match(r"^release_(\d\.\d)$", branch_name).group(1)
+ except:
+ print("Can't detect current acrn-hypervisor version, document string will link to latest")
+
+ return version
+
+ def convert(self, docstring):
+ rst = self.rst_normalizer.normalize(docstring)
+ html = publish_string(rst, writer_name='html5').decode('utf-8')
+ soup = BeautifulSoup(html, 'lxml')
+ for link in soup.select('a'):
+ link['target'] = '_blank'
+ try:
+ fragment = soup.select_one('div.document').prettify()
+ except:
+ fragment = '\n'.join([str(x) for x in soup.select_one('main').children]).strip()
+ return fragment
+
+
+doc_html = ''
+if __name__ == '__main__':
+ WEB = False
+ if 'params' in globals():
+ WEB = True
+ # noinspection PyUnboundLocalVariable,PyUnresolvedReferences
+ params_data = base64.b64decode(params)
+ params = json.loads(params_data)
+ else:
+ params = {
+ "text": open('configdoc.txt').read(),
+ "objectsInv": None
+ }
+
+ print(params)
+ doc_html = ACRNDocumentStringConvertor(params['objectsInv']).convert(params['text'])
+ if not WEB:
+ print(doc_html)
+
+# for pyodide
+(lambda x: x)(doc_html)
diff --git a/misc/config_tools/scenario_config/schema_slicer.py b/misc/config_tools/scenario_config/schema_slicer.py
index e0a6a84e6..e0572c097 100755
--- a/misc/config_tools/scenario_config/schema_slicer.py
+++ b/misc/config_tools/scenario_config/schema_slicer.py
@@ -236,12 +236,17 @@ def main(args):
print(f"Sliced schema written to {args.out}")
+
if __name__ == "__main__":
- config_tools_dir = os.path.join(os.path.dirname(__file__), "..")
+ # abs __file__ path to ignore `__file__ == 'schema_slicer.py'` issue
+ config_tools_dir = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
schema_dir = os.path.join(config_tools_dir, "schema")
+ configurator_build_dir = os.path.join(config_tools_dir, 'configurator', 'build')
+ if not os.path.isdir(configurator_build_dir):
+ os.mkdir(configurator_build_dir)
parser = argparse.ArgumentParser(description="Slice a given scenario schema by VM types and views")
- parser.add_argument("out", nargs="?", default=os.path.join(schema_dir, "sliced.xsd"), help="Path where the output is placed")
+ parser.add_argument("out", nargs="?", default=os.path.join(configurator_build_dir, "sliced.xsd"), help="Path where the output is placed")
parser.add_argument("--schema", default=os.path.join(schema_dir, "config.xsd"), help="the XML schema that defines the syntax of scenario XMLs")
args = parser.parse_args()
diff --git a/misc/config_tools/schema/VMtypes.xsd b/misc/config_tools/schema/VMtypes.xsd
index 16d83d74d..db9a40cbb 100644
--- a/misc/config_tools/schema/VMtypes.xsd
+++ b/misc/config_tools/schema/VMtypes.xsd
@@ -184,8 +184,16 @@ CLOSID 0 and the second is mapped to virtual CLOSID 1, etc.
-
-
+
+
+ Virtual UART port
+
+
+
+
+ Virtual I/O address
+
+
diff --git a/misc/config_tools/schema/types.xsd b/misc/config_tools/schema/types.xsd
index b40424b35..e6f612862 100644
--- a/misc/config_tools/schema/types.xsd
+++ b/misc/config_tools/schema/types.xsd
@@ -196,7 +196,7 @@ Read more about the available scheduling options in :ref:`cpu_sharing`.
-
+
Name of the VM which use this IVSHMEM.
diff --git a/misc/packaging/gen_acrn_deb.py b/misc/packaging/gen_acrn_deb.py
index 788d08ee2..649792c48 100644
--- a/misc/packaging/gen_acrn_deb.py
+++ b/misc/packaging/gen_acrn_deb.py
@@ -1,40 +1,58 @@
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#* Copyright (c) 2020 Intel Corporation
-import os,sys,copy,json
-import subprocess
-import datetime
-import time
+#
+# Copyright (C) 2022 Intel Corporation.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+import sys
+import os
+import json
import shlex
-import glob
+import shutil
+import subprocess
import argparse
-import multiprocessing
+
+from pathlib import Path
+
+DEBUG = False
+
def run_command(cmd, path):
- ret_code = 0
- #print("cmd = %s, path = %s" % (cmd, path))
- cmd_proc = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd = path, universal_newlines=True)
- while True:
- output = cmd_proc.stdout.readline()
- #print(output.strip())
- ret_code = cmd_proc.poll()
- if ret_code is not None:
- break
- return ret_code
+ if DEBUG:
+ print("cmd = %s, path = %s" % (cmd, path))
+ cmd_proc = subprocess.Popen(
+ shlex.split(cmd),
+ cwd=path,
+ stdout=sys.stdout,
+ stderr=sys.stderr,
+ universal_newlines=True
+ )
+ while True:
+ ret_code = cmd_proc.poll()
+ if ret_code is not None:
+ break
+ return ret_code
+
def add_cmd_list(cmd_list, cmd_str, dir_str):
- cmd = {}
- cmd['cmd'] = cmd_str
- cmd['dir'] = dir_str
+ cmd = {
+ 'cmd': cmd_str,
+ 'dir': dir_str
+ }
cmd_list.append(cmd)
+
def run_cmd_list(cmd_list):
- for i, cmd in enumerate(cmd_list):
+ for cmd in cmd_list:
ret = run_command(cmd['cmd'], cmd['dir'])
if ret != 0:
print("cmd(%s) run in dir(%s) failed and exit" % (cmd['cmd'], cmd['dir']))
exit(-1)
return
+
def create_acrn_deb(board, scenario, version, build_dir):
cur_dir = build_dir + '/../'
deb_dir = build_dir + '/acrn_release_deb/'
@@ -45,43 +63,45 @@ def create_acrn_deb(board, scenario, version, build_dir):
add_cmd_list(cmd_list, 'mkdir -p acrn_release_deb/boot', build_dir)
add_cmd_list(cmd_list, 'mkdir DEBIAN', deb_dir)
add_cmd_list(cmd_list, 'touch DEBIAN/control', deb_dir)
- deb_bin_name ='acrn.%s.%s.bin' % (scenario,board)
- deb_out_name ='acrn.%s.%s.32.out' % (scenario,board)
+ deb_bin_name = 'acrn.%s.%s.bin' % (scenario, board)
+ deb_out_name = 'acrn.%s.%s.32.out' % (scenario, board)
build_bin_name = build_dir + '/hypervisor/acrn.bin'
build_out_name = build_dir + '/hypervisor/acrn.32.out'
- add_cmd_list(cmd_list, 'cp %s acrn_release_deb/boot/%s' %(build_bin_name, deb_bin_name), build_dir)
- add_cmd_list(cmd_list, 'cp %s acrn_release_deb/boot/%s' %(build_out_name, deb_out_name), build_dir)
+ add_cmd_list(cmd_list, 'cp %s acrn_release_deb/boot/%s' % (build_bin_name, deb_bin_name), build_dir)
+ add_cmd_list(cmd_list, 'cp %s acrn_release_deb/boot/%s' % (build_out_name, deb_out_name), build_dir)
run_cmd_list(cmd_list)
- lines=[]
- f=open(cur_dir + "/misc/packaging/acrn-hypervisor.postinst",'r')
+ lines = []
+ f = open(cur_dir + "/misc/packaging/acrn-hypervisor.postinst", 'r')
for line in f:
lines.append(line)
f.close()
start = lines.index('#Build info Start\n')
end = lines.index('#Build info End\n')
- del lines[(start+1):(end-1)]
- lines.insert(start+1,"\nSCENARIO=(%s)\n"%scenario)
- lines.insert(start+2,"\nBOARD=(%s)\n"%board)
+ del lines[(start + 1):(end - 1)]
+ lines.insert(start + 1, "\nSCENARIO=(%s)\n" % scenario)
+ lines.insert(start + 2, "\nBOARD=(%s)\n" % board)
with open(cur_dir + "/misc/packaging/acrn-hypervisor.postinst", "w") as f:
for line in lines:
f.write(line)
f.close()
- listcontrol=['Package: acrn-hypervisor\n',
- 'version: %s \n'% version,
- 'Depends: libcjson1\n',
- 'Section: free \n',
- 'Priority: optional \n',
- 'Architecture: amd64 \n',
- 'Maintainer: acrn-dev@lists.projectacrn.org \n',
- 'Description: ACRN Hypervisor for IoT \n',
- '\n']
- with open(deb_dir + '/DEBIAN/control','w',encoding='utf-8') as fr:
- fr.writelines(listcontrol)
+ listcontrol = [
+ 'Package: acrn-hypervisor\n',
+ 'version: %s \n' % version,
+ 'Depends: libcjson1\n',
+ 'Section: free \n',
+ 'Priority: optional \n',
+ 'Architecture: amd64 \n',
+ 'Maintainer: acrn-dev@lists.projectacrn.org \n',
+ 'Description: ACRN Hypervisor for IoT \n',
+ '\n'
+ ]
+ with open(deb_dir + '/DEBIAN/control', 'w', encoding='utf-8') as fr:
+ fr.writelines(listcontrol)
- #design in acrn_data
- with open(cur_dir + "/.deb.conf","r") as load_deb:
+ # design in acrn_data
+ with open(cur_dir + "/.deb.conf", "r") as load_deb:
deb_info = json.load(load_deb)
load_deb.close()
@@ -108,14 +128,17 @@ def create_acrn_deb(board, scenario, version, build_dir):
run_command('chmod +x ./build/acrn_release_deb/DEBIAN/preinst', cur_dir)
run_command('sed -i \'s/\r//\' ./build/acrn_release_deb/DEBIAN/preinst', cur_dir)
- ret = run_command('dpkg -b acrn_release_deb acrn-%s-%s-%s.deb' %(board, scenario, version), build_dir)
+ ret = run_command('dpkg -b acrn_release_deb acrn-%s-%s-%s.deb' % (board, scenario, version), build_dir)
if ret != 0:
print("ERROR : generate ACRN debian package acrn-{}-{}-{}.deb failed! \
Please check all the files in {}/acrn_release_deb".format(board, scenario, version, build_dir))
else:
- print("ACRN debian package acrn-{}-{}-{}.deb was successfully created in the {}.".format(board, scenario, version, build_dir))
+ print(
+ "ACRN debian package acrn-{}-{}-{}.deb was successfully created in the {}.".format(board, scenario, version,
+ build_dir))
return
+
def create_acrn_board_inspector_deb(version, build_dir):
cur_dir = build_dir + '/../'
deb_dir = build_dir + '/acrn_board_inspector_deb/'
@@ -127,34 +150,74 @@ def create_acrn_board_inspector_deb(version, build_dir):
add_cmd_list(cmd_list, 'touch DEBIAN/control', deb_dir)
run_cmd_list(cmd_list)
- #control file description
- listcontrol=['Package: acrn-board-inspector\n',
- 'version: %s \n'% version,
- 'Section: free \n',
- 'Priority: optional \n',
- 'Architecture: amd64 \n',
- 'Maintainer: acrn-dev@lists.projectacrn.org \n',
- 'Description: ACRN board inspector tools \n',
- 'Depends: cpuid, msr-tools, pciutils, dmidecode, python3, python3-pip, python3-lxml \n',
- '\n']
- with open(deb_dir + '/DEBIAN/control','w',encoding='utf-8') as fr:
- fr.writelines(listcontrol)
+ # control file description
+ listcontrol = [
+ 'Package: acrn-board-inspector\n',
+ 'version: %s \n' % version,
+ 'Section: free \n',
+ 'Priority: optional \n',
+ 'Architecture: amd64 \n',
+ 'Maintainer: acrn-dev@lists.projectacrn.org \n',
+ 'Description: ACRN board inspector tools \n',
+ 'Depends: cpuid, msr-tools, pciutils, dmidecode, python3, python3-pip, python3-lxml \n',
+ '\n'
+ ]
+ with open(deb_dir + '/DEBIAN/control', 'w', encoding='utf-8') as fr:
+ fr.writelines(listcontrol)
run_command('cp -r ./misc/config_tools/board_inspector/ ./build/acrn_board_inspector_deb/bin/', cur_dir)
- run_command('cp ./misc/packaging/acrn-board-inspector.postinst ./build/acrn_board_inspector_deb/DEBIAN/postinst', cur_dir)
+ run_command('cp ./misc/packaging/acrn-board-inspector.postinst ./build/acrn_board_inspector_deb/DEBIAN/postinst',
+ cur_dir)
run_command('chmod +x ./build/acrn_board_inspector_deb/DEBIAN/postinst', cur_dir)
run_command('sed -i \'s/\r//\' ./build/acrn_board_inspector_deb/DEBIAN/postinst', cur_dir)
run_command('cp ./misc/packaging/acrn-board-inspector.prerm ./build/acrn_board_inspector_deb/DEBIAN/prerm', cur_dir)
run_command('chmod +x ./build/acrn_board_inspector_deb/DEBIAN/prerm', cur_dir)
run_command('sed -i \'s/\r//\' ./build/acrn_board_inspector_deb/DEBIAN/prerm', cur_dir)
- ret = run_command('dpkg -b acrn_board_inspector_deb acrn-board-inspector-%s.deb' %(version), build_dir)
+ ret = run_command('dpkg -b acrn_board_inspector_deb acrn-board-inspector-%s.deb' % version, build_dir)
if ret != 0:
- print("ERROR : generate board_inspector debian package acrn-board-inspector-{}.deb failed! \
-Please check all the files in {}/acrn_board_inspector_deb".format(version, build_dir))
+ print(
+ "ERROR : generate board_inspector debian package acrn-board-inspector-{}.deb failed! "
+ "Please check all the files in {}/acrn_board_inspector_deb".format(version, build_dir)
+ )
else:
- print("board_inspector debian package acrn-board-inspector-{}.deb was successfully created in the {}.".format(version, build_dir))
+ print(
+ "board_inspector debian package acrn-board-inspector-{}.deb"
+ " was successfully created in the {}.".format(version, build_dir)
+ )
return
+
+def create_configurator_deb(build_dir):
+ cmd_list = []
+
+ # get folder path
+ project_base = Path(__file__).parent.parent.parent
+ configurator_path = Path(__file__).parent.parent / 'config_tools' / 'configurator'
+ scenario_config_path = project_base / "misc" / "config_tools" / "scenario_config"
+ deb_dir = configurator_path / 'src-tauri' / 'target' / 'release' / 'bundle' / 'deb'
+
+ # clean old directory
+ if os.path.isdir(deb_dir):
+ shutil.rmtree(deb_dir)
+
+ # build command, if you update this, please update misc/config_tools/configurator/README.md#L55
+ add_cmd_list(cmd_list, 'python3 schema_slicer.py', scenario_config_path)
+ add_cmd_list(cmd_list, 'python3 converter.py', scenario_config_path / "jsonschema")
+ add_cmd_list(cmd_list, 'yarn', configurator_path)
+ add_cmd_list(cmd_list, 'yarn build', configurator_path)
+ run_cmd_list(cmd_list)
+
+ deb_name = [x for x in os.listdir(deb_dir) if x.endswith('.deb')]
+ if not deb_name:
+ print('ERROR! No acrn-configurator deb found!')
+ return
+ deb_name = deb_name[0]
+ with open(deb_dir / deb_name, 'rb') as src:
+ with open(os.path.join(build_dir, deb_name), 'wb') as dest:
+ dest.write(src.read())
+ return
+
+
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("deb_mode", help="choose deb mode, e.g. acrn_all or board_inspector")
@@ -162,11 +225,16 @@ if __name__ == "__main__":
parser.add_argument("--version", default="1.0", help="the acrn-hypervisor version")
parser.add_argument("--board_name", default="board", help="the name of the board that runs the ACRN hypervisor")
parser.add_argument("--scenario", default="scenario", help="the acrn hypervisor scenario setting")
+ parser.add_argument("--debug", default=False, help="debug mode")
args = parser.parse_args()
+ DEBUG = args.debug
+
if args.deb_mode == 'board_inspector':
create_acrn_board_inspector_deb(args.version, args.build_dir)
elif args.deb_mode == 'acrn_all':
create_acrn_deb(args.board_name, args.scenario, args.version, args.build_dir)
+ elif args.deb_mode == 'configurator':
+ create_configurator_deb(args.build_dir)
else:
print("ERROR: Please check the value of deb_mode: the value shall be acrn_all or board_inspector.")