InícioDocs para devstable.interface: Busca de documentos autorizados
Exemplos por kind 5 min de leitura

table.interface: Busca de documentos autorizados

Busque documentos da mesa em um painel e mostre nomes, kinds e trechos como texto.

O que você vai construir

Busque documentos da mesa em um painel e mostre nomes, kinds e trechos como texto.

Superfície: table.interface · Modo: augment · Gravewright 0.3.0 / SDK 1.x.

Quando monta e quem pode usar

Abra a mesa, digite uma consulta e envie Buscar. api.table.search do módulo verifica novamente vínculo e acesso. O exemplo apenas exibe resultados; não inventa APIs de abertura de documento.

O dono instala o ZIP local em Módulos → Instalar módulo → Instalar por ZIP. Depois, o mestre ativa a versão na lista de extensões da mesa. O exemplo não pede permissões adicionais nem obtém um handle global; todas as leituras usam o api fornecido pela montagem. Mestre e jogador recebem somente os dados autorizados pelo servidor. Streamers não recebem esta montagem autenticada.

Baixe o pacote completo

Baixar ZIP dos fontes · manifest.json · main.mjs · styles.css · LICENSE.txt

Salve os quatro arquivos na mesma pasta. Compacte o conteúdo da pasta, com manifest.json diretamente na raiz do ZIP. Não inclua uma pasta extra, estado local ou chaves privadas. O upload local é uma decisão de confiança do dono; para distribuição por catálogo, use o fluxo de assinatura da referência de módulos.

text
table-interface/
  manifest.json
  main.mjs
  styles.css
  LICENSE.txt

manifest.json

json
{
  "id": "example.surface-table-interface",
  "name": "Authorized document search",
  "version": "1.0.0",
  "sdk": {
    "requires": ">=1.0.0 <2.0.0",
    "tested": "1.0.0"
  },
  "description": "Search table documents from a workspace panel and render their names, kinds and snippets as text.",
  "author": "Gravewright documentation",
  "license": "MIT",
  "entry": "main.mjs"
}

main.mjs

javascript
// Original documentation example. License: MIT. Target: Gravewright 0.3.0, SDK 1.x.
export default {
  start({ api, styles }) {
    styles.use("styles.css");
    api.i18n.register({
      "en": {
        "Search": "Search",
        "Search documents": "Search documents",
        "No results": "No results",
        "Refresh": "Refresh",
        "Loading…": "Loading…",
        "Error: {code}": "Error: {code}",
        "Authorized document search": "Authorized document search"
      },
      "pt-BR": {
        "Search": "Buscar",
        "Search documents": "Buscar documentos",
        "No results": "Nenhum resultado",
        "Refresh": "Atualizar",
        "Loading…": "Carregando…",
        "Error: {code}": "Erro: {code}",
        "Authorized document search": "Busca de documentos autorizados"
      }
    });
  },
  register({ register }) {
    register("table.interface", { mode: "augment", mount: this.mount });
  },
  async mount({ root, context, api, storage, viewport, events, signal, onDispose }) {
    const el = (tag, text) => {
      const node = root.ownerDocument.createElement(tag);
      if (text !== undefined) node.textContent = text;
      return node;
    };
    const t = (key, params) => api.i18n.t(key, params);
    const panel = el("section"); panel.className = "gw-example";
    const heading = el("h3", t("Authorized document search"));
    const output = el("pre"); output.setAttribute("role", "status");
    panel.append(heading, output); root.append(panel);
    onDispose(() => panel.remove());
    const report = error => {
      if (!signal.aborted) output.textContent = t("Error: {code}", { code: error.code ?? "unavailable" });
    };
    const form = el("form");
    const label = el("label", t("Search documents"));
    const input = el("input"); input.type = "search"; input.maxLength = 200;
    const submit = el("button", t("Search")); submit.type = "submit";
    label.append(input); form.append(label, submit); panel.append(form);
    let ticket = 0;
    form.addEventListener("submit", async event => {
      event.preventDefault();
      const current = ++ticket;
      try {
        const value = await api.table.search(input.value.trim(), { signal });
        if (signal.aborted || current !== ticket) return;
        output.textContent = value.map(result => result.title + " (" + result.kind + ") · " + (result.snippet ?? "")).join("\n") || t("No results");
      } catch (error) { if (current === ticket) report(error); }
    }, { signal });
  },
  stop() {
    // Mount and module lifetimes already dispose styles, catalogs and subscriptions.
  }
};

