Remove unused components, change zod to v4 and cahnge a few character properties

This commit is contained in:
Clément Pons
2025-08-26 13:21:42 +02:00
parent 5387dc66c3
commit 80a94bee86
60 changed files with 170 additions and 2742 deletions

View File

@@ -13,7 +13,7 @@ const schemaList: Record<string, z.ZodObject<any> | null> = {
</script>
<script setup lang="ts">
import { z } from 'zod';
import { z } from 'zod/v4';
import { Icon } from '@iconify/vue/dist/iconify.js';
definePageMeta({

View File

@@ -7,6 +7,7 @@ import type { SpellConfig } from '~/types/character';
import type { CharacterConfig } from '~/types/character';
import { CharacterCompiler, defaultCharacter, elementTexts, spellTypeTexts } from '~/shared/character.util';
import { getText } from '~/shared/i18n';
import { fakeA } from '~/shared/proses';
const config = characterConfig as CharacterConfig;
@@ -16,7 +17,6 @@ const { add } = useToast();
const { data, status, error } = await useFetch(`/api/character/${id}`);
const compiler = new CharacterCompiler(data.value ?? defaultCharacter);
console.log(compiler);
const character = ref(compiler.compiled);
/*
text-light-red dark:text-dark-red border-light-red dark:border-dark-red bg-light-red dark:bg-dark-red
@@ -89,26 +89,26 @@ text-light-purple dark:text-dark-purple border-light-purple dark:border-dark-pur
<div class="flex flex-col">
<span class="text-lg font-semibold border-b border-light-30 dark:border-dark-30">Maitrise d'arme</span>
<div class="grid grid-cols-2 gap-x-3 gap-y-1">
<PreviewA v-if="character.mastery.strength + character.mastery.dexterity > 0" href="1. Règles/99. Annexes/4. Équipement#Les armes légères">Arme légère</PreviewA>
<PreviewA v-if="character.mastery.strength + character.mastery.dexterity > 0" href="1. Règles/99. Annexes/4. Équipement#Les armes de jet">Arme de jet</PreviewA>
<PreviewA v-if="character.mastery.strength + character.mastery.dexterity > 0" href="1. Règles/99. Annexes/4. Équipement#Les armes naturelles">Arme naturelle</PreviewA>
<PreviewA v-if="character.mastery.strength > 1" href="1. Règles/99. Annexes/4. Équipement#Les armes">Arme standard</PreviewA>
<PreviewA v-if="character.mastery.strength > 1" href="1. Règles/99. Annexes/4. Équipement#Les armes improvisées">Arme improvisée</PreviewA>
<PreviewA v-if="character.mastery.strength > 2" href="1. Règles/99. Annexes/4. Équipement#Les armes lourdes">Arme lourde</PreviewA>
<PreviewA v-if="character.mastery.strength > 3" href="1. Règles/99. Annexes/4. Équipement#Les armes à deux mains">Arme à deux mains</PreviewA>
<PreviewA v-if="character.mastery.dexterity > 0 && character.mastery.strength > 1" href="1. Règles/99. Annexes/4. Équipement#Les armes maniables">Arme maniable</PreviewA>
<PreviewA v-if="character.mastery.dexterity > 1 && character.mastery.strength > 1" href="1. Règles/99. Annexes/4. Équipement#Les armes à projectiles">Arme à projectiles</PreviewA>
<PreviewA v-if="character.mastery.dexterity > 1 && character.mastery.strength > 2" href="1. Règles/99. Annexes/4. Équipement#Les armes longues">Arme longue</PreviewA>
<PreviewA v-if="character.mastery.shield > 0" href="1. Règles/99. Annexes/4. Équipement#Les boucliers">Bouclier</PreviewA>
<PreviewA v-if="character.mastery.shield > 0 && character.mastery.strength > 3" href="1. Règles/99. Annexes/4. Équipement#Les boucliers à deux mains">Bouclier à deux mains</PreviewA>
<PreviewA v-if="character.mastery.strength + character.mastery.dexterity > 0" href="regles/annexes/equipement#Les armes légères" label="Arme légère" />
<PreviewA v-if="character.mastery.strength + character.mastery.dexterity > 0" href="regles/annexes/equipement#Les armes de jet" label="Arme de jet" />
<PreviewA v-if="character.mastery.strength + character.mastery.dexterity > 0" href="regles/annexes/equipement#Les armes naturelles" label="Arme naturelle" />
<PreviewA v-if="character.mastery.strength > 1" href="regles/annexes/equipement#Les armes" label="Arme standard" />
<PreviewA v-if="character.mastery.strength > 1" href="regles/annexes/equipement#Les armes improvisées" label="Arme improvisée" />
<PreviewA v-if="character.mastery.strength > 2" href="regles/annexes/equipement#Les armes lourdes" label="Arme lourde" />
<PreviewA v-if="character.mastery.strength > 3" href="regles/annexes/equipement#Les armes à deux mains" label="Arme à deux mains" />
<PreviewA v-if="character.mastery.dexterity > 0 && character.mastery.strength > 1" href="regles/annexes/equipement#Les armes maniables" label="Arme maniable" />
<PreviewA v-if="character.mastery.dexterity > 1 && character.mastery.strength > 1" href="regles/annexes/equipement#Les armes à projectiles" label="Arme à projectiles" />
<PreviewA v-if="character.mastery.dexterity > 1 && character.mastery.strength > 2" href="regles/annexes/equipement#Les armes longues" label="Arme longue" />
<PreviewA v-if="character.mastery.shield > 0" href="regles/annexes/equipement#Les boucliers" label="Bouclier" />
<PreviewA v-if="character.mastery.shield > 0 && character.mastery.strength > 3" href="regles/annexes/equipement#Les boucliers à deux mains" label="Bouclier à deux mains" />
</div>
</div>
<div v-if="character.mastery.armor > 0" class="flex flex-col">
<span class="text-lg font-semibold border-b border-light-30 dark:border-dark-30">Maitrise d'armure</span>
<div class="grid grid-cols-2 gap-x-3 gap-y-1">
<PreviewA v-if="character.mastery.armor > 0" href="1. Règles/99. Annexes/4. Équipement#Les armures légères">Armure légère</PreviewA>
<PreviewA v-if="character.mastery.armor > 1" href="1. Règles/99. Annexes/4. Équipement#Les armures">Armure standard</PreviewA>
<PreviewA v-if="character.mastery.armor > 2" href="1. Règles/99. Annexes/4. Équipement#Les armures lourdes">Armure lourde</PreviewA>
<PreviewA v-if="character.mastery.armor > 0" href="regles/annexes/equipement#Les armures légères" label="Armure légère" />
<PreviewA v-if="character.mastery.armor > 1" href="regles/annexes/equipement#Les armures" label="Armure standard" />
<PreviewA v-if="character.mastery.armor > 2" href="regles/annexes/equipement#Les armures lourdes" label="Armure lourde" />
</div>
</div>
<div class="flex flex-col">
@@ -137,22 +137,22 @@ text-light-purple dark:text-dark-purple border-light-purple dark:border-dark-pur
<div class="flex flex-col">
<span class="text-lg font-semibold">Actions</span>
<span class="text-sm text-light-70 dark:text-dark-70">Attaquer - Saisir - Faire chuter - Déplacer - Courir - Pas de coté - Lancer un sort - S'interposer - Se transformer - Utiliser un objet - Anticiper une action - Improviser</span>
<MarkdownRenderer :content="character.lists.action?.map(e => getText(e))?.join('\n')" />
<MarkdownRenderer :content="character.lists.action?.map(e => getText(e))?.join('\n')" :properties="{ tags: { a: fakeA } }" />
</div>
<div class="flex flex-col">
<span class="text-lg font-semibold">Réactions</span>
<span class="text-sm text-light-70 dark:text-dark-70">Parade - Esquive - Saisir une opportunité - Prendre en tenaille - Intercepter - Désarmer</span>
<MarkdownRenderer :content="character.lists.reaction?.map(e => getText(e))?.join('\n')" />
<MarkdownRenderer :content="character.lists.reaction?.map(e => getText(e))?.join('\n')" :properties="{ tags: { a: fakeA } }" />
</div>
<div class="flex flex-col">
<span class="text-lg font-semibold">Actions libre</span>
<span class="text-sm text-light-70 dark:text-dark-70">Analyser une situation - Communiquer</span>
<MarkdownRenderer :content="character.lists.freeaction?.map(e => getText(e))?.join('\n')" />
<MarkdownRenderer :content="character.lists.freeaction?.map(e => getText(e))?.join('\n')" :properties="{ tags: { a: fakeA } }" />
</div>
</div>
<div class="flex flex-col">
<span class="text-lg font-semibold">Aptitudes</span>
<MarkdownRenderer :content="character.lists.passive?.map(e => getText(e))?.map(e => `> ${e}`).join('\n\n')" />
<MarkdownRenderer :content="character.lists.passive?.map(e => getText(e))?.map(e => `> ${e}`).join('\n\n')" :properties="{ tags: { a: fakeA } }" />
</div>
</div>
</TabsContent>

View File

@@ -1,5 +1,4 @@
<script setup lang="ts">
import { Icon } from '@iconify/vue/dist/iconify.js';
import characterConfig from '#shared/character-config.json';
import type { CharacterConfig } from '~/types/character';
@@ -7,7 +6,6 @@ definePageMeta({
guestsGoesTo: '/user/login',
})
const { add } = useToast();
const { user } = useUserSession();
const { data: characters, error, status } = await useFetch(`/api/character`);
const config = characterConfig as CharacterConfig;

View File

@@ -23,54 +23,4 @@ onMounted(() => {
<Title>d[any] - Edition de données</Title>
</Head>
<div ref="container" class="flex flex-1 max-w-full flex-col gap-8 justify-start items-center px-8 w-full"></div>
<!-- <TabsRoot class="flex flex-1 max-w-full flex-col gap-8 justify-start items-center px-8 w-full" default-value="features">
<TabsList class="flex flex-row gap-4 self-center relative px-4">
<TabsIndicator class="absolute left-0 h-[3px] bottom-0 w-[--radix-tabs-indicator-size] translate-x-[--radix-tabs-indicator-position] transition-[width,transform] duration-300 bg-accent-blue"></TabsIndicator>
<TabsTrigger value="peoples" class="px-2 py-1 border-b border-transparent hover:border-accent-blue">Peuples ({{ config.peoples.length }})</TabsTrigger>
<TabsTrigger value="training" class="px-2 py-1 border-b border-transparent hover:border-accent-blue">Entrainement</TabsTrigger>
<TabsTrigger value="abilities" class="px-2 py-1 border-b border-transparent hover:border-accent-blue">Compétences ({{ Object.keys(config.abilities).length }})</TabsTrigger>
<TabsTrigger value="aspects" class="px-2 py-1 border-b border-transparent hover:border-accent-blue">Aspects ({{ config.aspects.length }})</TabsTrigger>
<TabsTrigger value="spells" class="px-2 py-1 border-b border-transparent hover:border-accent-blue">Sorts ({{ config.spells.length }})</TabsTrigger>
<TabsTrigger value="features" class="px-2 py-1 border-b border-transparent hover:border-accent-blue">Features ({{ Object.keys(config.features).length }})</TabsTrigger>
<Tooltip message="Copier le JSON" side="right"><Button icon @click="copy" class="p-2"><Icon icon="radix-icons:clipboard-copy" /></Button></Tooltip>
</TabsList>
<div class="flex flex-row outline-none max-w-full w-full relative overflow-hidden">
<div class="flex flex-1 outline-none max-w-full overflow-hidden">
<TabsContent value="peoples" class="outline-none flex gap-4 flex-col overflow-hidden">
<div class=""></div>
</TabsContent>
<TabsContent value="training" class="outline-none flex gap-4 flex-col overflow-hidden">
<div class="">
</div>
</TabsContent>
<TabsContent value="abilities" class="outline-none flex gap-4 flex-col overflow-hidden">
<div class=""></div>
</TabsContent>
<TabsContent value="aspects" class="outline-none flex gap-4 flex-col overflow-hidden">
<div class=""></div>
</TabsContent>
<TabsContent value="spells" class="outline-none flex gap-4 flex-col overflow-hidden">
<div class=""></div>
</TabsContent>
<TabsContent value="features" class="outline-none flex gap-4 flex-col overflow-hidden">
<div class="flex flex-col w-full gap-2 justify-end items-end relative">
<Button icon @click="createFeature"><Icon icon="radix-icons:plus" class="w-6 h-6" /></Button>
</div>
<div class="flex flex-col gap-2 overflow-x-hidden pe-2">
<div class="flex flex-row gap-2 w-full border-b border-light-35 dark:border-dark-35 pb-2" v-for="feature of config.features">
<div class="w-full flex flex-row px-4 gap-8 items-center">
<span class="font-mono">{{ feature.id }}</span>
<span class="truncate">{{ feature.description }}</span>
</div>
<div class="flex flex-row gap-2 items-center">
<Button icon @click="editFeature(feature.id)"><Icon icon="radix-icons:pencil-1" /></Button>
<Button icon @click="deleteFeature(feature.id)"><Icon icon="radix-icons:trash" /></Button>
</div>
</div>
</div>
</TabsContent>
</div>
</div>
</TabsRoot> -->
</template>

View File

@@ -95,193 +95,4 @@ onMounted(async () => {
onBeforeUnmount(() => {
editor?.unmount();
});
/* import { Icon } from '@iconify/vue/dist/iconify.js';
import type { Instruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/dist/types/tree-item';
import type { FileType, LocalContent, TreeItem } from '#shared/content.util';
import { DEFAULT_CONTENT, iconByType, Content, getPath } from '#shared/content.util';
import type { Preferences } from '~/types/general';
import { fakeA as proseA } from '#shared/proses';
import { parsePath } from '~/shared/general.util';
import type { CanvasContent } from '~/types/canvas';
export type TreeItemEditable = LocalContent &
{
parent?: string;
name?: string;
customPath?: boolean;
children?: TreeItemEditable[];
}
definePageMeta({
rights: ['admin', 'editor'],
layout: 'null',
});
const { user } = useUserSession();
const router = useRouter();
const open = ref(true), topOpen = ref(true);
const toaster = useToast();
const saveStatus = ref<'idle' | 'pending' | 'success' | 'error'>('idle');
let navigation = Content.tree as TreeItemEditable[];
const selected = ref<TreeItemEditable>();
const preferences = useCookie<Preferences>('preferences', { default: () => ({ markdown: { editing: 'split' }, canvas: { gridSnap: true, neighborSnap: true, spacing: 32 } }), watch: true, maxAge: 60*60*24*31 });
watch(selected, async (value, old) => {
if(selected.value)
{
if(!selected.value.content && selected.value.path)
{
selected.value = await Content.content(selected.value.path);
}
router.replace({ hash: '#' + selected.value!.path || getPath(selected.value!) });
}
else
{
router.replace({ hash: '' });
}
})
const debouncedSave = useDebounceFn(save, 60000, { maxWait: 180000 });
useShortcuts({
//meta_s: { usingInput: true, handler: () => save(), prevent: true },
meta_n: { usingInput: true, handler: () => add('markdown'), prevent: true },
meta_shift_n: { usingInput: true, handler: () => add('folder'), prevent: true },
meta_shift_z: { usingInput: true, handler: () => router.push({ name: 'explore-path', params: { path: 'index' } }), prevent: true }
})
function add(type: FileType): void
{
if(!navigation)
{
return;
}
const news = [...tree.search(navigation, 'title', 'Nouveau')].filter((e, i, a) => a.indexOf(e) === i);
const title = `Nouveau${news.length > 0 ? ' (' + news.length +')' : ''}`;
const item: TreeItemEditable = { navigable: true, private: false, parent: '', path: '', title: title, name: parsePath(title), type: type, order: 0, children: [], customPath: false, content: DEFAULT_CONTENT[type], owner: -1, timestamp: new Date(), visit: 0 };
if(!selected.value)
{
navigation = [...navigation, item];
}
else if(selected.value?.children)
{
item.parent = getPath(selected.value);
navigation = tree.insertChild(navigation, item.parent, item);
}
else
{
navigation = tree.insertAfter(navigation, getPath(selected.value), item);
}
}
function updateTree(instruction: Instruction, itemId: string, targetId: string) : TreeItemEditable[] | undefined {
if(!navigation)
return;
const item = tree.find(navigation, itemId);
const target = tree.find(navigation, targetId);
if(!item)
return;
if (instruction.type === 'reparent') {
const path = tree.getPathToItem({
current: navigation,
targetId: targetId,
});
if (!path) {
console.error(`missing ${path}`);
return;
}
const desiredId = path[instruction.desiredLevel];
let result = tree.remove(navigation, itemId);
result = tree.insertAfter(result, desiredId, item);
return result;
}
// the rest of the actions require you to drop on something else
if (itemId === targetId)
return navigation;
if (instruction.type === 'reorder-above') {
let result = tree.remove(navigation, itemId);
result = tree.insertBefore(result, targetId, item);
return result;
}
if (instruction.type === 'reorder-below') {
let result = tree.remove(navigation, itemId);
result = tree.insertAfter(result, targetId, item);
return result;
}
if (instruction.type === 'make-child') {
if(!target || target.type !== 'folder')
return;
let result = tree.remove(navigation, itemId);
result = tree.insertChild(result, targetId, item);
rebuildPath([item], targetId);
return result;
}
return navigation;
}
function transform(items: TreeItem[] | undefined): TreeItemEditable[] | undefined
{
return items?.map(e => ({
...e,
parent: e.path.substring(0, e.path.lastIndexOf('/')),
name: e.path.substring(e.path.lastIndexOf('/') + 1),
customPath: e.path.substring(e.path.lastIndexOf('/') + 1) !== parsePath(e.title),
children: transform(e.children)
})) as TreeItemEditable[] | undefined;
}
function flatten(items: TreeItemEditable[] | undefined): TreeItemEditable[]
{
return items?.flatMap(e => [e, ...flatten(e.children)]) ?? [];
}
function drop(instruction: Instruction, itemId: string, targetId: string)
{
navigation = updateTree(instruction, itemId, targetId) ?? navigation ?? [];
}
function rebuildPath(tree: TreeItemEditable[] | null | undefined, parentPath: string)
{
if(!tree)
return;
tree.forEach(e => {
e.parent = parentPath;
rebuildPath(e.children, getPath(e));
});
}
function save()
{
if(selected.value && selected.value.content)
{
selected.value.path = getPath(selected.value);
Content.save(selected.value);
}
}
const defaultExpanded = computed(() => {
if(router.currentRoute.value.hash)
{
const split = router.currentRoute.value.hash.substring(1).split('/');
split.forEach((e, i) => { if(i !== 0) split[i] = split[i - 1] + '/' + e });
return split;
}
});
/*watch(router.currentRoute, (value) => {
if(value && value.hash && navigation)
selected.value = tree.find(navigation, value.hash.substring(1));
else
selected.value = undefined;
}, { immediate: true }); */
</script>

View File

@@ -18,7 +18,7 @@
</template>
<script setup lang="ts">
import type { ZodError } from 'zod';
import type { ZodError } from 'zod/v4';
import { schema, type Login } from '~/schemas/login';
import { Icon } from '@iconify/vue/dist/iconify.js';

View File

@@ -27,7 +27,7 @@
</template>
<script setup lang="ts">
import { ZodError } from 'zod';
import { ZodError } from 'zod/v4';
import { schema, type Registration } from '~/schemas/registration';
import { Icon } from '@iconify/vue/dist/iconify.js';