Pular para o conteúdo
SheetForge

O Kernel de Autoria — Construindo uma Segunda Superfície de Autoria

Avançado. Para autores de asset/ferramentas que querem construir sua própria UI de autoria (por exemplo, um canvas de grafo de nós) em cima do motor do SheetForge. Equipes de jogo que usam o Data Studio não precisam desta página.

A janela de autoria não é o motor. O Data Studio — e o app web ao seu lado — são consumidores de um kernel de autoria agnóstico de janela.

Tudo que eles fazem (preparação, validação, orquestração do reflect, limites de undo, reimportação) é conduzido através de tipos públicos que uma terceira superfície pode conduzir igualmente bem. Duas superfícies já fazem isso, o que é a prova prática de que essa costura é real, não aspiracional.

Antes de construir uma superfície inteira, veja se um ponto de extensão já cobre a necessidade. Um plugin pode adicionar verbos, painéis, selos e widgets de célula à janela distribuída sem possuir uma janela própria, descritos como dado para que sejam renderizados no editor e no navegador — veja Autoria de Plugins §4.16. Esta página é para o caso em que você quer o seu próprio canvas.

Um assembly de teste de simulação de consumidor (SheetForge.Tests.Consumer, sem acesso via InternalsVisibleTo ao Core ou ao Editor) implementa uma superfície de autoria virtual de ponta a ponta apenas contra a API pública. Se qualquer membro necessário fosse internal, esse assembly falharia ao compilar (CS0122) — por isso ele serve como a especificação executável da superfície descrita abaixo.

O motor de três objetos

┌─────────────────────┐     ┌──────────────────────────┐     ┌───────────────┐
│  AuthoringSession   │────▶│   AuthoringDispatcher    │────▶│ BaselineStore │
│  (staging state)    │     │   .Reflect()             │     │ (round-trip   │
│                     │     │   (the full cycle)       │     │  snapshots)   │
└─────────────────────┘     └────────────┬─────────────┘     └───────────────┘
                                         │ binds
                            ┌────────────▼─────────────┐
                            │ AuthoringDispatchCallbacks│
                            │ (view concerns — YOUR UI) │
                            └──────────────────────────┘

AuthoringSession — o estado de preparação

Uma classe simples [Serializable] (deliberadamente não um ScriptableObject): mantenha-a em um campo [SerializeField] da sua EditorWindow e você ganha snapshots de Undo nativos da Unity e sobrevivência ao domain reload de graça — o mesmo mecanismo por trás do Ctrl+Z da janela de autoria.

Ela possui todo o estado preparado:

  • edições de célula (Edits), novas linhas (NewRows), operações de estrutura (StructOps);
  • reordenações por aba (Reorders), renomeações de aba (TabRenames);
  • âncoras de baseline, edições isoladas.

Além desse estado, ela expõe a API de mutação/consulta:

  • SetStaged(...) — prepara uma edição de célula. Edições carregam um endereço lógico (aba · RecordId · campo); o ordinal físico da linha é um cache derivado, reresolvido pouco antes do reflect.
  • ResolveBaselineEdits(provider) — reancora todas as edições em relação ao baseline atual. Edições resolvíveis prosseguem; os três casos não resolvíveis (renomeação externa / exclusão externa / conflito de chave) são movidos para IsolatedEdits — excluídos do reflect, sinalizados, nunca descartados silenciosamente, nunca bloqueando a sessão.
  • Superfície de leitura do baseline: TabNames, TryGetBaselineTable(tab, out SheetTable) — acesso a esquema tipado (TypeToken, @desc, @overlap) sem você mesmo tocar no parser.
  • EffectiveStructOps() / PendingStructCount() — a visão composta e canônica das operações de estrutura.
  • Hooks de remapeamento (RemapFieldName / RemapRecordId / RemapTab) mantêm o estado preparado coerente através de renomeações.
  • LastProjectionResult armazena em cache a projeção mais recente.

AuthoringDispatcher — a orquestração do reflect

var dispatcher = new AuthoringDispatcher(session, callbacks, baselineStore);
dispatcher.Reflect();   // the entire cycle, one call

Reflect() executa o ciclo inteiro, em ordem:

  • validação prévia
  • reflect por origem — gravações cirúrgicas para local, reescrita segura para Google, seu próprio alvo para provedores personalizados
  • limpeza de estado retido
  • o limite de confirmação ClearUndo
  • reimportação automática com relatório

Além disso:

  • BuildProjectionResult() — uma projeção livre de efeitos colaterais do estado preparado atual como um ImportResult (valida como se tivesse sido refletido). Use-a para selos de erro ao vivo.
  • Session / Callbacks / Baselines públicos — provedores de origem personalizados os usam para montar seus alvos de reflect.

AuthoringDispatchCallbacks — o contrato da sua UI

