More things

This commit is contained in:
2026-07-18 19:03:30 +02:00
parent 55a6bf7636
commit 39e2583974
2 changed files with 123 additions and 16 deletions

View File

@@ -4,7 +4,7 @@ import { useTreeState } from '~/composables/useTreeState'
defineProps({ nodes: Array, depth: { type: Number, default: 0 } }) defineProps({ nodes: Array, depth: { type: Number, default: 0 } })
const router = useRouter() const router = useRouter()
const { collapsed } = useTreeState() // ← shared state, not local ref const { collapsed } = useTreeState()
function getTitle(node) { function getTitle(node) {
if (node.title === 'index') return 'Index' if (node.title === 'index') return 'Index'
@@ -19,12 +19,37 @@ function navigate(path) {
function toggle(key) { function toggle(key) {
collapsed.value[key] = !collapsed.value[key] collapsed.value[key] = !collapsed.value[key]
} }
/**
* If the folder contains a child whose path/title matches the folder itself
* (e.g. places/ has a child at places/places.md), return that child.
* Otherwise return null.
*/
function getSelfTitledChild(node) {
if (!node.children?.length) return null
const folderSlug = node.path?.split('/').pop() ?? node.title
return node.children.find((child) => {
const childSlug = child.path?.split('/').pop() ?? child.title
return childSlug === folderSlug
}) ?? null
}
/**
* Children to actually render — excludes the self-titled child so it
* doesn't appear twice (once as the clickable folder label, once in the list).
*/
function visibleChildren(node) {
const selfTitled = getSelfTitledChild(node)
if (!selfTitled) return node.children
return node.children.filter((c) => c !== selfTitled)
}
</script> </script>
<template> <template>
<ul :style="{ paddingLeft: depth ? '1rem' : '0' }"> <ul :style="{ paddingLeft: depth ? '1rem' : '0' }">
<li v-for="node in nodes" :key="node.path ?? node.title"> <li v-for="node in nodes" :key="node.path ?? node.title">
<!-- Leaf node (no children) -->
<template v-if="!node.children?.length"> <template v-if="!node.children?.length">
<a v-if="node.path" :href="node.path" @click.prevent="navigate(node.path)"> <a v-if="node.path" :href="node.path" @click.prevent="navigate(node.path)">
{{ getTitle(node) }} {{ getTitle(node) }}
@@ -32,14 +57,33 @@ function toggle(key) {
<span v-else>{{ getTitle(node) }}</span> <span v-else>{{ getTitle(node) }}</span>
</template> </template>
<!-- Folder node -->
<template v-else> <template v-else>
<button class="folder-toggle" @click="toggle(node.path ?? node.title)"> <div class="folder-row">
<!-- Chevron toggle (always present) -->
<button
class="folder-toggle"
@click="toggle(node.path ?? node.title)"
:aria-expanded="!collapsed[node.path ?? node.title]"
>
<span class="chevron" :class="{ open: !collapsed[node.path ?? node.title] }"></span> <span class="chevron" :class="{ open: !collapsed[node.path ?? node.title] }"></span>
{{ getTitle(node) }}
</button> </button>
<!-- Folder label: clickable link if there's a self-titled child, plain text otherwise -->
<a
v-if="getSelfTitledChild(node)"
class="folder-label folder-label--link"
:href="getSelfTitledChild(node).path"
@click.prevent="navigate(getSelfTitledChild(node).path)"
>
{{ getTitle(node) }}
</a>
<span v-else class="folder-label">{{ getTitle(node) }}</span>
</div>
<TreeNode <TreeNode
v-if="!collapsed[node.path ?? node.title]" v-if="!collapsed[node.path ?? node.title]"
:nodes="node.children" :nodes="visibleChildren(node)"
:depth="depth + 1" :depth="depth + 1"
/> />
</template> </template>
@@ -62,24 +106,44 @@ a {
color: var(--text-link); color: var(--text-link);
} }
.folder-row {
display: flex;
align-items: center;
gap: 0.2rem;
}
.folder-toggle { .folder-toggle {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.35rem; flex-shrink: 0;
background: none; background: none;
border: none; border: none;
padding: 0; padding: 0;
cursor: pointer; cursor: pointer;
color: inherit; color: inherit;
font: inherit; font: inherit;
width: 100%; line-height: 1;
text-align: left;
&:hover { &:hover {
color: var(--text-link); color: var(--text-link);
} }
} }
.folder-label {
flex: 1;
cursor: default;
&--link {
color: var(--text-link);
cursor: pointer;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
.chevron { .chevron {
display: inline-block; display: inline-block;
font-style: normal; font-style: normal;

View File

@@ -2,10 +2,53 @@
visible: true visible: true
--- ---
# Millor campana # Como asesinar a tu dragón
Hola que tal! Hauria de funcionar Hola! En esta web encontraréis nota y info adicional de la campaña que iré modificando conforme vaya avanzando. Le he puesto este horripilante nombre de momento porque no se me ocurre ninguno otro.
Test Es como una wiki, a la izquierda teneis diferentes notas que también tienen links entre ellas.
<a href="/razas" class="dead-link" data-hidden="Razas">Razas</a> Y que más cualquier cosa me decis por Whatsapp o por Discord si quereis.
## Links importantes
Os dejo aqui debajo links importantes. La campaña la haremos en Foundry. Para los jugadores no es muy complicado de usar comparado con otras webs como Nivel20 o Roll20.
- **Foundry**: [https://foundry.aranroig.com](https://foundry.aranroig.com)
- **Esta web**: [https://caatd.rol.aranroig.com](https://caatd.rol.aranroig.com)
# FAQ
> Os aconsejo encarecidamente leer esto porfa 🙏
## ¿Qué sistema?
DnD 5E
## ¿Por dónde empiezo?
Aquí: [0 - Prologo](/diario/0-prologo) hay escrito en que situación se encontrarian vuestros personajes en este preciso instante.
El resto de cosas es todo worldbuilding que podéis leer si quereis más immersion. Podeis empezar a leer por [Hibolia](/hibolia/hibolia). De momento aquí hay conocimiento general que cualquier persona dentro del mundo sabe. Mi idea es que a medida que vayais descubriendo cosas vaya publicando y escribiendo más cosas aquí.
Si alguno de vuestros personajes tuviese el rol de ir escribiendo o documentando las aventuras que va haciendo siguiendo un diario o algo así pues seria este, se podría adaptar y creo que molaria así como idea.
## ¿Qué días hay campaña?
Sábados o domingos dependiendo de disponibilidad cada semana sesión de 2h a 4h aproximadamente si a todo el mundo va bien se puede negociar.
## Hola hago ingeniería informática ¿Cómo te has montado esto?
Esto es un vault de Obsidian (archivos de markdown con mods) dentro de un repositorio de git. En un server self-hosted de Gitea ([https://git.aranroig.com](https://git.aranroig.com)) tengo una acción que coge un frontend hecho con Nuxt Pages que es Nuxt pero con un plugin raro de generar webs a partir de markdown. Al hacer push desde un plugin que integra Git con Obsidian, se hace una acción al servidor de gitea que entre muchas cosas llama a un script vibecodeado que transforma las notas a el formato de Nuxt Pages y entonces se compila todo un frontend que luego se hace deploy también en el mini datacenter de mi casa.
El código fuente de todo esto está [aquí](https://git.aranroig.com/Syndria98/dnd-vault-template)
## Me he encontrado un link de color gris y me da mucho miedo ¿Qué es?
Un link gris como <a href="/este-link-de-aqu" class="dead-link" data-missing="Este link de aquí">Este link de aquí</a> significa una referencia a un concepto, sitio, lugar o cosa en general que aún no está escrito
## ¿Eres buen DM?
No :D