InícioDocs para devsArquitetura do frontend
Referência do projeto 46 min de leitura

Arquitetura do frontend

Jinja2, Datastar, JavaScript nativo, PixiJS, estilos e preparação de assets.

Índice da documentação · English · Referência da API

A aplicação renderiza HTML Jinja2 no servidor, usa Datastar para sinais reativos e atualizações de fragmentos e executa controladores JavaScript nos espaços interativos. O tabuleiro usa PixiJS e possui pipeline próprio de fontes/build. Não existe um build único de SPA para toda a aplicação.

Acompanhe a página pelo template

base.html carrega CSS compartilhado, Datastar distribuído localmente e gravewright_modules/frontend-api.js, que cria window.gravewright. O ambiente /inside e a mesa de jogo estendem essa estrutura. table/page.html compõe templates e carrega controladores de mesa, mapa, documentos, atores, dados e módulos.

Áreas úteis para começar:

LocalFinalidade
gravewright/web/jinja2/Estrutura compartilhada, páginas Inside e HTML reutilizável
gravewright/accounts/jinja2/Templates de autenticação e configuração inicial da conta
gravewright/web/static/gravewright_web/css/Variáveis visuais, estilos base e componentes
gravewright/web/static/gravewright_web/inside/Controladores/estilos de contas, campanhas e administração
gravewright/web/static/gravewright_web/windows.jsJanelas móveis e redimensionáveis
gravewright/table/static/gravewright_table/realtime.jsSocket, assinaturas, fragmentos autenticados e eventos
gravewright/modules/static/gravewright_modules/API pública de navegador, runtime de módulos, transporte e ciclo de vida
gravewright/maps/frontend/Fontes editáveis de tabuleiro, paredes, tokens, iluminação, seleção e ferramentas
gravewright/maps/static/gravewright_maps/workspace.jsIntegração do espaço de mapas
gravewright/pdf_system/frontend/Fontes editáveis de mapeamento/controlador PDF
jinja2/, static/ e messages.json de cada appInterface, estilos e textos do domínio

IDs e atributos data-* dos templates conectam controladores nativos e superfícies de extensão. Alterá-los pode afetar o controlador, sinais Datastar e montagem de módulos. Confira os três ao renomear seletores ou substituir painéis inteiros.

Área do Map Builder

GET /game/<campaign_id>/map-builder renderiza maps/builder.html por maps/builder.py. A rota confere o vínculo atual de mestre e valida a consulta opcional folder contra a campanha. O seletor de nova cena usa navegação normal na mesma aba; o builder usa a estrutura base com controladores próprios em static/gravewright_maps/builder/, sem montar o runtime da mesa, assinaturas de cena ou a área de jogo da mesa. Os efeitos nativos usam a prévia dedicada descrita abaixo.

O diretório de cenas da mesa lê o campo opcional builderProjectId da projeção de mestre em maps.services.state(). Uma consulta de IDs limitada à campanha resolve o vínculo atual do projeto com a cena; projeções de outros papéis não incluem esse campo. A cena vinculada mantém a linha compacta e o mesmo ícone de mapa, colorido com --danger, e acrescenta Editar com MapBuilder junto da edição normal no menu de contexto. Shift+F10 ou a tecla de menu de contexto abre o menu da cena focada. O menu usa o documento do diretório, inclusive em janelas destacadas; a ação abre o editor na aba principal do jogo. O endpoint do projeto confere novamente o papel de mestre e a campanha ao carregar.

A entrada é uma operação de leitura. O workspace.js do Estúdio compõe os controladores do editor, a navegação e os gestos de desenho. Seus módulos vizinhos, servidos diretamente, possuem estas responsabilidades:

Módulo em static/gravewright_maps/builder/Responsabilidade
project-persistence.jsRecuperação/armazenamento de rascunhos, HTTP/CSRF, identidade dos recibos e fila de salvamento
workspace-library.jsCatálogo de assets da campanha, escolha de pincéis e controles de material
workspace-import.jsModais de importação/edição de assets e ciclo de vida dos listeners
inspector-controller.jsCampos compartilhados da seleção, modais por seção e configuração de regiões/tochas
object-menu.jsMenu de contexto dos objetos, navegação por teclado e submenu de modelos
interactive-presets.jsBlueprints genéricos configuráveis e modal do catálogo de objetos
layer-controller.jsControles, conteúdo e seleção de camadas e listeners de arraste
canvas-renderer.jsArte SVG, marcações de seleção e caches descartáveis de terreno/parede

Os controladores recebem estado e callbacks explícitos, sem importar o workspace nem compartilhar globais. Os testes de persistência importam sua factory pública, sem extrair texto de funções do workspace.js. Um salvamento cujo dono foi descartado não sobrescreve um rascunho recuperado ao receber confirmação tardia. model.js possui a geometria em células e o histórico de desfazer, e viewport.js possui a câmera SVG independente. Listeners, captura de ponteiro, requisições e observação de tamanho terminam em pagehide; a restauração pelo histórico remonta o editor. builder_services.py possui as revisões de projeto por campanha, recibos de repetição, validação e geração e atualização de cenas. Consulte Map Builder para o formato do projeto, a geometria nativa e os limites da imagem.

newDocument() cria uma camada genérica vazia com um UUID novo; o controlador traduz seu nome inicial Camada 1. Não cria camadas por tipo de objeto. A normalização da versão 3 preserva as camadas originais, inclusive uma lista vazia após remover a última camada vazia. O servidor aceita essa lista apenas sem objetos, e a geração do mapa vazio renderiza somente o fundo. Projetos de versões 1/2 mantêm a conversão existente para a camada Base.

O seletor de modo no cabeçalho respeita editingMode: "tiles" | "paint" opcional; a ausência usa Tiles sem mudar o projeto. Tiles e Pintura de terreno compartilham camadas, objetos nativos, câmera e histórico. Cor/opacidade existentes em floor.paint continuam renderizadas independentemente pelo SVG e pelo raster, multiplicando cobertura da forma, transparência da textura e opacidade. Escolher o novo pincel de terreno não reinterpreta esses estilos anteriores nem converte a geometria existente. O chão mantém seus contratos de camada e transformação. SVG e Pillow usam antialiasing diferente em bordas coincidentes de contorno e recorte; comparações de pixels mantêm o interior exato e isolam essa faixa da borda.

floor-painter.js controla gestos temporários de chão e aplica uma edição completa por vez. model.js fornece a área percorrida pelo pincel, preenchimento por família, captura de material e recorte atômico do chão. Fragmentos da prévia mantêm UUIDs estáveis; gestos sem alteração não ocupam etapas de desfazer. floor-geometry.js fornece contornos contínuos e recortes com o encaixe desligado, seleção pela forma real e limites de complexidade do projeto e do raster. O servidor valida o mesmo contrato opcional de forma do chão e gera a máscara da união dos contornos menos os recortes, com antialiasing limitado. layer-model.js conta o conteúdo das camadas e o move ou mescla preservando identidades dos objetos e ordem de pintura. Duplicar uma camada reutiliza a duplicação atômica de objetos com deslocamento exato de uma célula em cada eixo, novos IDs e referências internas de Objetos Inteligentes remapeadas. Limites do mapa, sobreposição global de portas e limites do projeto podem recusar a cópia inteira. O isolamento é um filtro do editor; a visibilidade do projeto e a geração nativa mantêm sua semântica existente. Os controles de desenho ficam na aba Ferramentas do painel auxiliar, deixando o espaço do canvas disponível quando o painel está fechado.

Todos os tipos de elemento aceitam name autoral de até 120 caracteres. Nomes opcionais continuam ausentes nos elementos antigos sem nome; regiões e shaders mantêm nomes obrigatórios. O inspetor usa o rótulo Objeto vazio nos controladores antigos sem nome. element_names.py valida tipo, espaços, tamanho e NUL no servidor. Edições nativas, geração e atualização vinculada preservam os rótulos sem trocar UUIDs ou alvos do Blueprint. Chão estático continua na imagem rasterizada; superfícies dinâmicas levam o nome da origem para a imagem nativa. O inspetor de Seleção possui um campo de nome por elemento selecionado, e camadas, catálogos de comportamento e escolhas de luz/porta priorizam os nomes definidos pelo autor.

