-
Main
-
-
- {#each inventory.storage ?? [] as item, index (index)}
- {@render slot(item, `storage-${index}`)}
- {/each}
+
+
+
+
+
Main
+
+
+ {#each inventory.storage ?? [] as item, index (index)}
+ {@render slot(item, `storage-${index}`)}
+ {/each}
+
+
+ {#each inventory.hotbar ?? [] as item, index (index)}
+ {@render slot(item, `hotbar-${index}`)}
+ {/each}
+
+
+
+
+
+
Armor
+
+ {@render slot(inventory.armor?.helmet, 'armor-helmet')}
+ {@render slot(inventory.armor?.chest, 'armor-chest')}
+ {@render slot(inventory.armor?.legs, 'armor-legs')}
+ {@render slot(inventory.armor?.boots, 'armor-boots')}
+
+
+
+
Offhand
+ {@render slot(inventory.offhand, 'offhand')}
+
+
-
- {#each inventory.hotbar ?? [] as item, index (index)}
- {@render slot(item, `hotbar-${index}`)}
- {/each}
-
-
-
-
-
Armor
-
- {@render slot(inventory.armor?.helmet, 'armor-helmet')}
- {@render slot(inventory.armor?.chest, 'armor-chest')}
- {@render slot(inventory.armor?.legs, 'armor-legs')}
- {@render slot(inventory.armor?.boots, 'armor-boots')}
-
-
-
-
Offhand
- {@render slot(inventory.offhand, 'offhand')}
-
+
+
+ {#if selectedItem}
+
+
+
+
+
+
+ {#if selectedItem.name}
+
{selectedItem.name}
+ {/if}
+
{selectedItem.type}
+
Count: {selectedItem.amount}
+
+
+
+ {#if selectedItem.lore?.length}
+
+
Lore
+
+ {#each selectedItem.lore as line, index (index)}
+ - {line}
+ {/each}
+
+
+ {/if}
+
+ {#if selectedItem.enchants}
+
+
Enchantments
+
+ {#each Object.entries(selectedItem.enchants) as [key, value] (key)}
+ - {titleCase(key)}{ROMAN[value] || value}
+ {/each}
+
+
+ {/if}
+
+ {#if selectedItem.nbt}
+
+
NBT
+
{selectedItem.nbt}
+
+ {/if}
+
+ {:else}
+
+ Select an occupied slot to inspect the item.
+
+ {/if}
-
-
-
- {#if selectedItem}
-
-
-
-
-
-
- {#if selectedItem.name}
-
{selectedItem.name}
- {/if}
-
{selectedItem.type}
-
Count: {selectedItem.amount}
-
-
-
- {#if selectedItem.lore?.length}
-
-
Lore
-
- {#each selectedItem.lore as line, index (index)}
- - {line}
- {/each}
-
-
- {/if}
-
- {#if selectedItem.enchants}
-
-
Enchantments
-
- {#each Object.entries(selectedItem.enchants) as [key, value] (key)}
- - {titleCase(key)}{ROMAN[value] || value}
- {/each}
-
-
- {/if}
-
- {#if selectedItem.nbt}
-
-
NBT
-
{selectedItem.nbt}
-
- {/if}
-
- {:else}
-
- Select an occupied slot to inspect the item.
-
- {/if}
-
-
{/if}
diff --git a/src/main/frontend/src/lib/components/ui/ItemIcon.svelte b/src/main/frontend/src/lib/components/ui/ItemIcon.svelte
index f857a7d..278414f 100644
--- a/src/main/frontend/src/lib/components/ui/ItemIcon.svelte
+++ b/src/main/frontend/src/lib/components/ui/ItemIcon.svelte
@@ -1,31 +1,31 @@
{#if url}
-

+

{:else}
{normalized.replace(/_/g, ' ')}
diff --git a/src/main/frontend/src/lib/components/ui/badge/badge.svelte b/src/main/frontend/src/lib/components/ui/badge/badge.svelte
index 9b6b18e..a99b94d 100644
--- a/src/main/frontend/src/lib/components/ui/badge/badge.svelte
+++ b/src/main/frontend/src/lib/components/ui/badge/badge.svelte
@@ -1,49 +1,49 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/badge/index.ts b/src/main/frontend/src/lib/components/ui/badge/index.ts
index 64e0aa9..d332eb4 100644
--- a/src/main/frontend/src/lib/components/ui/badge/index.ts
+++ b/src/main/frontend/src/lib/components/ui/badge/index.ts
@@ -1,2 +1,2 @@
-export { default as Badge } from "./badge.svelte";
-export { badgeVariants, type BadgeVariant } from "./badge.svelte";
+export {default as Badge} from "./badge.svelte";
+export {badgeVariants, type BadgeVariant} from "./badge.svelte";
diff --git a/src/main/frontend/src/lib/components/ui/button/button.svelte b/src/main/frontend/src/lib/components/ui/button/button.svelte
index 6c1b9e0..1733b16 100644
--- a/src/main/frontend/src/lib/components/ui/button/button.svelte
+++ b/src/main/frontend/src/lib/components/ui/button/button.svelte
@@ -1,82 +1,82 @@
{#if href}
-
- {@render children?.()}
-
+
+ {@render children?.()}
+
{:else}
-
+
{/if}
diff --git a/src/main/frontend/src/lib/components/ui/button/index.ts b/src/main/frontend/src/lib/components/ui/button/index.ts
index fb585d7..4fb817b 100644
--- a/src/main/frontend/src/lib/components/ui/button/index.ts
+++ b/src/main/frontend/src/lib/components/ui/button/index.ts
@@ -1,17 +1,17 @@
import Root, {
- type ButtonProps,
- type ButtonSize,
- type ButtonVariant,
- buttonVariants,
+ type ButtonProps,
+ type ButtonSize,
+ type ButtonVariant,
+ buttonVariants,
} from "./button.svelte";
export {
- Root,
- type ButtonProps as Props,
- //
- Root as Button,
- buttonVariants,
- type ButtonProps,
- type ButtonSize,
- type ButtonVariant,
+ Root,
+ type ButtonProps as Props,
+ //
+ Root as Button,
+ buttonVariants,
+ type ButtonProps,
+ type ButtonSize,
+ type ButtonVariant,
};
diff --git a/src/main/frontend/src/lib/components/ui/card/card-action.svelte b/src/main/frontend/src/lib/components/ui/card/card-action.svelte
index 7c48844..a0a4833 100644
--- a/src/main/frontend/src/lib/components/ui/card/card-action.svelte
+++ b/src/main/frontend/src/lib/components/ui/card/card-action.svelte
@@ -1,23 +1,23 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/card/card-content.svelte b/src/main/frontend/src/lib/components/ui/card/card-content.svelte
index 082a786..48a3d9e 100644
--- a/src/main/frontend/src/lib/components/ui/card/card-content.svelte
+++ b/src/main/frontend/src/lib/components/ui/card/card-content.svelte
@@ -1,20 +1,20 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/card/card-description.svelte b/src/main/frontend/src/lib/components/ui/card/card-description.svelte
index 9b20ac7..2fb1e84 100644
--- a/src/main/frontend/src/lib/components/ui/card/card-description.svelte
+++ b/src/main/frontend/src/lib/components/ui/card/card-description.svelte
@@ -1,20 +1,20 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/card/card-footer.svelte b/src/main/frontend/src/lib/components/ui/card/card-footer.svelte
index 591c3f7..042d3b6 100644
--- a/src/main/frontend/src/lib/components/ui/card/card-footer.svelte
+++ b/src/main/frontend/src/lib/components/ui/card/card-footer.svelte
@@ -1,20 +1,20 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/card/card-header.svelte b/src/main/frontend/src/lib/components/ui/card/card-header.svelte
index e968e72..e65378b 100644
--- a/src/main/frontend/src/lib/components/ui/card/card-header.svelte
+++ b/src/main/frontend/src/lib/components/ui/card/card-header.svelte
@@ -1,23 +1,23 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/card/card-title.svelte b/src/main/frontend/src/lib/components/ui/card/card-title.svelte
index 9ee7256..24b2f04 100644
--- a/src/main/frontend/src/lib/components/ui/card/card-title.svelte
+++ b/src/main/frontend/src/lib/components/ui/card/card-title.svelte
@@ -1,20 +1,20 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/card/card.svelte b/src/main/frontend/src/lib/components/ui/card/card.svelte
index 35f0d19..5c00f28 100644
--- a/src/main/frontend/src/lib/components/ui/card/card.svelte
+++ b/src/main/frontend/src/lib/components/ui/card/card.svelte
@@ -1,22 +1,22 @@
img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className)}
- {...restProps}
+ bind:this={ref}
+ data-slot="card"
+ data-size={size}
+ class={cn("ring-foreground/10 bg-card text-card-foreground gap-6 overflow-hidden rounded-2xl py-6 text-sm ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className)}
+ {...restProps}
>
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/card/index.ts b/src/main/frontend/src/lib/components/ui/card/index.ts
index 4d3fce4..19df11a 100644
--- a/src/main/frontend/src/lib/components/ui/card/index.ts
+++ b/src/main/frontend/src/lib/components/ui/card/index.ts
@@ -7,19 +7,19 @@ import Title from "./card-title.svelte";
import Action from "./card-action.svelte";
export {
- Root,
- Content,
- Description,
- Footer,
- Header,
- Title,
- Action,
- //
- Root as Card,
- Content as CardContent,
- Description as CardDescription,
- Footer as CardFooter,
- Header as CardHeader,
- Title as CardTitle,
- Action as CardAction,
+ Root,
+ Content,
+ Description,
+ Footer,
+ Header,
+ Title,
+ Action,
+ //
+ Root as Card,
+ Content as CardContent,
+ Description as CardDescription,
+ Footer as CardFooter,
+ Header as CardHeader,
+ Title as CardTitle,
+ Action as CardAction,
};
diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-close.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-close.svelte
index de68f2f..662c7ca 100644
--- a/src/main/frontend/src/lib/components/ui/dialog/dialog-close.svelte
+++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-close.svelte
@@ -1,11 +1,11 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-content.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-content.svelte
index 90f3baa..84039bd 100644
--- a/src/main/frontend/src/lib/components/ui/dialog/dialog-content.svelte
+++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-content.svelte
@@ -1,49 +1,49 @@
-
-
+
- {@render children?.()}
- {#if showCloseButton}
-
- {#snippet child({ props })}
-
- {/snippet}
-
- {/if}
-
+ {...restProps}
+ >
+ {@render children?.()}
+ {#if showCloseButton}
+
+ {#snippet child({props})}
+
+ {/snippet}
+
+ {/if}
+
diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-description.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-description.svelte
index 0102d91..93cf59d 100644
--- a/src/main/frontend/src/lib/components/ui/dialog/dialog-description.svelte
+++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-description.svelte
@@ -1,17 +1,17 @@
diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-footer.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-footer.svelte
index 5685895..791d15e 100644
--- a/src/main/frontend/src/lib/components/ui/dialog/dialog-footer.svelte
+++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-footer.svelte
@@ -1,32 +1,32 @@
- {@render children?.()}
- {#if showCloseButton}
-
- {#snippet child({ props })}
-
- {/snippet}
-
- {/if}
+ {@render children?.()}
+ {#if showCloseButton}
+
+ {#snippet child({props})}
+
+ {/snippet}
+
+ {/if}
diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-header.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-header.svelte
index c3ce8a2..08c1939 100644
--- a/src/main/frontend/src/lib/components/ui/dialog/dialog-header.svelte
+++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-header.svelte
@@ -1,20 +1,20 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-overlay.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-overlay.svelte
index 5db2841..76c134e 100644
--- a/src/main/frontend/src/lib/components/ui/dialog/dialog-overlay.svelte
+++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-overlay.svelte
@@ -1,17 +1,17 @@
diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-portal.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-portal.svelte
index ccfa79c..7498c23 100644
--- a/src/main/frontend/src/lib/components/ui/dialog/dialog-portal.svelte
+++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-portal.svelte
@@ -1,7 +1,7 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-title.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-title.svelte
index 6ff1a4a..f53ced3 100644
--- a/src/main/frontend/src/lib/components/ui/dialog/dialog-title.svelte
+++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-title.svelte
@@ -1,17 +1,17 @@
diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-trigger.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-trigger.svelte
index 589ee0c..3ced002 100644
--- a/src/main/frontend/src/lib/components/ui/dialog/dialog-trigger.svelte
+++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-trigger.svelte
@@ -1,11 +1,11 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog.svelte
index 211672c..cbb8049 100644
--- a/src/main/frontend/src/lib/components/ui/dialog/dialog.svelte
+++ b/src/main/frontend/src/lib/components/ui/dialog/dialog.svelte
@@ -1,7 +1,7 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/dialog/index.ts b/src/main/frontend/src/lib/components/ui/dialog/index.ts
index 076cef5..29b3bb4 100644
--- a/src/main/frontend/src/lib/components/ui/dialog/index.ts
+++ b/src/main/frontend/src/lib/components/ui/dialog/index.ts
@@ -10,25 +10,25 @@ import Trigger from "./dialog-trigger.svelte";
import Close from "./dialog-close.svelte";
export {
- Root,
- Title,
- Portal,
- Footer,
- Header,
- Trigger,
- Overlay,
- Content,
- Description,
- Close,
- //
- Root as Dialog,
- Title as DialogTitle,
- Portal as DialogPortal,
- Footer as DialogFooter,
- Header as DialogHeader,
- Trigger as DialogTrigger,
- Overlay as DialogOverlay,
- Content as DialogContent,
- Description as DialogDescription,
- Close as DialogClose,
+ Root,
+ Title,
+ Portal,
+ Footer,
+ Header,
+ Trigger,
+ Overlay,
+ Content,
+ Description,
+ Close,
+ //
+ Root as Dialog,
+ Title as DialogTitle,
+ Portal as DialogPortal,
+ Footer as DialogFooter,
+ Header as DialogHeader,
+ Trigger as DialogTrigger,
+ Overlay as DialogOverlay,
+ Content as DialogContent,
+ Description as DialogDescription,
+ Close as DialogClose,
};
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte
index e0e1971..88c7a23 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte
@@ -1,16 +1,16 @@
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte
index 30f3e04..da4f755 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte
@@ -1,45 +1,45 @@
- {#snippet children({ checked, indeterminate })}
+ {#snippet children({checked, indeterminate})}
+ class="absolute right-2 flex items-center justify-center pointer-events-none"
+ data-slot="dropdown-menu-checkbox-item-indicator"
+ >
{#if indeterminate}
-
+
{:else if checked}
-
+
{/if}
- {@render childrenProp?.()}
- {/snippet}
+ {@render childrenProp?.()}
+ {/snippet}
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte
index a950f1e..9b24a14 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte
@@ -1,31 +1,31 @@
-
+ {...restProps}
+ />
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte
index 433540f..6efb520 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte
@@ -1,22 +1,22 @@
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte
index aca1f7b..334b96a 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte
@@ -1,7 +1,7 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte
index 06c4960..59932d7 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte
@@ -1,27 +1,27 @@
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte
index 60cee80..155c7c2 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte
@@ -1,24 +1,24 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte
index 274cfef..d2bf4f1 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte
@@ -1,7 +1,7 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte
index 189aef4..60dfe05 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte
@@ -1,16 +1,16 @@
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte
index 50d0692..bc1a3bf 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte
@@ -1,35 +1,35 @@
- {#snippet children({ checked })}
+ {#snippet children({checked})}
+ class="absolute right-2 flex items-center justify-center pointer-events-none"
+ data-slot="dropdown-menu-radio-item-indicator"
+ >
{#if checked}
-
+
{/if}
- {@render childrenProp?.({ checked })}
- {/snippet}
+ {@render childrenProp?.({checked})}
+ {/snippet}
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte
index 1d0cc57..ed6d3f0 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte
@@ -1,17 +1,17 @@
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte
index ed7cc85..0685d10 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte
@@ -1,20 +1,20 @@
{@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte
index 28dc804..deae797 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte
@@ -1,17 +1,17 @@
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte
index 3c51ece..2996e9f 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte
@@ -1,30 +1,30 @@
- {@render children?.()}
-
+ {@render children?.()}
+
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte
index f044581..6993b5f 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte
@@ -1,7 +1,7 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte
index cb05344..0aa29c5 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte
@@ -1,7 +1,7 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte
index cb4bc62..9c32785 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte
@@ -1,7 +1,7 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/index.ts b/src/main/frontend/src/lib/components/ui/dropdown-menu/index.ts
index 7850c6a..eca3d3f 100644
--- a/src/main/frontend/src/lib/components/ui/dropdown-menu/index.ts
+++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/index.ts
@@ -17,38 +17,38 @@ import GroupHeading from "./dropdown-menu-group-heading.svelte";
import Portal from "./dropdown-menu-portal.svelte";
export {
- CheckboxGroup,
- CheckboxItem,
- Content,
- Portal,
- Root as DropdownMenu,
- CheckboxGroup as DropdownMenuCheckboxGroup,
- CheckboxItem as DropdownMenuCheckboxItem,
- Content as DropdownMenuContent,
- Portal as DropdownMenuPortal,
- Group as DropdownMenuGroup,
- Item as DropdownMenuItem,
- Label as DropdownMenuLabel,
- RadioGroup as DropdownMenuRadioGroup,
- RadioItem as DropdownMenuRadioItem,
- Separator as DropdownMenuSeparator,
- Shortcut as DropdownMenuShortcut,
- Sub as DropdownMenuSub,
- SubContent as DropdownMenuSubContent,
- SubTrigger as DropdownMenuSubTrigger,
- Trigger as DropdownMenuTrigger,
- GroupHeading as DropdownMenuGroupHeading,
- Group,
- GroupHeading,
- Item,
- Label,
- RadioGroup,
- RadioItem,
- Root,
- Separator,
- Shortcut,
- Sub,
- SubContent,
- SubTrigger,
- Trigger,
+ CheckboxGroup,
+ CheckboxItem,
+ Content,
+ Portal,
+ Root as DropdownMenu,
+ CheckboxGroup as DropdownMenuCheckboxGroup,
+ CheckboxItem as DropdownMenuCheckboxItem,
+ Content as DropdownMenuContent,
+ Portal as DropdownMenuPortal,
+ Group as DropdownMenuGroup,
+ Item as DropdownMenuItem,
+ Label as DropdownMenuLabel,
+ RadioGroup as DropdownMenuRadioGroup,
+ RadioItem as DropdownMenuRadioItem,
+ Separator as DropdownMenuSeparator,
+ Shortcut as DropdownMenuShortcut,
+ Sub as DropdownMenuSub,
+ SubContent as DropdownMenuSubContent,
+ SubTrigger as DropdownMenuSubTrigger,
+ Trigger as DropdownMenuTrigger,
+ GroupHeading as DropdownMenuGroupHeading,
+ Group,
+ GroupHeading,
+ Item,
+ Label,
+ RadioGroup,
+ RadioItem,
+ Root,
+ Separator,
+ Shortcut,
+ Sub,
+ SubContent,
+ SubTrigger,
+ Trigger,
};
diff --git a/src/main/frontend/src/lib/components/ui/input/index.ts b/src/main/frontend/src/lib/components/ui/input/index.ts
index f47b6d3..927b39f 100644
--- a/src/main/frontend/src/lib/components/ui/input/index.ts
+++ b/src/main/frontend/src/lib/components/ui/input/index.ts
@@ -1,7 +1,7 @@
import Root from "./input.svelte";
export {
- Root,
- //
- Root as Input,
+ Root,
+ //
+ Root as Input,
};
diff --git a/src/main/frontend/src/lib/components/ui/input/input.svelte b/src/main/frontend/src/lib/components/ui/input/input.svelte
index 256847f..fbc2c00 100644
--- a/src/main/frontend/src/lib/components/ui/input/input.svelte
+++ b/src/main/frontend/src/lib/components/ui/input/input.svelte
@@ -1,48 +1,48 @@
{#if type === "file"}
-
+ type="file"
+ bind:files
+ bind:value
+ {...restProps}
+ />
{:else}
-
+ {type}
+ bind:value
+ {...restProps}
+ />
{/if}
diff --git a/src/main/frontend/src/lib/components/ui/label/index.ts b/src/main/frontend/src/lib/components/ui/label/index.ts
index 8bfca0b..d92d8e2 100644
--- a/src/main/frontend/src/lib/components/ui/label/index.ts
+++ b/src/main/frontend/src/lib/components/ui/label/index.ts
@@ -1,7 +1,7 @@
import Root from "./label.svelte";
export {
- Root,
- //
- Root as Label,
+ Root,
+ //
+ Root as Label,
};
diff --git a/src/main/frontend/src/lib/components/ui/label/label.svelte b/src/main/frontend/src/lib/components/ui/label/label.svelte
index d5e3086..2e27c5d 100644
--- a/src/main/frontend/src/lib/components/ui/label/label.svelte
+++ b/src/main/frontend/src/lib/components/ui/label/label.svelte
@@ -1,20 +1,20 @@
diff --git a/src/main/frontend/src/lib/components/ui/select/index.ts b/src/main/frontend/src/lib/components/ui/select/index.ts
index 4dec358..ff2dde7 100644
--- a/src/main/frontend/src/lib/components/ui/select/index.ts
+++ b/src/main/frontend/src/lib/components/ui/select/index.ts
@@ -11,27 +11,27 @@ import GroupHeading from "./select-group-heading.svelte";
import Portal from "./select-portal.svelte";
export {
- Root,
- Group,
- Label,
- Item,
- Content,
- Trigger,
- Separator,
- ScrollDownButton,
- ScrollUpButton,
- GroupHeading,
- Portal,
- //
- Root as Select,
- Group as SelectGroup,
- Label as SelectLabel,
- Item as SelectItem,
- Content as SelectContent,
- Trigger as SelectTrigger,
- Separator as SelectSeparator,
- ScrollDownButton as SelectScrollDownButton,
- ScrollUpButton as SelectScrollUpButton,
- GroupHeading as SelectGroupHeading,
- Portal as SelectPortal,
+ Root,
+ Group,
+ Label,
+ Item,
+ Content,
+ Trigger,
+ Separator,
+ ScrollDownButton,
+ ScrollUpButton,
+ GroupHeading,
+ Portal,
+ //
+ Root as Select,
+ Group as SelectGroup,
+ Label as SelectLabel,
+ Item as SelectItem,
+ Content as SelectContent,
+ Trigger as SelectTrigger,
+ Separator as SelectSeparator,
+ ScrollDownButton as SelectScrollDownButton,
+ ScrollUpButton as SelectScrollUpButton,
+ GroupHeading as SelectGroupHeading,
+ Portal as SelectPortal,
};
diff --git a/src/main/frontend/src/lib/components/ui/select/select-content.svelte b/src/main/frontend/src/lib/components/ui/select/select-content.svelte
index 36f91b0..20ed6bc 100644
--- a/src/main/frontend/src/lib/components/ui/select/select-content.svelte
+++ b/src/main/frontend/src/lib/components/ui/select/select-content.svelte
@@ -1,45 +1,45 @@
-
-
-
+
+
- {@render children?.()}
-
-
-
+ >
+ {@render children?.()}
+
+
+
diff --git a/src/main/frontend/src/lib/components/ui/select/select-group-heading.svelte b/src/main/frontend/src/lib/components/ui/select/select-group-heading.svelte
index 1fab5f0..2268718 100644
--- a/src/main/frontend/src/lib/components/ui/select/select-group-heading.svelte
+++ b/src/main/frontend/src/lib/components/ui/select/select-group-heading.svelte
@@ -1,21 +1,21 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/select/select-group.svelte b/src/main/frontend/src/lib/components/ui/select/select-group.svelte
index f666cb2..4bb1485 100644
--- a/src/main/frontend/src/lib/components/ui/select/select-group.svelte
+++ b/src/main/frontend/src/lib/components/ui/select/select-group.svelte
@@ -1,17 +1,17 @@
diff --git a/src/main/frontend/src/lib/components/ui/select/select-item.svelte b/src/main/frontend/src/lib/components/ui/select/select-item.svelte
index 379c31a..1a08eb6 100644
--- a/src/main/frontend/src/lib/components/ui/select/select-item.svelte
+++ b/src/main/frontend/src/lib/components/ui/select/select-item.svelte
@@ -1,39 +1,39 @@
- {#snippet children({ selected, highlighted })}
+ {#snippet children({selected, highlighted})}
{#if selected}
-
+
{/if}
- {#if childrenProp}
- {@render childrenProp({ selected, highlighted })}
- {:else}
- {label || value}
- {/if}
- {/snippet}
+ {#if childrenProp}
+ {@render childrenProp({selected, highlighted})}
+ {:else}
+ {label || value}
+ {/if}
+ {/snippet}
diff --git a/src/main/frontend/src/lib/components/ui/select/select-label.svelte b/src/main/frontend/src/lib/components/ui/select/select-label.svelte
index 10e8a45..2e37c1f 100644
--- a/src/main/frontend/src/lib/components/ui/select/select-label.svelte
+++ b/src/main/frontend/src/lib/components/ui/select/select-label.svelte
@@ -1,20 +1,20 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/select/select-portal.svelte b/src/main/frontend/src/lib/components/ui/select/select-portal.svelte
index 424bcdd..935b4eb 100644
--- a/src/main/frontend/src/lib/components/ui/select/select-portal.svelte
+++ b/src/main/frontend/src/lib/components/ui/select/select-portal.svelte
@@ -1,7 +1,7 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/select/select-scroll-down-button.svelte b/src/main/frontend/src/lib/components/ui/select/select-scroll-down-button.svelte
index 12d6b07..d189e85 100644
--- a/src/main/frontend/src/lib/components/ui/select/select-scroll-down-button.svelte
+++ b/src/main/frontend/src/lib/components/ui/select/select-scroll-down-button.svelte
@@ -1,21 +1,21 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/select/select-scroll-up-button.svelte b/src/main/frontend/src/lib/components/ui/select/select-scroll-up-button.svelte
index 650a044..a131126 100644
--- a/src/main/frontend/src/lib/components/ui/select/select-scroll-up-button.svelte
+++ b/src/main/frontend/src/lib/components/ui/select/select-scroll-up-button.svelte
@@ -1,21 +1,21 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/select/select-separator.svelte b/src/main/frontend/src/lib/components/ui/select/select-separator.svelte
index f97867d..0d3130d 100644
--- a/src/main/frontend/src/lib/components/ui/select/select-separator.svelte
+++ b/src/main/frontend/src/lib/components/ui/select/select-separator.svelte
@@ -1,18 +1,18 @@
diff --git a/src/main/frontend/src/lib/components/ui/select/select-trigger.svelte b/src/main/frontend/src/lib/components/ui/select/select-trigger.svelte
index 065d825..b315811 100644
--- a/src/main/frontend/src/lib/components/ui/select/select-trigger.svelte
+++ b/src/main/frontend/src/lib/components/ui/select/select-trigger.svelte
@@ -1,30 +1,30 @@
- {@render children?.()}
-
+ {@render children?.()}
+
diff --git a/src/main/frontend/src/lib/components/ui/select/select.svelte b/src/main/frontend/src/lib/components/ui/select/select.svelte
index 05eb663..44b1d43 100644
--- a/src/main/frontend/src/lib/components/ui/select/select.svelte
+++ b/src/main/frontend/src/lib/components/ui/select/select.svelte
@@ -1,11 +1,11 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/separator/index.ts b/src/main/frontend/src/lib/components/ui/separator/index.ts
index 82442d2..98a029d 100644
--- a/src/main/frontend/src/lib/components/ui/separator/index.ts
+++ b/src/main/frontend/src/lib/components/ui/separator/index.ts
@@ -1,7 +1,7 @@
import Root from "./separator.svelte";
export {
- Root,
- //
- Root as Separator,
+ Root,
+ //
+ Root as Separator,
};
diff --git a/src/main/frontend/src/lib/components/ui/separator/separator.svelte b/src/main/frontend/src/lib/components/ui/separator/separator.svelte
index 5fd8a42..4cb260d 100644
--- a/src/main/frontend/src/lib/components/ui/separator/separator.svelte
+++ b/src/main/frontend/src/lib/components/ui/separator/separator.svelte
@@ -1,23 +1,23 @@
diff --git a/src/main/frontend/src/lib/components/ui/sheet/index.ts b/src/main/frontend/src/lib/components/ui/sheet/index.ts
index 28d7da1..2f04a4f 100644
--- a/src/main/frontend/src/lib/components/ui/sheet/index.ts
+++ b/src/main/frontend/src/lib/components/ui/sheet/index.ts
@@ -10,25 +10,25 @@ import Title from "./sheet-title.svelte";
import Description from "./sheet-description.svelte";
export {
- Root,
- Close,
- Trigger,
- Portal,
- Overlay,
- Content,
- Header,
- Footer,
- Title,
- Description,
- //
- Root as Sheet,
- Close as SheetClose,
- Trigger as SheetTrigger,
- Portal as SheetPortal,
- Overlay as SheetOverlay,
- Content as SheetContent,
- Header as SheetHeader,
- Footer as SheetFooter,
- Title as SheetTitle,
- Description as SheetDescription,
+ Root,
+ Close,
+ Trigger,
+ Portal,
+ Overlay,
+ Content,
+ Header,
+ Footer,
+ Title,
+ Description,
+ //
+ Root as Sheet,
+ Close as SheetClose,
+ Trigger as SheetTrigger,
+ Portal as SheetPortal,
+ Overlay as SheetOverlay,
+ Content as SheetContent,
+ Header as SheetHeader,
+ Footer as SheetFooter,
+ Title as SheetTitle,
+ Description as SheetDescription,
};
diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-close.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-close.svelte
index ae382c1..d40b600 100644
--- a/src/main/frontend/src/lib/components/ui/sheet/sheet-close.svelte
+++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-close.svelte
@@ -1,7 +1,7 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-content.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-content.svelte
index ee4d8d5..2c63f5b 100644
--- a/src/main/frontend/src/lib/components/ui/sheet/sheet-content.svelte
+++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-content.svelte
@@ -1,56 +1,56 @@
-
-
+
- {@render children?.()}
- {#if showCloseButton}
-
- {#snippet child({ props })}
-
- {/snippet}
-
- {/if}
-
+ {...restProps}
+ >
+ {@render children?.()}
+ {#if showCloseButton}
+
+ {#snippet child({props})}
+
+ {/snippet}
+
+ {/if}
+
diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-description.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-description.svelte
index 333b17a..f402a46 100644
--- a/src/main/frontend/src/lib/components/ui/sheet/sheet-description.svelte
+++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-description.svelte
@@ -1,17 +1,17 @@
diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-footer.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-footer.svelte
index b21cb61..d072545 100644
--- a/src/main/frontend/src/lib/components/ui/sheet/sheet-footer.svelte
+++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-footer.svelte
@@ -1,20 +1,20 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-header.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-header.svelte
index 5cc0401..1cecfb2 100644
--- a/src/main/frontend/src/lib/components/ui/sheet/sheet-header.svelte
+++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-header.svelte
@@ -1,20 +1,20 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-overlay.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-overlay.svelte
index 4ffe2b2..559b78a 100644
--- a/src/main/frontend/src/lib/components/ui/sheet/sheet-overlay.svelte
+++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-overlay.svelte
@@ -1,17 +1,17 @@
diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-portal.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-portal.svelte
index f3085a3..18b27de 100644
--- a/src/main/frontend/src/lib/components/ui/sheet/sheet-portal.svelte
+++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-portal.svelte
@@ -1,7 +1,7 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-title.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-title.svelte
index 10c1717..935cb54 100644
--- a/src/main/frontend/src/lib/components/ui/sheet/sheet-title.svelte
+++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-title.svelte
@@ -1,17 +1,17 @@
diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-trigger.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-trigger.svelte
index e266975..e2f4995 100644
--- a/src/main/frontend/src/lib/components/ui/sheet/sheet-trigger.svelte
+++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-trigger.svelte
@@ -1,7 +1,7 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet.svelte
index 5bf9783..caa0e10 100644
--- a/src/main/frontend/src/lib/components/ui/sheet/sheet.svelte
+++ b/src/main/frontend/src/lib/components/ui/sheet/sheet.svelte
@@ -1,7 +1,7 @@
-
+
diff --git a/src/main/frontend/src/lib/components/ui/table/index.ts b/src/main/frontend/src/lib/components/ui/table/index.ts
index 14695c8..9296e83 100644
--- a/src/main/frontend/src/lib/components/ui/table/index.ts
+++ b/src/main/frontend/src/lib/components/ui/table/index.ts
@@ -8,21 +8,21 @@ import Header from "./table-header.svelte";
import Row from "./table-row.svelte";
export {
- Root,
- Body,
- Caption,
- Cell,
- Footer,
- Head,
- Header,
- Row,
- //
- Root as Table,
- Body as TableBody,
- Caption as TableCaption,
- Cell as TableCell,
- Footer as TableFooter,
- Head as TableHead,
- Header as TableHeader,
- Row as TableRow,
+ Root,
+ Body,
+ Caption,
+ Cell,
+ Footer,
+ Head,
+ Header,
+ Row,
+ //
+ Root as Table,
+ Body as TableBody,
+ Caption as TableCaption,
+ Cell as TableCell,
+ Footer as TableFooter,
+ Head as TableHead,
+ Header as TableHeader,
+ Row as TableRow,
};
diff --git a/src/main/frontend/src/lib/components/ui/table/table-body.svelte b/src/main/frontend/src/lib/components/ui/table/table-body.svelte
index 935feae..e464270 100644
--- a/src/main/frontend/src/lib/components/ui/table/table-body.svelte
+++ b/src/main/frontend/src/lib/components/ui/table/table-body.svelte
@@ -1,15 +1,15 @@
- {@render children?.()}
+{@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/table/table-caption.svelte b/src/main/frontend/src/lib/components/ui/table/table-caption.svelte
index 4696cff..e8cee36 100644
--- a/src/main/frontend/src/lib/components/ui/table/table-caption.svelte
+++ b/src/main/frontend/src/lib/components/ui/table/table-caption.svelte
@@ -1,20 +1,20 @@
- {@render children?.()}
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/table/table-cell.svelte b/src/main/frontend/src/lib/components/ui/table/table-cell.svelte
index d2f089f..62a122d 100644
--- a/src/main/frontend/src/lib/components/ui/table/table-cell.svelte
+++ b/src/main/frontend/src/lib/components/ui/table/table-cell.svelte
@@ -1,15 +1,16 @@
-
- {@render children?.()}
+ |
+ {@render children?.()}
|
diff --git a/src/main/frontend/src/lib/components/ui/table/table-footer.svelte b/src/main/frontend/src/lib/components/ui/table/table-footer.svelte
index b9b14eb..86c8433 100644
--- a/src/main/frontend/src/lib/components/ui/table/table-footer.svelte
+++ b/src/main/frontend/src/lib/components/ui/table/table-footer.svelte
@@ -1,20 +1,20 @@
tr]:last:border-b-0", className)}
- {...restProps}
+ bind:this={ref}
+ data-slot="table-footer"
+ class={cn("bg-muted/50 border-t font-medium [&>tr]:last:border-b-0", className)}
+ {...restProps}
>
- {@render children?.()}
+{@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/table/table-head.svelte b/src/main/frontend/src/lib/components/ui/table/table-head.svelte
index 76547c3..fcb642b 100644
--- a/src/main/frontend/src/lib/components/ui/table/table-head.svelte
+++ b/src/main/frontend/src/lib/components/ui/table/table-head.svelte
@@ -1,15 +1,17 @@
-
- {@render children?.()}
+ |
+ {@render children?.()}
|
diff --git a/src/main/frontend/src/lib/components/ui/table/table-header.svelte b/src/main/frontend/src/lib/components/ui/table/table-header.svelte
index f47d259..ac6003c 100644
--- a/src/main/frontend/src/lib/components/ui/table/table-header.svelte
+++ b/src/main/frontend/src/lib/components/ui/table/table-header.svelte
@@ -1,20 +1,20 @@
- {@render children?.()}
+{@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/table/table-row.svelte b/src/main/frontend/src/lib/components/ui/table/table-row.svelte
index 90b4e2a..fb4a504 100644
--- a/src/main/frontend/src/lib/components/ui/table/table-row.svelte
+++ b/src/main/frontend/src/lib/components/ui/table/table-row.svelte
@@ -1,15 +1,16 @@
-
- {@render children?.()}
+
+ {@render children?.()}
diff --git a/src/main/frontend/src/lib/components/ui/table/table.svelte b/src/main/frontend/src/lib/components/ui/table/table.svelte
index d95a02e..dbd3197 100644
--- a/src/main/frontend/src/lib/components/ui/table/table.svelte
+++ b/src/main/frontend/src/lib/components/ui/table/table.svelte
@@ -1,17 +1,17 @@
-
- {@render children?.()}
-
+
+ {@render children?.()}
+
diff --git a/src/main/frontend/src/lib/components/ui/textarea/index.ts b/src/main/frontend/src/lib/components/ui/textarea/index.ts
index ace797a..133f780 100644
--- a/src/main/frontend/src/lib/components/ui/textarea/index.ts
+++ b/src/main/frontend/src/lib/components/ui/textarea/index.ts
@@ -1,7 +1,7 @@
import Root from "./textarea.svelte";
export {
- Root,
- //
- Root as Textarea,
+ Root,
+ //
+ Root as Textarea,
};
diff --git a/src/main/frontend/src/lib/components/ui/textarea/textarea.svelte b/src/main/frontend/src/lib/components/ui/textarea/textarea.svelte
index ff38771..522eefb 100644
--- a/src/main/frontend/src/lib/components/ui/textarea/textarea.svelte
+++ b/src/main/frontend/src/lib/components/ui/textarea/textarea.svelte
@@ -1,23 +1,23 @@
diff --git a/src/main/frontend/src/lib/pages/CommandsPage.svelte b/src/main/frontend/src/lib/pages/CommandsPage.svelte
index 7815167..69800f1 100644
--- a/src/main/frontend/src/lib/pages/CommandsPage.svelte
+++ b/src/main/frontend/src/lib/pages/CommandsPage.svelte
@@ -1,91 +1,97 @@
{#if loading}
-
Loading commands...
+
Loading commands...
{:else if error}
-
{error}
+
{error}
{:else if visibleGroups.length === 0}
-
No commands match that filter.
+
No commands match that filter.
{:else}
-
- {#each visibleGroups as group (group.plugin)}
-
-
-
- {group.plugin}
- {group.commands.length} commands
-
-
- {#each group.commands as command (command.name)}
-
-
-
/{command.name}
- {#if command.aliases?.length}
-
{command.aliases.map((alias) => `/${alias}`).join(', ')}
- {/if}
-
-
-
{command.description || 'No description.'}
- {#if command.usage}
-
{command.usage}
- {/if}
- {#if command.permission}
-
{command.permission}
- {/if}
-
-
- {/each}
-
-
-
- {/each}
-
+
+ {#each visibleGroups as group (group.plugin)}
+
+
+
+ {group.plugin}
+ {group.commands.length}
+ commands
+
+
+ {#each group.commands as command (command.name)}
+
+
+
+ /{command.name}
+ {#if command.aliases?.length}
+
{command.aliases.map((alias) => `/${alias}`).join(', ')}
+ {/if}
+
+
+
{command.description || 'No description.'}
+ {#if command.usage}
+
{command.usage}
+ {/if}
+ {#if command.permission}
+
{command.permission}
+ {/if}
+
+
+ {/each}
+
+
+
+ {/each}
+
{/if}
diff --git a/src/main/frontend/src/lib/pages/HomePage.svelte b/src/main/frontend/src/lib/pages/HomePage.svelte
index 29818f5..7a9cee9 100644
--- a/src/main/frontend/src/lib/pages/HomePage.svelte
+++ b/src/main/frontend/src/lib/pages/HomePage.svelte
@@ -1,197 +1,203 @@
-
-
Overview
-
Minecraft version {stats?.server.version ?? '-'}
-
-
-
- {connected ? 'streaming' : 'disconnected'}
-
+
+
Overview
+
Minecraft version {stats?.server.version ?? '-'}
+
-
-
- Players
-
-
-
- {stats?.players.online ?? '-'}
- / {stats?.players.max ?? '-'}
-
-
- view list
-
+
+
+ Players
+
+
+
+ {stats?.players.online ?? '-'}
+ / {stats?.players.max ?? '-'}
+
+
+ view list
+
-
-
- CPU
-
-
- {pct(stats?.cpu.process)}
-
-
- {stats?.cpu.cores ?? '-'} cores
- system {pct(stats?.cpu.system)}
-
-
+
+
+ CPU
+
+
+ {pct(stats?.cpu.process)}
+
+
+ {stats?.cpu.cores ?? '-'} cores
+ system {pct(stats?.cpu.system)}
+
+
-
-
- Memory
-
-
-
- {formatBytes(stats?.memory.used).split(' ')[0]}
- {formatBytes(stats?.memory.used).split(' ')[1] ?? ''}
-
-
-
- {memoryPercent ? memoryPercent.toFixed(1) : '-'}%
- max {formatBytes(stats?.memory.max)}
-
-
+
+
+ Memory
+
+
+
+ {formatBytes(stats?.memory.used).split(' ')[0]}
+ {formatBytes(stats?.memory.used).split(' ')[1] ?? ''}
+
+
+
+ {memoryPercent ? memoryPercent.toFixed(1) : '-'}%
+ max {formatBytes(stats?.memory.max)}
+
+
-
-
- Ticks per second
-
-
-
- {tpsText(tps[0])}
- / 20.00
-
-
-
- 5m {tpsText(tps[1])}
- 15m {tpsText(tps[2])}
-
-
+
+
+ Ticks per second
+
+
+
+ {tpsText(tps[0])}
+ / 20.00
+
+
+
+ 5m {tpsText(tps[1])}
+ 15m {tpsText(tps[2])}
+
+
-
-
- Uptime
-
-
- {uptime}
-
+
+
+ Uptime
+
+
+ {uptime}
+
-
-
- World
-
-
-
- - Worlds
- {stats?.world.worlds ?? '-'}
- - Chunks
- {stats?.world.loadedChunks ?? '-'}
- - Entities
- {stats?.world.entities ?? '-'}
-
-
+
+
+ World
+
+
+
+
+
- Worlds
+ - {stats?.world.worlds ?? '-'}
+
+
+
- Chunks
+ - {stats?.world.loadedChunks ?? '-'}
+
+
+
- Entities
+ - {stats?.world.entities ?? '-'}
+
+
+
-
-
- Plugins
-
-
-
- {stats?.plugins.active ?? '-'}
- active
-
-
-
+
+
+ Plugins
+
+
+
+ {stats?.plugins.active ?? '-'}
+ active
+
+
+
diff --git a/src/main/frontend/src/lib/pages/IndefBansPage.svelte b/src/main/frontend/src/lib/pages/IndefBansPage.svelte
index 4719979..2f19775 100644
--- a/src/main/frontend/src/lib/pages/IndefBansPage.svelte
+++ b/src/main/frontend/src/lib/pages/IndefBansPage.svelte
@@ -1,81 +1,82 @@
- Indefinite bans
-
- {totals.groups} groups
- {totals.users} user keys
- {totals.uuids} uuids
- {totals.ips} ips
-
+ Indefinite bans
+
+ {totals.groups} groups
+ {totals.users} user keys
+ {totals.uuids} uuids
+ {totals.ips} ips
+
{#if loading}
-
Loading bans...
+
Loading bans...
{:else if error}
-
{error}
+
{error}
{:else if visible.length === 0}
-
No indefinite bans match that filter.
+
No indefinite bans match that filter.
{:else}
-
- {#each visible as ban, index (index)}
-
- Group {index + 1}
-
- {#each Object.entries(ban) as [key, value] (key)}
- - {titleCase(key)}
- - {display(value)}
- {/each}
-
-
- {/each}
-
+
+ {#each visible as ban, index (index)}
+
+ Group {index + 1}
+
+ {#each Object.entries(ban) as [key, value] (key)}
+ - {titleCase(key)}
+ - {display(value)}
+ {/each}
+
+
+ {/each}
+
{/if}
diff --git a/src/main/frontend/src/lib/pages/PlayerPage.svelte b/src/main/frontend/src/lib/pages/PlayerPage.svelte
index 1e3fd7a..c3e8864 100644
--- a/src/main/frontend/src/lib/pages/PlayerPage.svelte
+++ b/src/main/frontend/src/lib/pages/PlayerPage.svelte
@@ -1,248 +1,273 @@
{#if loading}
-
Loading player...
+
Loading player...
{:else if error}
-
- Player lookup failed
- {error}
-
+
+ Player lookup failed
+ {error}
+
{:else if player}
-
-
-
}.png`})
-
-
{player.name}
-
{player.uuid}
-
-
-
-
-
-
-
- Info
-
- - Status
- - {#if online}online{:else}offline{/if}
- - Ping
- - {online ? `${online.ping | 0}ms` : '-'}
- - World
- - {online?.world ?? '-'}
- - Gamemode
- - {online?.gamemode ? titleCase(online.gamemode) : '-'}
- - IP
- - {player.ip ?? '-'}
- - First played
- - {player.firstPlayed ?? '-'}
- - Punishments
- - View history
- {#if player.nameMcUrl}
- - NameMC
- - View profile
- {/if}
-
-
-
-
- Actions
- Issued punishments use the authenticated staff account.
-
- {#each actions as item (item.action)}
-
- {/each}
-
- {#if actionMessage}
- {actionMessage}
- {/if}
-
-
-
- {#if staff}
-
-
- Live inventory
-
-
(selectedSlot = slot)} />
+
+
+
}.png`})
+
+
{player.name}
+
{player.uuid}
+
-
+
- {/if}
- {#if activeAction}
-
-
-
- Confirm {activeAction.label.toLowerCase()}
-
- Target: {player.name}{'selected' in activeAction && activeAction.selected ? ` | Slot: ${selectedSlot}` : ''}
-
-
- {#if activeAction.reason}
-
-
-
-
- {/if}
- {#if activeAction.temporary}
-
-
-
-
- {/if}
- {#if actionError}
- {actionError}
- {/if}
-
-
-
-
-
-
- {/if}
+
+
+ Info
+
+ - Status
+ -
+ {#if online}
+ online
+ {:else}
+ offline
+ {/if}
+
+ - Ping
+ - {online ? `${online.ping | 0}ms` : '-'}
+ - World
+ - {online?.world ?? '-'}
+ - Gamemode
+ - {online?.gamemode ? titleCase(online.gamemode) : '-'}
+ - IP
+ - {player.ip ?? '-'}
+ - First played
+ - {player.firstPlayed ?? '-'}
+ - Punishments
+ - View history
+ {#if player.nameMcUrl}
+ - NameMC
+ - View profile
+
+
+ {/if}
+
+
+
+
+ Actions
+ Issued punishments use the authenticated staff account.
+
+ {#each actions as item (item.action)}
+
+ {/each}
+
+ {#if actionMessage}
+ {actionMessage}
+ {/if}
+
+
+
+ {#if staff}
+
+
+ Live inventory
+
+ (selectedSlot = slot)}/>
+
+
+
+ {/if}
+
+ {#if activeAction}
+
+
+
+ Confirm {activeAction.label.toLowerCase()}
+
+ Target: {player.name}{'selected' in activeAction && activeAction.selected ? ` | Slot: ${selectedSlot}` : ''}
+
+
+ {#if activeAction.reason}
+
+
+
+ {/if}
+ {#if activeAction.temporary}
+
+
+
+
+ {/if}
+ {#if actionError}
+ {actionError}
+ {/if}
+
+
+
+
+
+
+ {/if}
{/if}
diff --git a/src/main/frontend/src/lib/pages/PlayersPage.svelte b/src/main/frontend/src/lib/pages/PlayersPage.svelte
index 4330afc..4a339e0 100644
--- a/src/main/frontend/src/lib/pages/PlayersPage.svelte
+++ b/src/main/frontend/src/lib/pages/PlayersPage.svelte
@@ -1,98 +1,95 @@
- Players
-
+ Players
+
{players.length} / {max} online
- {#if visiblePlayers.length === 0}
-
-
- {players.length ? 'No players match that filter.' : 'No players online right now.'}
-
- {:else}
- {#each visiblePlayers as player (player.uuid)}
-
-
-
-
- {player.name}
- {#if player.op}
- op
- {/if}
- {#if staff && player.gamemode}
- {player.gamemode.toLowerCase()}
- {/if}
-
-
- {#if player.world}
- In {player.world}
- .
- {/if}
- {player.ping | 0}ms
-
-
- {#if staff}
-
- {/if}
-
- {/each}
- {/if}
+ {#if visiblePlayers.length === 0}
+
+
+ {players.length ? 'No players match that filter.' : 'No players online right now.'}
+
+ {:else}
+ {#each visiblePlayers as player (player.uuid)}
+
+
+
+
+ {player.name}
+ {#if player.op}
+ op
+ {/if}
+ {#if staff && player.gamemode}
+ {player.gamemode.toLowerCase()}
+ {/if}
+
+
+ {#if player.world}
+ In {player.world}
+ .
+ {/if}
+ {player.ping | 0}ms
+
+
+ {#if staff}
+
+ {/if}
+
+ {/each}
+ {/if}
diff --git a/src/main/frontend/src/lib/pages/PunishmentsDetailPage.svelte b/src/main/frontend/src/lib/pages/PunishmentsDetailPage.svelte
index 889e114..8fc7382 100644
--- a/src/main/frontend/src/lib/pages/PunishmentsDetailPage.svelte
+++ b/src/main/frontend/src/lib/pages/PunishmentsDetailPage.svelte
@@ -1,119 +1,129 @@
{#if loading}
- Loading punishments...
+ Loading punishments...
{:else if error}
- {error}
+ {error}
{:else if data}
-
-
-
}.png`})
-
-
{data.player.name}
-
{data.player.uuid}
-
-
- {punishments.length} punishments
-
-
-
-
-
-
- {#each types as item (item)}
-
- {/each}
-
- {#each ['all', 'active', 'expired'] as item (item)}
-
- {/each}
-
-
- {#if visible.length === 0}
- No punishments match those filters.
- {:else}
-
- {#each visible as punishment, index (String(punishment.id ?? index))}
- {@const itemType = String(punishment.type ?? punishment.kind ?? 'punishment')}
- {@const active = Boolean(punishment.active ?? punishment.isActive ?? punishment.current)}
-
-
-
-
{titleCase(itemType)}
-
{displayValue(punishment.reason)}
+
+
+
}.png`})
+
+
{data.player.name}
+
{data.player.uuid}
-
{active ? 'active' : 'expired'}
-
-
- {#each entries(punishment) as [key, value] (key)}
- - {titleCase(key)}
- - {displayValue(value)}
- {/each}
-
-
- {/each}
+
+
{punishments.length} punishments
- {/if}
+
+
+
+
+
+ {#each types as item (item)}
+
+ {/each}
+
+ {#each ['all', 'active', 'expired'] as item (item)}
+
+ {/each}
+
+
+ {#if visible.length === 0}
+
No punishments match those filters.
+ {:else}
+
+ {#each visible as punishment, index (String(punishment.id ?? index))}
+ {@const itemType = String(punishment.type ?? punishment.kind ?? 'punishment')}
+ {@const active = Boolean(punishment.active ?? punishment.isActive ?? punishment.current)}
+
+
+
+
{titleCase(itemType)}
+
{displayValue(punishment.reason)}
+
+
{active ? 'active' : 'expired'}
+
+
+ {#each entries(punishment) as [key, value] (key)}
+ - {titleCase(key)}
+ - {displayValue(value)}
+ {/each}
+
+
+ {/each}
+
+ {/if}
{/if}
diff --git a/src/main/frontend/src/lib/pages/PunishmentsSearchPage.svelte b/src/main/frontend/src/lib/pages/PunishmentsSearchPage.svelte
index bcedaed..5376d6b 100644
--- a/src/main/frontend/src/lib/pages/PunishmentsSearchPage.svelte
+++ b/src/main/frontend/src/lib/pages/PunishmentsSearchPage.svelte
@@ -1,30 +1,32 @@
- Punishments
+ Punishments
-
diff --git a/src/main/frontend/src/lib/pages/SchematicUploadPage.svelte b/src/main/frontend/src/lib/pages/SchematicUploadPage.svelte
index c2ec84e..d9bf44a 100644
--- a/src/main/frontend/src/lib/pages/SchematicUploadPage.svelte
+++ b/src/main/frontend/src/lib/pages/SchematicUploadPage.svelte
@@ -1,63 +1,65 @@
- Upload schematic
+ Upload schematic
-
-
+ {#if message}
+ {message}
+ {/if}
+ {#if error}
+ {error}
+ {/if}
+
diff --git a/src/main/frontend/src/lib/pages/SchematicsPage.svelte b/src/main/frontend/src/lib/pages/SchematicsPage.svelte
index 4518861..37bfb14 100644
--- a/src/main/frontend/src/lib/pages/SchematicsPage.svelte
+++ b/src/main/frontend/src/lib/pages/SchematicsPage.svelte
@@ -1,75 +1,88 @@
- Schematics
-
+ Schematics
+ {#if staff}
+
+ {/if}
{#if loading}
-
Loading schematics...
+
Loading schematics...
{:else if error}
-
{error}
+
{error}
{:else}
-
-
-
-
- | Name |
- Size |
- |
-
-
-
- {#each visible as schematic (schematic.name)}
-
- | {schematic.name} |
- {schematic.formattedSize || schematic.size} |
-
-
-
-
- |
-
- {:else}
- | No schematics match that filter. |
- {/each}
-
-
-
+
+
+
+
+ | Name |
+ Size |
+ |
+
+
+
+ {#each visible as schematic (schematic.name)}
+
+ | {schematic.name} |
+ {schematic.formattedSize || schematic.size} |
+
+
+
+
+ |
+
+ {:else}
+
+ | No schematics match that
+ filter.
+ |
+
+ {/each}
+
+
+
{/if}
diff --git a/src/main/frontend/src/lib/rendering/itemRenderer.ts b/src/main/frontend/src/lib/rendering/itemRenderer.ts
index 614b22c..9099f99 100644
--- a/src/main/frontend/src/lib/rendering/itemRenderer.ts
+++ b/src/main/frontend/src/lib/rendering/itemRenderer.ts
@@ -1,50 +1,50 @@
-import { BufferGeometry } from 'three/src/core/BufferGeometry.js';
-import { Float32BufferAttribute } from 'three/src/core/BufferAttribute.js';
-import { ClampToEdgeWrapping, DoubleSide, FrontSide, NearestFilter, SRGBColorSpace } from 'three/src/constants.js';
-import { Color } from 'three/src/math/Color.js';
+import {BufferGeometry} from 'three/src/core/BufferGeometry.js';
+import {Float32BufferAttribute} from 'three/src/core/BufferAttribute.js';
+import {ClampToEdgeWrapping, DoubleSide, FrontSide, NearestFilter, SRGBColorSpace} from 'three/src/constants.js';
+import {Color} from 'three/src/math/Color.js';
import * as MathUtils from 'three/src/math/MathUtils.js';
-import { Group } from 'three/src/objects/Group.js';
-import { Mesh } from 'three/src/objects/Mesh.js';
-import { MeshBasicMaterial } from 'three/src/materials/MeshBasicMaterial.js';
-import { OrthographicCamera } from 'three/src/cameras/OrthographicCamera.js';
-import { Scene } from 'three/src/scenes/Scene.js';
-import { TextureLoader } from 'three/src/loaders/TextureLoader.js';
-import { WebGLRenderer } from 'three/src/renderers/WebGLRenderer.js';
-import type { Material } from 'three/src/materials/Material.js';
-import type { Texture } from 'three/src/textures/Texture.js';
+import {Group} from 'three/src/objects/Group.js';
+import {Mesh} from 'three/src/objects/Mesh.js';
+import {MeshBasicMaterial} from 'three/src/materials/MeshBasicMaterial.js';
+import {OrthographicCamera} from 'three/src/cameras/OrthographicCamera.js';
+import {Scene} from 'three/src/scenes/Scene.js';
+import {TextureLoader} from 'three/src/loaders/TextureLoader.js';
+import {WebGLRenderer} from 'three/src/renderers/WebGLRenderer.js';
+import type {Material} from 'three/src/materials/Material.js';
+import type {Texture} from 'three/src/textures/Texture.js';
const RENDER_SIZE = 96;
-const FACE_BRIGHTNESS: Record
= { up: 1.0, down: 0.5, north: 0.8, south: 0.8, east: 0.6, west: 0.6 };
-const DEFAULT_GUI_TRANSFORM = { rotation: [0, 0, 0], translation: [0, 0, 0], scale: [1, 1, 1] };
-const DEFAULT_BLOCK_GUI = { rotation: [30, 225, 0], translation: [0, 0, 0], scale: [0.625, 0.625, 0.625] };
+const FACE_BRIGHTNESS: Record = {up: 1.0, down: 0.5, north: 0.8, south: 0.8, east: 0.6, west: 0.6};
+const DEFAULT_GUI_TRANSFORM = {rotation: [0, 0, 0], translation: [0, 0, 0], scale: [1, 1, 1]};
+const DEFAULT_BLOCK_GUI = {rotation: [30, 225, 0], translation: [0, 0, 0], scale: [0.625, 0.625, 0.625]};
const GRASS = 0x91bd59;
const FOLIAGE = 0x48b518;
const WATER = 0x3f76e4;
const TINT_RGB: Record = {
- grass_block: GRASS,
- short_grass: GRASS,
- tall_grass: GRASS,
- fern: GRASS,
- large_fern: GRASS,
- sugar_cane: GRASS,
- pink_petals: GRASS,
- oak_leaves: FOLIAGE,
- jungle_leaves: FOLIAGE,
- acacia_leaves: FOLIAGE,
- dark_oak_leaves: FOLIAGE,
- mangrove_leaves: FOLIAGE,
- vine: FOLIAGE,
- birch_leaves: 0x80a755,
- spruce_leaves: 0x619961,
- lily_pad: 0x208030,
- water: WATER,
- water_bucket: WATER,
- melon_stem: 0xe0c71c,
- pumpkin_stem: 0xe0c71c,
- attached_melon_stem: 0xe0c71c,
- attached_pumpkin_stem: 0xe0c71c,
- redstone_wire: 0xff0000
+ grass_block: GRASS,
+ short_grass: GRASS,
+ tall_grass: GRASS,
+ fern: GRASS,
+ large_fern: GRASS,
+ sugar_cane: GRASS,
+ pink_petals: GRASS,
+ oak_leaves: FOLIAGE,
+ jungle_leaves: FOLIAGE,
+ acacia_leaves: FOLIAGE,
+ dark_oak_leaves: FOLIAGE,
+ mangrove_leaves: FOLIAGE,
+ vine: FOLIAGE,
+ birch_leaves: 0x80a755,
+ spruce_leaves: 0x619961,
+ lily_pad: 0x208030,
+ water: WATER,
+ water_bucket: WATER,
+ melon_stem: 0xe0c71c,
+ pumpkin_stem: 0xe0c71c,
+ attached_melon_stem: 0xe0c71c,
+ attached_pumpkin_stem: 0xe0c71c,
+ redstone_wire: 0xff0000
};
type Model = Record;
@@ -60,389 +60,389 @@ let scene: Scene | null = null;
let camera: OrthographicCamera | null = null;
function initThree() {
- if (renderer) return;
- renderer = new WebGLRenderer({ alpha: true, antialias: false, preserveDrawingBuffer: false });
- renderer.setSize(RENDER_SIZE, RENDER_SIZE);
- renderer.setPixelRatio(1);
- renderer.setClearColor(0x000000, 0);
- scene = new Scene();
- const half = 8.2;
- camera = new OrthographicCamera(-half, half, half, -half, -200, 200);
- camera.position.set(0, 0, 100);
- camera.lookAt(0, 0, 0);
+ if (renderer) return;
+ renderer = new WebGLRenderer({alpha: true, antialias: false, preserveDrawingBuffer: false});
+ renderer.setSize(RENDER_SIZE, RENDER_SIZE);
+ renderer.setPixelRatio(1);
+ renderer.setClearColor(0x000000, 0);
+ scene = new Scene();
+ const half = 8.2;
+ camera = new OrthographicCamera(-half, half, half, -half, -200, 200);
+ camera.position.set(0, 0, 100);
+ camera.lookAt(0, 0, 0);
}
function stripNs(ref: string | null | undefined) {
- if (!ref) return ref;
- const index = ref.indexOf(':');
- return index >= 0 ? ref.substring(index + 1) : ref;
+ if (!ref) return ref;
+ const index = ref.indexOf(':');
+ return index >= 0 ? ref.substring(index + 1) : ref;
}
async function loadItemDef(name: string) {
- if (itemDefCache.has(name)) return itemDefCache.get(name) as Promise;
- const promise = fetch(`/assets/items/${name}.json`).then((response) => {
- if (!response.ok) throw new Error(`item def ${name}: ${response.status}`);
- return response.json() as Promise;
- });
- itemDefCache.set(name, promise);
- return promise;
+ if (itemDefCache.has(name)) return itemDefCache.get(name) as Promise;
+ const promise = fetch(`/assets/items/${name}.json`).then((response) => {
+ if (!response.ok) throw new Error(`item def ${name}: ${response.status}`);
+ return response.json() as Promise;
+ });
+ itemDefCache.set(name, promise);
+ return promise;
}
const BUILTIN: Record = {
- 'builtin/generated': { builtin: 'generated', textures: {}, display: {} },
- 'builtin/entity': { builtin: 'entity', textures: {}, display: {} }
+ 'builtin/generated': {builtin: 'generated', textures: {}, display: {}},
+ 'builtin/entity': {builtin: 'entity', textures: {}, display: {}}
};
async function loadModel(path: string): Promise {
- if (BUILTIN[path]) return BUILTIN[path];
- if (modelCache.has(path)) return modelCache.get(path) as Promise;
- const promise = (async () => {
- const response = await fetch(`/assets/models/${path}.json`);
- if (!response.ok) throw new Error(`model ${path}: ${response.status}`);
- const data = (await response.json()) as Model;
- if (!data.parent) return data;
- const parent = await loadModel(stripNs(data.parent) ?? '');
- return mergeModel(parent, data);
- })();
- modelCache.set(path, promise);
- return promise;
+ if (BUILTIN[path]) return BUILTIN[path];
+ if (modelCache.has(path)) return modelCache.get(path) as Promise;
+ const promise = (async () => {
+ const response = await fetch(`/assets/models/${path}.json`);
+ if (!response.ok) throw new Error(`model ${path}: ${response.status}`);
+ const data = (await response.json()) as Model;
+ if (!data.parent) return data;
+ const parent = await loadModel(stripNs(data.parent) ?? '');
+ return mergeModel(parent, data);
+ })();
+ modelCache.set(path, promise);
+ return promise;
}
function mergeModel(parent: Model, child: Model): Model {
- return {
- builtin: child.builtin ?? parent.builtin,
- elements: child.elements ?? parent.elements,
- gui_light: child.gui_light ?? parent.gui_light,
- textures: { ...(parent.textures || {}), ...(child.textures || {}) },
- display: { ...(parent.display || {}), ...(child.display || {}) }
- };
+ return {
+ builtin: child.builtin ?? parent.builtin,
+ elements: child.elements ?? parent.elements,
+ gui_light: child.gui_light ?? parent.gui_light,
+ textures: {...(parent.textures || {}), ...(child.textures || {})},
+ display: {...(parent.display || {}), ...(child.display || {})}
+ };
}
function resolveTextureRef(textures: Record, ref: string) {
- let current: string | undefined | null = ref;
- for (let i = 0; i < 16 && current; i += 1) {
- if (!current.startsWith('#')) return stripNs(current);
- current = textures[current.substring(1)];
- }
- return null;
+ let current: string | undefined | null = ref;
+ for (let i = 0; i < 16 && current; i += 1) {
+ if (!current.startsWith('#')) return stripNs(current);
+ current = textures[current.substring(1)];
+ }
+ return null;
}
async function loadTexture(path: string) {
- if (textureCache.has(path)) return textureCache.get(path) as Promise;
- const promise = new Promise((resolve, reject) => {
- new TextureLoader().load(
- `/assets/textures/${path}.png`,
- (texture) => {
- texture.magFilter = NearestFilter;
- texture.minFilter = NearestFilter;
- texture.colorSpace = SRGBColorSpace;
- texture.wrapS = ClampToEdgeWrapping;
- texture.wrapT = ClampToEdgeWrapping;
- texture.generateMipmaps = false;
- const image = texture.image as HTMLImageElement | undefined;
- if (image && image.height > image.width) {
- const frame = image.width / image.height;
- texture.repeat.y = frame;
- texture.offset.y = 1 - frame;
- texture.needsUpdate = true;
- }
- resolve(texture);
- },
- undefined,
- reject
- );
- });
- textureCache.set(path, promise);
- return promise;
+ if (textureCache.has(path)) return textureCache.get(path) as Promise;
+ const promise = new Promise((resolve, reject) => {
+ new TextureLoader().load(
+ `/assets/textures/${path}.png`,
+ (texture) => {
+ texture.magFilter = NearestFilter;
+ texture.minFilter = NearestFilter;
+ texture.colorSpace = SRGBColorSpace;
+ texture.wrapS = ClampToEdgeWrapping;
+ texture.wrapT = ClampToEdgeWrapping;
+ texture.generateMipmaps = false;
+ const image = texture.image as HTMLImageElement | undefined;
+ if (image && image.height > image.width) {
+ const frame = image.width / image.height;
+ texture.repeat.y = frame;
+ texture.offset.y = 1 - frame;
+ texture.needsUpdate = true;
+ }
+ resolve(texture);
+ },
+ undefined,
+ reject
+ );
+ });
+ textureCache.set(path, promise);
+ return promise;
}
function extractModelPath(node: any): string | null {
- if (!node) return null;
- if (typeof node === 'string') return node;
- if (node.type === 'minecraft:model' && typeof node.model === 'string') return node.model;
- if (node.type === 'minecraft:special' && typeof node.base === 'string') return node.base;
- for (const field of ['fallback', 'on_false', 'on_true', 'model']) {
- if (node[field]) {
- const result = extractModelPath(node[field]);
- if (result) return result;
+ if (!node) return null;
+ if (typeof node === 'string') return node;
+ if (node.type === 'minecraft:model' && typeof node.model === 'string') return node.model;
+ if (node.type === 'minecraft:special' && typeof node.base === 'string') return node.base;
+ for (const field of ['fallback', 'on_false', 'on_true', 'model']) {
+ if (node[field]) {
+ const result = extractModelPath(node[field]);
+ if (result) return result;
+ }
}
- }
- for (const arrayField of ['cases', 'entries']) {
- const items = node[arrayField];
- if (!Array.isArray(items)) continue;
- for (const entry of items) {
- const result = extractModelPath(entry.model || entry);
- if (result) return result;
+ for (const arrayField of ['cases', 'entries']) {
+ const items = node[arrayField];
+ if (!Array.isArray(items)) continue;
+ for (const entry of items) {
+ const result = extractModelPath(entry.model || entry);
+ if (result) return result;
+ }
}
- }
- return null;
+ return null;
}
function tintToRgb(tint: any) {
- if (!tint || typeof tint !== 'object') return null;
- const type = stripNs(tint.type);
- if (type === 'constant' && Number.isFinite(tint.value)) return tint.value & 0xffffff;
- if (type === 'grass') return GRASS;
- if (type === 'foliage') return FOLIAGE;
- if (type === 'water') return WATER;
- return null;
+ if (!tint || typeof tint !== 'object') return null;
+ const type = stripNs(tint.type);
+ if (type === 'constant' && Number.isFinite(tint.value)) return tint.value & 0xffffff;
+ if (type === 'grass') return GRASS;
+ if (type === 'foliage') return FOLIAGE;
+ if (type === 'water') return WATER;
+ return null;
}
function extractTintRgb(node: any, itemName: string): number | null {
- if (!node || typeof node === 'string') return TINT_RGB[itemName] ?? null;
- if (Array.isArray(node.tints) && node.tints.length) {
- const rgb = tintToRgb(node.tints[0]);
- if (rgb != null) return rgb;
- }
- for (const field of ['fallback', 'on_false', 'on_true', 'model']) {
- if (node[field] && typeof node[field] !== 'string') {
- const result = extractTintRgb(node[field], itemName);
- if (result != null) return result;
+ if (!node || typeof node === 'string') return TINT_RGB[itemName] ?? null;
+ if (Array.isArray(node.tints) && node.tints.length) {
+ const rgb = tintToRgb(node.tints[0]);
+ if (rgb != null) return rgb;
}
- }
- for (const arrayField of ['cases', 'entries']) {
- const items = node[arrayField];
- if (!Array.isArray(items)) continue;
- for (const entry of items) {
- const result = extractTintRgb(entry.model || entry, itemName);
- if (result != null) return result;
+ for (const field of ['fallback', 'on_false', 'on_true', 'model']) {
+ if (node[field] && typeof node[field] !== 'string') {
+ const result = extractTintRgb(node[field], itemName);
+ if (result != null) return result;
+ }
}
- }
- return TINT_RGB[itemName] ?? null;
+ for (const arrayField of ['cases', 'entries']) {
+ const items = node[arrayField];
+ if (!Array.isArray(items)) continue;
+ for (const entry of items) {
+ const result = extractTintRgb(entry.model || entry, itemName);
+ if (result != null) return result;
+ }
+ }
+ return TINT_RGB[itemName] ?? null;
}
function faceQuad(face: string, from: number[], to: number[], uv: number[]) {
- const [x1, y1, z1] = from;
- const [x2, y2, z2] = to;
- const [u1, v1, u2, v2] = uv;
- let positions: number[];
- switch (face) {
- case 'up':
- positions = [x1, y2, z1, x1, y2, z2, x2, y2, z2, x2, y2, z1];
- break;
- case 'down':
- positions = [x1, y1, z2, x1, y1, z1, x2, y1, z1, x2, y1, z2];
- break;
- case 'north':
- positions = [x2, y2, z1, x2, y1, z1, x1, y1, z1, x1, y2, z1];
- break;
- case 'south':
- positions = [x1, y2, z2, x1, y1, z2, x2, y1, z2, x2, y2, z2];
- break;
- case 'east':
- positions = [x2, y2, z2, x2, y1, z2, x2, y1, z1, x2, y2, z1];
- break;
- case 'west':
- positions = [x1, y2, z1, x1, y1, z1, x1, y1, z2, x1, y2, z2];
- break;
- default:
- return null;
- }
- const uvs = [u1 / 16, 1 - v1 / 16, u1 / 16, 1 - v2 / 16, u2 / 16, 1 - v2 / 16, u2 / 16, 1 - v1 / 16];
- return { positions, uvs };
+ const [x1, y1, z1] = from;
+ const [x2, y2, z2] = to;
+ const [u1, v1, u2, v2] = uv;
+ let positions: number[];
+ switch (face) {
+ case 'up':
+ positions = [x1, y2, z1, x1, y2, z2, x2, y2, z2, x2, y2, z1];
+ break;
+ case 'down':
+ positions = [x1, y1, z2, x1, y1, z1, x2, y1, z1, x2, y1, z2];
+ break;
+ case 'north':
+ positions = [x2, y2, z1, x2, y1, z1, x1, y1, z1, x1, y2, z1];
+ break;
+ case 'south':
+ positions = [x1, y2, z2, x1, y1, z2, x2, y1, z2, x2, y2, z2];
+ break;
+ case 'east':
+ positions = [x2, y2, z2, x2, y1, z2, x2, y1, z1, x2, y2, z1];
+ break;
+ case 'west':
+ positions = [x1, y2, z1, x1, y1, z1, x1, y1, z2, x1, y2, z2];
+ break;
+ default:
+ return null;
+ }
+ const uvs = [u1 / 16, 1 - v1 / 16, u1 / 16, 1 - v2 / 16, u2 / 16, 1 - v2 / 16, u2 / 16, 1 - v1 / 16];
+ return {positions, uvs};
}
function defaultUV(face: string, from: number[], to: number[]) {
- const [x1, y1, z1] = from;
- const [x2, y2, z2] = to;
- switch (face) {
- case 'up':
- case 'down':
- return [x1, z1, x2, z2];
- case 'north':
- case 'south':
- return [x1, 16 - y2, x2, 16 - y1];
- case 'east':
- case 'west':
- return [z1, 16 - y2, z2, 16 - y1];
- default:
- return [0, 0, 16, 16];
- }
+ const [x1, y1, z1] = from;
+ const [x2, y2, z2] = to;
+ switch (face) {
+ case 'up':
+ case 'down':
+ return [x1, z1, x2, z2];
+ case 'north':
+ case 'south':
+ return [x1, 16 - y2, x2, 16 - y1];
+ case 'east':
+ case 'west':
+ return [z1, 16 - y2, z2, 16 - y1];
+ default:
+ return [0, 0, 16, 16];
+ }
}
async function buildElementGroup(elem: any, textures: Record, guiLight: string | undefined, tintRgb: number | null) {
- const group = new Group();
- const sideLit = guiLight !== 'front';
- for (const [face, data] of Object.entries(elem.faces || {})) {
- const texturePath = resolveTextureRef(textures, data.texture);
- if (!texturePath) continue;
- let texture: Texture;
- try {
- texture = await loadTexture(texturePath);
- } catch {
- continue;
+ const group = new Group();
+ const sideLit = guiLight !== 'front';
+ for (const [face, data] of Object.entries(elem.faces || {})) {
+ const texturePath = resolveTextureRef(textures, data.texture);
+ if (!texturePath) continue;
+ let texture: Texture;
+ try {
+ texture = await loadTexture(texturePath);
+ } catch {
+ continue;
+ }
+ const quad = faceQuad(face, elem.from, elem.to, data.uv || defaultUV(face, elem.from, elem.to));
+ if (!quad) continue;
+ const geometry = new BufferGeometry();
+ geometry.setAttribute('position', new Float32BufferAttribute(quad.positions, 3));
+ geometry.setAttribute('uv', new Float32BufferAttribute(quad.uvs, 2));
+ geometry.setIndex([0, 1, 2, 0, 2, 3]);
+ const brightness = sideLit ? (FACE_BRIGHTNESS[face] ?? 1) : 1;
+ const tinted = data.tintindex !== undefined && tintRgb != null;
+ const red = tinted ? ((tintRgb >> 16) & 0xff) / 255 : 1;
+ const green = tinted ? ((tintRgb >> 8) & 0xff) / 255 : 1;
+ const blue = tinted ? (tintRgb & 0xff) / 255 : 1;
+ const material = new MeshBasicMaterial({
+ map: texture,
+ color: new Color(brightness * red, brightness * green, brightness * blue),
+ transparent: true,
+ alphaTest: 0.01,
+ side: DoubleSide,
+ depthWrite: true,
+ polygonOffset: tinted,
+ polygonOffsetFactor: tinted ? -1 : 0,
+ polygonOffsetUnits: tinted ? -1 : 0
+ });
+ group.add(new Mesh(geometry, material));
}
- const quad = faceQuad(face, elem.from, elem.to, data.uv || defaultUV(face, elem.from, elem.to));
- if (!quad) continue;
- const geometry = new BufferGeometry();
- geometry.setAttribute('position', new Float32BufferAttribute(quad.positions, 3));
- geometry.setAttribute('uv', new Float32BufferAttribute(quad.uvs, 2));
- geometry.setIndex([0, 1, 2, 0, 2, 3]);
- const brightness = sideLit ? (FACE_BRIGHTNESS[face] ?? 1) : 1;
- const tinted = data.tintindex !== undefined && tintRgb != null;
- const red = tinted ? ((tintRgb >> 16) & 0xff) / 255 : 1;
- const green = tinted ? ((tintRgb >> 8) & 0xff) / 255 : 1;
- const blue = tinted ? (tintRgb & 0xff) / 255 : 1;
- const material = new MeshBasicMaterial({
- map: texture,
- color: new Color(brightness * red, brightness * green, brightness * blue),
- transparent: true,
- alphaTest: 0.01,
- side: DoubleSide,
- depthWrite: true,
- polygonOffset: tinted,
- polygonOffsetFactor: tinted ? -1 : 0,
- polygonOffsetUnits: tinted ? -1 : 0
- });
- group.add(new Mesh(geometry, material));
- }
- if (!elem.rotation) return group;
- const origin = elem.rotation.origin || [8, 8, 8];
- const angle = ((elem.rotation.angle || 0) * Math.PI) / 180;
- const axis = elem.rotation.axis || 'y';
- const wrapTo = new Group();
- wrapTo.position.set(origin[0], origin[1], origin[2]);
- const rotation = new Group();
- if (axis === 'x') rotation.rotation.x = angle;
- else if (axis === 'y') rotation.rotation.y = angle;
- else if (axis === 'z') rotation.rotation.z = angle;
- wrapTo.add(rotation);
- const wrapBack = new Group();
- wrapBack.position.set(-origin[0], -origin[1], -origin[2]);
- rotation.add(wrapBack);
- wrapBack.add(group);
- return wrapTo;
+ if (!elem.rotation) return group;
+ const origin = elem.rotation.origin || [8, 8, 8];
+ const angle = ((elem.rotation.angle || 0) * Math.PI) / 180;
+ const axis = elem.rotation.axis || 'y';
+ const wrapTo = new Group();
+ wrapTo.position.set(origin[0], origin[1], origin[2]);
+ const rotation = new Group();
+ if (axis === 'x') rotation.rotation.x = angle;
+ else if (axis === 'y') rotation.rotation.y = angle;
+ else if (axis === 'z') rotation.rotation.z = angle;
+ wrapTo.add(rotation);
+ const wrapBack = new Group();
+ wrapBack.position.set(-origin[0], -origin[1], -origin[2]);
+ rotation.add(wrapBack);
+ wrapBack.add(group);
+ return wrapTo;
}
async function buildElementsModel(model: Model, tintRgb: number | null) {
- const group = new Group();
- for (const elem of model.elements || []) group.add(await buildElementGroup(elem, model.textures || {}, model.gui_light, tintRgb));
- return group;
+ const group = new Group();
+ for (const elem of model.elements || []) group.add(await buildElementGroup(elem, model.textures || {}, model.gui_light, tintRgb));
+ return group;
}
async function buildLayeredSprite(model: Model, tintRgb: number | null) {
- const group = new Group();
- const textures = model.textures || {};
- for (let i = 0; ; i += 1) {
- const ref = textures[`layer${i}`];
- if (!ref) break;
- let texture: Texture;
- try {
- texture = await loadTexture(stripNs(ref) ?? '');
- } catch {
- continue;
+ const group = new Group();
+ const textures = model.textures || {};
+ for (let i = 0; ; i += 1) {
+ const ref = textures[`layer${i}`];
+ if (!ref) break;
+ let texture: Texture;
+ try {
+ texture = await loadTexture(stripNs(ref) ?? '');
+ } catch {
+ continue;
+ }
+ const geometry = new BufferGeometry();
+ geometry.setAttribute('position', new Float32BufferAttribute([0, 16, 0, 0, 0, 0, 16, 0, 0, 16, 16, 0], 3));
+ geometry.setAttribute('uv', new Float32BufferAttribute([0, 1, 0, 0, 1, 0, 1, 1], 2));
+ geometry.setIndex([0, 1, 2, 0, 2, 3]);
+ const tinted = i === 0 && tintRgb != null;
+ const red = tinted ? ((tintRgb >> 16) & 0xff) / 255 : 1;
+ const green = tinted ? ((tintRgb >> 8) & 0xff) / 255 : 1;
+ const blue = tinted ? (tintRgb & 0xff) / 255 : 1;
+ const material = new MeshBasicMaterial({
+ map: texture,
+ color: new Color(red, green, blue),
+ transparent: true,
+ alphaTest: 0.01,
+ side: DoubleSide
+ });
+ const mesh = new Mesh(geometry, material);
+ mesh.position.z = i * 0.05;
+ group.add(mesh);
}
- const geometry = new BufferGeometry();
- geometry.setAttribute('position', new Float32BufferAttribute([0, 16, 0, 0, 0, 0, 16, 0, 0, 16, 16, 0], 3));
- geometry.setAttribute('uv', new Float32BufferAttribute([0, 1, 0, 0, 1, 0, 1, 1], 2));
- geometry.setIndex([0, 1, 2, 0, 2, 3]);
- const tinted = i === 0 && tintRgb != null;
- const red = tinted ? ((tintRgb >> 16) & 0xff) / 255 : 1;
- const green = tinted ? ((tintRgb >> 8) & 0xff) / 255 : 1;
- const blue = tinted ? (tintRgb & 0xff) / 255 : 1;
- const material = new MeshBasicMaterial({
- map: texture,
- color: new Color(red, green, blue),
- transparent: true,
- alphaTest: 0.01,
- side: DoubleSide
- });
- const mesh = new Mesh(geometry, material);
- mesh.position.z = i * 0.05;
- group.add(mesh);
- }
- return group;
+ return group;
}
function atlasUv(x1: number, y1: number, x2: number, y2: number, tw = 64, th = 64) {
- return [x1 / tw, 1 - y1 / th, x1 / tw, 1 - y2 / th, x2 / tw, 1 - y2 / th, x2 / tw, 1 - y1 / th];
+ return [x1 / tw, 1 - y1 / th, x1 / tw, 1 - y2 / th, x2 / tw, 1 - y2 / th, x2 / tw, 1 - y1 / th];
}
function addBoxFace(group: Group, positions: number[], uv: number[], material: Material) {
- const geometry = new BufferGeometry();
- geometry.setAttribute('position', new Float32BufferAttribute(positions, 3));
- geometry.setAttribute('uv', new Float32BufferAttribute(uv, 2));
- geometry.setIndex([0, 1, 2, 0, 2, 3]);
- group.add(new Mesh(geometry, material));
+ const geometry = new BufferGeometry();
+ geometry.setAttribute('position', new Float32BufferAttribute(positions, 3));
+ geometry.setAttribute('uv', new Float32BufferAttribute(uv, 2));
+ geometry.setIndex([0, 1, 2, 0, 2, 3]);
+ group.add(new Mesh(geometry, material));
}
function addUnwrappedBox(group: Group, from: number[], to: number[], texU: number, texV: number, sx: number, sy: number, sz: number, material: Material) {
- const [x1, y1, z1] = from;
- const [x2, y2, z2] = to;
- const u1 = texU + sz;
- const u2 = u1 + sx;
- const u3 = u2 + sz;
- const u4 = u3 + sx;
- const v1 = texV + sz;
- const v2 = v1 + sy;
- addBoxFace(group, [x1, y2, z2, x1, y1, z2, x2, y1, z2, x2, y2, z2], atlasUv(u1, v1, u2, v2), material);
- addBoxFace(group, [x2, y2, z1, x2, y1, z1, x1, y1, z1, x1, y2, z1], atlasUv(u3, v1, u4, v2), material);
- addBoxFace(group, [x1, y2, z1, x1, y2, z2, x2, y2, z2, x2, y2, z1], atlasUv(u1, texV, u2, v1), material);
- addBoxFace(group, [x1, y1, z2, x1, y1, z1, x2, y1, z1, x2, y1, z2], atlasUv(u2, v1, u2 + sx, texV), material);
- addBoxFace(group, [x2, y2, z2, x2, y1, z2, x2, y1, z1, x2, y2, z1], atlasUv(u2, v1, u3, v2), material);
- addBoxFace(group, [x1, y2, z1, x1, y1, z1, x1, y1, z2, x1, y2, z2], atlasUv(texU, v1, u1, v2), material);
+ const [x1, y1, z1] = from;
+ const [x2, y2, z2] = to;
+ const u1 = texU + sz;
+ const u2 = u1 + sx;
+ const u3 = u2 + sz;
+ const u4 = u3 + sx;
+ const v1 = texV + sz;
+ const v2 = v1 + sy;
+ addBoxFace(group, [x1, y2, z2, x1, y1, z2, x2, y1, z2, x2, y2, z2], atlasUv(u1, v1, u2, v2), material);
+ addBoxFace(group, [x2, y2, z1, x2, y1, z1, x1, y1, z1, x1, y2, z1], atlasUv(u3, v1, u4, v2), material);
+ addBoxFace(group, [x1, y2, z1, x1, y2, z2, x2, y2, z2, x2, y2, z1], atlasUv(u1, texV, u2, v1), material);
+ addBoxFace(group, [x1, y1, z2, x1, y1, z1, x2, y1, z1, x2, y1, z2], atlasUv(u2, v1, u2 + sx, texV), material);
+ addBoxFace(group, [x2, y2, z2, x2, y1, z2, x2, y1, z1, x2, y2, z1], atlasUv(u2, v1, u3, v2), material);
+ addBoxFace(group, [x1, y2, z1, x1, y1, z1, x1, y1, z2, x1, y2, z2], atlasUv(texU, v1, u1, v2), material);
}
async function buildShieldSprite() {
- const texture = await loadTexture('entity/shield/shield_base_nopattern');
- const material = new MeshBasicMaterial({ map: texture, transparent: true, alphaTest: 0.01, side: FrontSide });
- const group = new Group();
- addUnwrappedBox(group, [-6, -11, 1], [6, 11, 2], 0, 0, 12, 22, 1, material);
- addUnwrappedBox(group, [-1, -3, -5], [1, 3, 1], 26, 0, 2, 6, 6, material);
- return group;
+ const texture = await loadTexture('entity/shield/shield_base_nopattern');
+ const material = new MeshBasicMaterial({map: texture, transparent: true, alphaTest: 0.01, side: FrontSide});
+ const group = new Group();
+ addUnwrappedBox(group, [-6, -11, 1], [6, 11, 2], 0, 0, 12, 22, 1, material);
+ addUnwrappedBox(group, [-1, -3, -5], [1, 3, 1], 26, 0, 2, 6, 6, material);
+ return group;
}
function applyGuiTransform(group: Group, display: any, isBlockShape: boolean) {
- const gui = (display && display.gui) || (isBlockShape ? DEFAULT_BLOCK_GUI : DEFAULT_GUI_TRANSFORM);
- const rotation = gui.rotation || [0, 0, 0];
- const translation = gui.translation || [0, 0, 0];
- const scale = gui.scale || [1, 1, 1];
- const inner = new Group();
- inner.position.set(-8, -8, -8);
- inner.add(group);
- const outer = new Group();
- outer.rotation.set(MathUtils.degToRad(rotation[0]), MathUtils.degToRad(rotation[1]), MathUtils.degToRad(rotation[2]));
- outer.scale.set(scale[0], scale[1], scale[2]);
- outer.position.set(translation[0], translation[1], translation[2]);
- outer.add(inner);
- return outer;
+ const gui = (display && display.gui) || (isBlockShape ? DEFAULT_BLOCK_GUI : DEFAULT_GUI_TRANSFORM);
+ const rotation = gui.rotation || [0, 0, 0];
+ const translation = gui.translation || [0, 0, 0];
+ const scale = gui.scale || [1, 1, 1];
+ const inner = new Group();
+ inner.position.set(-8, -8, -8);
+ inner.add(group);
+ const outer = new Group();
+ outer.rotation.set(MathUtils.degToRad(rotation[0]), MathUtils.degToRad(rotation[1]), MathUtils.degToRad(rotation[2]));
+ outer.scale.set(scale[0], scale[1], scale[2]);
+ outer.position.set(translation[0], translation[1], translation[2]);
+ outer.add(inner);
+ return outer;
}
function disposeGroup(group: Group) {
- group.traverse((object) => {
- const mesh = object as Mesh;
- mesh.geometry?.dispose();
- const material = mesh.material;
- if (Array.isArray(material)) material.forEach((item) => item.dispose());
- else material?.dispose();
- });
+ group.traverse((object) => {
+ const mesh = object as Mesh;
+ mesh.geometry?.dispose();
+ const material = mesh.material;
+ if (Array.isArray(material)) material.forEach((item) => item.dispose());
+ else material?.dispose();
+ });
}
export async function renderItem(name: string) {
- if (itemCache.has(name)) return itemCache.get(name) as Promise;
- const promise = (async () => {
- try {
- initThree();
- if (!renderer || !scene || !camera) return null;
- const itemDef = await loadItemDef(name);
- const modelRef = extractModelPath(itemDef.model);
- if (!modelRef) return null;
- const model = await loadModel(stripNs(modelRef) ?? '');
- const isBlockShape = Boolean(model.elements && model.elements.length);
- const tintRgb = extractTintRgb(itemDef.model, name);
- const inner = name === 'shield' ? await buildShieldSprite() : isBlockShape ? await buildElementsModel(model, tintRgb) : await buildLayeredSprite(model, tintRgb);
- const outer = applyGuiTransform(inner, model.display, isBlockShape);
- scene.add(outer);
- renderer.render(scene, camera);
- const dataUrl = renderer.domElement.toDataURL('image/png');
- scene.remove(outer);
- disposeGroup(outer);
- return dataUrl;
- } catch (error) {
- console.warn('itemRenderer: failed', name, error);
- return null;
- }
- })();
- itemCache.set(name, promise);
- return promise;
+ if (itemCache.has(name)) return itemCache.get(name) as Promise;
+ const promise = (async () => {
+ try {
+ initThree();
+ if (!renderer || !scene || !camera) return null;
+ const itemDef = await loadItemDef(name);
+ const modelRef = extractModelPath(itemDef.model);
+ if (!modelRef) return null;
+ const model = await loadModel(stripNs(modelRef) ?? '');
+ const isBlockShape = Boolean(model.elements && model.elements.length);
+ const tintRgb = extractTintRgb(itemDef.model, name);
+ const inner = name === 'shield' ? await buildShieldSprite() : isBlockShape ? await buildElementsModel(model, tintRgb) : await buildLayeredSprite(model, tintRgb);
+ const outer = applyGuiTransform(inner, model.display, isBlockShape);
+ scene.add(outer);
+ renderer.render(scene, camera);
+ const dataUrl = renderer.domElement.toDataURL('image/png');
+ scene.remove(outer);
+ disposeGroup(outer);
+ return dataUrl;
+ } catch (error) {
+ console.warn('itemRenderer: failed', name, error);
+ return null;
+ }
+ })();
+ itemCache.set(name, promise);
+ return promise;
}
diff --git a/src/main/frontend/src/lib/router.ts b/src/main/frontend/src/lib/router.ts
index be1f089..62fa017 100644
--- a/src/main/frontend/src/lib/router.ts
+++ b/src/main/frontend/src/lib/router.ts
@@ -1,43 +1,43 @@
export interface Route {
- path: string;
- params: Record;
+ path: string;
+ params: Record;
}
const routes = [
- { id: 'home', pattern: /^\/$/ },
- { id: 'players', pattern: /^\/players\/?$/ },
- { id: 'player', pattern: /^\/player\/([^/]+)\/?$/ },
- { id: 'commands', pattern: /^\/commands\/?$/ },
- { id: 'punishments', pattern: /^\/punishments\/?$/ },
- { id: 'punishments-detail', pattern: /^\/punishments\/([^/]+)\/?$/ },
- { id: 'indefbans', pattern: /^\/indefbans\/?$/ },
- { id: 'schematics', pattern: /^\/schematics\/?$/ },
- { id: 'schematics-upload', pattern: /^\/schematics\/upload\/?$/ }
+ {id: 'home', pattern: /^\/$/},
+ {id: 'players', pattern: /^\/players\/?$/},
+ {id: 'player', pattern: /^\/player\/([^/]+)\/?$/},
+ {id: 'commands', pattern: /^\/commands\/?$/},
+ {id: 'punishments', pattern: /^\/punishments\/?$/},
+ {id: 'punishments-detail', pattern: /^\/punishments\/([^/]+)\/?$/},
+ {id: 'indefbans', pattern: /^\/indefbans\/?$/},
+ {id: 'schematics', pattern: /^\/schematics\/?$/},
+ {id: 'schematics-upload', pattern: /^\/schematics\/upload\/?$/}
] as const;
export type RouteId = (typeof routes)[number]['id'] | 'not-found';
export function parseRoute(pathname: string): Route {
- for (const route of routes) {
- const match = pathname.match(route.pattern);
- if (!match) continue;
- const params: Record = {};
- if (route.id === 'player' || route.id === 'punishments-detail') {
- params.id = decodeURIComponent(match[1]);
+ for (const route of routes) {
+ const match = pathname.match(route.pattern);
+ if (!match) continue;
+ const params: Record = {};
+ if (route.id === 'player' || route.id === 'punishments-detail') {
+ params.id = decodeURIComponent(match[1]);
+ }
+ return {path: route.id, params};
}
- return { path: route.id, params };
- }
- return { path: 'not-found', params: {} };
+ return {path: 'not-found', params: {}};
}
export function navigate(path: string) {
- history.pushState({}, '', path);
- window.dispatchEvent(new PopStateEvent('popstate'));
+ history.pushState({}, '', path);
+ window.dispatchEvent(new PopStateEvent('popstate'));
}
export function isInternalAppLink(anchor: HTMLAnchorElement) {
- if (anchor.target || anchor.hasAttribute('download')) return false;
- const url = new URL(anchor.href);
- if (url.origin !== window.location.origin) return false;
- return routes.some((route) => route.pattern.test(url.pathname));
+ if (anchor.target || anchor.hasAttribute('download')) return false;
+ const url = new URL(anchor.href);
+ if (url.origin !== window.location.origin) return false;
+ return routes.some((route) => route.pattern.test(url.pathname));
}
diff --git a/src/main/frontend/src/lib/utils.ts b/src/main/frontend/src/lib/utils.ts
index c2b6ade..c49fff9 100644
--- a/src/main/frontend/src/lib/utils.ts
+++ b/src/main/frontend/src/lib/utils.ts
@@ -1,55 +1,55 @@
-import { clsx, type ClassValue } from "clsx";
-import { twMerge } from "tailwind-merge";
+import {clsx, type ClassValue} from "clsx";
+import {twMerge} from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs));
+ return twMerge(clsx(inputs));
}
export function titleCase(value: string) {
- return value
- .replace(/[_-]+/g, " ")
- .replace(/\s+/g, " ")
- .trim()
- .replace(/\w\S*/g, (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());
+ return value
+ .replace(/[_-]+/g, " ")
+ .replace(/\s+/g, " ")
+ .trim()
+ .replace(/\w\S*/g, (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());
}
export function lowerSearch(value: unknown): string {
- if (value == null) return "";
- if (typeof value === "string") return value.toLowerCase();
- if (typeof value === "number" || typeof value === "boolean") return String(value).toLowerCase();
- if (Array.isArray(value)) return value.map(lowerSearch).join(" ");
- if (typeof value === "object") return Object.values(value).map(lowerSearch).join(" ");
- return "";
+ if (value == null) return "";
+ if (typeof value === "string") return value.toLowerCase();
+ if (typeof value === "number" || typeof value === "boolean") return String(value).toLowerCase();
+ if (Array.isArray(value)) return value.map(lowerSearch).join(" ");
+ if (typeof value === "object") return Object.values(value).map(lowerSearch).join(" ");
+ return "";
}
export function formatBytes(value: number | null | undefined) {
- if (!Number.isFinite(value ?? NaN)) return "-";
- const units = ["B", "KB", "MB", "GB", "TB"];
- let amount = Math.max(0, value as number);
- let unit = 0;
- while (amount >= 1024 && unit < units.length - 1) {
- amount /= 1024;
- unit += 1;
- }
- return `${amount >= 10 || unit === 0 ? amount.toFixed(0) : amount.toFixed(1)} ${units[unit]}`;
+ if (!Number.isFinite(value ?? NaN)) return "-";
+ const units = ["B", "KB", "MB", "GB", "TB"];
+ let amount = Math.max(0, value as number);
+ let unit = 0;
+ while (amount >= 1024 && unit < units.length - 1) {
+ amount /= 1024;
+ unit += 1;
+ }
+ return `${amount >= 10 || unit === 0 ? amount.toFixed(0) : amount.toFixed(1)} ${units[unit]}`;
}
export function formatDuration(milliseconds: number) {
- if (!Number.isFinite(milliseconds) || milliseconds < 0) return "-";
- const seconds = Math.floor(milliseconds / 1000);
- const days = Math.floor(seconds / 86400);
- const hours = Math.floor((seconds % 86400) / 3600);
- const minutes = Math.floor((seconds % 3600) / 60);
- if (days > 0) return `${days}d ${hours}h`;
- if (hours > 0) return `${hours}h ${minutes}m`;
- return `${minutes}m`;
+ if (!Number.isFinite(milliseconds) || milliseconds < 0) return "-";
+ const seconds = Math.floor(milliseconds / 1000);
+ const days = Math.floor(seconds / 86400);
+ const hours = Math.floor((seconds % 86400) / 3600);
+ const minutes = Math.floor((seconds % 3600) / 60);
+ if (days > 0) return `${days}d ${hours}h`;
+ if (hours > 0) return `${hours}h ${minutes}m`;
+ return `${minutes}m`;
}
export function pingClass(ping: number | null | undefined) {
- if (!Number.isFinite(ping ?? NaN)) return "text-muted-foreground";
- if ((ping as number) < 100) return "text-success";
- if ((ping as number) < 250) return "text-warning";
- return "text-destructive";
+ if (!Number.isFinite(ping ?? NaN)) return "text-muted-foreground";
+ if ((ping as number) < 100) return "text-success";
+ if ((ping as number) < 250) return "text-warning";
+ return "text-destructive";
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
diff --git a/src/main/frontend/src/main.ts b/src/main/frontend/src/main.ts
index 44b2491..a02d98d 100644
--- a/src/main/frontend/src/main.ts
+++ b/src/main/frontend/src/main.ts
@@ -1,7 +1,7 @@
-import { mount } from 'svelte';
+import {mount} from 'svelte';
import App from './App.svelte';
import './app.css';
mount(App, {
- target: document.getElementById('app') as HTMLElement
+ target: document.getElementById('app') as HTMLElement
});
diff --git a/src/main/frontend/svelte.config.js b/src/main/frontend/svelte.config.js
index bfb5c98..d04f4d7 100644
--- a/src/main/frontend/svelte.config.js
+++ b/src/main/frontend/svelte.config.js
@@ -1,5 +1,5 @@
-import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
+import {vitePreprocess} from '@sveltejs/vite-plugin-svelte';
export default {
- preprocess: vitePreprocess({ script: true })
+ preprocess: vitePreprocess({script: true})
};
diff --git a/src/main/frontend/tsconfig.json b/src/main/frontend/tsconfig.json
index d5a1cb4..7c3d600 100644
--- a/src/main/frontend/tsconfig.json
+++ b/src/main/frontend/tsconfig.json
@@ -15,9 +15,18 @@
"target": "ES2022",
"verbatimModuleSyntax": true,
"paths": {
- "$lib": ["./src/lib"],
- "$lib/*": ["./src/lib/*"]
+ "$lib": [
+ "./src/lib"
+ ],
+ "$lib/*": [
+ "./src/lib/*"
+ ]
}
},
- "include": ["src/**/*.ts", "src/**/*.svelte", "vite.config.ts", "svelte.config.js"]
+ "include": [
+ "src/**/*.ts",
+ "src/**/*.svelte",
+ "vite.config.ts",
+ "svelte.config.js"
+ ]
}
diff --git a/src/main/frontend/tsconfig.node.json b/src/main/frontend/tsconfig.node.json
index ecd285b..a00eeaf 100644
--- a/src/main/frontend/tsconfig.node.json
+++ b/src/main/frontend/tsconfig.node.json
@@ -4,7 +4,12 @@
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
- "types": ["node"]
+ "types": [
+ "node"
+ ]
},
- "include": ["vite.config.ts", "svelte.config.js"]
+ "include": [
+ "vite.config.ts",
+ "svelte.config.js"
+ ]
}
diff --git a/src/main/frontend/vite.config.ts b/src/main/frontend/vite.config.ts
index 37d388e..ed67e17 100644
--- a/src/main/frontend/vite.config.ts
+++ b/src/main/frontend/vite.config.ts
@@ -1,31 +1,31 @@
-import { svelte } from '@sveltejs/vite-plugin-svelte';
+import {svelte} from '@sveltejs/vite-plugin-svelte';
import tailwindcss from '@tailwindcss/vite';
-import { defineConfig } from 'vite';
-import { fileURLToPath, URL } from 'node:url';
+import {defineConfig} from 'vite';
+import {fileURLToPath, URL} from 'node:url';
export default defineConfig({
- base: '/app/',
- plugins: [svelte(), tailwindcss()],
- resolve: {
- alias: {
- $lib: fileURLToPath(new URL('./src/lib', import.meta.url))
- }
- },
- build: {
- outDir: '../../../build/generated/frontend-resources/httpd/app',
- emptyOutDir: true,
- rolldownOptions: {
- output: {
- codeSplitting: {
- groups: [
- {
- name: 'three-renderer',
- test: /node_modules[\\/]three[\\/]/,
- maxSize: 475 * 1024
- }
- ]
+ base: '/app/',
+ plugins: [svelte(), tailwindcss()],
+ resolve: {
+ alias: {
+ $lib: fileURLToPath(new URL('./src/lib', import.meta.url))
+ }
+ },
+ build: {
+ outDir: '../../../build/generated/frontend-resources/httpd/app',
+ emptyOutDir: true,
+ rolldownOptions: {
+ output: {
+ codeSplitting: {
+ groups: [
+ {
+ name: 'three-renderer',
+ test: /node_modules[\\/]three[\\/]/,
+ maxSize: 475 * 1024
+ }
+ ]
+ }
+ }
}
- }
}
- }
});
diff --git a/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java b/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java
index 9432ab3..7b629c6 100644
--- a/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java
+++ b/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java
@@ -62,6 +62,11 @@ public class AuthenticationEndpoint extends AbstractServlet
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Authentication is not enabled.");
return null;
}
+ if ("access_denied".equals(request.getParameter("error")))
+ {
+ module.api().logging().info("OAuth2 sign-in cancelled by user.");
+ return signInFailed(response, HttpServletResponse.SC_UNAUTHORIZED, "Sign-in was cancelled.");
+ }
try
{
provider.handleCallback(request, response);
@@ -69,13 +74,7 @@ public class AuthenticationEndpoint extends AbstractServlet
catch (AuthenticationException e)
{
module.api().logging().error("OAuth2 callback failed: " + e.getMessage());
- response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
- response.setContentType("text/html; charset=UTF-8");
- return "Sign-in failed"
- + ""
- + "Sign-in failed
"
- + "" + escape(e.getMessage()) + "
"
- + "Try again
";
+ return signInFailed(response, HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
}
String raw = readCookie(request, RETURN_TO_COOKIE);
@@ -86,6 +85,17 @@ public class AuthenticationEndpoint extends AbstractServlet
return null;
}
+ private static String signInFailed(HttpServletResponse response, int status, String message)
+ {
+ response.setStatus(status);
+ response.setContentType("text/html; charset=UTF-8");
+ return "Sign-in failed"
+ + ""
+ + "Sign-in failed
"
+ + "" + escape(message) + "
"
+ + "Try again
";
+ }
+
@GetMapping(endpoint = "/oauth2/logout")
public String logout(HttpServletRequest request, HttpServletResponse response) throws IOException
{
diff --git a/src/main/java/dev/plex/request/impl/CommandsEndpoint.java b/src/main/java/dev/plex/request/impl/CommandsEndpoint.java
index 4487c17..3d0602e 100644
--- a/src/main/java/dev/plex/request/impl/CommandsEndpoint.java
+++ b/src/main/java/dev/plex/request/impl/CommandsEndpoint.java
@@ -11,9 +11,11 @@ import jakarta.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -35,13 +37,21 @@ public class CommandsEndpoint extends AbstractServlet
@MappingHeaders(headers = "content-type;application/json; charset=utf-8")
public String getCommands(HttpServletRequest request, HttpServletResponse response)
{
- if (cachedGroups == null)
+ try
{
- cachedGroups = buildGroups();
+ if (cachedGroups == null)
+ {
+ cachedGroups = buildGroups();
+ }
+ Map body = new LinkedHashMap<>();
+ body.put("groups", cachedGroups);
+ return JsonResponse.json(response, body);
+ }
+ catch (RuntimeException e)
+ {
+ module.api().logging().error("Failed to build HTTPD command list: " + e.getMessage());
+ return JsonResponse.error(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to load commands.");
}
- Map body = new LinkedHashMap<>();
- body.put("groups", cachedGroups);
- return JsonResponse.json(response, body);
}
private List buildGroups()
@@ -55,8 +65,13 @@ public class CommandsEndpoint extends AbstractServlet
}
final CommandMap map = Bukkit.getCommandMap();
+ Set seenCommands = java.util.Collections.newSetFromMap(new IdentityHashMap<>());
for (Command command : map.getKnownCommands().values())
{
+ if (!seenCommands.add(command))
+ {
+ continue;
+ }
String plugin = "Bukkit";
if (command instanceof PluginIdentifiableCommand pic)
{
@@ -100,14 +115,30 @@ public class CommandsEndpoint extends AbstractServlet
{
private static CommandInfo from(PlexCommand command)
{
- List aliases = command.getAliases() == null ? List.of() : command.getAliases();
- return new CommandInfo(command.getName(), aliases, command.getDescription(), cleanUsage(command.getUsage()), cleanPermission(command.getPermission()));
+ return new CommandInfo(clean(command.getName()), cleanAliases(command.getAliases()), clean(command.getDescription()), cleanUsage(command.getUsage()), cleanPermission(command.getPermission()));
}
private static CommandInfo from(Command command)
{
- List aliases = command.getAliases() == null ? List.of() : command.getAliases();
- return new CommandInfo(command.getName(), aliases, command.getDescription(), cleanUsage(command.getUsage()), cleanPermission(command.getPermission()));
+ return new CommandInfo(clean(command.getName()), cleanAliases(command.getAliases()), clean(command.getDescription()), cleanUsage(command.getUsage()), cleanPermission(command.getPermission()));
}
}
+
+ private static List cleanAliases(List aliases)
+ {
+ if (aliases == null || aliases.isEmpty())
+ {
+ return List.of();
+ }
+ return aliases.stream()
+ .filter(alias -> alias != null && !alias.isBlank())
+ .map(String::trim)
+ .distinct()
+ .toList();
+ }
+
+ private static String clean(String value)
+ {
+ return value == null ? "" : value;
+ }
}
diff --git a/src/main/java/dev/plex/request/impl/FrontendEndpoint.java b/src/main/java/dev/plex/request/impl/FrontendEndpoint.java
index 07a16e1..bbe8660 100644
--- a/src/main/java/dev/plex/request/impl/FrontendEndpoint.java
+++ b/src/main/java/dev/plex/request/impl/FrontendEndpoint.java
@@ -41,6 +41,10 @@ public class FrontendEndpoint extends AbstractServlet
@GetMapping(endpoint = "/player/")
public String player(HttpServletRequest request, HttpServletResponse response)
{
+ if (currentStaff(request) == null)
+ {
+ return staffOnly(request, response, "to access player admin tools");
+ }
return indexHtml(response);
}
@@ -59,15 +63,45 @@ public class FrontendEndpoint extends AbstractServlet
@GetMapping(endpoint = "/indefbans/")
public String indefBans(HttpServletRequest request, HttpServletResponse response)
{
+ if (currentStaff(request) == null)
+ {
+ return staffOnly(request, response, "to view this page");
+ }
return indexHtml(response);
}
@GetMapping(endpoint = "/schematics/")
public String schematics(HttpServletRequest request, HttpServletResponse response)
{
+ if (requestPath(request).startsWith("/schematics/upload") && currentStaff(request) == null)
+ {
+ return staffOnly(request, response, "to upload schematics");
+ }
return indexHtml(response);
}
+ private String staffOnly(HttpServletRequest request, HttpServletResponse response, String action)
+ {
+ response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ response.setContentType("text/html; charset=UTF-8");
+ return "Staff access required"
+ + ""
+ + "Staff access required
"
+ + "" + signInPrompt(request, action) + "
"
+ + "Back to overview
";
+ }
+
+ private static String requestPath(HttpServletRequest request)
+ {
+ String uri = request.getRequestURI();
+ String contextPath = request.getContextPath();
+ if (contextPath != null && !contextPath.isEmpty() && !contextPath.equals("/") && uri.startsWith(contextPath))
+ {
+ uri = uri.substring(contextPath.length());
+ }
+ return uri.isEmpty() ? "/" : uri;
+ }
+
public static String indexHtml(HttpServletResponse response)
{
response.setContentType("text/html; charset=UTF-8");