12 Commits

26 changed files with 5465 additions and 3856 deletions

View File

@ -34,7 +34,7 @@
"eslint-plugin-svelte": "^2.46.0",
"postcss-nesting": "^13.0.1",
"sass": "^1.81.0",
"svelte": "^5.2.7",
"svelte": "^5.16.0",
"tailwind-merge": "^2.5.5",
"tailwindcss": "^3.4.15",
"three": "^0.170.0",

8570
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -42,7 +42,7 @@
}
</script>
<div>
<div class="p-3 bg-gray-200 dark:bg-neutral-800 rounded-2xl w-3/4 mx-auto">
<table>
<thead>
<tr class="font-bold border-b">
@ -71,9 +71,3 @@
</tbody>
</table>
</div>
<style lang="scss">
div {
@apply p-3 bg-gray-200 dark:bg-neutral-800 rounded-2xl w-3/4 mx-auto;
}
</style>

View File

@ -48,8 +48,8 @@
}).sort((a, b) => b.points - a.points);
</script>
<div>
<table>
<div class="p-3 bg-gray-200 dark:bg-neutral-800 rounded-2xl w-3/4 mx-auto">
<table class="w-full">
<thead>
<tr class="font-bold border-b">
{#each Array(rows) as i (i)}
@ -70,12 +70,3 @@
</tbody>
</table>
</div>
<style lang="scss">
table {
@apply w-full;
}
div {
@apply p-3 bg-gray-200 dark:bg-neutral-800 rounded-2xl w-3/4 mx-auto;
}
</style>

View File

@ -23,37 +23,60 @@
import { t } from "astro-i18n";
import { l } from "../util/util";
import { onMount } from "svelte";
import { loggedIn } from "@repo/authv2.ts";
interface Props {
logo?: import('svelte').Snippet;
logo?: import("svelte").Snippet;
}
let { logo }: Props = $props();
let navbar: HTMLDivElement = $state();
let navbar = $state<HTMLDivElement>();
let searchOpen = $state(false);
let accountBtn = $state<HTMLAnchorElement>();
$effect(() => {
if ($loggedIn) {
accountBtn!.href = l("/dashboard");
} else {
accountBtn!.href = l("/login");
}
});
onMount(() => {
handleScroll();
})
});
function handleScroll() {
if (window.scrollY > 0) {
navbar.classList.add("before:scale-y-100");
navbar!.classList.add("before:scale-y-100");
} else {
navbar.classList.remove("before:scale-y-100");
navbar!.classList.remove("before:scale-y-100");
}
}
</script>
<svelte:window onscroll={handleScroll} />
<nav data-pagefind-ignore class="fixed top-0 left-0 right-0 sm:px-4 py-1 transition-colors z-10 flex justify-center before:backdrop-blur before:shadow-2xl before:absolute before:top-0 before:left-0 before:bottom-0 before:right-0 before:-z-10 before:scale-y-0 before:transition-transform before:origin-top" bind:this={navbar}>
<div class="flex flex-row items-center justify-evenly md:justify-between match">
<nav
data-pagefind-ignore
class="fixed top-0 left-0 right-0 sm:px-4 py-1 transition-colors z-10 flex justify-center before:backdrop-blur before:shadow-2xl before:absolute before:top-0 before:left-0 before:bottom-0 before:right-0 before:-z-10 before:scale-y-0 before:transition-transform before:origin-top"
bind:this={navbar}
>
<div
class="flex flex-row items-center justify-evenly md:justify-between match"
>
<a class="flex items-center" href={l("/")}>
{@render logo?.()}
<span class="text-2xl uppercase font-bold dark:text-white hidden md:inline-block">
<span
class="text-2xl uppercase font-bold dark:text-white hidden md:inline-block"
>
{t("navbar.title")}
<span class="before:scale-y-100" style="display: none" aria-hidden="true"></span>
<span
class="before:scale-y-100"
style="display: none"
aria-hidden="true"
></span>
</span>
</a>
<div class="flex justify-center flex-wrap">
@ -65,10 +88,21 @@
<CaretDownOutline class="ml-2 mt-auto" />
</button>
<div>
<a class="btn btn-gray" href={l("/announcements")}>{t("navbar.links.home.announcements")}</a>
<a class="btn btn-gray" href={l("/downloads")}>{t("navbar.links.home.downloads")}</a>
<a class="btn btn-gray" href={l("/faq")}>{t("navbar.links.home.faq")}</a>
<a class="btn btn-gray" href={l("/code-of-conduct")}>{t("navbar.links.rules.coc")}</a>
<a class="btn btn-gray" href={l("/announcements")}
>{t("navbar.links.home.announcements")}</a
>
<a class="btn btn-gray" href={l("/downloads")}
>{t("navbar.links.home.downloads")}</a
>
<a class="btn btn-gray" href={l("/tutorials")}
>{t("navbar.links.home.tutorials")}</a
>
<a class="btn btn-gray" href={l("/faq")}
>{t("navbar.links.home.faq")}</a
>
<a class="btn btn-gray" href={l("/code-of-conduct")}
>{t("navbar.links.rules.coc")}</a
>
</div>
</div>
<div class="btn-dropdown">
@ -79,17 +113,35 @@
<CaretDownOutline class="ml-2 mt-auto" />
</button>
<div>
<a href={l("/rules/wargear")} class="btn btn-gray">{t("navbar.links.rules.wg")}</a>
<a href={l("/rules/miniwargear")} class="btn btn-gray">{t("navbar.links.rules.mwg")}</a>
<a href={l("/rules/warship")} class="btn btn-gray">{t("navbar.links.rules.ws")}</a>
<a href={l("/rules/airship")} class="btn btn-gray">{t("navbar.links.rules.as")}</a>
<a href={l("/rules/quickgear")} class="btn btn-gray">{t("navbar.links.rules.qg")}</a>
<a href={l("/rules/wargear")} class="btn btn-gray"
>{t("navbar.links.rules.wg")}</a
>
<a href={l("/rules/miniwargear")} class="btn btn-gray"
>{t("navbar.links.rules.mwg")}</a
>
<a href={l("/rules/warship")} class="btn btn-gray"
>{t("navbar.links.rules.ws")}</a
>
<a href={l("/rules/airship")} class="btn btn-gray"
>{t("navbar.links.rules.as")}</a
>
<a href={l("/rules/quickgear")} class="btn btn-gray"
>{t("navbar.links.rules.qg")}</a
>
<h2 class="px-2 text-gray-300">{t("navbar.links.rules.rotating")}</h2>
<a href={l("/rules/megawargear")}
class="btn btn-gray">{t("navbar.links.rules.megawg")}</a>
<a href={l("/rules/microwargear")}
class="btn btn-gray">{t("navbar.links.rules.micro")}</a>
<a href={l("/rules/streetfight")} class="btn btn-gray">{t("navbar.links.rules.sf")}</a>
<a href={l("/rules/megawargear")} class="btn btn-gray"
>{t("navbar.links.rules.megawg")}</a
>
<a href={l("/rules/microwargear")} class="btn btn-gray"
>{t("navbar.links.rules.micro")}</a
>
<a href={l("/rules/streetfight")} class="btn btn-gray"
>{t("navbar.links.rules.sf")}</a
>
<h2 class="px-2 text-gray-300">{t("navbar.links.rules.ranked")}</h2>
<a href={l("/rangliste/missilewars")} class="btn btn-gray"
>{t("navbar.links.ranked.mw")}</a
>
</div>
</div>
<!-- TODO: Add help center
@ -106,7 +158,7 @@
</div>
</div>
-->
<a class="btn" href={l("/login")}>
<a class="btn" href={l("/login")} bind:this={accountBtn}>
<span class="btn__text">{t("navbar.links.account")}</span>
</a>
<!--