styles.css

css
[data-module-id="example.surface-table-interface"] .gw-example { box-sizing: border-box; max-width: 640px; padding: var(--space-3); margin: var(--space-2); border: 1px solid var(--border); border-radius: var(--radius-md); color: var(--text); background: var(--surface); font: 14px/1.5 system-ui, sans-serif; }
[data-module-id="example.surface-table-interface"] .gw-example h3 { font-size: 16px; margin: var(--space-0) var(--space-0) var(--space-2); }
[data-module-id="example.surface-table-interface"] .gw-example pre { white-space: pre-wrap; overflow-wrap: anywhere; font: inherit; }
[data-module-id="example.surface-table-interface"] .gw-example button, [data-module-id="example.surface-table-interface"] .gw-example select, [data-module-id="example.surface-table-interface"] .gw-example input { color: inherit; background: var(--surface); font: inherit; padding: var(--space-1) var(--space-2); margin: var(--space-1); border: 1px solid var(--border); border-radius: var(--radius-sm); }
[data-module-id="example.surface-table-interface"] .gw-example button:focus-visible, [data-module-id="example.surface-table-interface"] .gw-example input:focus-visible, [data-module-id="example.surface-table-interface"] .gw-example select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
[data-module-id="example.surface-table-interface"] .gw-example--compact { padding: var(--space-1); }
[data-module-id="example.surface-table-interface"] .gw-example-center { position: absolute; transform: translate(-50%, -50%); pointer-events: none; color: var(--text); background: var(--surface); padding: var(--space-1); border-radius: var(--radius-sm); font: 14px/1.5 system-ui, sans-serif; }
[data-module-id="example.surface-table-interface"] > .gw-example { position: fixed; top: 64px; left: var(--space-4); width: 360px; max-width: min(360px, calc(100vw - 32px)); max-height: 40vh; overflow: auto; margin: var(--space-0); z-index: 20; }

Resultado esperado e liberação

Busque documentos da mesa em um painel e mostre nomes, kinds e trechos como texto.

A interface nativa continua funcionando. Fechar a ficha, trocar a cena, sair da mesa ou desativar o módulo aborta a montagem. Os listeners usam signal; os eventos e elementos próprios são liberados por onDispose. A leitura assíncrona verifica o aborto e descarta respostas antigas antes de desenhar. Erros autorizados aparecem por error.code, sem esconder falhas de rede.

O painel de busca flutua abaixo da barra superior, limitado à largura da janela e a 40% de sua altura, com rolagem própria. Seu z-index: 20 fica acima dos controles da mesa e abaixo de janelas e modais. Todos os seletores deste pacote usam data-module-id, preservando os estilos do host e de outros módulos.

Valide em uma mesa descartável

  1. Busque o mesmo termo como mestre e jogador; documentos ocultos não devem aparecer. Envie duas buscas rápidas e confira que prevalece a mais recente.

  2. Abra/feche três vezes e desative durante uma leitura ou modal pendente. Confira que nenhum painel ou erro antigo reaparece após o encerramento.

  3. Teste estado vazio, nomes longos, teclado e janela estreita. Compare contas de mestre e jogador.

  4. Confira o console. Estes exemplos têm testes do contrato de runtime; instalação e aparência ainda precisam ser validadas no host alvo.

Como usar a IA para esta superfície

text
Adapte o exemplo table.interface para [objetivo concreto] no Gravewright 0.3.0.
Leia registry.json, o schema de contexto desta superfície, frontend.json e os serviços nativos usados.
Preserve start/register/stop, api da montagem, signal, onDispose e os modais nativos.
Mostre manifesto e arquivos completos. Não use app.shell/inside.content em pacotes.
Não invente campos de payload, regras de RPG no core, permissões ou APIs.
Teste jogador/mestre, dados vazios, aborto, montagem repetida e o comportamento específico do exemplo.
Explique limitações e o que foi realmente executado.

Continue

SDK · API · Exemplos por kind