Skip to content

Commit 469eb01

Browse files
authored
H-5631: Restore Petrinaut's external interface, split out dev/demo site (#8083)
1 parent 658b9a4 commit 469eb01

File tree

73 files changed

+3130
-2880
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+3130
-2880
lines changed

.changeset/big-humans-check.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
"@blockprotocol/type-system": patch
2+
"@blockprotocol/type-system": minor
33
---
44

55
major overhaul including new metadata, data type inheritance

.changeset/rotten-waves-brush.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hashintel/petrinaut": patch
3+
---
4+
5+
implement SDCPN features, update UI

.changeset/strong-ravens-smell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@blockprotocol/graph": minor
3+
---
4+
5+
major overhaul that removes temporal/non-temporal split (all now temporal) and implements latest type system

libs/@hashintel/ds-components/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@hashintel/ds-components",
3-
"version": "0.0.1",
3+
"version": "0.0.1-c",
44
"description": "HASH Component Library built with React, Ark UI, and PandaCSS",
55
"repository": {
66
"type": "git",
@@ -36,7 +36,7 @@
3636
},
3737
"dependencies": {
3838
"@ark-ui/react": "5.26.2",
39-
"@hashintel/ds-helpers": "0.0.1",
39+
"@hashintel/ds-helpers": "0.0.1-b",
4040
"@hashintel/refractive": "0.0.0",
4141
"canvas": "3.2.0",
4242
"motion": "12.23.24"

libs/@hashintel/ds-helpers/package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@hashintel/ds-helpers",
3-
"version": "0.0.1",
3+
"version": "0.0.1-b",
44
"description": "HASH PandaCSS Styled System",
55
"repository": {
66
"type": "git",
@@ -46,10 +46,8 @@
4646
"dev": "panda codegen --watch",
4747
"prepare": "panda codegen --clean"
4848
},
49-
"dependencies": {
50-
"@hashintel/ds-theme": "0.0.1"
51-
},
5249
"devDependencies": {
50+
"@hashintel/ds-theme": "0.0.1",
5351
"@pandacss/dev": "1.4.3"
5452
}
5553
}