View File

@ -43,8 +43,6 @@
let member = $state(event.maxTeamMembers);
let schemType = $state(event.schemType);
let publicOnly = $state(event.publicSchemsOnly);
let addReferee: {name: string, id: number}[] = [];
let removeReferee: {name: string, id: number}[] = [];
let errorOpen = $state(false);
let error: any = $state(undefined);
@ -69,9 +67,7 @@
endDate.diff(dayjs(event.end)) !== 0 ||
member !== event.maxTeamMembers ||
schemType != event.schemType ||
publicOnly !== event.publicSchemsOnly ||
addReferee.length > 0 ||
removeReferee.length > 0);
publicOnly !== event.publicSchemsOnly);
async function del() {
@ -95,8 +91,8 @@
publicSchemsOnly: publicOnly,
schemType: schemType ?? "null",
start: startDate,
addReferee: addReferee.map((ref) => ref.id),
removeReferee: removeReferee.map((ref) => ref.id)
addReferee: [],
removeReferee: []
};
try {

View File

@ -19,7 +19,7 @@
<script lang="ts">
import {EditOutline, TrashBinOutline} from "flowbite-svelte-icons";
import {Button, Modal, Toolbar, ToolbarButton} from "flowbite-svelte";
import {Button, Checkbox, Modal, Toolbar, ToolbarButton} from "flowbite-svelte";
import type {EventFight, ExtendedEvent} from "@type/event.ts";
import FightEditModal from "./modals/FightEditModal.svelte";
import {createEventDispatcher} from "svelte";
@ -32,6 +32,8 @@
i: number;
selected?: boolean;
hideEdit?: boolean;
select: () => void;
update: () => void;
}
let {
@ -39,29 +41,34 @@
data = $bindable(),
i,
selected = false,
hideEdit = false
hideEdit = false,
select,
update,
}: Props = $props();
let deleteOpen = $state(false);
let editOpen = $state(false);
let dispatcher = createEventDispatcher();
function dispatchSelect() {
setTimeout(() => {
if (!deleteOpen && !editOpen) {
dispatcher("select");
select();
}
}, 1);
}
async function deleteFight() {
await $fightRepo.deleteFight(fight.id);
dispatcher("update");
update();
}
let isUneven = $derived(i % 2 === 0);
</script>
<div class="flex h-16 {i % 2 === 0 ? 'bg-gray-800' : ''} mx-4 mt-6 rounded border {selected ? 'border-orange-700' : 'border-gray-700'} p-2 hover:bg-gray-700 transition justify-between shadow-lg cursor-pointer"
<div class={{"flex h-16 mx-4 mt-6 rounded border p-2 hover:bg-gray-700 transition justify-between shadow-lg cursor-pointer": true,
"bg-gray-800": isUneven,
"border-orange-700": selected,
"border-gray-700": !selected}}
onclick={dispatchSelect} onkeypress={dispatchSelect} role="checkbox" aria-checked={selected} tabindex="0"
>
<div class="flex">

