Pular para o conteúdo

Sistema de Placeholders

O sistema de placeholders permite que os leitores personalizem endereços IP, ASNs e outros valores específicos de implantação ao longo da documentação. Os autores escrevem tokens em seus arquivos Markdown; o navegador os substitui por valores fornecidos pelo usuário em tempo de execução.

Os tokens seguem o padrão regex:

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

Um token começa e termina com um x minúsculo e contém um identificador em maiúsculas. Por exemplo, xCUSTOMER_ASNx referencia o placeholder CUSTOMER_ASN.

O regex é definido em src/scripts/placeholder-dom.ts:

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

Todos os placeholders são declarados em src/data/placeholders.json. Cada entrada possui o seguinte formato:

{
"CUSTOMER_ASN": {
"type": "text",
"default": "64496",
"description": "Your public ASN (registered with ARIN/RIR)"
}
}
CampoObrigatórioDescrição
typesim"text" para entrada de texto livre, "dropdown" para menus de seleção
defaultsimValor inicial exibido antes de o leitor alterar qualquer coisa
descriptionsimRótulo exibido no formulário
optionsapenas para dropdownArray de valores permitidos

src/lib/placeholder-store.ts gerencia todo o estado dos placeholders.

Os valores são persistidos no localStorage sob a chave f5xc-placeholders. O store expõe quatro funções:

FunçãoPropósito
getDefaults()Retorna um mapa de cada chave de placeholder para seu valor default do JSON
loadValues()Lê do localStorage, recorre a getDefaults() como fallback
saveValues(values)Grava o mapa atual no localStorage
clearValues()Remove a entrada do localStorage

FIELD_GROUPS organiza as chaves dos placeholders em seções rotuladas para a interface do formulário:

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'] },
// ... mais grupos
];

Alguns valores são derivados da entrada do usuário em vez de inseridos diretamente. getComputedValues() calcula esses valores a partir de tabelas de consulta:

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

Dois placeholders computados são produzidos:

Chave ComputadaDerivado DeExemplo
PROTECTED_MASK_V4PROTECTED_CIDR_V4 via consulta em cidrToMask255.255.255.0
PROTECTED_PREFIX_V4PROTECTED_NET_V4 + PROTECTED_CIDR_V4 via cidrToShort192.0.2.0/24

getAllValues() mescla os valores inseridos pelo usuário com os valores computados, fornecendo um mapa completo para substituição.

emitChange() despacha um CustomEvent placeholder-change no document com o mapa completo de valores como detail:

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

Este evento aciona tanto as atualizações dos spans no DOM quanto a re-renderização dos diagramas Mermaid.

src/components/PlaceholderForm.tsx fornece a interface de edição.

  • Estado: useState inicializado a partir de loadValues()
  • Na montagem: useEffect chama emitChange() para acionar a substituição inicial no DOM
  • handleChange: Atualiza o estado do React, chama saveValues() e emitChange()
  • handleReset: Chama clearValues(), redefine o estado para getDefaults(), emite a mudança
  • Renderização: Itera sobre FIELD_GROUPS, renderizando um <fieldset> por grupo. Cada chave recebe um <input> (tipo text) ou <select> (tipo dropdown)
  • Layout: O formulário é envolvido em um elemento <details>, recolhido por padrão

src/components/PlaceholderFormWrapper.astro conecta o componente React à página Astro:

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

client:only="react" instrui o Astro a hidratar o componente puramente no cliente (sem SSR). A tag <script> importa o DOM walker para que ele seja executado em toda página que inclua este wrapper.

O wrapper também injeta CSS global para estilização do formulário (.ph-form-wrapper, .ph-grid, .ph-value, etc.).

src/scripts/placeholder-dom.ts gerencia a substituição de tokens no lado do cliente.

No carregamento da página, init() é executado:

  1. Seleciona .sl-markdown-content como raiz (recorre a document.body como fallback)
  2. Chama walkTextNodes(root, values) que utiliza document.createTreeWalker com NodeFilter.SHOW_TEXT
  3. Para cada nó de texto que corresponda ao regex do token, divide-o em um fragmento de documento contendo nós de texto simples e elementos <span data-ph="KEY" class="ph-value">
  4. Substitui o nó de texto original pelo fragmento

Após a caminhada, o DOM contém spans com atributos data-ph em vez de tokens brutos.

Quando o formulário emite um evento placeholder-change, updateSpans() é executado:

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

Isso evita percorrer a árvore novamente — atualiza diretamente o conteúdo de texto dos spans.

O script registra dois listeners:

EventoHandlerPropósito
placeholder-changehandleChangeAtualiza os spans e re-renderiza os diagramas Mermaid
astro:page-loadinitPercorre novamente o DOM após a navegação client-side do Astro
  1. Adicione a entrada no JSON em src/data/placeholders.json:

    "MY_NEW_VALUE": {
    "type": "text",
    "default": "example",
    "description": "Description shown in the form"
    }
  2. Adicione a chave a um grupo de campos em src/lib/placeholder-store.ts. Adicione-a ao array keys de um grupo existente ou crie um novo grupo em FIELD_GROUPS.

  3. Use o token no conteúdo: Escreva xMY_NEW_VALUEx em qualquer arquivo .mdx. O DOM walker o substituirá em tempo de execução.

Valores computados são derivados de outros placeholders. Para adicionar um:

  1. Adicione uma tabela de consulta (se necessário) em src/lib/placeholder-store.ts, seguindo o padrão de cidrToMask.

  2. Estenda getComputedValues() para incluir a nova chave 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, // adicione aqui
    };
    }
  3. Use xMY_COMPUTEDx no conteúdo como qualquer outro token. Valores computados não precisam de uma entrada em placeholders.json nem de um grupo de campos — eles são invisíveis para o formulário.