void) => exportLocaleStore.subscribe(modPack?.id, localePath, callback),
+ [modPack?.id, localePath],
+ );
+ return useSyncExternalStore(subscribe, () => exportLocaleStore.getSnapshot(modPack?.id));
+}
diff --git a/packages/web_ui/src/model/export_manifest.ts b/packages/web_ui/src/model/export_manifest.ts
deleted file mode 100644
index 5ec39b28..00000000
--- a/packages/web_ui/src/model/export_manifest.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { useEffect, useContext, useState } from "react";
-
-import * as lib from "@clusterio/lib";
-import ControlContext from "../components/ControlContext";
-
-let exportManifestCache: lib.ExportManifest|null = null;
-let loaded = false;
-export function useExportManifest(): lib.ExportManifest|null {
- const control = useContext(ControlContext);
- let [exportManifest, setExportManifest] = useState
(exportManifestCache);
- useEffect(() => {
- async function load() {
- let modPack = await control.send(new lib.ModPackGetDefaultRequest());
- if (modPack.exportManifest) {
- exportManifestCache = modPack.exportManifest;
- setExportManifest(exportManifestCache);
- }
- loaded = true;
- }
-
- if (!loaded) {
- load();
- }
- }, []);
-
- return exportManifest;
-}
diff --git a/packages/web_ui/src/model/export_prototype_metadata.ts b/packages/web_ui/src/model/export_prototype_metadata.ts
new file mode 100644
index 00000000..3b1e1fe1
--- /dev/null
+++ b/packages/web_ui/src/model/export_prototype_metadata.ts
@@ -0,0 +1,17 @@
+import { useCallback, useSyncExternalStore } from "react";
+
+import { ModPack } from "@clusterio/lib";
+import { exportPrototypeMetadataStore } from "../store/export_prototype_metadata_store";
+
+
+/**
+ * Load the unified spritesheet metadata and inject CSS rules for every icon.
+ * Required for {@link FactorioIcon}.
+ */
+export function useExportPrototypeMetadata(modPack: ModPack | undefined) {
+ const subscribe = useCallback(
+ (callback: () => void) => exportPrototypeMetadataStore.subscribe(modPack, callback),
+ [modPack]
+ );
+ return useSyncExternalStore(subscribe, () => exportPrototypeMetadataStore.getSnapshot(modPack?.id));
+}
diff --git a/packages/web_ui/src/model/item_metadata.ts b/packages/web_ui/src/model/item_metadata.ts
deleted file mode 100644
index e8e1bc05..00000000
--- a/packages/web_ui/src/model/item_metadata.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import { useEffect, useState } from "react";
-
-import { useExportManifest } from "./export_manifest";
-import { ExportMetadata, ExportMetadataEntry } from "@clusterio/lib";
-
-
-const metadataCaches = new Map>();
-let cssInjected = false;
-
-/**
- * Load the unified spritesheet metadata and inject CSS rules for every icon.
- * CSS class name: `.{category}-{CSS.escape(name)}`
- * e.g. `.item-iron-plate`, `.recipe-iron-plate`, `.signal-signal-red`, `.planet-nauvis`
- */
-function useSpriteMetadata(category: string): Map {
- const exportManifest = useExportManifest();
- const [metadata, setMetadata] = useState