View File

@ -82,7 +82,7 @@
} else {
groupFights.forEach(fight => selectedFights.add(fight));
}
selectedFights = selectedFights;
selectedFights = new Set(selectedFights);
}
let deleteOpen = $state(false);
@ -227,16 +227,17 @@
<h1 class="ml-4 text-2xl">{group.group ?? "Ungrouped"}</h1>
</div>
{#each group.fights.sort((a, b) => a.start - b.start) as fight, i (fight.id)}
<FightCard {fight} {i} {data} selected={selectedFights.has(fight)}
on:select={() => {
{@const isSelected = selectedFights.has(fight)}
<FightCard {fight} {i} {data} selected={isSelected}
select={() => {
if (selectedFights.has(fight)) {
selectedFights.delete(fight);
} else {
selectedFights.add(fight);
}
selectedFights = selectedFights;
}}
on:update={async () => fights = await $fightRepo.listFights(data.event.id)}
selectedFights = new Set(selectedFights);
}} update={async () => fights = await $fightRepo.listFights(data.event.id)}
/>
{/each}
{/each}

View File

@ -43,6 +43,14 @@
async function addReferee() {
if (selectedPlayer) {
referees = (await $eventRepo.updateEvent(data.event.id.toString(), {
deadline: null,
end: null,
maxTeamMembers: null,
name: null,
publicSchemsOnly: null,
removeReferee: null,
schemType: null,
start: null,
addReferee: [selectedPlayer]
})).referees;
}
@ -53,7 +61,15 @@
function removeReferee(id: string) {
return async () => {
referees = (await $eventRepo.updateEvent(data.event.id.toString(), {
removeReferee: [id]
deadline: null,
end: null,
maxTeamMembers: null,
name: null,
publicSchemsOnly: null,
addReferee: null,
schemType: null,
start: null,
removeReferee: [id],
})).referees;
}
}

View File

@ -35,12 +35,13 @@
open?: boolean;
}
let { fight = $bindable(), data, open = $bindable(false) }: Props = $props();
let { fight = $bindable(), data = $bindable(), open = $bindable(false) }: Props = $props();
let redTeam = $state(fight.redTeam.id.toString());
let blueTeam = $state(fight.blueTeam.id.toString());
let start = $state(dayjs(fight.start).utc(true).toISOString().slice(0, -1));
let spectatePort = $state(fight.spectatePort?.toString() ?? null);
$inspect(spectatePort, fight.spectatePort)
let gamemode = $state(fight.spielmodus);
let map = $state(fight.map);
let group = $state(fight.group);
@ -62,6 +63,8 @@
start: dayjs(start)
};
console.log(update)
$fightRepo.updateFight(fight.id, update)
.then(value => {
open = false;

View File

@ -31,15 +31,15 @@ export interface CreateEvent {
}
export interface UpdateEvent {
name?: string;
start?: Dayjs;
end?: Dayjs;
deadline?: Dayjs;
maxTeamMembers?: number;
schemType?: string | null;
publicSchemsOnly?: boolean;
addReferee?: string[];
removeReferee?: string[];
name: string | null;
start: Dayjs | null;
end: Dayjs | null;
deadline: Dayjs | null;
maxTeamMembers: number | null;
schemType: string | null;
publicSchemsOnly: boolean | null;
addReferee: string[] | null;
removeReferee: string[] | null;
}
export class EventRepo {

View File

@ -80,7 +80,7 @@ export class FightRepo {
blueTeam: fight.blueTeam,
redTeam: fight.redTeam,
start: fight.start?.valueOf(),
spectatePort: fight.spectatePort ?? 0,
spectatePort: fight.spectatePort,
group: fight.group,
}),
}).then(value => value.json())

