diff --git a/packages/ui/index.html b/packages/ui/index.html
index 69895eb22..39a9286d4 100644
--- a/packages/ui/index.html
+++ b/packages/ui/index.html
@@ -3,7 +3,8 @@
diff --git a/packages/ui/public/favicon.ico b/packages/ui/public/favicon.ico
new file mode 100644
index 000000000..d5e6b335e
Binary files /dev/null and b/packages/ui/public/favicon.ico differ
diff --git a/packages/ui/src/components/shared.tsx b/packages/ui/src/components/shared.tsx
index 1210b0ccf..48005feea 100644
--- a/packages/ui/src/components/shared.tsx
+++ b/packages/ui/src/components/shared.tsx
@@ -14,3 +14,7 @@ export const Panel = styled.div`
border: 1px solid black;
padding: 1rem;
`;
+
+export const Breadcrumb = styled.div`
+ padding-bottom: 1rem;
+`;
diff --git a/packages/ui/src/main.tsx b/packages/ui/src/main.tsx
index 1bcf46e48..246006793 100644
--- a/packages/ui/src/main.tsx
+++ b/packages/ui/src/main.tsx
@@ -7,7 +7,6 @@ import {
} from "@nomicfoundation/ignition-core/ui-helpers";
import ReactDOM from "react-dom/client";
import { RouterProvider, createHashRouter } from "react-router-dom";
-import { FutureDetails } from "./pages/future-details/future-details";
import { VisualizationOverview } from "./pages/visualization-overview/visualization-overview";
const loadDeploymentFromEmbeddedDiv = (): IgnitionModule<
@@ -49,10 +48,6 @@ const main = async () => {
path: "/",
element:
,
},
- {
- path: "/future/:futureId",
- element:
,
- },
]);
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
diff --git a/packages/ui/src/pages/future-details/components/future-summary.tsx b/packages/ui/src/pages/future-details/components/future-summary.tsx
deleted file mode 100644
index 3ffbd30e8..000000000
--- a/packages/ui/src/pages/future-details/components/future-summary.tsx
+++ /dev/null
@@ -1,193 +0,0 @@
-import {
- Future,
- FutureType,
- isFuture,
-} from "@nomicfoundation/ignition-core/ui-helpers";
-import { PageTitle, Panel } from "../../../components/shared";
-import { SummaryHeader } from "../../../components/summary-header";
-import { argumentTypeToString } from "../../../utils/argumentTypeToString";
-
-export const FutureSummary: React.FC<{
- future: Future;
-}> = ({ future }) => {
- const title = resolveTitleFor(future);
-
- return (
-
- );
-};
-
-function resolveTitleFor(future: Future): string {
- switch (future.type) {
- case FutureType.NAMED_ARTIFACT_CONTRACT_DEPLOYMENT:
- return `Contract deploy - ${future.contractName}`;
- case FutureType.CONTRACT_DEPLOYMENT:
- return `Contract deploy from Artifact - ${future.contractName}`;
- case FutureType.NAMED_ARTIFACT_LIBRARY_DEPLOYMENT:
- return `Library deploy - ${future.contractName}`;
- case FutureType.LIBRARY_DEPLOYMENT:
- return `Library deploy from Artifact - ${future.contractName}`;
- case FutureType.CONTRACT_CALL:
- return `Call - ${future.contract.contractName}/${future.functionName}`;
- case FutureType.STATIC_CALL:
- return `Static call - ${future.contract.contractName}/${future.functionName}`;
- case FutureType.NAMED_ARTIFACT_CONTRACT_AT:
- return `Existing contract - ${future.contractName} (${
- typeof future.address === "string"
- ? future.address
- : isFuture(future.address)
- ? future.address.id
- : argumentTypeToString(future.address)
- })`;
- case FutureType.CONTRACT_AT:
- return `Existing contract from Artifact - ${future.contractName} (${
- typeof future.address === "string"
- ? future.address
- : isFuture(future.address)
- ? future.address.id
- : argumentTypeToString(future.address)
- })`;
- case FutureType.READ_EVENT_ARGUMENT:
- return `Read event argument from future - ${future.id}`;
- case FutureType.SEND_DATA:
- return `Send data - ${future.id}`;
- }
-}
-
-const FutureDetailsSection: React.FC<{ future: Future }> = ({ future }) => {
- switch (future.type) {
- case FutureType.NAMED_ARTIFACT_CONTRACT_DEPLOYMENT:
- return (
-
-
Contract - {future.contractName}
-
Constructor Args
-
- {Object.entries(future.constructorArgs).map(([key, value]) => (
- -
- {key} - {argumentTypeToString(value)}
-
- ))}
-
-
- );
- case FutureType.CONTRACT_DEPLOYMENT:
- return (
-
-
Contract - {future.contractName}
-
Constructor Args
-
- {Object.entries(future.constructorArgs).map(([key, value]) => (
- -
- {key} - {argumentTypeToString(value)}
-
- ))}
-
-
- );
- case FutureType.NAMED_ARTIFACT_LIBRARY_DEPLOYMENT:
- return (
-
-
Library - {future.contractName}
-
- );
- case FutureType.LIBRARY_DEPLOYMENT:
- return (
-
-
Library - {future.contractName}
-
- );
- case FutureType.CONTRACT_CALL:
- return (
-
-
Contract - {future.contract.contractName}
-
function - {future.functionName}
-
Args
-
- {Object.entries(future.args).map(([, value]) => (
- - {argumentTypeToString(value)}
- ))}
-
-
- );
- case FutureType.STATIC_CALL:
- return (
-
-
Contract - {future.contract.contractName}
-
function - {future.functionName}
-
Args
-
- {Object.entries(future.args).map(([, value]) => (
- - {argumentTypeToString(value)}
- ))}
-
-
- );
- case FutureType.NAMED_ARTIFACT_CONTRACT_AT:
- return (
-
-
Contract - {future.contractName}
-
- Address -{" "}
- {typeof future.address === "string"
- ? future.address
- : isFuture(future.address)
- ? future.address.id
- : argumentTypeToString(future.address)}
-
-
- );
-
- case FutureType.CONTRACT_AT:
- return (
-
-
Contract - {future.contractName}
-
- Address -{" "}
- {typeof future.address === "string"
- ? future.address
- : isFuture(future.address)
- ? future.address.id
- : argumentTypeToString(future.address)}
-
-
- );
- case FutureType.READ_EVENT_ARGUMENT:
- return (
-
-
Future - {future.futureToReadFrom.id}
- {future.futureToReadFrom !== future.emitter ? (
-
Emitter - {future.emitter.id}
- ) : null}
-
Event - {future.eventName}
-
Event index - {future.eventIndex}
-
Argument - {future.nameOrIndex}
-
- );
- case FutureType.SEND_DATA:
- return (
-
-
- To -{" "}
- {typeof future.to === "string"
- ? future.to
- : isFuture(future.to)
- ? future.to.id
- : argumentTypeToString(future.to)}
-
-
Data - {future.data}
-
- );
- }
-};
diff --git a/packages/ui/src/pages/future-details/future-details.tsx b/packages/ui/src/pages/future-details/future-details.tsx
deleted file mode 100644
index 5b0a22027..000000000
--- a/packages/ui/src/pages/future-details/future-details.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import {
- IgnitionModule,
- IgnitionModuleResult,
-} from "@nomicfoundation/ignition-core/ui-helpers";
-import React, { useMemo } from "react";
-import { useParams } from "react-router-dom";
-import { Page } from "../../components/shared";
-import { getFutureById } from "../../queries/futures";
-import { FutureSummary } from "./components/future-summary";
-
-export const FutureDetails: React.FC<{
- ignitionModule: IgnitionModule
>;
-}> = ({ ignitionModule }) => {
- const { futureId } = useParams();
-
- const future = useMemo(
- () => getFutureById(ignitionModule, futureId),
- [ignitionModule, futureId]
- );
-
- if (future === undefined) {
- return (
-
- Future not found
-
- );
- }
-
- return (
-
-
-
- );
-};
diff --git a/packages/ui/src/pages/visualization-overview/components/action.tsx b/packages/ui/src/pages/visualization-overview/components/action.tsx
deleted file mode 100644
index 848173066..000000000
--- a/packages/ui/src/pages/visualization-overview/components/action.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import {
- Future,
- FutureType,
- isFuture,
-} from "@nomicfoundation/ignition-core/ui-helpers";
-import React, { useCallback } from "react";
-import { useNavigate } from "react-router-dom";
-import styled, { css } from "styled-components";
-import { argumentTypeToString } from "../../../utils/argumentTypeToString";
-
-export const Action: React.FC<{
- future: Future;
-}> = ({ future }) => {
- const navigate = useNavigate();
-
- const displayText = toDisplayText(future);
-
- const navigateToFuture = useCallback(() => {
- return navigate(`/future/${encodeURIComponent(future.id)}`);
- }, [future.id, navigate]);
-
- return (
-
- {displayText}
-
- );
-};
-
-function toDisplayText(future: Future): string {
- switch (future.type) {
- case FutureType.NAMED_ARTIFACT_CONTRACT_DEPLOYMENT:
- return `Contract deploy ${future.contractName}`;
- case FutureType.CONTRACT_DEPLOYMENT:
- return `Deploy contract ${future.contractName} from artifact`;
- case FutureType.NAMED_ARTIFACT_LIBRARY_DEPLOYMENT:
- return `Library deploy ${future.contractName}`;
- case FutureType.LIBRARY_DEPLOYMENT:
- return `Library deploy ${future.contractName} from artifact`;
- case FutureType.CONTRACT_CALL:
- return `Call ${future.contract.contractName}/${future.functionName}`;
- case FutureType.STATIC_CALL:
- return `Static call ${future.contract.contractName}/${future.functionName}`;
- case FutureType.NAMED_ARTIFACT_CONTRACT_AT:
- return `Existing contract ${future.contractName} (${
- typeof future.address === "string"
- ? future.address
- : isFuture(future.address)
- ? future.address.id
- : argumentTypeToString(future.address)
- })`;
- case FutureType.CONTRACT_AT:
- return `Existing contract ${future.contractName} from artifact (${
- typeof future.address === "string"
- ? future.address
- : isFuture(future.address)
- ? future.address.id
- : argumentTypeToString(future.address)
- })`;
- case FutureType.READ_EVENT_ARGUMENT:
- return `Read event from future ${future.futureToReadFrom.id} (event ${future.eventName} argument ${future.nameOrIndex})`;
- case FutureType.SEND_DATA:
- return `Send data to ${
- typeof future.to === "string"
- ? future.to
- : isFuture(future.to)
- ? future.to.id
- : argumentTypeToString(future.to)
- }`;
- }
-}
-
-const Text = styled.p`
- margin: 0;
-`;
-
-const ActionBtn = styled.div<{ futureType: FutureType }>`
- border: 1px solid black;
- padding: 1rem;
- font-weight: bold;
-
- &:hover {
- background: blue;
- cursor: pointer;
- }
-
- ${(props) =>
- [
- FutureType.NAMED_ARTIFACT_CONTRACT_DEPLOYMENT,
- FutureType.CONTRACT_DEPLOYMENT,
- FutureType.NAMED_ARTIFACT_LIBRARY_DEPLOYMENT,
- FutureType.LIBRARY_DEPLOYMENT,
- ].includes(props.futureType) &&
- css`
- background: green;
- color: white;
- `}
-
- ${(props) =>
- [FutureType.CONTRACT_CALL, FutureType.STATIC_CALL].includes(
- props.futureType
- ) &&
- css`
- background: yellow;
- color: black;
- `}
-`;
diff --git a/packages/ui/src/pages/visualization-overview/components/future-block.tsx b/packages/ui/src/pages/visualization-overview/components/future-block.tsx
new file mode 100644
index 000000000..72f158852
--- /dev/null
+++ b/packages/ui/src/pages/visualization-overview/components/future-block.tsx
@@ -0,0 +1,232 @@
+import {
+ ArgumentType,
+ Future,
+ FutureType,
+ isFuture,
+} from "@nomicfoundation/ignition-core/ui-helpers";
+import React from "react";
+import styled, { css } from "styled-components";
+import { argumentTypeToString } from "../../../utils/argumentTypeToString";
+
+export const FutureBlock: React.FC<{
+ future: Future;
+ toggleState: Record;
+ setToggled: (id: string) => void;
+}> = ({ future, toggleState, setToggled }) => {
+ const futureId = future.id;
+ const toggled = toggleState[futureId];
+
+ const displayText = toDisplayText(future);
+
+ const fontWeight = toggled ? "normal" : "bold";
+
+ const isLibrary =
+ future.type === FutureType.LIBRARY_DEPLOYMENT ||
+ future.type === FutureType.NAMED_ARTIFACT_LIBRARY_DEPLOYMENT;
+
+ return (
+
+ {!isLibrary && (
+ setToggled(futureId)} toggled={toggled} />
+ )}
+ {displayText}
+ [{future.module.id}]
+ {toggled && (
+
+ )}
+
+ );
+};
+
+function toDisplayText(future: Future): string {
+ switch (future.type) {
+ case FutureType.NAMED_ARTIFACT_CONTRACT_DEPLOYMENT:
+ return `Contract deploy ${future.id}`;
+ case FutureType.CONTRACT_DEPLOYMENT:
+ return `Deploy contract ${future.id} from artifact`;
+ case FutureType.NAMED_ARTIFACT_LIBRARY_DEPLOYMENT:
+ return `Library deploy ${future.id}`;
+ case FutureType.LIBRARY_DEPLOYMENT:
+ return `Library deploy ${future.id} from artifact`;
+ case FutureType.CONTRACT_CALL:
+ return `Call ${future.id}`;
+ case FutureType.STATIC_CALL:
+ return `Static call ${future.id}`;
+ case FutureType.NAMED_ARTIFACT_CONTRACT_AT:
+ return `Existing contract ${future.id} (${
+ typeof future.address === "string"
+ ? future.address
+ : isFuture(future.address)
+ ? future.address.id
+ : argumentTypeToString(future.address)
+ })`;
+ case FutureType.CONTRACT_AT:
+ return `Existing contract ${future.id} from artifact (${
+ typeof future.address === "string"
+ ? future.address
+ : isFuture(future.address)
+ ? future.address.id
+ : argumentTypeToString(future.address)
+ })`;
+ case FutureType.READ_EVENT_ARGUMENT:
+ return `Read event from future ${future.futureToReadFrom.id} (event ${future.eventName} argument ${future.nameOrIndex})`;
+ case FutureType.SEND_DATA:
+ return `Send data ${future.id} to ${
+ typeof future.to === "string"
+ ? future.to
+ : isFuture(future.to)
+ ? future.to.id
+ : argumentTypeToString(future.to)
+ }`;
+ }
+}
+
+const Text = styled.div`
+ margin: 0;
+ display: inline;
+`;
+
+const FutureBtn = styled.div<{ futureType: FutureType }>`
+ border: 1px solid black;
+ padding: 1rem;
+ font-weight: normal;
+
+ ${(props) =>
+ [
+ FutureType.NAMED_ARTIFACT_CONTRACT_DEPLOYMENT,
+ FutureType.CONTRACT_DEPLOYMENT,
+ FutureType.NAMED_ARTIFACT_LIBRARY_DEPLOYMENT,
+ FutureType.LIBRARY_DEPLOYMENT,
+ ].includes(props.futureType) &&
+ css`
+ background: green;
+ color: white;
+ `}
+
+ ${(props) =>
+ [FutureType.CONTRACT_CALL, FutureType.STATIC_CALL].includes(
+ props.futureType
+ ) &&
+ css`
+ background: yellow;
+ color: black;
+ `}
+`;
+
+const ToggleBtn: React.FC<{
+ toggled: boolean;
+ setToggled: () => void;
+}> = ({ toggled, setToggled }) => {
+ return (
+
+ {toggled ? "- " : "+ "}
+
+ );
+};
+
+const FutureDetailsSection: React.FC<{
+ future: Future;
+ setToggled: (id: string) => void;
+}> = ({ future, setToggled }) => {
+ switch (future.type) {
+ case FutureType.NAMED_ARTIFACT_CONTRACT_DEPLOYMENT:
+ case FutureType.CONTRACT_DEPLOYMENT:
+ return (
+
+
Constructor Arguments
+
+ {Object.entries(future.constructorArgs).map(([, arg]) => (
+
+ ))}
+
+
+ );
+ case FutureType.NAMED_ARTIFACT_LIBRARY_DEPLOYMENT:
+ case FutureType.LIBRARY_DEPLOYMENT:
+ return null;
+ case FutureType.CONTRACT_CALL:
+ return (
+
+
Arguments
+
+ {Object.entries(future.args).map(([, arg]) => (
+
+ ))}
+
+
+ );
+ case FutureType.STATIC_CALL:
+ return (
+
+
Arguments
+
+ {Object.entries(future.args).map(([, arg]) => (
+
+ ))}
+
+
+ );
+ case FutureType.NAMED_ARTIFACT_CONTRACT_AT:
+ case FutureType.CONTRACT_AT:
+ return (
+
+
Contract - {future.contractName}
+
+ Address -{" "}
+ {typeof future.address === "string" ? (
+ future.address
+ ) : (
+
+ )}
+
+
+ );
+ case FutureType.READ_EVENT_ARGUMENT:
+ return (
+
+
Emitter - {future.emitter.id}
+
Event - {future.eventName}
+
Event index - {future.eventIndex}
+
Argument - {future.nameOrIndex}
+
+ );
+ case FutureType.SEND_DATA:
+ return (
+
+
+ To -{" "}
+ {typeof future.to === "string" ? (
+ future.to
+ ) : (
+
+ )}
+
+
Data - {future.data}
+
+ );
+ }
+};
+
+const Argument: React.FC<{
+ setToggled: (id: string) => void;
+ arg: ArgumentType;
+}> = ({ setToggled, arg }) => {
+ if (isFuture(arg)) {
+ return (
+ setToggled(arg.id)}
+ >
+ {argumentTypeToString(arg)}
+
+ );
+ }
+ return {argumentTypeToString(arg)};
+};
diff --git a/packages/ui/src/pages/visualization-overview/components/visualization-details.tsx b/packages/ui/src/pages/visualization-overview/components/visualization-details.tsx
index f3e6213dd..299f24f0c 100644
--- a/packages/ui/src/pages/visualization-overview/components/visualization-details.tsx
+++ b/packages/ui/src/pages/visualization-overview/components/visualization-details.tsx
@@ -1,12 +1,13 @@
import {
+ FutureType,
IgnitionModule,
IgnitionModuleResult,
} from "@nomicfoundation/ignition-core/ui-helpers";
-import { useMemo } from "react";
+import { useMemo, useState } from "react";
import styled from "styled-components";
import { Mermaid } from "../../../components/mermaid";
import { getAllFuturesForModule } from "../../../queries/futures";
-import { Action } from "./action";
+import { FutureBlock } from "./future-block";
export const VisualizationDetails: React.FC<{
ignitionModule: IgnitionModule>;
@@ -16,6 +17,23 @@ export const VisualizationDetails: React.FC<{
[ignitionModule]
);
+ const toggleMap = Object.fromEntries(
+ futures
+ .filter(
+ ({ type }) =>
+ type !== FutureType.LIBRARY_DEPLOYMENT &&
+ type !== FutureType.NAMED_ARTIFACT_LIBRARY_DEPLOYMENT
+ )
+ .map(({ id }) => [id, false])
+ );
+
+ const [toggleState, setToggledInternal] = useState(toggleMap);
+
+ const setToggled = (id: string) => {
+ const newState = { ...toggleState, [id]: !toggleState[id] };
+ setToggledInternal(newState);
+ };
+
return (
Visualization
@@ -24,10 +42,15 @@ export const VisualizationDetails: React.FC<{
- Actions
+ Futures
{futures.map((future) => (
-
+
))}
diff --git a/packages/ui/src/pages/visualization-overview/components/visualization-summary.tsx b/packages/ui/src/pages/visualization-overview/components/visualization-summary.tsx
index 6f16006f9..7b9847d5d 100644
--- a/packages/ui/src/pages/visualization-overview/components/visualization-summary.tsx
+++ b/packages/ui/src/pages/visualization-overview/components/visualization-summary.tsx
@@ -27,9 +27,8 @@ export const VisualizationSummary: React.FC<{
- The successful completion of the deployment will send{" "}
- {deployFutures.length + callFutures.length}
- transactions:
+ The successful completion of the deployment will apply{" "}
+ {deployFutures.length + callFutures.length} updates on-chain: