Guia de Scripting & TypeScript do NovelAI
Este skill fornece instruções abrangentes, referências de API, regras de tipagem e diretrizes arquiteturais para desenvolver Scripts de Usuário do NovelAI e interagir com o motor de geração de texto do NovelAI.
1. Referência Rápida e Arquivos de Documentação
Toda a documentação detalhada e tipos estão localizados no diretório references/:
- Diretrizes TypeScript — Regras críticas para evitar falhas de tempo de execução no interpretador Web Worker, padrões de tipagem, manipulação de eventos e casting.
- Documentação Completa da API de Scripting — Referência completa para todas as namespaces
api.v1(Storage, UI, Document, Hooks, Generation, Lorebook, TTS, etc.). - Documentação de Geração de Texto — Informações sobre o editor e recursos do site NovelAI (modelos, configurações de contexto, amostradores, interface do lorebook, ATTG, Text Adventure). Raramente necessário para código de script; consulte-o quando um script precisar raciocinar sobre como o editor constrói o contexto ou o que uma configuração significa.
- Definição de Tipos de Script (
.d.ts) — As definições oficiais do TypeScript (dehttps://novelai.net/scripting/types/script-types.d.ts). Esta é a fonte da verdade para nomes de propriedades e valores permitidos. Quando ela discordar da documentação em prosa, confie nos tipos. - Guia JSX + Tipos JSX — Partes de UI
jsxbaseadas em Preact para visuais personalizados (tabelas, gráficos SVG/canvas, widgets ao vivo): globais (h,render, hooks,createStore), a lista de elementos/atributos permitidos, eventos, canvas e como fazer verificação de tipos. Opt-in apenas: use JSX somente quando o usuário explicitamente pedir. Caso contrário, construa a UI a partir de partes padrão, mesmo para layouts complexos, e no máximo mencione que JSX pode ajudar. - Tipos Experimentais —
api.v1.editor.block()eapi.v1.document.transaction(), controlados pela configuraçãoexperimentalScriptingApido usuário (veja Seção 18 da documentação da API). Não use estes a menos que o usuário diga que os habilitou. - Sistema de Componentes de UI — Como construir UI reutilizável e escalável: tokens, predefinições de estilo, fábricas de componentes tipados, campos vinculados ao armazenamento, estratégia de renderização/atualização, diálogos reutilizáveis. Use esta como a estrutura padrão para qualquer script com UI.
- Exemplo de Cabeçalho e Configuração de Script — O cabeçalho de script
/*--- ... ---*/com cada tipo de opção de configuração (string, string multilinha, número, booleano, seleção). - Exemplos oficiais: https://github.com/NovelAI/novelai-script-examples. O cenário Scripting Demo e scripts como Find & Replace, Notes, e Smart Summarizer mostram padrões do mundo real.
2. Arquitetura Central e Ambiente de Execução
- Execução Isolada em Web Worker:
- Scripts são executados dentro de um interpretador JavaScript isolado em um Web Worker.
- Scripts não podem acessar elementos arbitrários do DOM ou fazer requisições de rede brutas.
- Todas as interações com o NovelAI ocorrem através da interface assíncrona versionada
api.v1. - Os globais
setTimeout/clearTimeoutnão estão disponíveis. Useawait api.v1.timers.setTimeout(...),api.v1.timers.clearTimeout(...), eapi.v1.timers.sleep(ms).
- Scripts São Transientes:
- Scripts podem ser recarregados ou descarregados quando as histórias mudam, quando o modal de Scripts de Usuário fecha, ou quando são ativados/desativados.
- Nunca armazene estado persistente em variáveis de memória global. Sempre persista o estado usando
api.v1.storage.
- Rejeições Silenciosas de Promessas:
- Promessas rejeitadas não tratadas no worker não são exibidas no console ou na interface.
- Sempre envolva chamadas assíncronas com
try/catchou anexe manipuladores.catch((err) => ...).
- Recuperação de Emergência:
- Se um script causar instabilidade, anexe
?noscript=trueà URL do NovelAI para desabilitar a execução do script no carregamento.
- Se um script causar instabilidade, anexe
3. Diretrizes Críticas de TypeScript para Scripts NovelAI
O interpretador Web Worker requer código TypeScript limpo e estritamente anotado para evitar erros de compilação/tempo de execução:
A. Anotações Explícitas de Parâmetros
Sempre tipifique todos os parâmetros em funções padrão e callbacks de arrow:
// ❌ ERRADO
function processEntry(id, data) { ... }
itens.filter(item => item.active);
// ✅ CORRETO
function processEntry(id: string, data: any): void { ... }
itens.filter((item: any) => item.active);
B. Casting em Iteração de Objetos
Object.entries() e Object.values() devem ser explicitamente convertidos:
// ✅ CORRETO
for (const [key, value] of Object.entries(meuObj) as [string, any][]) { ... }
for (const val of Object.values(meuObj) as any[]) { ... }
C. Verificações de Indefinido em shift de Array
Array.prototype.shift() retorna T | undefined:
// ✅ CORRETO
const item = fila.shift();
if (!item) return;
D. Await Apenas em Contextos Assíncronos
Funções auxiliares e de construção de UI que invocam métodos assíncronos da API devem ser async:
// ✅ CORRETO
async function carregarDadosUsuario(): Promise<void> {
const temDados = await api.v1.storage.has('userData');
if (temDados) {
const dados = await api.v1.storage.get('userData');
}
}
4. Principais Namespaces da API (api.v1)
| Namespace / Método | Propósito |
|---|---|
api.v1.storage | Armazenamento chave-valor persistente: get, getOrDefault, set, setIfAbsent, has, remove, list (também storyStorage, historyStorage, tempStorage). |
api.v1.hooks | Registrar hooks do ciclo de vida de eventos (onGenerationRequested, onBeforeContextBuild, onContextBuilt, onResponse, onGenerationEnd, onScriptsLoaded, onLorebookEntrySelected, onTextAdventureInput, onHistoryNavigated, onDocumentConvertedToText). Um callback por hook por script; registrar novamente substitui o anterior. |
api.v1.ui | Criar extensões de UI (scriptPanel, sidebarPanel, lorebookPanel, toolbarButton, contextMenuButton, toolboxOption), modais, janelas, notificações; updateParts() para alterar partes individuais por id. |
api.v1.document | Ler e editar o documento da história por ID de seção (parágrafo): scan(), sectionIds(), textFromSelection(), append(), appendParagraph(s)(), insertParagraph(s)After(), updateParagraph(s)(), removeParagraph(s)(), além de document.history. |
api.v1.editor | generate(), isBlocked(), selection.get(), e editor.decorations (destaques, marcadores, widgets). |
api.v1.memory / systemPrompt / prefill | Obter ou definir a Memória, o Prompt do Sistema e o Preenchimento da história (definir requer storyEdit). |
api.v1.generate() | Executar geração de texto direta com parâmetros personalizados e contexto. |
api.v1.generateWithStory() | Executar geração usando o contexto e as configurações atuais da história. |
api.v1.lorebook | Consultar, atualizar, criar ou excluir entradas e categorias do Lorebook. |
api.v1.an | Obter ou definir o texto da Nota do Autor. |
api.v1.buildContext() | Acessar e inspecionar a montagem do contexto antes de ser enviado à IA. |
api.v1.config | await api.v1.config.get('nome') lê um valor configurável pelo usuário declarado no cabeçalho do script (veja abaixo). |
api.v1.permissions | has() / request() / list(). Solicite documentEdit, storyEdit, lorebookEdit, editorDecorations, clipboardWrite, fileInput, fileDownload (e presetEdit) antes de usar as APIs controladas. |
Cabeçalho e Configuração do Script
Todo .naiscript começa com um cabeçalho YAML em um comentário /*--- ... ---*/ (compatibilityVersion: naiscript-1.0, id, name, version, author, description, memoryLimit, opcional config). As entradas config (name, prettyName, type, default, description) aparecem na aba Config do modal de Scripts de Usuário, permitindo que os usuários as alterem sem editar o código. Os tipos são string (opcionalmente multiline: true), number (opcional min/max/decimal), boolean, e select (options: [{ label, value }]; padrão é a primeira opção). Veja script_config_examples.ts. Leia valores em tempo de execução com um fallback:
const abertoAoCriar: boolean = (await api.v1.config.get('open_on_create')) ?? true;
5. Estrutura Padrão de Script
Ao criar um novo script NovelAI, siga esta estrutura:
(async () => {
try {
const CHAVE_ARMAZENAMENTO = "meus_dados_script_personalizado";
// 1. Inicializar estado persistente
const estado = await api.v1.storage.getOrDefault(CHAVE_ARMAZENAMENTO, { habilitado: true, contador: 0 });
await api.v1.storage.setIfAbsent(CHAVE_ARMAZENAMENTO, estado);
// 2. Registrar hooks do ciclo de vida
// onGenerationRequested é executado antes da construção do contexto; só pode parar a geração ou preparar o estado.
api.v1.hooks.register('onGenerationRequested', async (params: any) => {
if (!estado.habilitado) return;
// Prepare o estado para hooks posteriores aqui, ou retorne { stopGeneration: true } para bloqueá-lo
});
// Para modificar o que o modelo vê, use onBeforeContextBuild (memória, lorebook, AN...)
// ou onContextBuilt (retorne { messages } com o array de mensagens editado).
api.v1.hooks.register('onContextBuilt', async (params: any) => {
if (!estado.habilitado || params.dryRun) return;
return { messages: params.messages };
});
// 3. Registrar UI — sempre use update(), NUNCA register()
api.v1.hooks.register('onScriptsLoaded', async () => {
await api.v1.ui.update([{
type: 'sidebarPanel' as const,
id: 'meu-painel-script',
name: 'Ferramenta Personalizada',
content: [
{ type: 'text' as const, text: `Status: ${estado.habilitado ? 'Ativo' : 'Inativo'}` } as any
]
}]);
});
} catch (err: any) {
api.v1.error("Erro de Inicialização do Script:", err);
}
})();
6. Estilização, Tokens de Tema e Funções Fábrica
Scripts NovelAI são executados em um Web Worker isolado com nenhum acesso ao DOM. Nunca use document.createElement, document.head, cloneNode, ou qualquer API de navegador que toque na página diretamente. Toda a UI é construída a partir de objetos de Parte de UI declarativos passados para api.v1.ui.update().
Arquitetura padrão para qualquer script com UI: siga NovelAI_UI_Component_System.md. Ele tem uma camada reutilizável e escalável que faz verificação de tipos contra os tipos oficiais:
- Namespace e tokens:
NS+id(), mapa de tokens de temaCOLOR, constantesSPACE/RADIUS- Predefinições de estilo combinadas com
sx()- Fábricas de componentes construídas em
api.v1.ui.part.*com objetos de opções e variantes- Ligações
field()que mantêm prefixosstorageKeye APIs de armazenamento sincronizados- Um
render(), comupdateParts/{{templates}}para pequenas atualizações- Diálogos reutilizáveis
confirm()/notify()Os trechos abaixo são a versão mínima da mesma ideia. Se o usuário pedir JSX, veja NovelAI_JSX_Guide.md. Estilos JSX usam CSS normal e
var(--theme-*), não nomes de tokens. Não mude para JSX por iniciativa própria.
Predefinições de Objetos de Estilo
Defina objetos de estilo tipados no topo do seu script e espalhe-os nas partes de UI:
const ESTILO_CARD: Record<string, string> = {
backgroundColor: 'bg1', // token de tema
border: '1px solid bg2', // token de tema
borderRadius: '6px',
padding: '10px',
margin: '6px 0',
};
const ESTILO_PERIGO: Record<string, string> = {
...ESTILO_CARD,
borderColor: 'warning',
};
// Aplique via spread em uma Parte de UI:
{ type: 'container' as const, style: ESTILO_CARD, content: [...] } as any
Tokens de Tema
Em vez de codificar cores hexadecimais, use strings de token de tema NovelAI diretamente como valores em objetos style. No momento da renderização, cada token é substituído pelo valor do tema ativo do usuário como um valor inline literal (por exemplo, 'bg1' torna-se um código rgb(...)). Estes não são variáveis CSS: não há propriedades personalizadas --theme-* para usar com var(), e CSS personalizado em outro lugar no site não pode referenciá-los. Para valores brutos do tema dentro de um script, use api.v1.theme.get(). (Exceção: de acordo com os tipos oficiais, partes de UI JSX obtêm propriedades personalizadas --theme-* dentro de seu próprio shadow DOM.)
| Token | Função |
|---|---|
'bg0' | Fundo mais profundo / mais externo |
'bg1' | Fundo padrão do painel |
'bg2' | Superfície elevada / borda / fundo do botão |
'bg3' | Superfície destacada / selecionada |
'textHeadings' | Títulos de alto contraste e texto de título |
'textMain' | Texto do corpo padrão |
'textDisabled' | Texto desabilitado / silenciado |
'textPlaceholder' | Texto de espaço reservado de entrada |
'textHighlight' | Cor de pesquisa / destaque |
'warning' | Estado de aviso / alerta |
'textPrompt', 'textUser', 'textAI', 'textEdit' | Cores do texto da história |
'lowIntensityColor', 'midIntensityColor', 'highIntensityColor' | Cores de probabilidade baixa/média/alta (como usadas por probabilidades de token) |
'default' | Família de fonte do corpo (use em fontFamily) |
'headings' | Família de fonte de títulos (use em fontFamily) |
'code' | Família de fonte monoespaçada de código |
'field' | Família de fonte de campo de formulário/entrada |
⚠️ Nota sobre
'primary': NovelAI NÃO tem um token de tema'primary'ou'error'. Usarcolor: 'primary'produz CSS inválido que os navegadores ignoram. Para superfícies ativas/destacadas use'bg3', para acentos de alto contraste use'textHeadings', ou forneça strings de cores hex/rgba explícitas (por exemplo,'#90caf9').
Funções Fábrica (Substitui cloneNode / Templates)
Escreva funções fábrica no topo do seu script que retornam objetos de Parte de UI pré-estilizados e pré-configurados. Isso evita repetir objetos de estilo inline em todo o script.
// Fábrica de botão estilizado reutilizável
function criarBotao(
texto: string,
aoClicar: () => Promise<void>
…
[truncado para tradução]