diff --git a/tgui/packages/tgui/interfaces/Stack.jsx b/tgui/packages/tgui/interfaces/Stack.jsx
new file mode 100644
index 00000000000..158c26c402a
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/Stack.jsx
@@ -0,0 +1,191 @@
+import { sortBy } from 'common/collections';
+import { createSearch } from 'common/string';
+
+import { useBackend, useLocalState } from '../backend';
+import {
+ Box,
+ Button,
+ Collapsible,
+ Input,
+ NoticeBox,
+ Section,
+ Table,
+} from '../components';
+import { Window } from '../layouts';
+
+export const Stack = (props) => {
+ const { act, data } = useBackend();
+
+ const { amount, recipes = [] } = data;
+
+ const [searchText, setSearchText] = useLocalState('searchText', '');
+
+ const testSearch = createSearch(searchText, (item) => {
+ return item;
+ });
+
+ const items =
+ (searchText.length > 0 &&
+ Object.keys(recipes)
+ .filter(testSearch)
+ .reduce((obj, key) => {
+ obj[key] = recipes[key];
+ return obj;
+ }, {})) ||
+ recipes;
+
+ const height = Math.max(94 + Object.keys(recipes).length * 26, 250);
+
+ return (
+
+
+
+
+
+ );
+};
+
+const RecipeList = (props) => {
+ const { act, data } = useBackend();
+
+ const { recipes } = props;
+
+ const sortedKeys = sortBy((key) => key.toLowerCase())(Object.keys(recipes));
+
+ return sortedKeys.map((title) => {
+ const recipe = recipes[title];
+ if (recipe.ref === undefined) {
+ return (
+
+
+
+
+
+ );
+ } else {
+ return ;
+ }
+ });
+};
+
+const buildMultiplier = (recipe, amount) => {
+ if (recipe.req_amount > amount) {
+ return 0;
+ }
+
+ return Math.floor(amount / recipe.req_amount);
+};
+
+const Multipliers = (props) => {
+ const { act, data } = useBackend();
+
+ const { recipe, maxMultiplier } = props;
+
+ const maxM = Math.min(
+ maxMultiplier,
+ Math.floor(recipe.max_res_amount / recipe.res_amount),
+ );
+
+ const multipliers = [5, 10, 25];
+
+ let finalResult = [];
+
+ for (const multiplier of multipliers) {
+ if (maxM >= multiplier) {
+ finalResult.push(
+