diff --git a/src/generated/resources/.cache/cache b/src/generated/resources/.cache/cache
index 3250272bdb..840b6279b0 100644
--- a/src/generated/resources/.cache/cache
+++ b/src/generated/resources/.cache/cache
@@ -566,8 +566,8 @@ bf2b0310500213ff853c748c236eb5d01f61658e assets/create/blockstates/yellow_toolbo
 5616dda664dd106d576848124fc0fc1de18d0fd3 assets/create/blockstates/yellow_valve_handle.json
 7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json
 b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json
-f1bedeb51c35e70a2247178634e61ea637a6622e assets/create/lang/en_ud.json
-59fd557ef593efa3c7b783195ec5dc789eae1834 assets/create/lang/en_us.json
+0f4e5a2fc58580df5b156fdac438ddeb6b57e386 assets/create/lang/en_ud.json
+85d790bedbdc65bb6e6377edcc63e7b00455e879 assets/create/lang/en_us.json
 487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json
 b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json
 3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json
@@ -1663,6 +1663,10 @@ f7aca6aff65e1de269a99cf2a280d9841b7a0076 assets/create/models/item/brass_sheet.j
 87637b39c3a5a386457d52b37eb65f1c4bcabaf0 assets/create/models/item/chocolate_glazed_berries.json
 fe67c3f380d17735a9436a4579a8be1a02b8e4a0 assets/create/models/item/chute.json
 6680a68526576ded5dac2aa3bc9fb9de3e744146 assets/create/models/item/cinder_flour.json
+0f79260d962011817a41af8c46996f426669b089 assets/create/models/item/clipboard.json
+1bd8ad56fe261f3ff2f7bb2ac12f691a76ae5c94 assets/create/models/item/clipboard_0.json
+7a4d58451e051796e21138faf3428f3d3c14ef85 assets/create/models/item/clipboard_1.json
+376e88de90f33fd32f3f2ffe0062ae5b3bdc1370 assets/create/models/item/clipboard_2.json
 c1da21be9f1af4f7a2ef4ec9cd92195d65ada316 assets/create/models/item/clockwork_bearing.json
 0a2a0f0aafeab0088172f77afd40c1fa2cc1f2b8 assets/create/models/item/clutch.json
 dcb09deae110077bcddf090996b51cc66e9a7de3 assets/create/models/item/cogwheel.json
diff --git a/src/generated/resources/assets/create/lang/en_ud.json b/src/generated/resources/assets/create/lang/en_ud.json
index ebd1117181..f04109d00c 100644
--- a/src/generated/resources/assets/create/lang/en_ud.json
+++ b/src/generated/resources/assets/create/lang/en_ud.json
@@ -595,6 +595,7 @@
   "item.create.chocolate_glazed_berries": "s\u01DD\u0131\u0279\u0279\u01DD\u15FA p\u01DDz\u0250\u05DF\u2141 \u01DD\u0287\u0250\u05DFo\u0254o\u0265\u0186",
   "item.create.chromatic_compound": "punod\u026Fo\u0186 \u0254\u0131\u0287\u0250\u026Fo\u0279\u0265\u0186",
   "item.create.cinder_flour": "\u0279no\u05DF\u2132 \u0279\u01DDpu\u0131\u0186",
+  "item.create.clipboard": "p\u0279\u0250oqd\u0131\u05DF\u0186",
   "item.create.copper_backtank": "\u029Eu\u0250\u0287\u029E\u0254\u0250\u15FA \u0279\u01DDddo\u0186",
   "item.create.copper_backtank_placeable": "\u01DD\u05DFq\u0250\u01DD\u0254\u0250\u05DF\u0500 \u029Eu\u0250\u0287\u029E\u0254\u0250\u15FA \u0279\u01DDddo\u0186",
   "item.create.copper_diving_boots": "s\u0287oo\u15FA bu\u0131\u028C\u0131\u15E1 \u0279\u01DDddo\u0186",
diff --git a/src/generated/resources/assets/create/lang/en_us.json b/src/generated/resources/assets/create/lang/en_us.json
index af41fc01de..05c6ce5ab5 100644
--- a/src/generated/resources/assets/create/lang/en_us.json
+++ b/src/generated/resources/assets/create/lang/en_us.json
@@ -602,6 +602,7 @@
 	"item.create.chocolate_glazed_berries": "Chocolate Glazed Berries",
 	"item.create.chromatic_compound": "Chromatic Compound",
 	"item.create.cinder_flour": "Cinder Flour",
+	"item.create.clipboard": "Clipboard",
 	"item.create.copper_backtank": "Copper Backtank",
 	"item.create.copper_backtank_placeable": "Copper Backtank Placeable",
 	"item.create.copper_diving_boots": "Copper Diving Boots",
@@ -1130,6 +1131,7 @@
 	"create.gui.sequenced_gearshift.speed.forward_fast": "Double speed, Forwards",
 	"create.gui.sequenced_gearshift.speed.back": "Input speed, Reversed",
 	"create.gui.sequenced_gearshift.speed.back_fast": "Double speed, Reversed",
+	"create.gui.clipboard.erase_checked": "Erase checked items",
 
 	"create.schematicAndQuill.dimensions": "Schematic Size: %1$sx%2$sx%3$s",
 	"create.schematicAndQuill.firstPos": "First position set.",