A faixa persistente de ferramentas da biblioteca usa data-builder-dock-tool nos controles adicionais; os seletores das ferramentas do painel continuam distintos. Trocar de ferramenta aplica a propriedade em foco antes de devolver o foco ao canvas. A faixa tem rolagem própria em telas estreitas e mantém seu layout ao substituir os cards de imagens pelos de luzes/efeitos nativos.

O botão Painéis abre as seis abas vinculadas do painel em uma grade de três colunas. Cada aba mantém sua posição de rolagem; o cabeçalho e as abas permanecem visíveis acima do conteúdo rolável. Setas, Home e End navegam pelas abas, e fechar o painel devolve o foco a quem o abriu. Os ajustes do pincel ativo vêm antes da paleta compacta de ferramentas, com a ajuda detalhada em uma seção expansível. Navegar ou rolar os painéis não altera o projeto salvo nem seu histórico.

A repintura Tiles compara o material de chão visível por cima antes de substituir a cobertura. Uma célula igual ou um traço livre inteiramente igual preserva os registros de origem, sem sortear novamente variações válidas da família nem adicionar histórico. A cobertura considera a união de vários pisos e seus recortes; outro material acima deles impede a correspondência. Gestos com encaixe filtram cada célula igual individualmente. Gestos livres mistos substituem toda sua área, preservando a geometria fora dela. A prova de cobertura tem um orçamento limitado de trabalho geométrico e recusa a operação inteira quando ele é excedido. A Pintura de terreno usa sua própria representação de pincel radial em vez da composição uniforme anterior de floor.paint.

Pintura de terreno no Map Builder

terrain-painter.js controla um gesto circular temporário, carimbos espaçados por distância e acúmulo parado a cada 50 ms. Aplica uma entrada floor.brush por gesto concluído e encerra o timer ao cancelar, concluir ou descartar. O workspace possui a captura de ponteiro, tamanho compartilhado, controles de modo, histórico e renderização; trocar de ferramenta ou modo cancela o traço ativo. Tiles continua usando floor-painter.js e sua semântica de substituição e gestos sem alteração.

terrain-brush.js e maps/terrain_brush.py calculam o mesmo kernel radial nos centros absolutos dos pixels. A dureza define um centro uniforme com queda linear até o raio; a transparência dos carimbos acumula com arredondamento inteiro limitado na composição. A origem guarda pontos normalizados locais, raios horizontal/vertical, dureza e operação paint/erase na caixa do chão. A textura mantém a fase e a rotação existentes no espaço do mapa; o pincel fornece a cobertura.

Uma entrada de borracha aplica destination-out somente aos pisos anteriores da mesma camada. O SVG envolve a camada acumulada na máscara; o Pillow mantém uma superfície RGBA separada para a camada. Pintura posterior fica fora do alcance da borracha anterior. Mesclar uma camada superior com borrachas sobre outra inferior com pisos é recusado, mantendo esse isolamento. terrain-render.js guarda imagens temporárias de máscara pela assinatura da origem e libera o cache na limpeza/descarte; elas não são assets de imagem da campanha. Limites restringem carimbos por traço/projeto e o trabalho real de pixels por carimbo antes da alocação do raster, recusando operações grandes demais de forma atômica.

As referências oficiais TerrainBrush do Dungeondraft e Terrain orientaram a separação entre tamanho/intensidade do pincel e superfície de terreno. O Gravewright implementa e salva seu próprio contrato de pincel radial.

Shift e arrastar com o botão esquerdo redimensionam o pincel de chão usando a distância horizontal na tela (40 pixels por célula). A guia ancorada acompanha o encaixe Tiles ou o raio contínuo do terreno. Concluir atualiza apenas preferências do editor; Escape restaura o tamanho anterior sem gravar origem ou histórico.

native-effects.js adapta o builder aos mesmos catálogos de luz, partículas e shaders da mesa. O bundle pequeno builder-catalog.js fornece predefinições sem iniciar WebGL; builder-native.js é carregado quando há fontes visíveis. As entradas editáveis são frontend/native/builder-catalog.js e frontend/native/builder-preview.js. A última reutiliza PixiBoardRenderer e SceneLayers numa camada transparente sincronizada com a câmera SVG e a geometria das paredes. Camadas ocultas/vazias, página oculta e descarte liberam recursos de renderização e animação. Os efeitos nativos ficam separados do raster gerado.

frontend/native/builder-editors.js hospeda os mesmos widgets LightEditor e EffectEditor usados no jogo sobre um rascunho separado do builder. Seu adaptador builder-editor-model.js converte os nomes dos raios de luz e resolve referências compactas de presets para editar GLSL sem expandir uma origem inalterada. Os rodapés transacionais dos widgets ficam ativos somente nesse host; o salvamento automático do jogo continua disponível em seu host normal. Validação e prévia do rascunho nunca chamam comandos de cena. Salvar devolve um candidato ao workspace.js, que verifica se o objeto permite edição e aplica uma etapa ao histórico do projeto. Cancelar/fechar libera timers da prévia, validação de shader e qualquer prompt interno, preservando projeto e cena ativa.

static/gravewright_maps/wall-editor.js e o template wall_editor.html compartilham a janela de configuração com o controlador de paredes da mesa. Seu host no builder grava os ajustes nativos opcionais de aparência, canais, elevação e descoberta na parede/porta de origem. As janelas de comportamento de Objetos Inteligentes de portas e alavancas continuam separadas da configuração da barreira física.

builder/torch-model.js vincula uma decoração às luzes e efeitos nativos do projeto. Mecanismo e chama são independentes; a prévia aplica o quarto de volta e controla os componentes ativos sem reescrever coordenadas de autoria. model.js expande a seleção composta uma vez nas transformações e cópias; object-removal.js concilia vínculos e referências de luz dos Blueprints de forma atômica. Botões de componente reutilizam builder-editors.js e conservam a seleção da tocha. Na mesa, lever-controls.js atende alavancas e tochas; image-components.js projeta os objetos vinculados nas prévias de imagem e seleção sem recriar recursos da GPU. O domínio torches.py valida propriedade na cena, aplica transformações e estado da chama e mantém as operações do conjunto nas transações dos comandos de mapa.

Os nós V2 all/any são valores booleanos puros com entradas a–d e saída result. Entradas conectadas são avaliadas em ordem fixa sem interrupção antecipada; menos de duas retorna Falso. Valores compartilhados são memorizados somente no evento atual. Python e JavaScript validam tipos booleanos, uma origem por entrada e limites de ciclo/tamanho. O catálogo agrupa os nós em Lógica e o cartão mostra a quantidade de conexões; simulação e registro do mestre mostram os resultados avaliados.

builder/region-model.js cria retângulos nomeados em células e a predefinição explícita de placa ligada à porta. Regiões participam da seleção, camadas, cópias e histórico do projeto, mas não da imagem ou prévia do jogador. regions.py deriva a ocupação privada dos comandos validados de token na mesma transação; zones.py estabelece a ocupação sem eventos durante edição. A modal nativa em features/regions mostra estado/quantidade ao mestre e edita a zona.

blueprint-editor.js edita um rascunho privado de porta, alavanca, tocha ou região por meio do helper compartilhado mountModal; blueprint-model.js valida conexões e simula o evento selecionado localmente. O editor usa DOM/SVG nativos, sem biblioteca de grafos: os nós continuam elementos focáveis sobre um plano deslocado e escalado, e a rolagem nativa causada pelo foco é incorporada a essa câmera. Seu histórico de desfazer próprio cobre somente o rascunho aberto e termina com a modal. Salvar aplica uma operação de desfazer ao projeto. Portas geradas mantêm o door_state nativo, e imagens de alavanca mantêm lever.state; blueprint_runtime.py executa efeitos dentro da transação e do recibo do comando de mapa existente, que também registra uma execução só para o mestre exibida por automation-log.js, a janela Automação da mesa. Luzes e sons usam seus domínios e transporte em tempo real atuais. A projeção do público inclui somente a aparência ativa, mantendo grafos e imagens inativas privados para o mestre.

blueprint-templates.js captura modelos reutilizáveis da campanha, substituindo somente campos de recursos tipados por vínculos e expondo parâmetros escalares permitidos. Aplicar uma cópia resolvida é uma operação no histórico do rascunho, sem dependência futura do modelo. scene-network.js deriva relações dos mesmos grafos de cada elemento, mostra até 40 cartões por página e abre o editor já existente. blueprint_network.py salva regras nativas com revisões de objeto e projeto e recibo vinculado ao conteúdo, traduzindo referências para a fonte do Builder na mesma transação. A visão da rede não executa regras.

builder_dynamic.py promove pisos/paredes marcados explicitamente a planos de imagem nativos com máscaras; mapas comuns continuam incorporados à imagem. Grupos ordenados de pintura/borracha preservam o alfa do piso; surface-material.js amostra materiais substitutos no referencial original da textura e surface-group.js compõe os planos quando mudam, fora do loop de quadros. As imagens geradas pertencem à cena e ficam fora da biblioteca. Visibilidade e material mudam o estado durante o jogo sem criar arquivos. Limites controlam superfícies promovidas, pixels, bytes e trabalho de cache. A preparação serializa o trabalho dos rasters dinâmicos/base e libera cada buffer PNG após gravar o artefato da operação, antes de abrir a transação de escrita. Ações repetidas de material de parede compartilham um cache limitado por comando. A importação valida os novos metadados, o orçamento agregado de buffers e a propriedade da cena sobre arte/máscaras geradas. A entrega de arquivos revalida essa propriedade para quem não é mestre; o renderer calcula o orçamento de todos os grupos antes de alocar buffers.

blueprint_world.py valida públicos de apresentação e transporte na mesma cena dentro da transação do comando existente. Eventos automation.presentation filtrados por destinatário são enviados após o commit e revalidados na entrega. automation-presentation.js exibe texto autorado com textContent, abre a janela nativa de diário e elimina repetições de IDs recentes. O foco de câmera só atua na cena visualizada e exige uma preferência por perfil/dispositivo, desativada inicialmente. O teleporte usa o token original do gatilho autenticado, valida espaço livre para toda sua área na região de destino e processa a presença de chegada depois de persistir os objetos da ação atual. Uma trava de chegada persistente impede voltas até o token sair da região; navegação de cenas e composição do grupo permanecem as mesmas.

O Catálogo de objetos interativos na seleção do Builder, Modelos prontos no menu de contexto do objeto e o Catálogo de objetos do editor de nós compartilham 14 Blueprints editáveis de mecanismos genéricos. Os modelos cobrem interruptores, baús, passagens secretas, livros, altares, armadilhas de aviso, sinos, placas de pressão, pedidos de dados, ativação/conclusão de missões, placas de entrada, interruptores temporizados e botões de porta. A modal de configuração oferece som/volume e imagem do estado ativo opcionais; opções desmarcadas não geram nós de som nem substituição de imagem. As imagens de estado vêm apenas de assets de Objeto e o estado inativo restaura a imagem original. Aplicar um modelo preserva a identidade e a geometria; substituir um comportamento autorado exige confirmação na modal da aplicação e pode ser desfeito. A armadilha avisa na entrada enquanto armada, sem aplicar dano específico de um sistema de RPG. As opções de Comportamento no inspetor abrem a mesma configuração. As modais de propriedades reutilizam os controles do inspetor e salvam pelas operações e pelo histórico existentes.

O nó requestRoll do Blueprint V2 armazena uma expression de dados validada (até 512 caracteres), label (até 48), visibility (public ou gm) e audience. O público pode ser quem interagiu, todos os jogadores, um jogador ou usuários escolhidos. Um jogador usa o escopo compatível users com um UUID. Os destinatários autorizados recebem uma modal nativa e escolhem Rolar; o serviço normal de dados valida e publica o resultado no chat. O UUID do evento identifica a mesma rolagem por usuário entre tentativas e abas, mantendo a cena de origem. Streamers não recebem pedidos de rolagem. A ação continua imediatamente: ela não cria ramificações com base no resultado.

Abrir diário usa o contrato existente showHandout para diários, missões e quadros de missões, sem conceder acesso de leitura. setQuestStatus armazena o journalId de uma missão e um dos estados draft, available, active, completed, failed ou archived. O servidor valida campanha e tipo e aplica a ação fixa autorada pelo mestre pelo serviço de diários, dentro da transação do mapa; os jogadores não recebem permissão de edição do diário. Ensaios mostram alterações de missão e pedidos de dados sem persistir diários nem abrir janelas para jogadores. Modelos, referências adiadas e arquivos de exportação preservam esses vínculos e os rótulos de rolagem autorados.

Blueprint V2 elementGroup é um conjunto puro tipado e ordenado: name (1–80 caracteres), memberType (element, light, sound) e de 1 a 32 UUIDs únicos em targetIds. A saída targets se torna elementset, lightset ou soundset. lightReference.targetId produz light: light; soundReference.trackId produz sound: sound. O reference legado conserva o ID de saída door: element e também alcança imagens nativas comuns, cujo estado é null. Paredes/chão incorporados à imagem ficam fora do contrato. acceptsDataType(port, type) usa acceptedTypes nas portas Alvo: ações de elemento aceitam elemento/conjunto, ações de luz/som aceitam o recurso/conjunto correspondente, e transformações aceitam elemento/luz ou seus conjuntos. Ler propriedade continua individual. Um Alvo conectado substitui o vínculo de recurso legado ou o próprio elemento; UUIDs ausentes ficam conectados e são ignorados/registrados, sem redirecionamento. A ordem é explícita e cada ação expandida conta no limite de ações existente.

move guarda dx/dy finitos em células, limitados a ±1.000; rotate guarda degrees relativo finito de até ±36.000. blueprint_actions.py usa validadores nativos de imagem, porta, luz e região dentro da transação do comando. O giro usa o centro de cada alvo, e retângulos de presença exigem múltiplos de 90 graus. Componentes de tocha acompanham a imagem. Transformações autorizadas reconhecem a geometria pertencente ao projeto sem mudar a fonte; atualizar a cena reaplica transformações autoradas e mantém conflitos de outras edições manuais. setInteraction.enabled e setRegion.enabled são booleanos estritos. O primeiro controla requisições diretas e controles de ponteiro/teclado/chama de jogadores, conservando a operação manual pelo mestre, sem bloquear automações autoradas. A projeção pública nativa inclui interactionEnabled; omitir a configuração autorada mantém o padrão habilitado. Interação durante o jogo sobrevive à atualização quando seu padrão autorado não muda; uma alteração explícita de autoria vence. A ativação de região é conservada enquanto ativação/camada autoradas não mudam; mudanças de autoria vencem e recalculam a base de ocupação silenciosamente. Cópias nativas e arquivos de campanha conservam a configuração; o remapeamento toca referências/membros tipados, sem alterar textos escalares. Excluir uma ação de recurso legada conecta somente seus fios de execução; fios de dados nunca se tornam execução, e novas referências/grupos ausentes permanecem inertes.

A importação usa remap_omitted_bindings para novas referências/grupos de som e IDs de atores/tokens dos filtros quando seus registros são omitidos. Uma tabela por modelo/UUID cria um UUID inerte novo para cada identidade ausente e o reutiliza nos documentos do projeto, dados nativos e manifestos de geração. Vínculos importados ficam inertes até escolher recursos substitutos; nomes, rótulos de evento/grupo e textos escalares autorados são preservados, inclusive textos que se parecem com UUIDs.

eventContext.property seleciona um value tipado de leitura. Propriedades de texto são event, sourceId, userId, userName, tokenId, actorId e presence; count é um inteiro de 0 a 1.000.000, hasToken é booleano e state/previous transportam Estado. blueprint_context.py monta os dados originais com usuários autenticados e regiões/tokens validados. Sinais e tarefas persistentes os conservam. Evento/estado/anterior sempre vêm do avaliador para o retrato do grafo atual; Delay conserva o retrato salvo, enquanto Temporizador lê estado novo com a identidade original. Sem token/ator, IDs são vazios, a contagem começa em zero e hasToken deriva da presença do token. Campos arbitrários de dados/papel são recusados. A fixture opcional context.eventContext da simulação é privada e não grava identidade no servidor nem dispara efeitos.

Filtros de presença ficam em region.filters opcional na fonte e zone.region.filters nativo: tokenIds/actorIds (até 64 UUIDs únicos por lista), dispositions (friendly, neutral, hostile, unknown) e visibility (any, visible, hidden). Listas usam OU e critérios usam E; listas vazias não restringem. A visibilidade é a configuração de token oculto, independente de controle ou visão. O modelo puro compartilhado static/gravewright_maps/region-filters.js é reexportado pelos modelos nativo e do Builder. normalizeRegionFilters completa padrões de edição sem mudar a fonte; documentos antigos conservam a ausência de filtros. regions.py valida propriedade das identidades e calcula ocupação/eventos pelos comandos de token aceitos. Configurar ou habilitar/transformar uma região durante o jogo estabelece uma base silenciosa, sem presença artificial. Desabilitar presença não cancela sinais nomeados ou tarefas agendadas independentes. O editor nativo usa o catálogo atual de tokens e uma requisição cancelável de atores do mestre, descartando resultados posteriores ao fechamento. Filtros e identidades dos ocupantes continuam privados.

Blueprint V2 também implementa getVariable, setVariable, compareValue e emitEvent. Nós de variável guardam scope (element/scene), name, valueType (boolean/number/text) e default ou value tipado; comparações guardam valueType, operator e expected. dataInputs/dataOutputs derivam o tipo da porta Value do nó. Mudar o tipo remove conexões incompatíveis em uma etapa de desfazer. Chaves de grafo signal:<name> são eventos receptores nomeados; emissores os enfileiram para o próprio elemento, um Alvo Elemento ou receptores da mesma cena. Sinais não carregam dados personalizados. Os grafos continuam na versão 2, sem declarações de variável nem memória de execução no nível superior.

blueprint_variables.py valida nomes, tipos, definições e memória privada: 64 entradas por escopo, nomes ASCII de até 64 caracteres começando com letra, números finitos entre ±1.000.000, textos de até 256 caracteres e 16 grafos receptores por blueprint. Elementos usam SceneObject.data.automationVariables; cenas usam Scene.settings.automationVariables, ambos {name: {type, value}}. Conflitos de tipo recusam o comando, sem converter valores salvos. Cada evento lê seu retrato inicial; atribuições enfileiradas ficam visíveis ao próximo evento, inclusive receptores de sinais. Falhas revertem memória e demais efeitos juntos.

simulateGraph aceita um sexto argumento opcional {variables: {element: {...}, scene: {...}}}. Ele valida a memória privada de prévia e retorna atribuições/emissões como ações sem gravá-la nem executar receptores. Os controles Variáveis da simulação configuram somente essa prévia. A resposta de automação exclusiva do mestre inclui variables; a visão Variáveis envia automation/reset-variable pelo serviço de comandos existente com escopo, nome, elementId quando aplicável e o {type, value} exibido como expected. Redefinir compara aquela entrada, remove somente ela e não dispara grafos; conflitos exigem atualização. Projeções normais de cena, jogador e streamer omitem a memória privada. Atualizações vinculadas e arquivos de campanha preservam a memória; duplicar um elemento nativo limpa só sua própria memória, sem limpar os valores compartilhados da cena.

Blueprint V2 switch e random são controles de execução com portas dinâmicas, sem automationMemory por nó. Escolher caminho guarda valueType, value tipado de reserva e de um a quatro casos exatos {id, value}. IDs dos casos são únicos entre case1–case4, e seus valores são únicos dentro do tipo escolhido booleano/número/texto; booleanos permitem no máximo dois. Os limites dos valores são os mesmos das variáveis. O tipo da entrada Value deriva de valueType. Igualdade estrita seleciona o ID de um caso configurado ou default, usando a reserva somente sem conexão em Value. Leituras conectadas usam o retrato inicial do evento. Mudar o tipo redefine os valores e remove fios de Value incompatíveis e casos descartados em uma etapa de desfazer; remover um caso conserva os IDs e fios dos restantes.

Aleatório guarda de dois a quatro weights: inteiros de 0 a 1.000 com soma positiva. Suas portas são branch1 até a quantidade configurada. O sorteio usa o peso total de todas as saídas configuradas, inclusive desconectadas. Peso zero nunca é escolhido; um caminho escolhido sem conexão encerra o ramo sem novo sorteio. executionOutputs(node) retorna descritores dinâmicos e nodeOutputs(node) deriva seus IDs. Nós de escolha executam uma vez por percurso do grafo, e decisions registra strings das portas escolhidas junto às decisões booleanas existentes. O registro do mestre mostra tipo/valor lido/saída de Escolher caminho e saída/peso escolhido/soma de Aleatório. Projeções de público não expõem escolhas privadas.

O runtime deriva cada sorteio usando HMAC sob a chave privada do servidor da instalação, com UUID da requisição, posição do evento raiz, posição do sorteio na cadeia, elemento, evento e ID do nó; depois usa um gerador isolado para o peso total configurado. Não consome nem reinicia o estado global de RNG do processo. Repetir um recibo não executa de novo; repetir um comando lógico revertido usa a mesma identidade de sorteio. Continuações de Delay preservam saídas escolhidas e nós já visitados; Delay e Temporizador carregam as posições privadas budget.randomRoot e budget.randomDraws entre trechos; tarefas antigas usam zero para ambos. Movimento de tokens e exclusão de personagem/pasta conservam o contexto do comando original, distinguindo eventos raiz de presença sem reiniciar a sequência em chamadas aninhadas. Estado privado inválido é recusado, e permissões, reversão de transação e limites da cadeia continuam valendo. Os valores autorados de reserva/casos de Escolher caminho sobrevivem à geração, cópia nativa e reescrita de referências de arquivo como conteúdo, inclusive textos semelhantes a UUIDs.

O simulateGraph local aceita context.randomDraw(nodeId, total), retornando um inteiro em [0, total), ou usa Math.random para essa prévia. Somente nós Aleatório alcançados sorteiam; resultados convertidos ou fora do intervalo são recusados. Escolhas da prévia não consomem a sequência do servidor nem gravam memória de execução; também não executam receptores de sinais ou retomam tarefas agendadas.

Blueprint V2 counter, once e cooldown são controles de execução com uma Entrada e ramos true/false, sem portas de valor. O target inteiro do Contador vai de 1 a 1.000.000; a contagem incrementa antes da comparação e para em 1.000.000. Uma vez guarda somente true depois da primeira visita. O seconds finito de Recarga vai de 0,1 a 86.400; uma visita permitida guarda milissegundos UTC inteiros now + ceil(seconds * 1000), e uma visita bloqueada conserva o prazo. O runtime fornece a hora do servidor; a avaliação pura usa zero por padrão. A passagem do tempo não inicia um evento; a continuação agendada usa o nó Delay separado.

blueprint_memory.py valida e reconcilia a memória privada em SceneObject.data.automationMemory, no formato {event: {nodeId: {type, value}}} e com até 128 entradas. Essa memória é distinta das variáveis tipadas e pertence a um elemento. Remover evento/nó ou mudar seu tipo descarta o progresso; editar parâmetros, posição e conexões o conserva. Uma nova duração de Recarga vale na próxima ativação permitida, mantendo o prazo atual. O progresso participa da transação e do recibo: falhas o revertem e repetições não o duplicam. Atualizações vinculadas e arquivos de campanha o preservam; cópias nativas o limpam. Projeções de público omitem a memória para todos os papéis fora da resposta de automação exclusiva do mestre.

validateBlueprintMemory e reconcileBlueprintMemory espelham essas regras no modelo puro. simulateGraph também aceita {memory, now} no sexto argumento, copia/reconcilia essa memória e retorna memory e decisions dos ramos nos grafos de controle. Os campos Prévia dos nós persistentes da modal fornecem valores iniciais temporários para cada simulação. memory.elements na resposta de automação alimenta a visão Memória dos nós do mestre; automation/reset-memory recebe elementId, event, nodeId e o {type, value} exibido em expected. A redefinição compara e remove a entrada sem emitir eventos; conflitos exigem atualização antes de tentar novamente.

Blueprint V2 delay é um controle de execução com Entrada e Próximo, seconds finito de 0,1 a 86.400 e sem portas de valor ou entrada em automationMemory. simulateGraph retorna delay: {nodeId, seconds} na primeira espera alcançada e interrompe todo o restante do evento, incluindo ramos posteriores de Sequência. A trilha inclui Delay e as ações incluem somente efeitos anteriores; o navegador não agenda trabalho nem simula uma continuação.

O servidor guarda uma continuação privada em AutomationDelay na transação que grava os efeitos anteriores. Pilha de execução em profundidade, nós já visitados, valores puros já calculados e retratos originais do evento sobrevivem a reinícios, preservando a ordem de Sequência e evitando repetir nós convergentes. Estado, estado anterior, estados referenciados e variáveis tipadas conservam os valores do início do evento mesmo na primeira leitura após uma espera; a memória dos controles é consultada no momento da retomada. Tomada da tarefa, efeitos, conclusão e recibo da continuação são gravados juntos. Os limites atravessam todos os trechos: 32 operações de elemento, 256 entregas de sinal de cena, quatro operações por elemento, 1.024 ações e 32 retomadas temporizadas. Delay e Temporizador compartilham 64 tarefas pendentes por cena e 256 por campanha. Retomar uma continuação não consome outra operação de elemento.

blueprint_delays.py valida a identidade do comportamento e os alvos/recursos referenciados, confere a participação e o acesso atual sem promover o papel original e registra cancelamentos/falhas. Elementos alvo ausentes usados pela continuação cancelam a espera; referências ausentes de luz, som ou imagem em qualquer parte do blueprint também cancelam, mesmo quando usadas somente antes da suspensão. Mudar o comportamento do blueprint cancela o trabalho pendente; mover cartões em x/y não cancela. Pausar a cena conserva esperas vencidas até retomar. Um executor em segundo plano no servidor processa as entradas vencidas sem depender de HTTP ou atividade do navegador; nenhuma transação fica aberta durante a espera. Continuações não entram em arquivos de conteúdo ou cópias de campanha nem nas projeções de público. A fila conserva no máximo 50 entradas encerradas por cena para diagnóstico privado.

A lista delays da resposta de automação exclusiva do mestre alimenta a visão Aguardando. O comando normal automation/cancel-delay envia delayId e o expectedDueAt exibido, compara a entrada pendente e cancela sem mudar o estado do jogo nem disparar um grafo. Conflitos exigem atualização.

Blueprint V2 timer é um controle de execução com Entrada/Próximo, sem portas de valor nem entrada em automationMemory. Seus campos autorados exatos são name e action (start, stop ou restart), com seconds finito de 0,1 a 86.400 obrigatório somente em start/restart e ausente em stop. Nomes usam o formato ASCII dos sinais/variáveis, com distinção entre maiúsculas e minúsculas. simulateGraph enfileira uma cópia da ação e segue imediatamente por Próximo; não avança uma contagem nem executa seu receptor. Temporizadores alcançados antes de Delay continuam enfileirados; os posteriores aguardam a continuação no servidor. A modal oculta a duração em Parar e avisa quando não há grafo receptor signal:nome no blueprint proprietário, sem recusar essa configuração.

AutomationDelay também guarda temporizadores com kind="timer" e name. Uma restrição de banco permite um temporizador pendente por elemento/nome; nós e grafos distintos desse elemento controlam a mesma tarefa nomeada. control_timer aplica as ações na ordem autorada dentro da transação do comando. Iniciar conserva identidade, prazo, autor e limites de uma entrada existente; Parar cancela sem emitir; Reiniciar cria uma substituta sob o autor e os limites do evento atual. Cada ação continua o grafo imediatamente. Falhas de capacidade ou de ações posteriores revertem as operações de temporizador com os demais efeitos.

O mesmo executor persistente entrega o vencimento como um novo signal:nome no proprietário, lendo estado, referências e variáveis atuais em vez do retrato de um evento salvo. Sem receptor, conclui sem efeitos. Acesso/autor, cancelamento por edição de comportamento ou recursos ausentes, pausa/retomada, projeções privadas e exclusão de arquivos/cópias seguem a política de Delay. Efeitos, conclusão e recibo são gravados juntos, conservando os limites da cadeia entre execuções do servidor. Vencer um Temporizador consome uma operação de elemento; retomar uma continuação de Delay não. Ambos consomem o limite compartilhado de 32 retomadas temporizadas, e os limites por elemento restringem grafos que reiniciam a contagem. Não existe modo recorrente/global.

A projeção delays do mestre identifica temporizadores com kind: "timer" e name. Aguardando mostra linhas com nome e vencimento e os controles Parar temporizador e Reiniciar temporizador. Parar usa automation/cancel-delay; automation/restart-timer recebe delayId e expectedDueAt, confere a entrada pendente exibida e cria uma substituta com prazo após sua duração salva. Esse reinício pelo mestre conserva autor original, limite do papel e limites da cadeia. Ambos exigem atualização diante de trabalho desatualizado e não emitem um evento imediato de jogo.

Decorações de alavanca compartilham o registro de adaptadores e referências tipadas de elemento. features/images/ui/lever-controls.js trata os dois cliques nativos antes da seleção mista, com verificações de papel, ferramenta e camada, bloqueio de comandos simultâneos e descarte de listeners. Edições de geometria/configuração não executam seus grafos.

floor-painter.js coordena gestos de pincel livre, retângulo, círculo e polígono sem registrar eventos. Ele captura o documento e as opções do pincel no início, mantendo a imagem e o ID do objeto de cada célula entre prévias. model.js enumera as células da forma e aplica a substituição do chão de forma atômica; workspace.js trata a entrada e registra cada gesto concluído uma vez no histórico de desfazer. Cancelamento ou falha por limite de objetos preserva o documento original.

O Estúdio possui um canvas em toda a largura e uma biblioteca horizontal abaixo dele. A aba Ferramentas do painel auxiliar existente reúne botões das ferramentas, material, opções do pincel, camada ativa e encaixe na grade. Esses controles mantêm os hooks data-builder-* e listeners existentes, sem duplicação no DOM. O cabeçalho reúne o campo rápido de nome do mapa, atalhos Selecionar, Pintar e Apagar e tamanho do pincel; depois, desfazer/refazer antes do botão de estado do salvamento, prévia e geração de cena. Os controles rápidos usam hooks próprios e compartilham o estado existente de ferramenta e pincel com Ferramentas. Pintar restaura o pincel livre de chão e a operação de pintura e seleciona a categoria Chão na biblioteca. Apagar mantém a exclusão do objeto inteiro. O tamanho espelha Ferramentas e fica ativo para o pincel livre de chão, inclusive borracha por área. Mantém os limites de 1–20 células com encaixe e 0,05–20 sem encaixe e os atalhos de colchetes. A seleção mantém Shift-clique e seleção por caixa. Prévia e requisições pendentes desativam esses controles de edição. O nome rápido espelha Configurações: Enter ou sair do campo aplica uma operação de histórico, Escape cancela o rascunho e Ctrl+S aplica o nome atual antes de salvar. Os atalhos do histórico continuam Ctrl+Z, Ctrl+Shift+Z e Ctrl+Y. Busca, importação, coleção e categorias por tipo continuam acessíveis ao recolher os cards da biblioteca. O destaque inicial de Chão mostra todas as imagens disponíveis da campanha; escolher a categoria Chão, Parede, Porta ou Objeto também ativa sua ferramenta de desenho. Escolher um preset de luz/efeito nativo ativa sua ferramenta de posicionamento. O seletor em Ferramentas acompanha o pincel ativo e inclui imagens individuais, famílias de assets e sua aparência padrão. Cards de famílias também aparecem na biblioteca. A importação usa a modal padrão da aplicação para escolher arquivos, tipo de imagem e família opcional; dois cliques no card de uma imagem abrem seu editor de tipo e família. Luzes mostram instruções de posicionamento sem se tornarem uma classificação de asset de imagem. Painéis, no canto superior direito da área de desenho, abre o mesmo painel auxiliar com ferramentas, camadas, seleção, configurações, projetos e revisão. O painel começa fechado; selecionar ou pintar não o abre. Fechá-lo mantém a biblioteca acessível. Os formulários existentes de propriedades e configuração permanecem nesses painéis, e os hooks antigos ocultos no DOM preservam a compatibilidade do controlador. As propriedades dos objetos ficam em Seleção; esta interface não acrescenta uma modal de propriedades. Componentes permanece planejado. Revisão mostra resumo, geração e atualização da cena vinculada. Controles de visualização e dimensões em células ficam na parte inferior da área de desenho. O estado do painel, da biblioteca e da visualização pertence ao editor e não ao documento do projeto; esses controles não devem alterar a cena gerada. Camadas nomeadas do projeto são distintas: ordem, nomes, visibilidade e bloqueio persistem na versão 3 do formato, junto com orientação do chão, decorações, luzes e metadados locais dos assets. Alças permitem reordenar camadas por arraste, além das setas. Listas de conteúdo expansíveis selecionam objetos individuais sem alterar o layout da área de desenho. O campo opcional family do asset permanece na versão 3; seus membros compartilham um tipo de imagem. A pintura de chão salva um assetId específico por célula de 1 × 1, mantendo as variações escolhidas na prévia, em desfazer/refazer, na reabertura e na geração. Visibilidade e bloqueio controlam seleção e edição; a geração inclui todas as camadas do projeto. model.js aplica movimentos de grupo, duplicação e rotação de forma atômica, preservando giros exatos de 90 graus e pontos de portas antigas. A prévia desativa a edição, oculta suas guias e mostra temporariamente todas as camadas, restaurando a visibilidade anterior ao sair. Ela não altera a câmera ou o documento do projeto; salvar e gerar cena continuam disponíveis. A imagem contém chão e faixas de parede; imagens nativas, luzes e aparência de portas permanecem objetos durante o jogo. A ordem das camadas mantém as etapas existentes do raster: todo o chão é desenhado antes das faixas de parede. Objetos nativos mantêm seus contratos de renderização do VTT. A geração registra a propriedade dos recursos no projeto. Atualizar a cena vinculada substitui apenas esses recursos e as fatias, preservando conteúdo manual e estado das portas durante o jogo. Revisões do projeto/cena e os dados originais dos objetos são conferidos novamente na transação; edições conflitantes no VTT interrompem a atualização. A mesa recarrega o documento restaurado do cache de histórico em pageshow.persisted, pois o renderizador e os ciclos dos módulos já foram encerrados.