View File

@ -0,0 +1,17 @@
---
title: Neujahrsevent 2025 Eventplan
key: Neujahrsevent-2025-eventplan
description: Der Kampfplan für das Neujahrsevent 2025.
created: 2025-01-01
tags:
- event
- microwargear
---
# Gruppenphase
<group-table data-event="64"> </group-table>
## Kampfplan
<fight-table data-event="64" data-group="Gruppe 1"> </fight-table>

View File

@ -11,8 +11,8 @@ image: ../../../images/SchneeballschlachtMWG.png
# Gruppenphase
<group-table data-event="65" />
<group-table data-event="65"> </group-table>
## Kampfplan
<fight-table data-event="65" data-group="Gruppe 1" />
<fight-table data-event="65" data-group="Gruppe 1"> </fight-table>

View File

@ -104,6 +104,14 @@ export const publics = defineCollection({
}),
});
export const tutorials = defineCollection({
type: "content",
schema: z.object({
title: z.string(),
translationKey: z.string(),
}),
});
export const collections = {
"pages": pages,
"help": help,
@ -112,4 +120,5 @@ export const collections = {
"downloads": downloads,
"announcements": announcements,
"publics": publics,
"tutorials": tutorials
};

View File

@ -0,0 +1,56 @@
---
title: Tracer
translationKey: tracer
slug: tracer
---
# Der Tracer
![](../../images/tutorials/tracer-cover.png)
Der Tracer ist eines der Kern-Tools unseres BauSystems. Durch ihn kannst du
dir komfortabel angucken wie sich die TNT deiner Kanonen Tick für Tick
verhalten. Dies tut der Tracer, indem er alle Daten der auf einem Plot
gezündeten TNTs aufnimmt und speichert. Du kannst dir dann verschiedene
Darstellungsformen der aufgezeichneten TNTs angucken.
## Den Tracer starten
Um die Schüsse deiner Kanone auf einem Plot aufzunehmen, musst du den Tracer
für diese Plot einschalten. Nutze hierfür `/trace auto`.
Nun da der Tracer an ist, wird für jeden Kanonenschuss ein Trace erstellt.
## Traces anzeigen und analysieren
Jetzt da der Tracer an ist und du einen Schuss abgegeben hast, kannst du dir
den Trace mit
`/trace show` anzeigen (Falls du mehrere Schüsse abgegeben hast kannst du
dir mit
`/trace isolate [Schuss-Nummer]` den Trace eines einzelnen Schusses anzeigen).
Zur Analyse eines Schusses stellen wir dir verschiedene **View-Flags** zur
Verfügung. Diese aktivierst du, indem du sie an den Trace-show Command
anhängst.
| Flag | Beschreibung |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `count` | Zeige dir die Zahl der TNTs an die an einem Trace-Punkt aufgenommen wurden |
| `explosion` | Zeige dir nur die Trace-Punkte an, an denen TNT explodiert ist |
| `fuse` | Zeige dir an wie weit die TNTs an einem Trace Punkt von der Explosion weg waren |
| `ignite` | Ohne diese Flag zeigt der Tracer nur Trace-Punkte an, die nach der ersten Explosion entstanden sind. Mit dieser Flag werden alle Trace-Punkte angezeigt |
| `micromotion` | Zeige nur TNT an, die sich zu einem beliebigen Zeitpunkt mir einer Geschwindigkeit von kleiner 0.001 bewegt haben |
| `source` | Zeige dir nur Trace-Punkte an, bei denen TNT gezündet wurde |
| `build-destroy-only` | Zeige dir nur TNT an, die im Bau-Bereich explodiert sind |
| `testblock-destroy-only` | Zeige dir nur TNT an, die im Testblock-Bereich explodiert sind |
| `advanced` | Zeige dir die TNT-Zwischenberechnungspunkte auf den einzelnen Achsen an |
\
Du kannst dir auch nur einen Abschnitt des Traces mit
`\trace show from [Start-Zeit] to [End-Zeit] with [View-Flags]` anzeigen. Du kann auch `from` oder `to` weglassen, um dir den Trace von bzw. bis zu einem gewissen Zeitpunkt angucken.
## Traces löschen
Allgemein empfiehlt es sich Traces nicht ewig rumliegen zu lassen, da ein
`/trace show` schnell den PC zum erliegen bringen kann. Nutze `/trace
delete [Schuss-Nummer]` um einzelne Traces zu löschen und `/trace clear`
um alle Traces auf einem Plot zu löschen.
## Traces gemeinsam betrachten
Wenn man zusammen an einer Kanonen arbeitet kann es praktisch sein, die selben Traces, mit den selben View-Flags zu betrachten. Dies kannst du machen mit `/trace follow [Spieler]`. Den Follow kannst du mit `/trace unfollow` wieder aufheben. Zusätzlich kannst du alle Spieler auf einem Bau-Server mit `/trace broadcast` einladen deiner Trace-View zu folgen.

View File

@ -0,0 +1,50 @@
# Der Tracer
![](../../../images/tutorials/bausystem/tracer/tracer-cover.png)
Der Tracer ist eines der Kern-Tools unseres BauSystems. Durch ihn kannst du
dir komfortabel angucken wie sich die TNT deiner Kanonen Tick für Tick
verhalten. Dies tut der Tracer, indem er alle Daten der auf einem Plot
gezündeten TNTs aufnimmt und speichert. Du kannst dir dann verschiedene
Darstellungsformen der aufgezeichneten TNTs angucken.
## Den Tracer starten
Um die Schüsse deiner Kanone auf einem Plot aufzunehmen, musst du den Tracer
für diese Plot einschalten. Nutze hierfür `/trace auto`.
Nun da der Tracer an ist, wird für jeden Kanonenschuss ein Trace erstellt.
## Traces anzeigen und analysieren
Jetzt da der Tracer an ist und du einen Schuss abgegeben hast, kannst du dir
den Trace mit
`/trace show` anzeigen (Falls du mehrere Schüsse abgegeben hast kannst du
dir mit
`/trace isolate [Schuss-Nummer]` den Trace eines einzelnen Schusses anzeigen).
Zur Analyse eines Schusses stellen wir dir verschiedene **View-Flags** zur
Verfügung. Diese aktivierst du, indem du sie an den Trace-show Command
anhängst.
| Flag | Beschreibung |
| - | - |
| `count` | Zeige dir die Zahl der TNTs an die an einem Trace-Punkt aufgenommen wurden |
| `explosion` | Zeige dir nur die Trace-Punkte an, an denen TNT explodiert ist |
| `fuse` | Zeige dir an wie weit die TNTs an einem Trace Punkt von der Explosion weg waren |
| `ignite` | Ohne diese Flag zeigt der Tracer nur Trace-Punkte an, die nach der ersten Explosion entstanden sind. Mit dieser Flag werden alle Trace-Punkte angezeigt |
| `micromotion` | Zeige nur TNT an, die sich zu einem beliebigen Zeitpunkt mir einer Geschwindigkeit von kleiner 0.001 bewegt haben |
| `source` | Zeige dir nur Trace-Punkte an, bei denen TNT gezündet wurde |
| `build-destroy-only` | Zeige dir nur TNT an, die im Bau-Bereich explodiert sind |
| `testblock-destroy-only` | Zeige dir nur TNT an, die im Testblock-Bereich explodiert sind |
| `advanced` | Zeige dir die TNT-Zwischenberechnungspunkte auf den einzelnen Achsen an |
\
Du kannst dir auch nur einen Abschnitt des Traces mit
`\trace show from [Start-Zeit] to [End-Zeit] with [View-Flags]` anzeigen. Du kann auch `from` oder `to` weglassen, um dir den Trace von bzw. bis zu einem gewissen Zeitpunkt angucken.
## Traces löschen
Allgemein empfiehlt es sich Traces nicht ewig rumliegen zu lassen, da ein
`/trace show` schnell den PC zum erliegen bringen kann. Nutze `/trace
delete [Schuss-Nummer]` um einzelne Traces zu löschen und `/trace clear`
um alle Traces auf einem Plot zu löschen.
## Traces gemeinsam betrachten
Wenn man zusammen an einer Kanonen arbeitet kann es praktisch sein, die selben Traces, mit den selben View-Flags zu betrachten. Dies kannst du machen mit `/trace follow [Spieler]`. Den Follow kannst du mit `/trace unfollow` wieder aufheben. Zusätzlich kannst du alle Spieler auf einem Bau-Server mit `/trace broadcast` einladen deiner Trace-View zu folgen.

View File

@ -63,6 +63,7 @@
"announcements": "Ankündigungen",
"about": "Über Uns",
"downloads": "Downloads",
"tutorials": "Tutorials",
"faq": "FAQ"
},
"rules": {
@ -146,7 +147,8 @@
"winner": "Sieger",
"notPlayed": "Nicht gespielt",
"draw": "Unentschieden",
"points": "Punkte"
"points": "Punkte",
"team": "Team"
}
},
"blog": {

View File

@ -6,6 +6,7 @@
"announcements": "Announcements",
"about": "About",
"downloads": "Downloads",
"tutorials": "Tutorials",
"faq": "FAQ"
},
"rules": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

