diff --git a/.vitepress/theme/style.css b/.vitepress/theme/style.css
index 52d2614..25738b2 100644
--- a/.vitepress/theme/style.css
+++ b/.vitepress/theme/style.css
@@ -43,6 +43,12 @@
* in custom container, badges, etc.
* -------------------------------------------------------------------------- */
+@font-face {
+ font-family: Minecraft;
+ src: url("https://minecraft.wiki/images/Minecraft.woff2") format("woff2"),
+ url("https://minecraft.wiki/images/Minecraft.woff") format("woff");
+}
+
:root {
--vp-c-default-1: var(--vp-c-gray-1);
--vp-c-default-2: var(--vp-c-gray-2);
diff --git a/src/components/Infobox.vue b/src/components/Infobox.vue
new file mode 100644
index 0000000..23bbf39
--- /dev/null
+++ b/src/components/Infobox.vue
@@ -0,0 +1,48 @@
+
+
+ {{ name }}
+
+
+
+
+
+
+
+
+
diff --git a/src/components/InventorySlot.vue b/src/components/InventorySlot.vue
new file mode 100644
index 0000000..c791c47
--- /dev/null
+++ b/src/components/InventorySlot.vue
@@ -0,0 +1,92 @@
+
+
+
![]()
+
+ {{ image.displayName }}
+
+
+
+
+
+
+
diff --git a/src/components/Miniatures.vue b/src/components/Miniatures.vue
new file mode 100644
index 0000000..dda4f4c
--- /dev/null
+++ b/src/components/Miniatures.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/TabbedImages.vue b/src/components/TabbedImages.vue
new file mode 100644
index 0000000..49ffe7f
--- /dev/null
+++ b/src/components/TabbedImages.vue
@@ -0,0 +1,59 @@
+
+
+
+
![]()
+
+
+
+
+
+
diff --git a/src/components/helpers.ts b/src/components/helpers.ts
new file mode 100644
index 0000000..628ca73
--- /dev/null
+++ b/src/components/helpers.ts
@@ -0,0 +1,40 @@
+import { ItemData, Entry, TableData, ItemDataTableRowName } from "./types";
+
+const itemDataDisplayNames: Record = {
+ renewable: "[Renewable](https://minecraft.wiki/w/Renewable_resource)",
+ stackable: "Stackable",
+ tools: "Tool",
+ blastResistance:
+ "[Blast Resistance](https://minecraft.wiki/w/Explosion#Blast_resistance)",
+ hardness: "[Hardness](https://minecraft.wiki/w/Breaking#Blocks_by_hardness)",
+ isSolid: "[Solid Block](https://minecraft.wiki/w/Solid_block)",
+ isFull: "Full Block",
+ isTransparent: "[Transparent](https://minecraft.wiki/w/Opacity)",
+ isLuminous: "[Luminous](https://minecraft.wiki/w/Light)",
+ isFlammable: "[Flammable](https://minecraft.wiki/w/Flammable)",
+ isLavaFlammable: "Catches fire from lava",
+};
+
+export const typedEntries = (obj: T) => Object.entries(obj) as Entry[];
+export const itemImage = (item: string) => `/assets/items/webp/${item}.webp`;
+
+// TODO this should be refactored to provide autocomplete, which inevitably involves a huge file somewhere.
+// As a tradeoff, it will be MUCH easier to refer to items in the future, anywhere in the wiki
+
+// TODO this should also return more than just an image. Probably be renamed to item() and return more info.
+
+export const getDataDisplayName = (k: keyof ItemData): ItemDataTableRowName => {
+ const LINK_RE = /\[(.+)\]\((.+)\)/;
+ const text = itemDataDisplayNames[k];
+ if (LINK_RE.test(text)) {
+ const matches = text.match(LINK_RE);
+ return { isLink: true, text: matches[1], href: matches[2] };
+ } else {
+ return { isLink: false, text };
+ }
+};
+
+export const formatTableData = (itemData: ItemData) =>
+ typedEntries(itemData).map(
+ ([k, v]) => [getDataDisplayName(k), [k, v]] as TableData
+ );
diff --git a/src/components/index.md b/src/components/index.md
new file mode 100644
index 0000000..807f88a
--- /dev/null
+++ b/src/components/index.md
@@ -0,0 +1,38 @@
+# Component showcase
+
+## Infobox
+
+
+
+
diff --git a/src/components/info-table/InfoTable.vue b/src/components/info-table/InfoTable.vue
new file mode 100644
index 0000000..777b960
--- /dev/null
+++ b/src/components/info-table/InfoTable.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
diff --git a/src/components/info-table/TableRowName.vue b/src/components/info-table/TableRowName.vue
new file mode 100644
index 0000000..cb123be
--- /dev/null
+++ b/src/components/info-table/TableRowName.vue
@@ -0,0 +1,18 @@
+
+
+ {{ name.text }}
+ |
+ {{ name.text }} |
+
+
+
+
+
diff --git a/src/components/info-table/TableRowValue.vue b/src/components/info-table/TableRowValue.vue
new file mode 100644
index 0000000..d903c58
--- /dev/null
+++ b/src/components/info-table/TableRowValue.vue
@@ -0,0 +1,59 @@
+
+
+
+ |
+
+
+
+
+
diff --git a/src/components/info-table/ToolRow.vue b/src/components/info-table/ToolRow.vue
new file mode 100644
index 0000000..0891d2e
--- /dev/null
+++ b/src/components/info-table/ToolRow.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
diff --git a/src/components/types.ts b/src/components/types.ts
new file mode 100644
index 0000000..cd485a5
--- /dev/null
+++ b/src/components/types.ts
@@ -0,0 +1,49 @@
+// #region Utility types
+
+export type Entry = [k: keyof T, v: T[keyof T]];
+export type ValueOf = T[keyof T];
+
+// #endregion
+// #region Types
+
+export type Tool = { type: ToolType; quality: ToolQuality };
+export type ToolType = "axe" | "pickaxe" | "shovel" | "hoe";
+export type ToolQuality =
+ | "any"
+ | "wooden"
+ | "stone"
+ | "copper"
+ | "iron"
+ | "golden"
+ | "diamond"
+ | "netherite";
+
+export type ItemDataTableRowName =
+ | { isLink: false; text: string }
+ | { isLink: true; text: string; href: string };
+export type TableData = [name: ItemDataTableRowName, value: Entry];
+
+// #endregion
+// #region Interfaces
+
+export interface ItemData {
+ renewable: boolean;
+ stackable: [boolean, number];
+ tools: Tool[];
+ blastResistance: number;
+ hardness: number;
+ isSolid: boolean;
+ isFull: boolean;
+ isTransparent: boolean;
+ isLuminous: boolean;
+ isFlammable: boolean;
+ isLavaFlammable: boolean;
+}
+
+export interface Image {
+ displayName: string;
+ variantName: string;
+ srcUrl: string;
+}
+
+// #endregion
diff --git a/src/composables/useMouse.ts b/src/composables/useMouse.ts
new file mode 100644
index 0000000..f7f8669
--- /dev/null
+++ b/src/composables/useMouse.ts
@@ -0,0 +1,16 @@
+import { ref, onMounted, onUnmounted } from "vue";
+
+export function useMouse() {
+ const x = ref(0);
+ const y = ref(0);
+
+ const update = (event: MouseEvent) => {
+ x.value = event.clientX;
+ y.value = event.clientY;
+ };
+
+ onMounted(() => window.addEventListener("mousemove", update));
+ onUnmounted(() => window.removeEventListener("mousemove", update));
+
+ return { x, y };
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..ec90208
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "include": ["src/**/*.ts", "src/**/*.vue", "src/**/*.md"],
+ "vueCompilerOptions": {
+ "vitePressExtensions": [".md"]
+ }
+}