Redes de paredes no Map Builder

builder/wall-edit.js controla movimentos atômicos de nós, divisões, prolongamentos e conexões de pontas nos eixos de parede da versão três do projeto. Nós compartilhados são derivados de pontas coincidentes em uma mesma camada editável, independentemente de material ou espessura. Adicionar ou prolongar uma parede sobre uma borda editável divide essa borda e forma um nó em T. Portas continuam sendo objetos independentes. Falhas de limites do mapa, comprimento mínimo ou recursos deixam o documento e os IDs de fragmentos intactos.

builder/wall-controls.js controla os gestos de ponteiro e teclado da parede selecionada. A área de trabalho confirma cada operação concluída uma única vez; cancelar restaura o documento anterior ao gesto. Controles SVG de ponta, meio e prolongamento mantêm seu tamanho na tela ao mudar o zoom. O foco é restaurado após renderizações; Prévia, estado ocupado e permissões da camada desativam a edição. O prolongamento armado usa um marcador próprio e devolve o foco ao canvas.

builder/wall-joins.js e maps/wall_joins.py derivam a mesma malha visual após remover os intervalos ocupados por portas. Cantos usam uma junta limitada a quatro vezes a maior meia-espessura, com chanfro como alternativa. Trechos colineares contíguos compatíveis compartilham um referencial canônico; paredes isoladas mantêm seu referencial autoral. Trechos antigos colineares compatíveis são deduplicados na renderização. Os polígonos de cada parede formam uma única máscara de cobertura, preservando o alfa sem aplicá-lo duas vezes em seus próprios encontros. Camadas permanecem separadas, e encontros não fecham aberturas de portas. A malha pertence à apresentação: coordenadas, IDs e eixos de colisão nativos mantêm seu contrato de persistência.

Dimensões compartilhadas e tokens visuais

tokens.css define os valores visuais compartilhados. Tokens dimensionais fixos usam valores simples em px. Reutilize os tokens existentes quando forem adequados ao componente e introduza um novo valor compartilhado somente quando ele for necessário entre componentes. A escala Fibonacci e seu script de auditoria foram retirados; o CSS do núcleo usa as regras de espaçamento, raio e tipografia abaixo em vez de --fib-* ou --step-*.

Use --space-* para padding, margens e gaps, incluindo suas formas abreviadas, lógicas e de linha/coluna. Ao migrar uma dimensão fixa, escolha o valor mais próximo desta escala; em caso de empate, escolha o menor. Preserve o sinal de margens negativas. O sufixo do token conta passos de quatro pixels, com nomes separados para zero, um e dois pixels:

TokenValor
--space-00
--space-px1px
--space-half2px
--space-14px
--space-28px
--space-312px
--space-416px
--space-520px
--space-624px
--space-728px
--space-832px
--space-1040px
--space-1248px
--space-1456px
--space-1664px

O antigo valor de 34px de --space-9 é substituído por --space-8 com 32px. Os raios de borda usam estes nomes compartilhados:

TokenValor
--radius-sm4px
--radius-md8px
--radius-lg12px
--radius-xl16px
--radius-pill999px
--radius-circle50%

O CSS da interface do host deve renderizar textos com 12px ou mais. Confira os tamanhos efetivos vindos de estilos herdados, unidades relativas e formas abreviadas de fonte, além de font-size explícito. Os nomes existentes --text-3xs, --text-2xs e --text-xs continuam disponíveis com 12px. Textos de PDFs e desenhos, assim como rótulos de cena escalados pela câmera, preservam os tamanhos definidos pelo autor.

Larguras, alturas e demais geometrias dos componentes mantêm as dimensões necessárias. Um valor específico de um único componente pode usar um literal local em px; reserve tokens --size-* compartilhados para valores reutilizados entre componentes. A geometria responsiva ainda pode usar unidades relativas e expressões de layout como calc() e clamp() quando necessário.

Controles de formulário

O base.css compartilhado fornece padrões escuros com baixa especificidade para campos e botões nativos, botões de seleção de arquivos, caixas de seleção, sliders e amostras de cor, incluindo formulários montados após o carregamento. Use gw-field para rótulo, controle e ajuda; caixas de seleção, opções, sliders e cores mantêm suas dimensões próprias em vez de herdar as de campos de texto. Campos nativos inválidos e aria-invalid="true" usam o tratamento de erro do campo. Associe ajudas e erros com aria-describedby.

Seletores de escolha única herdam sua seta dos estilos base. Altere a superfície com background-color, pois o atalho background remove essa imagem, e reserve espaço no final para a seta. Seletores múltiplos ou com lista visível não usam seta. Áreas personalizadas de upload devem manter o campo de arquivo acessível pelo teclado, exibir seu foco e impedir o arraste da janela pelo alvo de upload. Verifique os estados vazio, selecionado, desabilitado e de erro em telas estreitas com dados sintéticos; a escolha de arquivos do sistema operacional continua nativa.

Estado nativo e tempo real

O frontend nativo usa a API de domínio compartilhada onde implementada e mantém adaptadores HTTP/socket especializados para uploads, assinaturas do tabuleiro e fragmentos renderizados. A API pública não substitui todos os transportes nativos.

O controlador de tempo real conecta o WebSocket autenticado da mesa atual, gerencia assinaturas/reconexão e dispara eventos gravewright:*. O servidor envia projeções autorizadas por usuário e HTML renderizado; dados privados, audiência do chat e visibilidade da cena são filtrados antes do envio. Insira texto de usuário com textContent. A inserção de fragmentos HTML existente destina-se ao HTML dos templates confiáveis do servidor, não a texto arbitrário de chat ou módulo.

gravewright:api-event é o evento interno consumido pela API de domínio; notificações api.changed provocam novas leituras autorizadas. Integrações públicas devem usar api.events.on(...), conforme eventos da API, em vez de copiar comandos internos de socket. Assinaturas não são um fluxo durável: reconecte e releia o estado. Criar gravewright.forTable(...) não abre um socket.