View File

@ -109,6 +109,7 @@ const ogImage = await getImage({
import GroupTable from "@components/GroupTable.svelte";
import {eventRepo} from "../../components/repo/event";
import type {ExtendedEvent} from "@type/event";
import {mount} from "svelte";
const eventMounts: Map<string, ((ev: ExtendedEvent) => void)[]> = new Map();
@ -119,7 +120,7 @@ const ogImage = await getImage({
}
const rows = Number.parseInt(this.dataset["rows"]!);
eventMounts.get(this.dataset["event"]!)!.push(ev => {
new FightTable({
mount(FightTable, {
target: this,
props: {
event: ev,
@ -138,7 +139,7 @@ const ogImage = await getImage({
}
const rows = Number.parseInt(this.dataset["rows"]!);
eventMounts.get(this.dataset["event"]!)!.push(ev => {
new GroupTable({
mount(GroupTable, {
target: this,
props: {
event: ev,

View File

@ -15,7 +15,7 @@ import {type Player} from "../components/types/data";
import PostComponent from "../components/PostComponent.astro";
import BackgroundImage from "../components/BackgroundImage.astro";
const teamMember: { [key: string]: Player[]} = await fetch("http://127.0.0.1:1337/data/team")
const teamMember: { [key: string]: Player[]} = await fetch("https://api.steamwar.de/data/team")
.then(value => value.json());
const posts = await getCollection("announcements", entry => entry.id.split("/")[0] === astroI18n.locale);

View File

@ -0,0 +1,19 @@
---
import { createGetStaticPaths } from "astro-i18n";
import { getCollection } from "astro:content";
const { tutorial } = Astro.props;
export const getStaticPaths = createGetStaticPaths(async () => {
const tutorials = await getCollection("tutorials");
return tutorials.map((entry) => ({
props: {
title: entry.data.title
},
params: {
title: entry.slug,
},
}));
});
---

View File

View File

@ -18,7 +18,7 @@
*/
table {
@apply w-full rounded-2xl shadow-lg overflow-clip;
@apply w-full overflow-clip;
:not(:has([data-no-head])) {
}