diff --git a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/place-properties/subviews/main.tsx b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/place-properties/subviews/main.tsx
index 0aba8a19bc2..bfd741128cf 100644
--- a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/place-properties/subviews/main.tsx
+++ b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/place-properties/subviews/main.tsx
@@ -494,5 +494,6 @@ export const placeMainContentSubView: SubView = {
main: true,
component: PlaceMainContent,
renderHeaderAction: () => ,
+ headerActionMutates: true,
alwaysShowHeaderAction: true,
};
diff --git a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/place-properties/subviews/place-initial-state/subview.tsx b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/place-properties/subviews/place-initial-state/subview.tsx
index e32baf7dd12..0cf379ffa15 100644
--- a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/place-properties/subviews/place-initial-state/subview.tsx
+++ b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/place-properties/subviews/place-initial-state/subview.tsx
@@ -12,6 +12,7 @@ import { css } from "@hashintel/ds-helpers/css";
import { PlaybackContext } from "../../../../../../../../react/playback/context";
import { SimulationContext } from "../../../../../../../../react/simulation/context";
import { UI_MESSAGES } from "../../../../../../../constants/ui-messages";
+import { usePetrinautPresentation } from "../../../../../../shared/presentation-context";
import { usePlacePropertiesContext } from "../../context";
import { InitialStateEditor } from "./initial-state-editor";
@@ -38,6 +39,7 @@ const scenarioInfoStyle = css({
* Only shown when not in simulation mode and there's data to clear.
*/
const ClearStateHeaderAction: React.FC = () => {
+ const presentation = usePetrinautPresentation();
const { place, placeType } = usePlacePropertiesContext();
const { state, initialMarking, setInitialMarking, selectedScenarioId } =
use(SimulationContext);
@@ -65,6 +67,13 @@ const ClearStateHeaderAction: React.FC = () => {
return null;
}
+ // This slot carries a status above and a mutation here, so the sub-view
+ // cannot declare itself one or the other: only the button goes when a
+ // presentation hides mutation actions.
+ if (!presentation.showMutationActions) {
+ return null;
+ }
+
const handleClear = () => {
setInitialMarking(
place.id,
diff --git a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/selected-item-properties.tsx b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/selected-item-properties.tsx
new file mode 100644
index 00000000000..c59e41473c1
--- /dev/null
+++ b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/selected-item-properties.tsx
@@ -0,0 +1,174 @@
+import { use } from "react";
+
+import { usePetrinautMutations } from "../../../../../react";
+import { ActiveNetContext } from "../../../../../react/state/active-net-context";
+import { SDCPNContext } from "../../../../../react/state/sdcpn-context";
+import { usePanelTarget } from "../../../../../react/state/use-selection";
+import { ArcProperties } from "./arc-properties/main";
+import { ComponentInstanceProperties } from "./component-instance-properties/main";
+import { DifferentialEquationProperties } from "./differential-equation-properties/main";
+import { MultiSelectionPanel } from "./multi-selection-panel";
+import { ParameterProperties } from "./parameter-properties/main";
+import { PlaceProperties } from "./place-properties/main";
+import { TransitionProperties } from "./transition-properties/main";
+import { TypeProperties } from "./type-properties/main";
+
+/**
+ * Resolves the editor selection to the existing entity-specific property
+ * content. Layout shells (such as the resizable editor panel)
+ * intentionally share this component so their entity rendering cannot drift.
+ */
+export const SelectedItemProperties: React.FC = () => {
+ const { activeNet: petriNetDefinition } = use(ActiveNetContext);
+ const { extensions, petriNetDefinition: fullSdcpn } = use(SDCPNContext);
+ const {
+ updatePlace,
+ updateTransition,
+ updateArcWeight,
+ updateArcType,
+ updateArcPlace,
+ removeArc,
+ updateType,
+ addTypeElement,
+ updateTypeElement,
+ removeTypeElement,
+ moveTypeElement,
+ updateDifferentialEquation,
+ updateParameter,
+ updateComponentInstance,
+ deleteItemsByIds,
+ } = usePetrinautMutations();
+ const panelTarget = usePanelTarget();
+
+ if (panelTarget.kind === "single") {
+ const { item } = panelTarget;
+
+ switch (item.type) {
+ case "place": {
+ const place = petriNetDefinition.places.find(
+ (candidate) => candidate.id === item.id,
+ );
+ return place ? (
+
+ ) : null;
+ }
+
+ case "transition": {
+ const transition = petriNetDefinition.transitions.find(
+ (candidate) => candidate.id === item.id,
+ );
+ return transition ? (
+
+ ) : null;
+ }
+
+ case "arc":
+ return (
+
+ );
+
+ case "type": {
+ if (!extensions.colors) {
+ return null;
+ }
+ const type = petriNetDefinition.types.find(
+ (candidate) => candidate.id === item.id,
+ );
+ return type ? (
+
+ ) : null;
+ }
+
+ case "differentialEquation": {
+ if (!extensions.colors || !extensions.dynamics) {
+ return null;
+ }
+ const differentialEquation =
+ petriNetDefinition.differentialEquations.find(
+ (candidate) => candidate.id === item.id,
+ );
+ return differentialEquation ? (
+
+ ) : null;
+ }
+
+ case "parameter": {
+ if (!extensions.parameters) {
+ return null;
+ }
+ const parameter = petriNetDefinition.parameters.find(
+ (candidate) => candidate.id === item.id,
+ );
+ return parameter ? (
+
+ ) : null;
+ }
+
+ case "componentInstance": {
+ const instance = petriNetDefinition.componentInstances.find(
+ (candidate) => candidate.id === item.id,
+ );
+ if (!instance) {
+ return null;
+ }
+ const subnet =
+ (fullSdcpn.subnets ?? []).find(
+ (candidate) => candidate.id === instance.subnetId,
+ ) ?? null;
+ return (
+
+ );
+ }
+ }
+ }
+
+ if (panelTarget.kind === "multi") {
+ return (
+
+ );
+ }
+
+ return null;
+};
diff --git a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/transition-properties/main.tsx b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/transition-properties/main.tsx
index 0eddd890522..7414f13d82b 100644
--- a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/transition-properties/main.tsx
+++ b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/transition-properties/main.tsx
@@ -5,14 +5,14 @@ import { getTransitionLogicAvailability } from "@hashintel/petrinaut-core";
import { SDCPNContext } from "../../../../../../react/state/sdcpn-context";
import { useIsReadOnly } from "../../../../../../react/state/use-is-read-only";
+import { createDeferredSubView } from "../../../../../components/sub-view/deferred-sub-view";
import { VerticalSubViewsContainer } from "../../../../../components/sub-view/vertical/vertical-sub-views-container";
+import { usePetrinautPresentation } from "../../../../shared/presentation-context";
import {
TransitionPropertiesProvider,
type TransitionLogicNet,
} from "./context";
import { transitionMainContentSubView } from "./subviews/main";
-import { transitionFiringTimeSubView } from "./subviews/transition-firing-time/subview";
-import { transitionResultsSubView } from "./subviews/transition-results/subview";
import type { PetrinautMutations } from "../../../../../../react";
import type { SubView } from "../../../../../components/sub-view/types";
@@ -25,6 +25,42 @@ const containerStyle = css({
minHeight: "[0]",
});
+const transitionFiringTimeSubView = createDeferredSubView({
+ id: "transition-firing-time",
+ headerActionMutates: true,
+ title: "Firing Time",
+ defaultCollapsed: true,
+ tooltip:
+ "Define the rate at or conditions under which this transition will fire, optionally based on each set of input tokens' data (where input tokens have types).",
+ hasHeaderAction: true,
+ resizable: {
+ minHeight: 250,
+ maxHeight: 1200,
+ defaultHeight: 300,
+ },
+ load: async () =>
+ (await import("./subviews/transition-firing-time/subview"))
+ .transitionFiringTimeSubView,
+});
+
+const transitionResultsSubView = createDeferredSubView({
+ id: "transition-results",
+ headerActionMutates: true,
+ title: "Transition Results",
+ defaultCollapsed: true,
+ tooltip:
+ "This function determines the data for output tokens, optionally based on the input token data and any global parameters defined.",
+ hasHeaderAction: true,
+ resizable: {
+ minHeight: 300,
+ maxHeight: 1200,
+ defaultHeight: 500,
+ },
+ load: async () =>
+ (await import("./subviews/transition-results/subview"))
+ .transitionResultsSubView,
+});
+
interface TransitionPropertiesProps {
transition: Transition;
net: TransitionLogicNet;
@@ -47,6 +83,7 @@ export const TransitionProperties: React.FC = ({
removeArc,
}) => {
const isReadOnly = useIsReadOnly();
+ const presentation = usePetrinautPresentation();
const { extensions, petriNetDefinition } = use(SDCPNContext);
const logicAvailability = getTransitionLogicAvailability(
transition,
@@ -57,8 +94,12 @@ export const TransitionProperties: React.FC = ({
const subViews: SubView[] = [
transitionMainContentSubView,
- ...(logicAvailability.lambda ? [transitionFiringTimeSubView] : []),
- ...(logicAvailability.transitionKernel ? [transitionResultsSubView] : []),
+ ...(presentation.showSourceCode && logicAvailability.lambda
+ ? [transitionFiringTimeSubView]
+ : []),
+ ...(presentation.showSourceCode && logicAvailability.transitionKernel
+ ? [transitionResultsSubView]
+ : []),
];
return (
diff --git a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/transition-properties/subviews/main.tsx b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/transition-properties/subviews/main.tsx
index 2f8f91ed198..59022cb2f49 100644
--- a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/transition-properties/subviews/main.tsx
+++ b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/transition-properties/subviews/main.tsx
@@ -302,6 +302,7 @@ export const transitionMainContentSubView: SubView = {
main: true,
component: TransitionMainContent,
renderHeaderAction: () => ,
+ headerActionMutates: true,
alwaysShowHeaderAction: true,
resizable: {
minHeight: 100,
diff --git a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/type-properties/subviews/main.tsx b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/type-properties/subviews/main.tsx
index 7d361614b3b..2aa97bfb145 100644
--- a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/type-properties/subviews/main.tsx
+++ b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/type-properties/subviews/main.tsx
@@ -21,6 +21,7 @@ import { DraftFieldInput } from "../../../../../../components/draft-field-input"
import { SectionList } from "../../../../../../components/section";
import { TokenTypeIcon } from "../../../../../../constants/entity-icons";
import { UI_MESSAGES } from "../../../../../../constants/ui-messages";
+import { usePetrinautPresentation } from "../../../../../shared/presentation-context";
import { ColorSelect } from "../color-select";
import { useTypePropertiesContext } from "../context";
@@ -185,6 +186,7 @@ const TypeMainContent: React.FC = () => {
moveTypeElement,
} = useTypePropertiesContext();
const isDisabled = useIsReadOnly();
+ const presentation = usePetrinautPresentation();
const [draggedIndex, setDraggedIndex] = useState(null);
const [dragOverIndex, setDragOverIndex] = useState(null);
const [elementNameInputs, setElementNameInputs] =
@@ -381,29 +383,33 @@ const TypeMainContent: React.FC = () => {
disabled={isDisabled}
labelTooltip="A type is an ordered tuple of token attributes. Real attributes can be updated by dynamics; integer and boolean attributes are discrete."
labelActions={
-
+ presentation.showMutationActions ? (
+
+ ) : undefined
}
>
{type.elements.length === 0 ? (
- No dimensions defined. Click + to add.
+ {presentation.showMutationActions
+ ? "No dimensions defined. Click + to add."
+ : "No dimensions defined."}