Um pacote de 13 delegates gerais que o dispatcher chama para cada preocupação de visualização: ResolveSettings, diálogos de confirmação (ConfirmKeyRenames, ConfirmTabRenames, …), RenderReport (um Action<ImportReport> — tolerante a null, é apenas observacional), PushApprover, TriggerReimport, ClearUndo, Rebuild, e assim por diante.

Os 14 delegates de diálogo específicos das origens embutidas Local/Google vivem em um pacote opcional separado, o BuiltInSourceDialogs, que uma superfície externa ou provedor nunca precisa vincular.

A janela de autoria vincula padrões que exibem diálogos; o seu canvas vincula os seus próprios (ou no-ops). O motor nunca desenha a UI por conta própria.

Material de grafo

Para uma projeção "nó = registro, aresta = referência ∪ declaração":

  • ReferenceScanner (Core) — a única fonte da verdade para enumerar ocorrências de referência em todas as tabelas: escalares, elementos de lista, padrões explícitos. A mesma enumeração que o validador de referência usa, então o seu grafo e a validação concordam por construção. Scan(tables) / ScanTable / ScanField / IsReferenceField.
  • IEdgeContributor / EdgeSpec / EdgeContributorRegistry (Core) — plugins de domínio declaram arestas que o scanner não consegue ver (dentro de valores de tipo personalizado, links de coluna type, arestas de registro com um registro de payload). Colete-as via PluginRegistry.BuildEdgeContributors do Editor.
  • ReferenceIndex / RecordEdge (Core) — o snapshot montado sobre o qual o próprio canvas do Data Studio roda: Build(...) funde as referências escaneadas com as arestas de contribuidor de uma vez, e então OutEdges / InEdges / InCount respondem em O(1) por registro. Lista completa de membros na Referência da API.
  • IRecordCanvasAugmenter / CanvasAugmentBuilder (Core) — o contrato de sobreposição por aba, caso você queira que pacotes de domínio estendam o seu canvas da mesma forma que estendem o do Studio (nós virtuais, arestas extras, dicas de camada e de exibição).
  • ProjectionErrorMapper (Editor, puro) — mapeia a coordenada física de um erro de projeção (aba/linha/campo) para um endereço lógico (aba/RecordId/campo), para que você possa fixar selos de erro em nós em vez de números de linha.

Peças de apoio

TipoPara que a sua superfície o usa
ImportEventsDois barramentos, ambos contratos públicos. ImportCompleted (ImportCompletedArgs: Tabs · BakeFolder) dispara quando a cadeia automática rodou até o fim, passando pelo bake, então um assinante pode ler os assets cozidos. BaselineUpdated (BaselineUpdatedArgs: Tabs · Quarantined) dispara sempre que um snapshot de planilha foi salvo — incluindo uma execução que falhou na validação — que é o que uma superfície assina se quiser mostrar as planilhas com falha e deixar as pessoas corrigi-las. Inscreva-se nos dois se a sua visualização mostra planilhas e valores cozidos; cancele a inscrição simetricamente em OnDisable.
IPipelineObserverSe o que precisa saber é um plugin, e não uma janela, este é o caminho mais leve: registre um observador e receba um PipelineRunView imutável ao final de cada ciclo de importação, sem nenhuma dependência de editor — funciona também no host do navegador. Veja Autoria de Plugins §4.17.
RecordIdMinterSugere ids para novos registros — detecção de prefixo + unificação segura contra colisão. Uma API de sugestão, deliberadamente não uma numeração automática.
EphemeralSoApplyPré-visualiza valores preparados nos SOs do bake temporariamente (a reimportação restaura). Aplica o subconjunto computável; retorna motivos de omissão para colunas pendentes e falhas de parsing. Nada na UI distribuída o aciona mais, então uma superfície que quer essa pré-visualização é dona do botão para isso.
KeyRenamePlannerPlaneja renomeações de chave (3 estágios: extração / propagação / cirurgia), da mesma forma que a janela de autoria faz. As confirmações de renomeação de aba passam, em vez disso, pelo callback público ConfirmTabRenames.
SourceProviderRegistryResolve o provedor de origem ativo da mesma forma que a UI de configurações faz.

Regras fundamentais que o kernel impõe (e que você herda)

  • A planilha permanece canônica — a sua superfície prepara e reflete; ela nunca grava SOs.
  • Validar-depois-refletirReflect() não grava nada se a validação prévia falhar.
  • Sem perda silenciosa — edições não resolvíveis se isolam com um motivo; confirmações passam pelos seus callbacks.
  • O Undo se integra nativamente — mantenha a sessão em um campo serializado e registre snapshots de undo na sua janela; ClearUndo marca o limite do reflect.
  • Agnóstico de domínio — o kernel contém zero vocabulário de domínio (testado por guarda). O seu domínio chega via os contratos de plugin, não via edições no kernel.

Páginas relacionadas