libs/@hashintel/petrinaut-old/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
"lint:eslint": "eslint --report-unused-disable-directives .",
2020
"lint:tsc": "tsc",
2121
"prepublishOnly": "PACKAGE_DIR=$(pwd) yarn workspace @local/repo-chores exe scripts/prepublish.ts",
22-
"preview:sdcpn": "vite preview --config src/petrinaut-sdcpn/vite.config.ts",
2322
"postpublish": "PACKAGE_DIR=$(pwd) yarn workspace @local/repo-chores exe scripts/postpublish.ts"
2423
},
2524
"dependencies": {

libs/@hashintel/petrinaut/index.html renamed to libs/@hashintel/petrinaut/demo-site/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
<body>
3131
<div id="root"></div>
32-
<script type="module" src="./src/main.tsx"></script>
32+
<script type="module" src="./main.tsx"></script>
3333
</body>
3434

35-
</html>
35+
</html>

libs/@hashintel/petrinaut/src/main.tsx renamed to libs/@hashintel/petrinaut/demo-site/main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { StrictMode } from "react";
22
import { createRoot } from "react-dom/client";
33

4-
import { DevApp } from "./app";
4+
import { DevApp } from "./main/app";
55

66
const root = createRoot(document.getElementById("root")!);
77

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import { produce } from "immer";
2+
import { useCallback, useEffect, useMemo, useState } from "react";
3+
4+
import type { MinimalNetMetadata, SDCPN } from "../../src/core/types/sdcpn";
5+
import { convertOldFormatToSDCPN } from "../../src/old-formats/convert-old-format";
6+
import { Petrinaut } from "../../src/petrinaut";
7+
import {
8+
isOldFormatInLocalStorage,
9+
type SDCPNInLocalStorage,
10+
useLocalStorageSDCPNs,
11+
} from "./app/use-local-storage-sdcpns";
12+
13+
export const DevApp = () => {
14+
const { storedSDCPNs, setStoredSDCPNs } = useLocalStorageSDCPNs();
15+
16+
const [currentNetId, setCurrentNetId] = useState<string | null>(null);
17+
18+
const currentNet = useMemo(() => {
19+
if (!currentNetId) {
20+
return null;
21+
}
22+
return storedSDCPNs[currentNetId] ?? null;
23+
}, [currentNetId, storedSDCPNs]);
24+
25+
const existingNets: MinimalNetMetadata[] = useMemo(() => {
26+
return Object.values(storedSDCPNs)
27+
.filter(
28+
(net): net is SDCPNInLocalStorage => !isOldFormatInLocalStorage(net),
29+
)
30+
.map((net) => ({
31+
netId: net.id,
32+
title: net.title,
33+
}));
34+
}, [storedSDCPNs]);
35+
36+
const createNewNet = useCallback(
37+
(params: {
38+
petriNetDefinition: SDCPN;
39+
title: string;
40+
}) => {
41+
const newNet: SDCPNInLocalStorage = {
42+
id: `net-${Date.now()}`,
43+
title: params.title,
44+
sdcpn: params.petriNetDefinition,
45+
lastUpdated: new Date().toISOString(),
46+
};
47+
48+
setStoredSDCPNs((prev) => ({ ...prev, [newNet.id]: newNet }));
49+
setCurrentNetId(newNet.id);
50+
},
51+
[setStoredSDCPNs],
52+
);
53+
54+
const loadPetriNet = useCallback((petriNetId: string) => {
55+
setCurrentNetId(petriNetId);
56+
}, []);
57+
58+
const setTitle = useCallback(
59+
(title: string) => {
60+
if (!currentNetId) {
61+
return;
62+
}
63+
64+
setStoredSDCPNs((prev) =>
65+
produce(prev, (draft) => {
66+
if (draft[currentNetId] && "title" in draft[currentNetId]) {
67+
draft[currentNetId].title = title;
68+
}
69+
}),
70+
);
71+
},
72+
[currentNetId, setStoredSDCPNs],
73+
);
74+
75+
const mutatePetriNetDefinition = useCallback(
76+
(definitionMutationFn: (draft: SDCPN) => void) => {
77+
if (!currentNetId) {
78+
return;
79+
}
80+
81+
setStoredSDCPNs((prev) =>
82+
produce(prev, (draft) => {
83+
if (draft[currentNetId]) {
84+
draft[currentNetId].sdcpn = produce(
85+
draft[currentNetId].sdcpn,
86+
definitionMutationFn,
87+
);
88+
}
89+
}),
90+
);
91+
},
92+
[currentNetId, setStoredSDCPNs],
93+
);
94+
95+
// Initialize with a default net if none exists
96+
useEffect(() => {
97+
const sdcpnsInStorage = Object.values(storedSDCPNs);
98+
99+
const convertedNets: Record<string, SDCPNInLocalStorage> = {};
100+
101+
for (const sdcpnInStorage of sdcpnsInStorage) {
102+
if (!isOldFormatInLocalStorage(sdcpnInStorage)) {
103+
continue;
104+
}
105+
106+
const convertedSdcpn = convertOldFormatToSDCPN(sdcpnInStorage.sdcpn);
107+
108+
if (!convertedSdcpn) {
109+
throw new Error(
110+
"Couldn't convert old format to SDCPN, but should have been able to",
111+
);
112+
}
113+
114+
convertedNets[sdcpnInStorage.sdcpn.id] = {
115+
/**
116+
* The id and title used to be in the SDCPN definition itself, so we add them back here.
117+
* A legacy provision only which can probably be removed once 2025 is over.
118+
*/
119+
id: sdcpnInStorage.sdcpn.id,
120+
title: sdcpnInStorage.sdcpn.title,
121+
sdcpn: convertedSdcpn,
122+
lastUpdated: sdcpnInStorage.lastUpdated,
123+
};
124+
}
125+
126+
if (Object.keys(convertedNets).length > 0) {
127+
setStoredSDCPNs((existingSDCPNs) => ({
128+
...existingSDCPNs,
129+
...convertedNets,
130+
}));
131+
return;
132+
}
133+
134+
if (!sdcpnsInStorage[0]) {
135+
createNewNet({
136+
petriNetDefinition: {
137+
places: [],
138+
transitions: [],
139+
types: [],
140+
parameters: [],
141+
differentialEquations: [],
142+
},
143+
title: "New Process",
144+
});
145+
} else if (isOldFormatInLocalStorage(sdcpnsInStorage[0])) {
146+
throw new Error(
147+
"Old format SDCPN found in storage, but should have been converted",
148+
);
149+
} else if (!currentNetId) {
150+
setCurrentNetId(sdcpnsInStorage[0].id);
151+
}
152+
}, [currentNetId, createNewNet, setStoredSDCPNs, storedSDCPNs]);
153+
154+
if (!currentNet || isOldFormatInLocalStorage(currentNet)) {
155+
return null;
156+
}
157+
158+
return (
159+
<div style={{ height: "100vh", width: "100vw" }}>
160+
<Petrinaut
161+
existingNets={existingNets}
162+
createNewNet={createNewNet}
163+
hideNetManagementControls={false}
164+
loadPetriNet={loadPetriNet}
165+
petriNetId={currentNetId}
166+
petriNetDefinition={currentNet.sdcpn}
167+
mutatePetriNetDefinition={mutatePetriNetDefinition}
168+
readonly={false}
169+
setTitle={setTitle}
170+
title={currentNet.title}
171+
/>
172+
</div>
173+
);
174+
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { useLocalStorage } from "@mantine/hooks";
2+
3+
import type { SDCPN } from "../../../src/core/types/sdcpn";
4+
import {
5+
isOldFormat,
6+
type OldFormat,
7+
} from "../../../src/old-formats/convert-old-format";
8+
9+
const rootLocalStorageKey = "petrinaut-sdcpn";
10+
11+
export type SDCPNInLocalStorage = {
12+
id: string;
13+
lastUpdated: string; // ISO timestamp
14+
sdcpn: SDCPN;
15+
title: string;
16+
};
17+
18+
type OldFormatInLocalStorage = {
19+
lastUpdated: string; // ISO timestamp
20+
sdcpn: OldFormat;
21+
};
22+
23+
type LocalStorageSDCPNsStore = Record<
24+
string,
25+
SDCPNInLocalStorage | OldFormatInLocalStorage
26+
>;
27+
28+
export const isOldFormatInLocalStorage = (
29+
stored: OldFormatInLocalStorage | SDCPNInLocalStorage,
30+
): stored is OldFormatInLocalStorage => {
31+
return !("id" in stored) && isOldFormat(stored.sdcpn);
32+
};
33+
34+
export const useLocalStorageSDCPNs = () => {
35+
const [storedSDCPNs, setStoredSDCPNs] =
36+
useLocalStorage<LocalStorageSDCPNsStore>({
37+
key: rootLocalStorageKey,
38+
defaultValue: {},
39+
getInitialValueInEffect: false,
40+
});
41+
42+
return { storedSDCPNs, setStoredSDCPNs };
43+
};

0 commit comments

Comments
 (0)