Ir al contenido

Sistema de Marcadores de Posición

El sistema de marcadores de posición permite a los lectores personalizar direcciones IP, ASNs y otros valores específicos de despliegue a lo largo de la documentación. Los autores escriben tokens en su Markdown; el navegador los reemplaza con valores proporcionados por el usuario en tiempo de ejecución.

Los tokens siguen el patrón regex:

x([A-Z][A-Z0-9_]+)x

Un token comienza y termina con una x minúscula y contiene un identificador en mayúsculas. Por ejemplo, xCUSTOMER_ASNx hace referencia al marcador de posición CUSTOMER_ASN.

La expresión regular está definida en src/scripts/placeholder-dom.ts:

const PH_REGEX = /x([A-Z][A-Z0-9_]+)x/g;

Todos los marcadores de posición se declaran en src/data/placeholders.json. Cada entrada tiene esta estructura:

{
"CUSTOMER_ASN": {
"type": "text",
"default": "64496",
"description": "Your public ASN (registered with ARIN/RIR)"
}
}
CampoRequeridoDescripción
type"text" para entrada libre, "dropdown" para menús de selección
defaultValor inicial mostrado antes de que el lector realice cambios
descriptionEtiqueta mostrada en el formulario
optionssolo para dropdownArray de valores permitidos

src/lib/placeholder-store.ts maneja todo el estado de los marcadores de posición.

Los valores se persisten en localStorage bajo la clave f5xc-placeholders. El almacén expone cuatro funciones:

FunciónPropósito
getDefaults()Devuelve un mapa de cada clave de marcador de posición con su valor default del JSON
loadValues()Lee desde localStorage, recurre a getDefaults() como respaldo
saveValues(values)Escribe el mapa actual en localStorage
clearValues()Elimina la entrada de localStorage

FIELD_GROUPS organiza las claves de marcadores de posición en secciones etiquetadas para la interfaz del formulario:

export const FIELD_GROUPS: FieldGroup[] = [
{ label: 'Data Center & Scrubbing Centers', keys: ['DC_NAME', 'CENTER_1', 'CENTER_2'] },
{ label: 'Protected Prefixes', keys: ['PROTECTED_CIDR_V4', 'PROTECTED_NET_V4', ...] },
{ label: 'BGP', keys: ['CUSTOMER_ASN', 'F5_XC_ASN', 'BGP_PASSWORD'] },
// ... más grupos
];

Algunos valores se derivan de la entrada del usuario en lugar de ingresarse directamente. getComputedValues() los calcula a partir de tablas de búsqueda:

const cidrToMask: Record<string, string> = {
'/24 (256 IPs)': '255.255.255.0',
'/23 (512 IPs)': '255.255.254.0',
// ...
};

Se producen dos marcadores de posición calculados:

Clave CalculadaDerivado DeEjemplo
PROTECTED_MASK_V4PROTECTED_CIDR_V4 mediante búsqueda en cidrToMask255.255.255.0
PROTECTED_PREFIX_V4PROTECTED_NET_V4 + PROTECTED_CIDR_V4 mediante cidrToShort192.0.2.0/24

getAllValues() fusiona los valores ingresados por el usuario con los valores calculados, proporcionando un mapa completo para la sustitución.

emitChange() despacha un CustomEvent placeholder-change en document con el mapa completo de valores como detail:

export function emitChange(values: Record<string, string>) {
document.dispatchEvent(
new CustomEvent('placeholder-change', { detail: getAllValues(values) }),
);
}

Este evento impulsa tanto las actualizaciones de los spans en el DOM como el re-renderizado de Mermaid.

src/components/PlaceholderForm.tsx proporciona la interfaz de edición.

  • Estado: useState inicializado desde loadValues()
  • Al montar: useEffect llama a emitChange() para activar la sustitución inicial del DOM
  • handleChange: Actualiza el estado de React, llama a saveValues() y emitChange()
  • handleReset: Llama a clearValues(), restablece el estado a getDefaults(), emite el cambio
  • Renderizado: Itera FIELD_GROUPS, renderizando un <fieldset> por grupo. Cada clave obtiene un <input> (tipo text) o un <select> (tipo dropdown)
  • Diseño: El formulario está envuelto en un elemento <details>, colapsado por defecto

src/components/PlaceholderFormWrapper.astro conecta el componente React con la página Astro:

<PlaceholderForm client:only="react" />
<script>
import '../scripts/placeholder-dom.ts';
</script>

client:only="react" le indica a Astro que hidrate el componente puramente en el cliente (sin SSR). La etiqueta <script> importa el recorredor del DOM para que se ejecute en cada página que incluya este envoltorio.

El envoltorio también inyecta CSS global para el estilo del formulario (.ph-form-wrapper, .ph-grid, .ph-value, etc.).

src/scripts/placeholder-dom.ts maneja el reemplazo de tokens del lado del cliente.

Al cargar la página, init() se ejecuta:

  1. Selecciona .sl-markdown-content como raíz (recurre a document.body como respaldo)
  2. Llama a walkTextNodes(root, values) que utiliza document.createTreeWalker con NodeFilter.SHOW_TEXT
  3. Para cada nodo de texto que coincida con la expresión regular del token, lo divide en un fragmento de documento con nodos de texto plano y elementos <span data-ph="KEY" class="ph-value">
  4. Reemplaza el nodo de texto original con el fragmento

Después del recorrido, el DOM contiene spans con atributos data-ph en lugar de tokens sin procesar.

Cuando el formulario emite un evento placeholder-change, updateSpans() se ejecuta:

document.querySelectorAll<HTMLSpanElement>('span[data-ph]').forEach((span) => {
const name = span.getAttribute('data-ph')!;
if (values[name] !== undefined) {
span.textContent = values[name];
}
});

Esto evita recorrer el árbol nuevamente — actualiza directamente el contenido de texto del span.

El script registra dos escuchadores:

EventoManejadorPropósito
placeholder-changehandleChangeActualiza los spans y re-renderiza los diagramas Mermaid
astro:page-loadinitRecorre el DOM nuevamente después de la navegación del lado del cliente de Astro

Cómo: Agregar un Nuevo Marcador de Posición

Sección titulada «Cómo: Agregar un Nuevo Marcador de Posición»
  1. Agregar la entrada JSON en src/data/placeholders.json:

    "MY_NEW_VALUE": {
    "type": "text",
    "default": "example",
    "description": "Description shown in the form"
    }
  2. Agregar la clave a un grupo de campos en src/lib/placeholder-store.ts. Ya sea agregándola al array keys de un grupo existente o creando un nuevo grupo en FIELD_GROUPS.

  3. Usar el token en el contenido: Escriba xMY_NEW_VALUEx en cualquier archivo .mdx. El recorredor del DOM lo reemplazará en tiempo de ejecución.

Los valores calculados se derivan de otros marcadores de posición. Para agregar uno:

  1. Agregue una tabla de búsqueda (si es necesario) en src/lib/placeholder-store.ts, siguiendo el patrón de cidrToMask.

  2. Extienda getComputedValues() para incluir la nueva clave derivada:

    export function getComputedValues(values: Record<string, string>): Record<string, string> {
    // ... lógica existente
    return {
    PROTECTED_MASK_V4: mask,
    PROTECTED_PREFIX_V4: `${net}${short}`,
    MY_COMPUTED: derivedValue, // agregar aquí
    };
    }
  3. Use xMY_COMPUTEDx en el contenido como cualquier otro token. Los valores calculados no necesitan una entrada en placeholders.json ni un grupo de campos — son invisibles para el formulario.