Os diretórios nativos de itens e compêndios ordenam cópias das entradas visíveis pelo nome exibido, usando o idioma ativo; a ordem armazenada e os IDs dos documentos permanecem intactos. Pacotes, filtros e prévias de compêndios usam linhas compactas e janelas com tamanho inicial ajustado ao conteúdo, com rolagem interna para conteúdos longos. O usuário mantém os controles compartilhados de mover, redimensionar, minimizar e redefinir janelas. Mestres podem criar o primeiro compêndio no diretório vazio; acesso de leitura não exibe controles de importação ou permissões. Os editores de itens de cada sistema permanecem em seus pacotes e usam os contratos existentes do host. Projeções de itens repetidas preservam menus de ações abertos e foco; alterações de documento ou permissão invalidam esses menus.

Fontes e arquivos gerados

A maioria dos JavaScripts de cada app em static/ é fonte servida diretamente. Alguns arquivos são resultados de build versionados. maps/scripts/build.cjs gera:

Entrada/fonte editávelDestino gerado
maps/frontend/widgets/game-board/ui/board.jsmaps/static/gravewright_maps/vendor/board.js e módulos nomeados em runtime/
maps/frontend/shared/rendering/render-profile.jsmaps/static/gravewright_maps/render-profile.js
maps/frontend/native/tools.jsmaps/static/gravewright_maps/tools.js
maps/frontend/native/builder-catalog.js, builder-preview.jsmaps/static/gravewright_maps/builder-catalog.js, builder-native.js
maps/frontend/native/builder-editors.jsmaps/static/gravewright_maps/builder-editors.js
maps/frontend/native/table-modules.jstable/static/gravewright_table/media-workspace.js
maps/frontend/features/walls/{ui,model}/walls.jsmaps/static/gravewright_maps/walls/
Fontes de camadas e perfis de iluminaçãomaps/static/gravewright_maps/sources/
maps/frontend/calibration.js, directory.jsModelos/controladores estáticos correspondentes
maps/frontend/shared/lib/dom/tree-drag.jsactors/static/gravewright_actors/tree-drag.js
maps/frontend/features/tokens/ui/controller.jstokens/static/gravewright_tokens/controller.js
pdf_system/frontend/controller.jspdf_system/static/gravewright_pdf_system/controller.js

Os caminhos da tabela são relativos a gravewright/. Edite fontes e reconstrua os destinos. Evite correções isoladas em chunks minificados ou bibliotecas de terceiros.

Ferramentas, tabuleiro, catálogos, editores e prévia do Builder mantêm suas URLs públicas de entrada e importam módulos ES nomeados de gravewright_maps/runtime/, seguindo os caminhos de cada recurso nas fontes. Módulos compartilhados têm uma única identidade no navegador; PixiJS e seu adaptador unsafe-eval são emitidos uma vez em vendor/pixi.js. Prévia e editores continuam com imports sob demanda, e o worker de visibilidade mantém a URL relativa do recurso e sua entrada pública existente. O manifesto do Runner inclui cada módulo e aviso de licença emitido. Distribua o grafo gerado completo. Essa organização usa mais requisições pequenas e evita cópias repetidas nos bundles; tamanhos de arquivos, sozinhos, não demonstram melhora de desempenho no navegador.

Na raiz do repositório:

sh
npm ci --include=dev --prefix gravewright/maps/frontend
npm run build --prefix gravewright/maps/frontend
npm test --prefix gravewright/maps/frontend