diff --git a/src/generated/resources/assets/create/models/item/clipboard.json b/src/generated/resources/assets/create/models/item/clipboard.json
new file mode 100644
index 0000000000..07fd628205
--- /dev/null
+++ b/src/generated/resources/assets/create/models/item/clipboard.json
@@ -0,0 +1,26 @@
+{
+  "parent": "minecraft:item/generated",
+  "textures": {
+    "layer0": "create:item/clipboard"
+  },
+  "overrides": [
+    {
+      "predicate": {
+        "create:clipboard_type": 0.0
+      },
+      "model": "create:item/clipboard_0"
+    },
+    {
+      "predicate": {
+        "create:clipboard_type": 1.0
+      },
+      "model": "create:item/clipboard_1"
+    },
+    {
+      "predicate": {
+        "create:clipboard_type": 2.0
+      },
+      "model": "create:item/clipboard_2"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/create/models/item/clipboard_0.json b/src/generated/resources/assets/create/models/item/clipboard_0.json
new file mode 100644
index 0000000000..46f7226fa4
--- /dev/null
+++ b/src/generated/resources/assets/create/models/item/clipboard_0.json
@@ -0,0 +1,6 @@
+{
+  "parent": "minecraft:item/generated",
+  "textures": {
+    "layer0": "create:item/empty_clipboard"
+  }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/create/models/item/clipboard_1.json b/src/generated/resources/assets/create/models/item/clipboard_1.json
new file mode 100644
index 0000000000..ee59b74f7b
--- /dev/null
+++ b/src/generated/resources/assets/create/models/item/clipboard_1.json
@@ -0,0 +1,6 @@
+{
+  "parent": "minecraft:item/generated",
+  "textures": {
+    "layer0": "create:item/clipboard"
+  }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/create/models/item/clipboard_2.json b/src/generated/resources/assets/create/models/item/clipboard_2.json
new file mode 100644
index 0000000000..a51972bbcc
--- /dev/null
+++ b/src/generated/resources/assets/create/models/item/clipboard_2.json
@@ -0,0 +1,6 @@
+{
+  "parent": "minecraft:item/generated",
+  "textures": {
+    "layer0": "create:item/clipboard_and_quill"
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/com/simibubi/create/AllItems.java b/src/main/java/com/simibubi/create/AllItems.java
index 3b92b62739..66717e1d02 100644
--- a/src/main/java/com/simibubi/create/AllItems.java
+++ b/src/main/java/com/simibubi/create/AllItems.java
@@ -39,6 +39,8 @@ import com.simibubi.create.content.curiosities.armor.BacktankItem;
 import com.simibubi.create.content.curiosities.armor.BacktankItem.BacktankBlockItem;
 import com.simibubi.create.content.curiosities.armor.DivingBootsItem;
 import com.simibubi.create.content.curiosities.armor.DivingHelmetItem;
+import com.simibubi.create.content.curiosities.clipboard.ClipboardItem;
+import com.simibubi.create.content.curiosities.clipboard.ClipboardOverrides;
 import com.simibubi.create.content.curiosities.symmetry.SymmetryWandItem;
 import com.simibubi.create.content.curiosities.tools.BlueprintItem;
 import com.simibubi.create.content.curiosities.tools.ExtendoGripItem;
@@ -72,7 +74,10 @@ public class AllItems {
 		REGISTRATE.creativeModeTab(() -> AllCreativeModeTabs.BASE_CREATIVE_TAB);
 	}
 
-	// Materials
+	public static final ItemEntry<ClipboardItem> CLIPBOARD = REGISTRATE.item("clipboard", ClipboardItem::new)
+		.onRegister(ClipboardItem::registerModelOverrides)
+		.model((c, p) -> ClipboardOverrides.addOverrideModels(c, p))
+		.register();
 
 	public static final ItemEntry<Item> WHEAT_FLOUR =
 		taggedIngredient("wheat_flour", forgeItemTag("flour/wheat"), forgeItemTag("flour")),
diff --git a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEditPacket.java b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEditPacket.java
new file mode 100644
index 0000000000..713201792d
--- /dev/null
+++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEditPacket.java
@@ -0,0 +1,44 @@
+package com.simibubi.create.content.curiosities.clipboard;
+
+import com.simibubi.create.AllItems;
+import com.simibubi.create.foundation.networking.SimplePacketBase;
+
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.item.ItemStack;
+import net.minecraftforge.network.NetworkEvent.Context;
+
+public class ClipboardEditPacket extends SimplePacketBase {
+
+	private int hotbarSlot;
+	private CompoundTag data;
+
+	public ClipboardEditPacket(int hotbarSlot, CompoundTag data) {
+		this.hotbarSlot = hotbarSlot;
+		this.data = data;
+	}
+
+	public ClipboardEditPacket(FriendlyByteBuf buffer) {
+		hotbarSlot = buffer.readVarInt();
+		data = buffer.readNbt();
+	}
+
+	@Override
+	public void write(FriendlyByteBuf buffer) {
+		buffer.writeVarInt(hotbarSlot);
+		buffer.writeNbt(data);
+	}
+
+	@Override
+	public boolean handle(Context context) {
+		ServerPlayer sender = context.getSender();
+		ItemStack itemStack = sender.getInventory()
+			.getItem(hotbarSlot);
+		if (!AllItems.CLIPBOARD.isIn(itemStack))
+			return true;
+		itemStack.setTag(data.isEmpty() ? null : data);
+		return true;
+	}
+
+}
diff --git a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEntry.java b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEntry.java
new file mode 100644
index 0000000000..570546412a
--- /dev/null
+++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEntry.java
@@ -0,0 +1,52 @@
+package com.simibubi.create.content.curiosities.clipboard;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.simibubi.create.foundation.utility.NBTHelper;
+
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.Tag;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.chat.MutableComponent;
+import net.minecraft.world.item.ItemStack;
+
+public class ClipboardEntry {
+
+	boolean checked;
+	MutableComponent text;
+
+	public ClipboardEntry(boolean checked, MutableComponent text) {
+		this.checked = checked;
+		this.text = text;
+	}
+
+	public static List<List<ClipboardEntry>> readAll(ItemStack clipboardItem) {
+		CompoundTag tag = clipboardItem.getTag();
+		if (tag == null)
+			return new ArrayList<>();
+		return NBTHelper.readCompoundList(tag.getList("Pages", Tag.TAG_COMPOUND), pageTag -> NBTHelper
+			.readCompoundList(pageTag.getList("Entries", Tag.TAG_COMPOUND), ClipboardEntry::readNBT));
+	}
+
+	public static void saveAll(List<List<ClipboardEntry>> entries, ItemStack clipboardItem) {
+		CompoundTag tag = clipboardItem.getOrCreateTag();
+		tag.put("Pages", NBTHelper.writeCompoundList(entries, list -> {
+			CompoundTag pageTag = new CompoundTag();
+			pageTag.put("Entries", NBTHelper.writeCompoundList(list, ClipboardEntry::writeNBT));
+			return pageTag;
+		}));
+	}
+
+	public CompoundTag writeNBT() {
+		CompoundTag nbt = new CompoundTag();
+		nbt.putBoolean("Checked", checked);
+		nbt.putString("Text", Component.Serializer.toJson(text));
+		return nbt;
+	}
+
+	public static ClipboardEntry readNBT(CompoundTag tag) {
+		return new ClipboardEntry(tag.getBoolean("Checked"), Component.Serializer.fromJson(tag.getString("Text")));
+	}
+
+}
diff --git a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardItem.java b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardItem.java
new file mode 100644
index 0000000000..478fa1f4ed
--- /dev/null
+++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardItem.java
@@ -0,0 +1,62 @@
+package com.simibubi.create.content.curiosities.clipboard;
+
+import javax.annotation.Nonnull;
+
+import com.simibubi.create.foundation.gui.ScreenOpener;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.world.InteractionHand;
+import net.minecraft.world.InteractionResult;
+import net.minecraft.world.InteractionResultHolder;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.context.UseOnContext;
+import net.minecraft.world.level.Level;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
+import net.minecraftforge.fml.DistExecutor;
+
+public class ClipboardItem extends Item {
+
+	public ClipboardItem(Properties pProperties) {
+		super(pProperties);
+	}
+
+	@Nonnull
+	@Override
+	public InteractionResult useOn(UseOnContext context) {
+		if (context.getPlayer() == null)
+			return InteractionResult.PASS;
+		return use(context.getLevel(), context.getPlayer(), context.getHand()).getResult();
+	}
+
+	@Override
+	public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
+		ItemStack heldItem = player.getItemInHand(hand);
+		if (hand == InteractionHand.OFF_HAND)
+			return InteractionResultHolder.pass(heldItem);
+
+		player.getCooldowns()
+			.addCooldown(heldItem.getItem(), 10);
+		if (world.isClientSide)
+			DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> openScreen(player, heldItem));
+		CompoundTag tag = heldItem.getOrCreateTag();
+		tag.putInt("Type", ClipboardOverrides.ClipboardType.EDITING.ordinal());
+		heldItem.setTag(tag);
+		
+		return InteractionResultHolder.success(heldItem);
+	}
+
+	@OnlyIn(Dist.CLIENT)
+	private void openScreen(Player player, ItemStack stack) {
+		if (Minecraft.getInstance().player == player)
+			ScreenOpener.open(new ClipboardScreen(player.getInventory().selected, stack));
+	}
+
+	public void registerModelOverrides() {
+		DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ClipboardOverrides.registerModelOverridesClient(this));
+	}
+
+}
diff --git a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardOverrides.java b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardOverrides.java
new file mode 100644
index 0000000000..2845b9d9fe
--- /dev/null
+++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardOverrides.java
@@ -0,0 +1,57 @@
+package com.simibubi.create.content.curiosities.clipboard;
+
+import com.simibubi.create.Create;
+import com.tterrag.registrate.providers.DataGenContext;
+import com.tterrag.registrate.providers.RegistrateItemModelProvider;
+
+import net.minecraft.client.renderer.item.ItemProperties;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.ItemStack;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
+import net.minecraftforge.client.model.generators.ItemModelBuilder;
+import net.minecraftforge.client.model.generators.ModelFile.UncheckedModelFile;
+
+public class ClipboardOverrides {
+
+	public enum ClipboardType {
+		EMPTY("empty_clipboard"), WRITTEN("clipboard"), EDITING("clipboard_and_quill");
+
+		public String file;
+		public static ResourceLocation ID = Create.asResource("clipboard_type");
+
+		private ClipboardType(String file) {
+			this.file = file;
+		}
+	}
+
+	public static void switchTo(ClipboardType type, ItemStack clipboardItem) {
+		CompoundTag tag = clipboardItem.getOrCreateTag();
+		tag.putInt("Type", type.ordinal());
+	}
+
+	@OnlyIn(Dist.CLIENT)
+	public static void registerModelOverridesClient(ClipboardItem item) {
+		ItemProperties.register(item, ClipboardType.ID, (pStack, pLevel, pEntity, pSeed) -> {
+			CompoundTag tag = pStack.getTag();
+			return tag == null ? 0 : tag.getInt("Type");
+		});
+	}
+
+	public static ItemModelBuilder addOverrideModels(DataGenContext<Item, ClipboardItem> c,
+		RegistrateItemModelProvider p) {
+		ItemModelBuilder builder = p.generated(() -> c.get());
+		for (int i = 0; i < ClipboardType.values().length; i++) {
+			builder.override()
+				.predicate(ClipboardType.ID, i)
+				.model(p.getBuilder(c.getName() + "_" + i)
+					.parent(new UncheckedModelFile("item/generated"))
+					.texture("layer0", Create.asResource("item/" + ClipboardType.values()[i].file)))
+				.end();
+		}
+		return builder;
+	}
+
+}
diff --git a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreen.java b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreen.java
new file mode 100644
index 0000000000..f9cf685813
--- /dev/null
+++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreen.java
@@ -0,0 +1,775 @@
+package com.simibubi.create.content.curiosities.clipboard;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.mutable.MutableBoolean;
+import org.apache.commons.lang3.mutable.MutableInt;
+
+import com.google.common.collect.Lists;
+import com.mojang.blaze3d.platform.GlStateManager;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.BufferBuilder;
+import com.mojang.blaze3d.vertex.DefaultVertexFormat;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.Tesselator;
+import com.mojang.blaze3d.vertex.VertexFormat;
+import com.simibubi.create.content.curiosities.clipboard.ClipboardOverrides.ClipboardType;
+import com.simibubi.create.foundation.gui.AbstractSimiScreen;
+import com.simibubi.create.foundation.gui.AllGuiTextures;
+import com.simibubi.create.foundation.gui.AllIcons;
+import com.simibubi.create.foundation.gui.widget.IconButton;
+import com.simibubi.create.foundation.networking.AllPackets;
+import com.simibubi.create.foundation.utility.Components;
+import com.simibubi.create.foundation.utility.Lang;
+
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
+import net.minecraft.SharedConstants;
+import net.minecraft.Util;
+import net.minecraft.client.StringSplitter;
+import net.minecraft.client.gui.Font;
+import net.minecraft.client.gui.GuiComponent;
+import net.minecraft.client.gui.font.TextFieldHelper;
+import net.minecraft.client.gui.screens.Screen;
+import net.minecraft.client.gui.screens.inventory.PageButton;
+import net.minecraft.client.renderer.GameRenderer;
+import net.minecraft.client.renderer.Rect2i;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.chat.Style;
+import net.minecraft.network.chat.TextComponent;
+import net.minecraft.network.chat.TranslatableComponent;
+import net.minecraft.util.FormattedCharSequence;
+import net.minecraft.util.Mth;
+import net.minecraft.world.item.ItemStack;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
+
+public class ClipboardScreen extends AbstractSimiScreen {
+
+	private ItemStack item;
+
+	List<List<ClipboardEntry>> pages;
+	List<ClipboardEntry> currentEntries;
+	int editingIndex;
+	int frameTick;
+	PageButton forward;
+	PageButton backward;
+	int currentPage;
+	long lastClickTime;
+	int lastIndex = -1;
+
+	int hoveredEntry;
+	boolean hoveredCheck;
+
+	DisplayCache displayCache = DisplayCache.EMPTY;
+	TextFieldHelper editContext;
+
+	IconButton closeBtn;
+	IconButton clearBtn;
+
+	private int targetSlot;
+
+	public ClipboardScreen(int targetSlot, ItemStack item) {
+		this.targetSlot = targetSlot;
+		this.item = item;
+		pages = ClipboardEntry.readAll(item);
+		if (pages.isEmpty())
+			pages.add(new ArrayList<>());
+		currentPage = item.getTag() == null ? 0
+			: item.getTag()
+				.getInt("PreviouslyOpenedPage");
+		currentPage = Mth.clamp(currentPage, 0, pages.size() - 1);
+		currentEntries = pages.get(currentPage);
+		boolean startEmpty = currentEntries.isEmpty();
+		if (startEmpty)
+			currentEntries.add(new ClipboardEntry(false, Components.empty()));
+		editingIndex = 0;
+		editContext = new TextFieldHelper(this::getCurrentEntryText, this::setCurrentEntryText, this::getClipboard,
+			this::setClipboard, this::validateTextForEntry);
+		editingIndex = startEmpty ? 0 : -1;
+	}
+
+	@Override
+	protected void init() {
+		setWindowSize(256, 256);
+		super.init();
+		minecraft.keyboardHandler.setSendRepeatsToGui(true);
+		clearDisplayCache();
+
+		int x = guiLeft;
+		int y = guiTop - 8;
+
+		clearWidgets();
+		clearBtn = new IconButton(x + 234, y + 153, AllIcons.I_CLEAR_CHECKED).withCallback(() -> {
+			editingIndex = -1;
+			currentEntries.removeIf(ce -> ce.checked);
+			if (currentEntries.isEmpty())
+				currentEntries.add(new ClipboardEntry(false, Components.empty()));
+		});
+		clearBtn.setToolTip(Lang.translateDirect("gui.clipboard.erase_checked"));
+		closeBtn = new IconButton(x + 234, y + 175, AllIcons.I_PRIORITY_VERY_LOW)
+			.withCallback(() -> minecraft.setScreen(null));
+		closeBtn.setToolTip(Lang.translateDirect("station.close"));
+		addRenderableWidget(closeBtn);
+		addRenderableWidget(clearBtn);
+
+		forward = new PageButton(x + 176, y + 229, true, $ -> changePage(true), true);
+		backward = new PageButton(x + 53, y + 229, false, $ -> changePage(false), true);
+		addRenderableWidget(forward);
+		addRenderableWidget(backward);
+
+		forward.visible = currentPage < 50;
+		backward.visible = currentPage > 0;
+	}
+
+	private int getNumPages() {
+		return pages.size();
+	}
+
+	public void tick() {
+		super.tick();
+		frameTick++;
+
+		int mx = (int) (this.minecraft.mouseHandler.xpos() * (double) this.minecraft.getWindow()
+			.getGuiScaledWidth() / (double) this.minecraft.getWindow()
+				.getScreenWidth());
+		int my = (int) (this.minecraft.mouseHandler.ypos() * (double) this.minecraft.getWindow()
+			.getGuiScaledHeight() / (double) this.minecraft.getWindow()
+				.getScreenHeight());
+
+		mx -= guiLeft + 35;
+		my -= guiTop + 41;
+
+		hoveredCheck = false;
+		hoveredEntry = -1;
+
+		if (mx > 0 && mx < 183 && my > 0 && my < 190) {
+			hoveredCheck = mx < 20;
+			int totalHeight = 0;
+			for (int i = 0; i < currentEntries.size(); i++) {
+				ClipboardEntry clipboardEntry = currentEntries.get(i);
+				String text = clipboardEntry.text.getString();
+				totalHeight += Math.max(12, font.split(Components.literal(text), 150)
+					.size() * 9 + 3);
+
+				if (totalHeight > my) {
+					hoveredEntry = i;
+					return;
+				}
+			}
+			hoveredEntry = currentEntries.size();
+		}
+	}
+
+	private String getCurrentEntryText() {
+		return currentEntries.get(editingIndex).text.getString();
+	}
+
+	private void setCurrentEntryText(String text) {
+		currentEntries.get(editingIndex).text = Components.literal(text);
+	}
+
+	private void setClipboard(String p_98148_) {
+		if (minecraft != null)
+			TextFieldHelper.setClipboardContents(minecraft, p_98148_);
+	}
+
+	private String getClipboard() {
+		return minecraft != null ? TextFieldHelper.getClipboardContents(minecraft) : "";
+	}
+
+	private boolean validateTextForEntry(String newText) {
+		int totalHeight = 0;
+		for (int i = 0; i < currentEntries.size(); i++) {
+			ClipboardEntry clipboardEntry = currentEntries.get(i);
+			String text = i == editingIndex ? newText : clipboardEntry.text.getString();
+			totalHeight += Math.max(12, font.split(Components.literal(text), 150)
+				.size() * 9 + 3);
+		}
+		return totalHeight < 185;
+	}
+
+	private int yOffsetOfEditingEntry() {
+		int totalHeight = 0;
+		for (int i = 0; i < currentEntries.size(); i++) {
+			if (i == editingIndex)
+				break;
+			ClipboardEntry clipboardEntry = currentEntries.get(i);
+			totalHeight += Math.max(12, font.split(clipboardEntry.text, 150)
+				.size() * 9 + 3);
+		}
+		return totalHeight;
+	}
+
+	private void changePage(boolean next) {
+		int previously = currentPage;
+		currentPage = Mth.clamp(currentPage + (next ? 1 : -1), 0, 50);
+		if (currentPage == previously)
+			return;
+		editingIndex = -1;
+		if (pages.size() <= currentPage)
+			pages.add(new ArrayList<>());
+		currentEntries = pages.get(currentPage);
+		if (currentEntries.isEmpty()) {
+			currentEntries.add(new ClipboardEntry(false, Components.empty()));
+			editingIndex = 0;
+			editContext.setCursorToEnd();
+			clearDisplayCacheAfterChange();
+		}
+
+		forward.visible = currentPage < 50;
+		backward.visible = currentPage > 0;
+
+		if (next)
+			return;
+		if (pages.get(currentPage + 1)
+			.stream()
+			.allMatch(ce -> ce.text.getString()
+				.isBlank()))
+			pages.remove(currentPage + 1);
+	}
+
+	@Override
+	protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
+		int x = guiLeft;
+		int y = guiTop - 8;
+
+		AllGuiTextures.CLIPBOARD.render(ms, x, y);
+		font.draw(ms, new TranslatableComponent("book.pageIndicator", currentPage + 1, getNumPages()), x + 150, y + 9,
+			0x43ffffff);
+
+		for (int i = 0; i < currentEntries.size(); i++) {
+			ClipboardEntry clipboardEntry = currentEntries.get(i);
+			boolean checked = clipboardEntry.checked;
+
+			font.draw(ms, "\u25A1", x + 45, y + 51, checked ? 0x668D7F6B : 0xff8D7F6B);
+			if (checked)
+				font.draw(ms, "\u2714", x + 45, y + 50, 0x31B25D);
+
+			List<FormattedCharSequence> split = font.split(clipboardEntry.text, 150);
+			if (split.isEmpty()) {
+				y += 12;
+				continue;
+			}
+
+			for (FormattedCharSequence sequence : split) {
+				if (i != editingIndex)
+					font.draw(ms, sequence, x + 58, y + 50, checked ? 0x31B25D : 0x311A00);
+				y += 9;
+			}
+			y += 3;
+		}
+
+		if (editingIndex == -1)
+			return;
+
+		boolean checked = currentEntries.get(editingIndex).checked;
+
+		setFocused(null);
+		DisplayCache cache = getDisplayCache();
+
+		for (LineInfo line : cache.lines)
+			font.draw(ms, line.asComponent, line.x, line.y, checked ? 0x31B25D : 0x311A00);
+
+		renderHighlight(cache.selection);
+		renderCursor(ms, cache.cursor, cache.cursorAtEnd);
+	}
+
+	@Override
+	public void removed() {
+		minecraft.keyboardHandler.setSendRepeatsToGui(false);
+		pages.forEach(list -> list.removeIf(ce -> ce.text.getString()
+			.isBlank()));
+		pages.removeIf(List::isEmpty);
+
+		ClipboardEntry.saveAll(pages, item);
+		ClipboardOverrides.switchTo(ClipboardType.WRITTEN, item);
+
+		for (int i = 0; i < pages.size(); i++)
+			if (pages.get(i) == currentEntries)
+				item.getTag()
+					.putInt("PreviouslyOpenedPage", i);
+
+		if (pages.isEmpty())
+			item.setTag(new CompoundTag());
+		AllPackets.getChannel()
+			.sendToServer(new ClipboardEditPacket(targetSlot, item.getOrCreateTag()));
+		super.removed();
+	}
+
+	@Override
+	public boolean isPauseScreen() {
+		return false;
+	}
+
+	@Override
+	public boolean keyPressed(int pKeyCode, int pScanCode, int pModifiers) {
+		if (pKeyCode == 266) {
+			backward.onPress();
+			return true;
+		}
+		if (pKeyCode == 267) {
+			forward.onPress();
+			return true;
+		}
+		if (editingIndex != -1 && pKeyCode != 256) {
+			keyPressedWhileEditing(pKeyCode, pScanCode, pModifiers);
+			clearDisplayCache();
+			return true;
+		}
+		if (super.keyPressed(pKeyCode, pScanCode, pModifiers))
+			return true;
+		return true;
+	}
+
+	@Override
+	public boolean charTyped(char pCodePoint, int pModifiers) {
+		if (super.charTyped(pCodePoint, pModifiers))
+			return true;
+		if (!SharedConstants.isAllowedChatCharacter(pCodePoint))
+			return false;
+		if (editingIndex == -1)
+			return false;
+		editContext.insertText(Character.toString(pCodePoint));
+		clearDisplayCache();
+		return true;
+	}
+
+	private boolean keyPressedWhileEditing(int pKeyCode, int pScanCode, int pModifiers) {
+		if (Screen.isSelectAll(pKeyCode)) {
+			editContext.selectAll();
+			return true;
+		} else if (Screen.isCopy(pKeyCode)) {
+			editContext.copy();
+			return true;
+		} else if (Screen.isPaste(pKeyCode)) {
+			editContext.paste();
+			return true;
+		} else if (Screen.isCut(pKeyCode)) {
+			editContext.cut();
+			return true;
+		} else {
+			switch (pKeyCode) {
+			case 257:
+			case 335:
+				if (hasShiftDown()) {
+					editContext.insertText("\n");
+					return true;
+				} else if (!hasControlDown()) {
+					if (currentEntries.size() <= editingIndex + 1
+						|| !currentEntries.get(editingIndex + 1).text.getString()
+							.isEmpty())
+						currentEntries.add(editingIndex + 1, new ClipboardEntry(false, Components.empty()));
+					editingIndex += 1;
+					editContext.setCursorToEnd();
+					if (validateTextForEntry(" "))
+						return true;
+					currentEntries.remove(editingIndex);
+					editingIndex -= 1;
+					editContext.setCursorToEnd();
+					return true;
+				}
+				editingIndex = -1;
+				return true;
+			case 259:
+				if (currentEntries.get(editingIndex).text.getString()
+					.isEmpty() && currentEntries.size() > 1) {
+					currentEntries.remove(editingIndex);
+					editingIndex -= 1;
+					editContext.setCursorToEnd();
+					return true;
+				} else if (hasControlDown()) {
+					int prevPos = editContext.getCursorPos();
+					editContext.moveByWords(-1);
+					if (prevPos != editContext.getCursorPos())
+						editContext.removeCharsFromCursor(prevPos - editContext.getCursorPos());
+					return true;
+				}
+				editContext.removeCharsFromCursor(-1);
+				return true;
+			case 261:
+				if (hasControlDown()) {
+					int prevPos = editContext.getCursorPos();
+					editContext.moveByWords(1);
+					if (prevPos != editContext.getCursorPos())
+						editContext.removeCharsFromCursor(prevPos - editContext.getCursorPos());
+					return true;
+				}
+				editContext.removeCharsFromCursor(1);
+				return true;
+			case 262:
+				if (hasControlDown()) {
+					editContext.moveByWords(1, Screen.hasShiftDown());
+					return true;
+				}
+				editContext.moveByChars(1, Screen.hasShiftDown());
+				return true;
+			case 263:
+				if (hasControlDown()) {
+					editContext.moveByWords(-1, Screen.hasShiftDown());
+					return true;
+				}
+				editContext.moveByChars(-1, Screen.hasShiftDown());
+				return true;
+			case 264:
+				keyDown();
+				return true;
+			case 265:
+				keyUp();
+				return true;
+			case 268:
+				keyHome();
+				return true;
+			case 269:
+				keyEnd();
+				return true;
+			default:
+				return false;
+			}
+		}
+	}
+
+	private void keyUp() {
+		changeLine(-1);
+	}
+
+	private void keyDown() {
+		changeLine(1);
+	}
+
+	private void changeLine(int pYChange) {
+		int i = editContext.getCursorPos();
+		int j = getDisplayCache().changeLine(i, pYChange);
+		editContext.setCursorPos(j, Screen.hasShiftDown());
+	}
+
+	private void keyHome() {
+		int i = editContext.getCursorPos();
+		int j = getDisplayCache().findLineStart(i);
+		editContext.setCursorPos(j, Screen.hasShiftDown());
+	}
+
+	private void keyEnd() {
+		DisplayCache cache = getDisplayCache();
+		int i = editContext.getCursorPos();
+		int j = cache.findLineEnd(i);
+		editContext.setCursorPos(j, Screen.hasShiftDown());
+	}
+
+	private void renderCursor(PoseStack pPoseStack, Pos2i pCursorPos, boolean pIsEndOfText) {
+		if (frameTick / 6 % 2 != 0)
+			return;
+		pCursorPos = convertLocalToScreen(pCursorPos);
+		if (!pIsEndOfText) {
+			GuiComponent.fill(pPoseStack, pCursorPos.x, pCursorPos.y - 1, pCursorPos.x + 1, pCursorPos.y + 9,
+				-16777216);
+		} else {
+			font.draw(pPoseStack, "_", (float) pCursorPos.x, (float) pCursorPos.y, 0);
+		}
+	}
+
+	private void renderHighlight(Rect2i[] pSelected) {
+		Tesselator tesselator = Tesselator.getInstance();
+		BufferBuilder bufferbuilder = tesselator.getBuilder();
+		RenderSystem.setShader(GameRenderer::getPositionShader);
+		RenderSystem.setShaderColor(0.0F, 0.0F, 255.0F, 255.0F);
+		RenderSystem.disableTexture();
+		RenderSystem.enableColorLogicOp();
+		RenderSystem.logicOp(GlStateManager.LogicOp.OR_REVERSE);
+		bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION);
+
+		for (Rect2i rect2i : pSelected) {
+			int i = rect2i.getX();
+			int j = rect2i.getY();
+			int k = i + rect2i.getWidth();
+			int l = j + rect2i.getHeight();
+			bufferbuilder.vertex((double) i, (double) l, 0.0D)
+				.endVertex();
+			bufferbuilder.vertex((double) k, (double) l, 0.0D)
+				.endVertex();
+			bufferbuilder.vertex((double) k, (double) j, 0.0D)
+				.endVertex();
+			bufferbuilder.vertex((double) i, (double) j, 0.0D)
+				.endVertex();
+		}
+
+		tesselator.end();
+		RenderSystem.disableColorLogicOp();
+		RenderSystem.enableTexture();
+	}
+
+	private Pos2i convertScreenToLocal(Pos2i pScreenPos) {
+		return new Pos2i(pScreenPos.x - (width - 192) / 2 - 36 + 10, pScreenPos.y - 32 - 24 - yOffsetOfEditingEntry());
+	}
+
+	private Pos2i convertLocalToScreen(Pos2i pLocalScreenPos) {
+		return new Pos2i(pLocalScreenPos.x + (width - 192) / 2 + 36 - 10,
+			pLocalScreenPos.y + 32 + 24 + yOffsetOfEditingEntry());
+	}
+
+	public boolean mouseClicked(double pMouseX, double pMouseY, int pButton) {
+		if (super.mouseClicked(pMouseX, pMouseY, pButton))
+			return true;
+		if (pButton != 0)
+			return true;
+
+		if (hoveredEntry != -1) {
+			if (hoveredCheck) {
+				editingIndex = -1;
+				if (hoveredEntry < currentEntries.size())
+					currentEntries.get(hoveredEntry).checked ^= true;
+				return true;
+			}
+
+			if (hoveredEntry != editingIndex) {
+				editingIndex = hoveredEntry;
+				if (hoveredEntry >= currentEntries.size()) {
+					currentEntries.add(new ClipboardEntry(false, Components.empty()));
+					if (!validateTextForEntry(" ")) {
+						currentEntries.remove(hoveredEntry);
+						editingIndex = -1;
+						return true;
+					}
+				}
+				clearDisplayCacheAfterChange();
+			}
+		}
+
+		if (editingIndex == -1)
+			return false;
+
+		long i = Util.getMillis();
+		DisplayCache cache = getDisplayCache();
+		int j = cache.getIndexAtPosition(font, convertScreenToLocal(new Pos2i((int) pMouseX, (int) pMouseY)));
+		if (j >= 0) {
+			if (j == lastIndex && i - lastClickTime < 250L) {
+				if (!editContext.isSelecting()) {
+					selectWord(j);
+				} else {
+					editContext.selectAll();
+				}
+			} else {
+				editContext.setCursorPos(j, Screen.hasShiftDown());
+			}
+
+			clearDisplayCache();
+		}
+
+		lastIndex = j;
+		lastClickTime = i;
+		return true;
+	}
+
+	private void selectWord(int pIndex) {
+		String s = getCurrentEntryText();
+		editContext.setSelectionRange(StringSplitter.getWordPosition(s, -1, pIndex, false),
+			StringSplitter.getWordPosition(s, 1, pIndex, false));
+	}
+
+	public boolean mouseDragged(double pMouseX, double pMouseY, int pButton, double pDragX, double pDragY) {
+		if (super.mouseDragged(pMouseX, pMouseY, pButton, pDragX, pDragY))
+			return true;
+		if (pButton != 0)
+			return true;
+		if (editingIndex == -1)
+			return false;
+
+		DisplayCache cache = getDisplayCache();
+		int i = cache.getIndexAtPosition(font, convertScreenToLocal(new Pos2i((int) pMouseX, (int) pMouseY)));
+		editContext.setCursorPos(i, true);
+		clearDisplayCache();
+		return true;
+	}
+
+	private DisplayCache getDisplayCache() {
+		if (displayCache == null)
+			displayCache = rebuildDisplayCache();
+		return displayCache;
+	}
+
+	private void clearDisplayCache() {
+		displayCache = null;
+	}
+
+	private void clearDisplayCacheAfterChange() {
+		editContext.setCursorToEnd();
+		clearDisplayCache();
+	}
+
+	private DisplayCache rebuildDisplayCache() {
+		String s = getCurrentEntryText();
+		if (s.isEmpty())
+			return DisplayCache.EMPTY;
+
+		int i = editContext.getCursorPos();
+		int j = editContext.getSelectionPos();
+		IntList intlist = new IntArrayList();
+		List<LineInfo> list = Lists.newArrayList();
+		MutableInt mutableint = new MutableInt();
+		MutableBoolean mutableboolean = new MutableBoolean();
+		StringSplitter stringsplitter = font.getSplitter();
+		stringsplitter.splitLines(s, 150, Style.EMPTY, true, (p_98132_, p_98133_, p_98134_) -> {
+			int k3 = mutableint.getAndIncrement();
+			String s2 = s.substring(p_98133_, p_98134_);
+			mutableboolean.setValue(s2.endsWith("\n"));
+			String s3 = StringUtils.stripEnd(s2, " \n");
+			int l3 = k3 * 9;
+			Pos2i pos1 = convertLocalToScreen(new Pos2i(0, l3));
+			intlist.add(p_98133_);
+			list.add(new LineInfo(p_98132_, s3, pos1.x, pos1.y));
+		});
+
+		int[] aint = intlist.toIntArray();
+		boolean flag = i == s.length();
+		Pos2i pos;
+		if (flag && mutableboolean.isTrue()) {
+			pos = new Pos2i(0, list.size() * 9);
+		} else {
+			int k = findLineFromPos(aint, i);
+			int l = font.width(s.substring(aint[k], i));
+			pos = new Pos2i(l, k * 9);
+		}
+
+		List<Rect2i> list1 = Lists.newArrayList();
+		if (i != j) {
+			int l2 = Math.min(i, j);
+			int i1 = Math.max(i, j);
+			int j1 = findLineFromPos(aint, l2);
+			int k1 = findLineFromPos(aint, i1);
+			if (j1 == k1) {
+				int l1 = j1 * 9;
+				int i2 = aint[j1];
+				list1.add(createPartialLineSelection(s, stringsplitter, l2, i1, l1, i2));
+			} else {
+				int i3 = j1 + 1 > aint.length ? s.length() : aint[j1 + 1];
+				list1.add(createPartialLineSelection(s, stringsplitter, l2, i3, j1 * 9, aint[j1]));
+
+				for (int j3 = j1 + 1; j3 < k1; ++j3) {
+					int j2 = j3 * 9;
+					String s1 = s.substring(aint[j3], aint[j3 + 1]);
+					int k2 = (int) stringsplitter.stringWidth(s1);
+					list1.add(createSelection(new Pos2i(0, j2), new Pos2i(k2, j2 + 9)));
+				}
+
+				list1.add(createPartialLineSelection(s, stringsplitter, aint[k1], i1, k1 * 9, aint[k1]));
+			}
+		}
+
+		return new DisplayCache(s, pos, flag, aint, list.toArray(new LineInfo[0]), list1.toArray(new Rect2i[0]));
+	}
+
+	static int findLineFromPos(int[] pLineStarts, int pFind) {
+		int i = Arrays.binarySearch(pLineStarts, pFind);
+		return i < 0 ? -(i + 2) : i;
+	}
+
+	private Rect2i createPartialLineSelection(String pInput, StringSplitter pSplitter, int p_98122_, int p_98123_,
+		int p_98124_, int p_98125_) {
+		String s = pInput.substring(p_98125_, p_98122_);
+		String s1 = pInput.substring(p_98125_, p_98123_);
+		Pos2i firstPos = new Pos2i((int) pSplitter.stringWidth(s), p_98124_);
+		Pos2i secondPos = new Pos2i((int) pSplitter.stringWidth(s1), p_98124_ + 9);
+		return createSelection(firstPos, secondPos);
+	}
+
+	private Rect2i createSelection(Pos2i pCorner1, Pos2i pCorner2) {
+		Pos2i firstPos = convertLocalToScreen(pCorner1);
+		Pos2i secondPos = convertLocalToScreen(pCorner2);
+		int i = Math.min(firstPos.x, secondPos.x);
+		int j = Math.max(firstPos.x, secondPos.x);
+		int k = Math.min(firstPos.y, secondPos.y);
+		int l = Math.max(firstPos.y, secondPos.y);
+		return new Rect2i(i, k, j - i, l - k);
+	}
+
+	@OnlyIn(Dist.CLIENT)
+	static class DisplayCache {
+		static final DisplayCache EMPTY = new DisplayCache("", new Pos2i(0, 0), true, new int[] { 0 },
+			new LineInfo[] { new LineInfo(Style.EMPTY, "", 0, 0) }, new Rect2i[0]);
+		private final String fullText;
+		final Pos2i cursor;
+		final boolean cursorAtEnd;
+		private final int[] lineStarts;
+		final LineInfo[] lines;
+		final Rect2i[] selection;
+
+		public DisplayCache(String pFullText, Pos2i pCursor, boolean pCursorAtEnd, int[] pLineStarts, LineInfo[] pLines,
+			Rect2i[] pSelection) {
+			fullText = pFullText;
+			cursor = pCursor;
+			cursorAtEnd = pCursorAtEnd;
+			lineStarts = pLineStarts;
+			lines = pLines;
+			selection = pSelection;
+		}
+
+		public int getIndexAtPosition(Font pFont, Pos2i pCursorPosition) {
+			int i = pCursorPosition.y / 9;
+			if (i < 0)
+				return 0;
+			if (i >= lines.length)
+				return fullText.length();
+			LineInfo line = lines[i];
+			return lineStarts[i] + pFont.getSplitter()
+				.plainIndexAtWidth(line.contents, pCursorPosition.x, line.style);
+		}
+
+		public int changeLine(int pXChange, int pYChange) {
+			int i = findLineFromPos(lineStarts, pXChange);
+			int j = i + pYChange;
+			int k;
+			if (0 <= j && j < lineStarts.length) {
+				int l = pXChange - lineStarts[i];
+				int i1 = lines[j].contents.length();
+				k = lineStarts[j] + Math.min(l, i1);
+			} else {
+				k = pXChange;
+			}
+
+			return k;
+		}
+
+		public int findLineStart(int pLine) {
+			int i = findLineFromPos(lineStarts, pLine);
+			return lineStarts[i];
+		}
+
+		public int findLineEnd(int pLine) {
+			int i = findLineFromPos(lineStarts, pLine);
+			return lineStarts[i] + lines[i].contents.length();
+		}
+	}
+
+	@OnlyIn(Dist.CLIENT)
+	static class LineInfo {
+		final Style style;
+		final String contents;
+		final Component asComponent;
+		final int x;
+		final int y;
+
+		public LineInfo(Style pStyle, String pContents, int pX, int pY) {
+			style = pStyle;
+			contents = pContents;
+			x = pX;
+			y = pY;
+			asComponent = (new TextComponent(pContents)).setStyle(pStyle);
+		}
+	}
+
+	@OnlyIn(Dist.CLIENT)
+	static class Pos2i {
+		public final int x;
+		public final int y;
+
+		Pos2i(int pX, int pY) {
+			x = pX;
+			y = pY;
+		}
+	}
+
+}
diff --git a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreenHelper.java b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreenHelper.java
new file mode 100644
index 0000000000..0e53a7075d
--- /dev/null
+++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreenHelper.java
@@ -0,0 +1,11 @@
+package com.simibubi.create.content.curiosities.clipboard;
+
+import net.minecraft.SharedConstants;
+import net.minecraft.client.gui.screens.Screen;
+import net.minecraft.client.gui.screens.inventory.BookEditScreen;
+
+public class ClipboardScreenHelper {
+
+	
+	
+}
diff --git a/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java b/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java
index bcb832da5e..52797d1da8 100644
--- a/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java
+++ b/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java
@@ -76,6 +76,8 @@ public enum AllGuiTextures implements ScreenElement {
 
 	LINKED_CONTROLLER("curiosities_2", 179, 109),
 	BLUEPRINT("curiosities_2", 0, 109, 179, 109),
+	
+	CLIPBOARD("clipboard", 0, 0, 256, 256),
 
 	PROJECTOR("projector", 235, 185),
 	PROJECTOR_FILTER_STRENGTH("projector", 0, 14, 162, 22),
diff --git a/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java b/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java
index b0bb6a60e1..d52de789a7 100644
--- a/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java
+++ b/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java
@@ -127,6 +127,8 @@ public class AllIcons implements ScreenElement {
 		I_PATTERN_CHANCE_75 = next(),
 		I_FOLLOW_DIAGONAL = next(),
 		I_FOLLOW_MATERIAL = next(),
+		
+		I_CLEAR_CHECKED = next(),
 
 		I_SCHEMATIC = newRow(),
 		I_SEQ_REPEAT = next(),
diff --git a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java
index 4ba7f21f4a..6220cffa9c 100644
--- a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java
+++ b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java
@@ -37,6 +37,7 @@ import com.simibubi.create.content.contraptions.relays.advanced.sequencer.Config
 import com.simibubi.create.content.contraptions.relays.gauge.GaugeObservedPacket;
 import com.simibubi.create.content.curiosities.armor.NetheriteDivingHandler;
 import com.simibubi.create.content.curiosities.bell.SoulPulseEffectPacket;
+import com.simibubi.create.content.curiosities.clipboard.ClipboardEditPacket;
 import com.simibubi.create.content.curiosities.symmetry.ConfigureSymmetryWandPacket;
 import com.simibubi.create.content.curiosities.symmetry.SymmetryEffectPacket;
 import com.simibubi.create.content.curiosities.toolbox.ToolboxDisposeAllPacket;
@@ -157,6 +158,7 @@ public enum AllPackets {
 	REQUEST_FLOOR_LIST(ElevatorFloorListPacket.RequestFloorList.class, ElevatorFloorListPacket.RequestFloorList::new,
 		PLAY_TO_SERVER),
 	ELEVATOR_SET_FLOOR(ElevatorTargetFloorPacket.class, ElevatorTargetFloorPacket::new, PLAY_TO_SERVER),
+	CLIPBOARD_EDIT(ClipboardEditPacket.class, ClipboardEditPacket::new, PLAY_TO_SERVER),
 
 	// Server to Client
 	SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT),
diff --git a/src/main/resources/assets/create/lang/default/interface.json b/src/main/resources/assets/create/lang/default/interface.json
index dd6433bd8b..e3fba6afb6 100644
--- a/src/main/resources/assets/create/lang/default/interface.json
+++ b/src/main/resources/assets/create/lang/default/interface.json
@@ -272,6 +272,8 @@
 	"create.gui.sequenced_gearshift.speed.back": "Input speed, Reversed",
 	"create.gui.sequenced_gearshift.speed.back_fast": "Double speed, Reversed",
 
+	"create.gui.clipboard.erase_checked": "Erase checked items",
+
 	"create.schematicAndQuill.dimensions": "Schematic Size: %1$sx%2$sx%3$s",
 	"create.schematicAndQuill.firstPos": "First position set.",
 	"create.schematicAndQuill.secondPos": "Second position set.",
diff --git a/src/main/resources/assets/create/textures/gui/clipboard.pdn b/src/main/resources/assets/create/textures/gui/clipboard.pdn
new file mode 100644
index 0000000000..a44cea80b7
Binary files /dev/null and b/src/main/resources/assets/create/textures/gui/clipboard.pdn differ
diff --git a/src/main/resources/assets/create/textures/gui/clipboard.png b/src/main/resources/assets/create/textures/gui/clipboard.png
new file mode 100644
index 0000000000..53e099083d
Binary files /dev/null and b/src/main/resources/assets/create/textures/gui/clipboard.png differ
diff --git a/src/main/resources/assets/create/textures/gui/icons.png b/src/main/resources/assets/create/textures/gui/icons.png
index ef0384d390..6a7a7d5cc2 100644
Binary files a/src/main/resources/assets/create/textures/gui/icons.png and b/src/main/resources/assets/create/textures/gui/icons.png differ
diff --git a/src/main/resources/assets/create/textures/item/clipboard.png b/src/main/resources/assets/create/textures/item/clipboard.png
new file mode 100644
index 0000000000..00ff940e12
Binary files /dev/null and b/src/main/resources/assets/create/textures/item/clipboard.png differ
diff --git a/src/main/resources/assets/create/textures/item/clipboard_and_quill.png b/src/main/resources/assets/create/textures/item/clipboard_and_quill.png
new file mode 100644
index 0000000000..7740e0dabc
Binary files /dev/null and b/src/main/resources/assets/create/textures/item/clipboard_and_quill.png differ
diff --git a/src/main/resources/assets/create/textures/item/empty_clipboard.png b/src/main/resources/assets/create/textures/item/empty_clipboard.png
new file mode 100644
index 0000000000..497a850ef4
Binary files /dev/null and b/src/main/resources/assets/create/textures/item/empty_clipboard.png differ