diff --git a/frontend/public/icons/wizard.svg b/frontend/public/icons/wizard.svg
new file mode 100644
index 000000000..f3f27b97c
--- /dev/null
+++ b/frontend/public/icons/wizard.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte
index acf3c4ec8..502887671 100644
--- a/frontend/src/App.svelte
+++ b/frontend/src/App.svelte
@@ -42,20 +42,23 @@
selectedResource,
settings,
dataLength,
+ viewCrumbs,
} from "./stores.js";
import { keyEventToString, shortcuts } from "./keyboard.js";
+ import RootWizardView from "./patch_wizard/RootWizardView.svelte";
+
printConsoleArt();
- let showRootResource = false,
- showProjectManager = false,
- dataLenPromise = Promise.resolve([]),
+ let dataLenPromise = Promise.resolve([]),
useAssemblyView = false,
useTextView = false,
rootResourceLoadPromise = new Promise((resolve) => {}),
resources = {};
let currentResource, rootResource, modifierView, bottomLeftPane;
+ let topLevelView = null;
+
// TODO: Move to settings
let riddleAnswered = JSON.parse(window.localStorage.getItem("riddleSolved"));
if (riddleAnswered === null || riddleAnswered === undefined) {
@@ -154,12 +157,14 @@ Answer by running riddle.answer('your answer here') from the console.`);
$: docstyle.setProperty("--accent-text-color", $settings.accentText);
$: docstyle.setProperty("--last-modified-color", $settings.lastModified);
$: docstyle.setProperty("--all-modified-color", $settings.allModified);
+
+ $: topLevelView = $viewCrumbs[$viewCrumbs.length - 1];
-{#if showRootResource}
+{#if topLevelView === "rootResource"}
{#await rootResourceLoadPromise}
{:then _}
@@ -176,8 +181,6 @@ Answer by running riddle.answer('your answer here') from the console.`);
rootResource="{rootResource}"
bind:bottomLeftPane="{bottomLeftPane}"
bind:modifierView="{modifierView}"
- bind:showProjectManager="{showProjectManager}"
- bind:showRootResource="{showRootResource}"
/>
{/if}
@@ -207,19 +210,17 @@ Answer by running riddle.answer('your answer here') from the console.`);
{/if}
-{:else if showProjectManager}
+{:else if topLevelView === "projectManager"}
-{:else}
+{:else if topLevelView === "patchWizard"}
+
+{:else if topLevelView === "start"}
diff --git a/frontend/src/ofrak/remote_resource.js b/frontend/src/ofrak/remote_resource.js
index 77cad8187..03dd07202 100644
--- a/frontend/src/ofrak/remote_resource.js
+++ b/frontend/src/ofrak/remote_resource.js
@@ -646,7 +646,7 @@ export class RemoteResource extends Resource {
});
}
- async run_component(component, configtype, response) {
+ async run_component(component, configtype, config) {
const result = await fetch(
`${this.uri}/run_component?component=${component}`,
{
@@ -654,7 +654,7 @@ export class RemoteResource extends Resource {
headers: {
"Content-Type": "application/json",
},
- body: JSON.stringify([configtype, response]),
+ body: JSON.stringify(config),
}
).then(async (r) => {
if (!r.ok) {
diff --git a/frontend/src/patch_wizard/FreeSpaceView.svelte b/frontend/src/patch_wizard/FreeSpaceView.svelte
new file mode 100644
index 000000000..032bc910b
--- /dev/null
+++ b/frontend/src/patch_wizard/FreeSpaceView.svelte
@@ -0,0 +1,408 @@
+
+
+
+
+{#if unallocatedSegments.length > 0}
+
+ {unallocatedSegments.length} segment(s) totaling 0x{totalSize(
+ unallocatedSegments
+ ).toString(16)} bytes still need to be allocated.
+
+{:else if patchInfo?.objectInfos}
+
+ No unallocated segments (out of {patchInfo.objectInfos
+ .map((objInfo) => objInfo.segments)
+ .flat()
+ .filter((seg) => seg.include).length} segments included).
+
+{:else}
+ No segments.
+{/if}
+
+
+
+
Segments to allocate:
+ R
+ W
+ X
+
+ Unallocated Only
+
+
+
+ Total {unallocatedOnly ? "unallocated" : ""}
+ {permsToString(segFilterR, segFilterW, segFilterX)} segment size: 0x{totalSize(
+ filteredUnallocatedSegments
+ ).toString(16)}
+
+
+ {#each filteredUnallocatedSegments as segInfo}
+
+ {/each}
+
+
+
+
+
Free Space to allocate:
+ R
+ W
+ X
+
+
+ {#await freeSpacePromise}
+
Fetching free space...
+ {:then freeSpace}
+ {#await filteredFreeSpacePromise}
+
Processing free space...
+ {:then filteredFreeSpace}
+
+ {filteredFreeSpace.length} matching blocks of {permsToString(
+ freeFilterR,
+ freeFilterW,
+ freeFilterX
+ )} free space (out of {freeSpace.map(([_, ranges]) => ranges).flat()
+ .length} total ranges with {freeSpace.length} unique permission spec{freeSpace.length >
+ 1
+ ? "s"
+ : ""}).
+
+ {#each filteredFreeSpace as [blockStart, blockEnd]}
+
+ [0x{blockStart.toString(16)}-0x{blockEnd.toString(16)} (0x{(
+ blockEnd - blockStart
+ ).toString(16)} bytes)]
+
+ {/each}
+
+ Total {permsToString(freeFilterR, freeFilterW, freeFilterX)} free space:
+ 0x{filteredFreeSpace
+ .reduce((sum, [start, end]) => sum + end - start, 0)
+ .toString(16)}
+
+ {/await}
+ {/await}
+
+
+
+
+
Create Free Space: Extend Binary (ELF Only)
+
+
Recover Unused Alignment
+
+ Reclaim unused alignment bytes between adjacent PT_LOAD segment in an ELF
+ and tag them as free space. Alignment bytes often exist between PT_LOAD
+ sections in ELF binaries. These alignment bytes are added to the preceding
+ PT_LOAD segment. This is the least invasive ELF extension technique.
+
+
+
Apply
+ {#if patchInfo.extensionMethodStatus.load_align.status}
+ {#if patchInfo.extensionMethodStatus.load_align.valid}
+
Success! {patchInfo.extensionMethodStatus.load_align.status}
+ {:else}
+
+ {patchInfo.extensionMethodStatus.load_align.status}
+
+ {/if}
+ {/if}
+
+
+
Replace NOTE Segment
+
+ If the ELF has a .NOTE segment, we can replace the .NOTE header with a
+ .LOAD header pointing to some new space we weill add at the end. Adds
+ 0x1000 bytes by default. WARNING: Will require the ELF to be re-analyzed
+ and unpacked.
+
+
Apply
+ {#if patchInfo.extensionMethodStatus.note.status}
+ {#if patchInfo.extensionMethodStatus.note.valid}
+
Success! {patchInfo.extensionMethodStatus.note.status}
+ {:else}
+
{patchInfo.extensionMethodStatus.note.status}
+ {/if}
+ {/if}
+
+
+
+
+
Create Free Space: Overwrite Existing Functions
+
+ {#await complexBlocksPromise}
+
+ {:then complexBlocks}
+
+ {#each complexBlocks as cbObject}
+
+
+
+ {cbObject.name} (0x{cbObject.vaddr.toString(16)}-0x{(
+ cbObject.vaddr + cbObject.size
+ ).toString(16)})
+
+
+ {/each}
+
+
+
+ 0x{totalSelectedComplexBlockSize(
+ complexBlocksToFree,
+ complexBlocks
+ ).toString(16)} bytes of RX space will be created.
+
+
+
Free
+ {/await}
+
diff --git a/frontend/src/patch_wizard/ObjectMappingView.svelte b/frontend/src/patch_wizard/ObjectMappingView.svelte
new file mode 100644
index 000000000..ea151f706
--- /dev/null
+++ b/frontend/src/patch_wizard/ObjectMappingView.svelte
@@ -0,0 +1,15 @@
+
+
+
+ {#each patchInfo.objectInfos as objInfo}
+
+ {/each}
+
diff --git a/frontend/src/patch_wizard/ObjectWidget.svelte b/frontend/src/patch_wizard/ObjectWidget.svelte
new file mode 100644
index 000000000..a86234f02
--- /dev/null
+++ b/frontend/src/patch_wizard/ObjectWidget.svelte
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
Segments
+
+ {#each objectInfo.segments as segInfo (segInfo.name)}
+
+ {/each}
+
+
+
+
Symbols
+
+ {#if locallyUndefinedSymbols.length > 0}
+
+ {locallyUndefinedSymbols.length} unresolved symbol(s)!
+
+ {/if}
+ Provides:
+ {#each objectInfo.strongSymbols as sym}
+
+ {/each}
+
+ Requires:
+ {#each objectInfo.unresolvedSymbols as sym}
+
+ {/each}
+
+
+
+
diff --git a/frontend/src/patch_wizard/PatchMakerLogsView.svelte b/frontend/src/patch_wizard/PatchMakerLogsView.svelte
new file mode 100644
index 000000000..18635a886
--- /dev/null
+++ b/frontend/src/patch_wizard/PatchMakerLogsView.svelte
@@ -0,0 +1,147 @@
+
+
+
+
+PatchMaker Logs
+
+ {#each messages as message}
+ {#if message === linebreakMarker}
+
+ {:else}
+
{message}
+ {/if}
+ {/each}
+
diff --git a/frontend/src/patch_wizard/PatchSymbol.svelte b/frontend/src/patch_wizard/PatchSymbol.svelte
new file mode 100644
index 000000000..f39ac6d02
--- /dev/null
+++ b/frontend/src/patch_wizard/PatchSymbol.svelte
@@ -0,0 +1,51 @@
+
+
+
+
+{#if symbolInfo.providedBy?.length > 0}
+
+ {symbolInfo.name}
+
+{:else}
+
+ {symbolInfo.name}
+
+{/if}
diff --git a/frontend/src/patch_wizard/RootWizardView.svelte b/frontend/src/patch_wizard/RootWizardView.svelte
new file mode 100644
index 000000000..56cb1197a
--- /dev/null
+++ b/frontend/src/patch_wizard/RootWizardView.svelte
@@ -0,0 +1,554 @@
+
+
+
+
+
+
+
+
+ ← Back
+
PATCH WIZARD
+
+ {#await patchInfoPromise}
+
+ {:then _}
+
+
+ {#if patchInfo.userInputs.toolchain && overview.nSources}
+
+ Using {patchInfo.userInputs.toolchain.split(".").pop()} to build
+ patch from
+ {overview.nSources} source code files.
+
+ {:else if overview.nSources}
+ No toolchain selected
+ to build {overview.nSources} source code files.
+ {:else if patchInfo.userInputs.toolchain}
+ Using {patchInfo.userInputs.toolchain.split(".").pop()}
+
+ but no source code files to build patch from!
+
+ {:else}
+
+ No toolchain selected and no source code files provided!
+
+ {/if}
+
+ Configure Toolchain
+ Configure Patch Sources
+
+
+ {#if overview.nSegments}
+
+ {overview.nSegments} segment{overview.nSegments > 1 ? "s" : ""} totaling
+ 0x{overview.totalBytes.toString(16)} bytes will be extracted from
+ the compiled sources and injected into the target binary at unique
+ addresses.
+
+ {:else}
+ No segments chosen for injection!
+ {/if}
+ {#if overview.unallocatedSegments.length > 0}
+
+ {overview.unallocatedSegments.length} segment(s) still need to be
+ allocated:
+
+ {#each overview.unallocatedSegments as seg}
+ {seg.unit}{seg.name}
+ {/each}
+ {/if}
+
+ Configure Patch Placement
+ Manage Free Space
+
+
+ {#if patchInfo.symbolRefMap}
+
+ Target binary provides {patchInfo.targetInfo.symbols.length} symbols:
+
+
+ {#each patchInfo.targetInfo.symbols as sym}
+
+ {/each}
+
+ {#if patchInfo.userInputs.symbols}
+
+ You are providing {patchInfo.userInputs.symbols.length} symbol(s).
+
+ {/if}
+
+ {#if overview.unresolvedSyms.size > 0}
+ {#if overview.unresolvedSyms.size === 1}
+ There as an unresolved symbol!
+ {:else}
+
+ There are {overview.unresolvedSyms.size} unresolved symbols!
+
+ {/if}
+ {#each Array.from(overview.unresolvedSyms) as sym}
+
+ {/each}
+
+ These symbols are referenced by the patch code, but not
+ defined in the patch code or the binary.
+
+ {:else}
+ There are no unresolved symbols.
+ {/if}
+ {/if}
+
+ Define Symbols Manually
+
+
+ {/await}
+
+
+ Step 4. Inject!
+
+
+ {injectionStatus}
+
+
+ {#await patchInfoPromise}
+
+ {:then _}
+
+ {/await}
+
+
+
+ {#if subMenu}
+
+ {/if}
+
+
diff --git a/frontend/src/patch_wizard/SegmentWidget.svelte b/frontend/src/patch_wizard/SegmentWidget.svelte
new file mode 100644
index 000000000..4499e2328
--- /dev/null
+++ b/frontend/src/patch_wizard/SegmentWidget.svelte
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+ {#if segmentInfo.include}
+ [0x{segmentInfo.size.toString(16)} bytes] =>
+
+
+
+ {:else}
+ [0x{segmentInfo.size.toString(16)} bytes] (discarded)
+ {/if}
+
+
diff --git a/frontend/src/patch_wizard/SourceMenuView.svelte b/frontend/src/patch_wizard/SourceMenuView.svelte
new file mode 100644
index 000000000..f35e3e351
--- /dev/null
+++ b/frontend/src/patch_wizard/SourceMenuView.svelte
@@ -0,0 +1,93 @@
+
+
+
+ {#each patchInfo.sourceInfos as sourceInfo}
+
+ {/each}
+
+ Add new source or header file
+
diff --git a/frontend/src/patch_wizard/SourceWidget.svelte b/frontend/src/patch_wizard/SourceWidget.svelte
new file mode 100644
index 000000000..93cd2c701
--- /dev/null
+++ b/frontend/src/patch_wizard/SourceWidget.svelte
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+ {#if !sourceHidden}
+
+
+
+ {/if}
+
diff --git a/frontend/src/patch_wizard/SummaryWidget.svelte b/frontend/src/patch_wizard/SummaryWidget.svelte
new file mode 100644
index 000000000..c1597b89b
--- /dev/null
+++ b/frontend/src/patch_wizard/SummaryWidget.svelte
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+ {#if !collapsed}
+
+
+
+ {/if}
+
diff --git a/frontend/src/patch_wizard/SymbolView.svelte b/frontend/src/patch_wizard/SymbolView.svelte
new file mode 100644
index 000000000..22e730f53
--- /dev/null
+++ b/frontend/src/patch_wizard/SymbolView.svelte
@@ -0,0 +1,154 @@
+
+
+
+
+
+ Define symbols which are required by the patch code and not provided by the
+ target binary.
+
+
+
+
+ {#if undefinedSymsCollapse}
+ [+]
+ {:else}
+ [-]
+ {/if}
+
+ {#if unresolvedSyms}
+ There are {unresolvedSyms.size} unresolved symbols.
+ {:else}
+ There are no unresolved symbols.
+ {/if}
+ {#if !undefinedSymsCollapse}
+
+ {#each Array.from(unresolvedSyms) as sym}
+
+ {/each}
+
+ {/if}
+
+
+
diff --git a/frontend/src/patch_wizard/ToolchainSetupView.svelte b/frontend/src/patch_wizard/ToolchainSetupView.svelte
new file mode 100644
index 000000000..039c52d6a
--- /dev/null
+++ b/frontend/src/patch_wizard/ToolchainSetupView.svelte
@@ -0,0 +1,67 @@
+
+
+{#await getToolchainList()}
+
+{:then toolchainConfig_structs}
+
+
+{:catch err}
+ Failed to fetch toolchain configuration structure!
+{/await}
diff --git a/frontend/src/patch_wizard/UserInputSymbol.svelte b/frontend/src/patch_wizard/UserInputSymbol.svelte
new file mode 100644
index 000000000..9d69ac50c
--- /dev/null
+++ b/frontend/src/patch_wizard/UserInputSymbol.svelte
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/project/ProjectManagerToolbar.svelte b/frontend/src/project/ProjectManagerToolbar.svelte
index 0dbbedc9f..692a4185d 100644
--- a/frontend/src/project/ProjectManagerToolbar.svelte
+++ b/frontend/src/project/ProjectManagerToolbar.svelte
@@ -1,10 +1,10 @@
@@ -46,7 +48,11 @@
- {@html hljs.highlight(script.join("\n"), { language: language }).value}
+ {#if language}
+ {@html hljs.highlight(script.join("\n"), { language: language }).value}
+ {:else}
+ {script.join("\n")}
+ {/if}
diff --git a/frontend/src/utils/SerializerInputForm.svelte b/frontend/src/utils/SerializerInputForm.svelte
deleted file mode 100644
index 9f40efe1f..000000000
--- a/frontend/src/utils/SerializerInputForm.svelte
+++ /dev/null
@@ -1,353 +0,0 @@
-
-
-
-
-
-
- {#if node["name"] && !["builtins.bool", "builtins.str", "builtins.bytes", "builtins.int"].includes(node["type"])}
- {nodeName}
- {/if}
-
-
- {#if node["type"] == "builtins.bool"}
-
- {nodeName}
-
-
-
- {:else if node["type"] == "builtins.str"}
-
- {nodeName}
-
-
-
-
- {:else if node["type"] == "builtins.bytes"}
-
- {nodeName}
-
-
-
-
- {:else if node["type"] == "builtins.int"}
-
- {nodeName}
-
-
-
-
- {:else if node["type"] == "typing.List" || node["type"] == "typing.Iterable"}
-
-
-
-
-
- {#each _element as elements}
- {#if !skip.includes(elements)}
-
- {/if}
- {/each}
-
-
- {:else if node["type"] == "typing.Tuple"}
- {#each node["args"] as arg, i}
-
- {/each}
-
-
- {:else if node["type"] == "typing.Dict"}
-
-
-
-
-
- {#each _element as elements, index}
- {#if !skip.includes(elements)}
-
-
-
-
-
- {elements}
-
-
Key
-
-
Value
-
-
- {/if}
- {/each}
-
-
- {:else if node["type"] == "typing.Union" || node["type"] == "typing.Optional"}
-
- {#each node["args"] as arg}
-
- {arg.type}
-
- {/each}
-
- {#if unionTypeSelect != null}
-
- {/if}
-
-
- {:else if node["type"] == "ofrak.core.patch_maker.modifiers.SourceBundle"}
-
-
-
- {:else if node["enum"] != null}
-
- {#each Object.entries(node["enum"]) as [name, enum_value]}
-
- {name}
-
- {/each}
-
-
-
- {:else if node["fields"] != null}
- {#each node["fields"] as field, i}
- {#if node["type"] == "ofrak_type.range.Range"}
-
- {:else}
-
- {/if}
- {/each}
- {/if}
-
-
diff --git a/frontend/src/utils/Toolbar.svelte b/frontend/src/utils/Toolbar.svelte
index cebcbaec5..b0387cbb6 100644
--- a/frontend/src/utils/Toolbar.svelte
+++ b/frontend/src/utils/Toolbar.svelte
@@ -61,6 +61,7 @@
button.shortcut.split('+').reverse().join(' + ') +
')'
: '')}"
+ disabled="{button.disabled && button.disabled()}"
>
{#if button.iconUrl}
diff --git a/frontend/src/utils/serializer_inputs/BaseSerializerInputForm.svelte b/frontend/src/utils/serializer_inputs/BaseSerializerInputForm.svelte
new file mode 100644
index 000000000..32ebe9c7c
--- /dev/null
+++ b/frontend/src/utils/serializer_inputs/BaseSerializerInputForm.svelte
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+ {#if node.name && !["builtins.bool", "builtins.str", "builtins.bytes", "builtins.int"].includes(node.type)}
+ {nodeName}
+ {/if}
+
+
+
+
diff --git a/frontend/src/utils/serializer_inputs/DictInputForm.svelte b/frontend/src/utils/serializer_inputs/DictInputForm.svelte
new file mode 100644
index 000000000..c5931e06f
--- /dev/null
+++ b/frontend/src/utils/serializer_inputs/DictInputForm.svelte
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+{#each element as [subElementKey, subElementValue], index}
+
+
+
+
+
+
+
Key
+
+
Value
+
+
+{/each}
diff --git a/frontend/src/utils/serializer_inputs/EnumInputForm.svelte b/frontend/src/utils/serializer_inputs/EnumInputForm.svelte
new file mode 100644
index 000000000..0eb32d6e7
--- /dev/null
+++ b/frontend/src/utils/serializer_inputs/EnumInputForm.svelte
@@ -0,0 +1,44 @@
+
+
+
+
+
+ {#if !element}
+
+ Please select a {node.type.split(".").at(-1)}
+
+ {/if}
+ {#each Object.entries(node.enum) as [name, enum_value]}
+
+ {name}
+
+ {/each}
+
diff --git a/frontend/src/utils/serializer_inputs/ListInputForm.svelte b/frontend/src/utils/serializer_inputs/ListInputForm.svelte
new file mode 100644
index 000000000..1eec0233e
--- /dev/null
+++ b/frontend/src/utils/serializer_inputs/ListInputForm.svelte
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+{#each element as subElement, i}
+
+{/each}
diff --git a/frontend/src/utils/serializer_inputs/ObjectInputForm.svelte b/frontend/src/utils/serializer_inputs/ObjectInputForm.svelte
new file mode 100644
index 000000000..fcdfa45bd
--- /dev/null
+++ b/frontend/src/utils/serializer_inputs/ObjectInputForm.svelte
@@ -0,0 +1,21 @@
+
+
+{#each node.fields as field, i}
+
+{/each}
diff --git a/frontend/src/utils/serializer_inputs/PrimitivesInputForm.svelte b/frontend/src/utils/serializer_inputs/PrimitivesInputForm.svelte
new file mode 100644
index 000000000..e20fd5644
--- /dev/null
+++ b/frontend/src/utils/serializer_inputs/PrimitivesInputForm.svelte
@@ -0,0 +1,117 @@
+
+
+
+
+{#if node.type === "builtins.bool"}
+
+ {nodeName}
+
+
+
+{:else if node.type === "builtins.str"}
+
+ {nodeName}
+
+
+
+
+{:else if node.type === "builtins.bytes"}
+
+ {nodeName}
+
+
+
+
+{:else if node.type === "builtins.int"}
+
+ {nodeName}
+
+
+
+
+{/if}
diff --git a/frontend/src/utils/serializer_inputs/RangeInputForm.svelte b/frontend/src/utils/serializer_inputs/RangeInputForm.svelte
new file mode 100644
index 000000000..a988a7510
--- /dev/null
+++ b/frontend/src/utils/serializer_inputs/RangeInputForm.svelte
@@ -0,0 +1,19 @@
+
+
+{#each node.fields as field, i}
+
+{/each}
diff --git a/frontend/src/utils/serializer_inputs/SourceBundleInputForm.svelte b/frontend/src/utils/serializer_inputs/SourceBundleInputForm.svelte
new file mode 100644
index 000000000..75921e648
--- /dev/null
+++ b/frontend/src/utils/serializer_inputs/SourceBundleInputForm.svelte
@@ -0,0 +1,28 @@
+
+
+
diff --git a/frontend/src/utils/serializer_inputs/TupleInputForm.svelte b/frontend/src/utils/serializer_inputs/TupleInputForm.svelte
new file mode 100644
index 000000000..350b465da
--- /dev/null
+++ b/frontend/src/utils/serializer_inputs/TupleInputForm.svelte
@@ -0,0 +1,19 @@
+
+
+{#each node.args as arg, i}
+
+{/each}
diff --git a/frontend/src/utils/serializer_inputs/UnionInputForm.svelte b/frontend/src/utils/serializer_inputs/UnionInputForm.svelte
new file mode 100644
index 000000000..dc0c19060
--- /dev/null
+++ b/frontend/src/utils/serializer_inputs/UnionInputForm.svelte
@@ -0,0 +1,130 @@
+
+
+
+
+{#if node.type === "typing.Union"}
+
+ {#if !unionTypeSelect}
+ Select a type
+ {/if}
+ {#each node.args as arg}
+ {#if unionTypeSelect === arg}
+
+ {arg.type}
+
+ {:else}
+
+ {arg.type}
+
+ {/if}
+ {/each}
+
+ {#if unionTypeSelect != null}
+
+ {/if}
+{:else if node.type === "typing.Optional"}
+
+ {#if optionalSupplied}
+
+ {/if}
+{/if}
diff --git a/frontend/src/views/ComponentsView.svelte b/frontend/src/views/ComponentsView.svelte
index d91e349a0..98e237048 100644
--- a/frontend/src/views/ComponentsView.svelte
+++ b/frontend/src/views/ComponentsView.svelte
@@ -120,10 +120,10 @@
import { splitAndCapitalize } from "../helpers.js";
import { onMount } from "svelte";
- import SerializerInputForm from "../utils/SerializerInputForm.svelte";
import LoadingText from "../utils/LoadingText.svelte";
import Checkbox from "../utils/Checkbox.svelte";
import Button from "../utils/Button.svelte";
+ import BaseSerializerInputForm from "../utils/serializer_inputs/BaseSerializerInputForm.svelte";
export let modifierView;
let errorMessage,
@@ -137,7 +137,7 @@
ofrakComponentsPromise = new Promise(() => {}),
ofrakTargetsPromise = new Promise(() => {}),
ofrakConfigsPromise = new Promise(() => {}),
- config = {},
+ config,
ofrakConfigName = null;
async function getTargetsAndComponents() {
@@ -306,7 +306,7 @@
{:then ofrakConfig}
{#if ofrakConfig.length != 0}
Configure {splitAndCapitalize(selectedComponent)}:
-
diff --git a/frontend/src/views/RunScriptView.svelte b/frontend/src/views/RunScriptView.svelte
index 6690d6123..e22f5d793 100644
--- a/frontend/src/views/RunScriptView.svelte
+++ b/frontend/src/views/RunScriptView.svelte
@@ -82,7 +82,6 @@