O build usa PixiJS/esbuild fixados em maps/frontend/package-lock.json, gera ES2022 e mantém imports /static/* externos. O módulo compartilhado de perfil de renderização permanece externo para todos os recursos usarem a mesma instância. O build também copia licenças/avisos das dependências e escreve vendor/packages.json; preserve esses arquivos na redistribuição. Ele rejeita a inclusão de pacotes Vue.

Preparação pelo executor Windows

O Gravewright Runner reutiliza Node.js/npm verificados dentro de data/runner/ ou baixa sua cópia gerenciada nessa instalação quando necessário. Seu arquivo em lote CMD chama scripts/prepare_frontend.py no modo --plan para conferir versões dos pacotes, impressões dos arquivos e uma operação real do esbuild. Uma instalação correspondente e funcional das dependências pode ser reaproveitada já na primeira execução. O próprio arquivo em lote executa npm ci --include=dev --include=optional quando é necessário instalar dependências e o npm run build existente na primeira preparação ou em um build invalidado. Depois chama o helper no modo --record para verificar e salvar o estado das saídas concluídas. O esbuild é necessário durante a preparação, embora as dependências Python de desenvolvimento sejam omitidas da instalação normal do executor.

O plano retorna código de saída 0 para recursos atuais, 10 para instalação npm e build, ou 11 para somente build. O launcher em lote trata essas decisões explicitamente. Ambos os modos do helper aceitam --node, --npm-cli e --state-dir; o modo padrão do helper continua disponível para realizar a preparação completa em scripts de desenvolvimento e testes de regressão.

O projeto npm permanece em gravewright/maps/frontend; seu node_modules/ ignorado é instalado ali e os recursos estáticos gerados são gravados nos destinos acima. Essas saídas são recursos versionados da aplicação; portanto, uma alteração nos fontes seguida da preparação pelo executor pode produzir um diff no controle de versão. Revise e inclua as saídas geradas e os avisos apropriados ao contribuir alterações de frontend.

O estado da preparação fica dentro da pasta descompactada em data/runner/frontend/<hash-local-do-código>/state.json. Ferramentas gerenciadas e caches do npm/uv também ficam nesta instalação. A verificação das dependências inclui arquivos de pacotes e versões Node/npm selecionadas. A verificação do build inclui fontes editáveis, script de build e hashes das saídas geradas. O arquivo em lote solicita ao build.cjs um manifesto das saídas, incluindo avisos de terceiros, pela variável de processo opcional GRAVEWRIGHT_BUILD_MANIFEST. Uma próxima execução pula um build válido e inalterado; entradas modificadas ou saídas ausentes/modificadas provocam nova preparação. Um build com falha impede a inicialização, em vez de servir um frontend não verificado. Depois, collectstatic copia os recursos públicos preparados para o diretório data/staticfiles/ dentro do projeto.

O registro de métodos de domínio possui geração própria:

sh
uv run --locked python scripts/generate_frontend_api.py

O script gera somente modules/static/gravewright_modules/frontend-contract.js a partir de contracts/frontend.json. generated.js e schemas do SDK original são artefatos separados, não gerados por esse script. collectstatic reúne arquivos para implantação, mas não executa builds JavaScript.

Camadas do tabuleiro e ciclo dos recursos

maps/frontend/ separa modelos de entidades, modelos/UI de funcionalidades, utilitários e widget do tabuleiro. O widget coordena renderização, entrada, agendamento/cache/pré-carregamento de tiles, camadas nativas, polígonos de visão, luzes e shaders. As funcionalidades incluem paredes, tokens, desenhos, medidas, efeitos e seleção. Testes de algoritmos ficam ao lado dos modelos e são empacotados por maps/scripts/test_models.cjs para o executor de testes do Node.

Mantenha um sistema de coordenadas consistente: pixels lógicos da cena, origem superior esquerda e centro dos tokens como âncora. Pan/zoom convertem posições em coordenadas CSS da área visível; a densidade de pixels altera a resolução de renderização. Misturar unidades desalinha seleção, tokens e luzes. A API de sobreposição oferece conversão explícita em vez de expor o renderer PixiJS.

Operações assíncronas pertencem a uma página, tabuleiro, ativação ou montagem. Lifetime cancela chamadas e remove assinaturas; os fontes de mapas possuem utilitários próprios. Adicione/remova listeners e observers com seu proprietário. Libere recursos gráficos e tarefas pendentes ao encerrar o tabuleiro ou trocar de cena. Listener ou API que sobrevive ao contexto pode atualizar a próxima cena com dados antigos.

Atualizações da cena atual preservam posição/zoom da câmera, imagens dos tokens e camadas montadas, inclusive revisões causadas por automações de teleporte. Somente a troca de cena redefine a câmera. Revisões do mapa atualizam os tiles existentes pela URL e mantêm os tiles anteriores decodificados visíveis até os substitutos estarem prontos; requisições superadas não podem desenhar uma revisão antiga. Uma falha na substituição mantém a imagem atual durante as tentativas limitadas de recuperação. Ocupação e geometria das regiões atualizam sua própria sobreposição, mantendo montados os recursos de piso, luz e shader não afetados.

Confirmações de tokens e estados em tempo real podem conter metadados nativos teleports: [{id, version}], filtrados pelos tokens visíveis ao destinatário e suas revisões exatas. A área de tokens conserva cada revisão de teleporte aceita nas leituras normais. O movimento não é interpolado e somente o objeto Pixi daquele token é recriado no destino; entregas repetidas mantêm a mesma recriação. Identidade lógica, seleção e dados da ficha são preservados, e cargas de imagens antigas não podem restaurar um objeto anterior.

Comandos de tokens bem-sucedidos aplicam o estado autorizado recebido na confirmação. Eles não repetem o GET de tokens nem solicitam novamente as camadas; mudanças confirmadas da cena chegam pela assinatura existente em tempo real. Falhas e conflitos de versão continuam recarregando o estado autorizado. Um movimento sem deslocamento e sem caminho percorrido não grava nem entra no estado ocupado. Caminhos que saem e retornam à mesma posição continuam sendo operações reais para preservar a automação de entrada/saída de regiões.

A área de tokens salva silenciosamente, inclusive quando a confirmação demora. Comandos pendentes mantêm a proteção de interação para edições concorrentes. Erros e instruções de rota continuam imediatos; não há aviso nem temporizador de salvamento. Textos de estado que não mudaram não são reescritos a cada renderização.

Prévias locais de tokens reutilizam a chave da cena estática e a atualização de imagens enquanto suas entradas imutáveis continuam iguais. Elas não percorrem todas as transformações das imagens nativas nem reconstroem o índice de origens dos componentes a cada quadro. Um novo estado de imagens continua atualizando suas transformações e arte; mudanças de paredes, efeitos, identidade da cena e papel continuam invalidando os recursos correspondentes.

Notificações de movimento atualizam camadas e tokens sem reconstruir o diretório de atores ou painel de combate que não mudaram. Edições de atores/combate e outros comandos de tokens mantêm as notificações correspondentes. Notificações pareadas de camadas/tokens entregam o áudio espacial uma vez pela atualização de tokens; edições independentes de camadas continuam atualizando o áudio. Autorização atual e eventos de mudança para extensões são preservados.

O teleporte na mesma cena avança a revisão do token, não a dos metadados da cena ou do raster. Suas notificações de camadas/tokens entregam mudanças de região e aparência sem solicitar outro manifesto nem alterar as URLs dos tiles existentes. Portais repetidos preservam os pisos decodificados e as texturas do mapa já carregadas.

Atualizações de imagens nativas

SceneImageArt atualiza texturas de imagens comuns independentemente da chave estática da cena. Uma troca de origem mantém o sprite e sua imagem anterior até decodificar a nova textura; resultados atrasados não substituem estados mais novos nem uma cena encerrada. A imagem atual sobrevive à reconstrução de luzes/geometria na mesma cena. Superfícies compostas e pisos agrupados usam um cache separado com o mesmo ciclo de vida. Composições inalteradas são vinculadas de forma síncrona durante reconstruções de efeitos; trocas de material mantêm a imagem atual até terminar a decodificação. Mudanças de máscara, geometria, componentes do grupo ou visibilidade revogam imediatamente a cobertura antiga. A composição fecha todos os bitmaps de entrada, inclusive em falhas e cancelamentos, e não publica pisos parciais. Cada cache de imagens conserva no máximo oito texturas ociosas e 16 MiB de dados RGBA decodificados ociosos, removendo as entradas usadas há mais tempo. Trocar de cena/papel ou encerrar o tabuleiro cancela tarefas pendentes e libera texturas/bitmaps. Apenas imagens da projeção autorizada são solicitadas; aparências inativas não são pré-carregadas.

Referências de imagens de tokens aguardam o descarregamento pendente da mesma URL antes de carregar novamente. Caso contrário, o loader do Pixi pode devolver a textura antiga do cache enquanto o descarregamento está prestes a destruí-la. URLs diferentes continuam concorrentes. Descarte aplicações com as opções explícitas removeView: true, releaseGlobalResources: false: o booleano true também libera pools globais ainda usados por outros tabuleiros e prévias. Texturas da cena e recursos do renderizador continuam sendo liberados pelo dono.

Imagens privadas do GM e marcadores de Objetos vazios continuam visíveis com 55% de opacidade em todas as abas; seleção e interação ficam restritas a GM. Desenhos privados multiplicam sua opacidade original pelo mesmo fator. As projeções do servidor continuam excluindo elementos privados de jogadores/streamers.

A entrega em tempo real envia maps.layers e tokens.state confirmados antes das atualizações dependentes de áudio/combate. Cada entrega mantém sua própria verificação de autorização. Mudanças disparadas por tokens podem compartilhar a revisão da cena; não descarte estados apenas porque essa revisão é igual.

Trabalho de iluminação e visão

SceneLayers calcula a oclusão das luzes uma vez por chave estática da cena e reutiliza os polígonos nas máscaras, no atlas de luz e na penumbra do mestre. createVisibilityContext compartilha os índices de paredes e mantém no máximo oito subconjuntos para alcance finito ou elevação. Preserve a aritmética dos raios e a composição das paredes: trocar a BVH pode mudar o arredondamento em extremidades coincidentes.

Quadros de tocha e pulso atualizam sprites aditivos do Pixi sobre texturas de atenuação imutáveis. Shaders recebem uma textura renderizada na GPU; apenas partículas que reagem à luz pedem amostras na CPU, limitadas a oito por segundo. Os perfis mantêm suas taxas de animação. A cena possui o pool de texturas e o libera depois dos vínculos dos shaders, do atlas e das camadas de sprites.

Mudanças de visão são agrupadas uma vez por quadro de animação. Cenas com pelo menos 128 segmentos de parede usam o worker de módulo da mesma origem gerado em vendor/visibility-worker.js; cenas menores ou sem worker usam a mesma geometria de forma síncrona. O worker mantém uma tarefa ativa e o último estado pendente, transferindo buffers de coordenadas. Observadores nativos levam um sourceId para que o movimento preserve a última visão concluída enquanto calcula a seguinte. Trocas de seleção, alcance, elevação, cone ou prévia revogam essa visão imediatamente; mudanças de paredes ou cena encerram o worker anterior. Não reutilize a visão entre esses limites nem arredonde o alcance configurado ou as posições dos tokens para células. Movimento pendente preserva a textura do véu até o resultado chegar; a visão do mestre sem prévia reutiliza o véu estático sem repintar ao mover tokens.

Ao medir, compare quadros completos e tarefas de worker concluídas. Enfileirar não equivale a concluir, e WebGL por software em navegador headless não comprova desempenho de uma GPU física. Gere as entradas do tabuleiro e do worker juntas pelo build normal de arquivos.

Estendendo a interface

O SurfaceRegistry do host aceita gravewright.ui.register(domain, declaration) e retorna função de cancelamento. Suporta superfícies da mesa, app.shell e inside.content. Uma montagem recebe {root, context, api, signal, onDispose}, ciclo de vida e raiz próprios. Falhas geram limpeza e restauram a visibilidade anterior dos elementos nativos substituídos. Esse registro global permite uma substituição por domínio.

Pacotes assinados usam ModuleRuntime: registro síncrono durante ativação, chamadas com identidade/lease/revisão e seleção de substituições pelas preferências da mesa. Use o callback register do módulo para pacotes instalados; o registro global é destinado a integrações da página do host e tem outras regras de propriedade/seleção. Veja exemplos e nomes em módulos.

Verificação do frontend

Execute as verificações afetadas pela alteração:

sh
node --test tests/modules/*.test.mjs tests/effects/*.test.mjs
npm test --prefix gravewright/maps/frontend
uv run --locked python tests/e2e/frontend_api.py

Para renderização/interação, use cenários relevantes de tests/e2e/, incluindo mixed_selection.py, token_routes.py, light_rendering.py, effect_geometry.py, effect_pixels.py, map_prefetch.py e realtime.py. O ambiente de navegador está descrito em testes. Verifique sessões de mestre e jogador quando houver permissões distintas e valide limpeza/reconexão de assinaturas novas. Mantenha textos e catálogos alinhados aos idiomas suportados pela aplicação.