Communitygithub.com

MidMeantime/novel-ai-scripting

NovelAI 使用者腳本、TypeScript 腳本規則、API v1 參考、UI 擴充、生成鉤子與文字生成配置的完整指南與參考。適用於編寫、除錯、重構或審查 NovelAI 腳本、Lorebook 整合、上下文構建或 NovelAI TypeScript 程式碼。

novel-ai-scripting 是什麼?

此技能提供開發 NovelAI 使用者腳本及與 NovelAI 文字生成引擎互動的完整指引,包括 TypeScript 編寫規則、API v1 參考、UI 擴充架構及文字生成配置。它基於 NovelAI 官方的腳本文檔和 TypeScript 類型定義,涵蓋從基礎架構(隔離的 Web Worker 執行環境)到進階主題(如 JSX UI 組件、主題 Token 與工廠函數)的所有內容。此技能支援 Claude Code、Cursor、Codex 等 AI 代理工具,幫助開發者編寫、除錯、重構或審查 NovelAI 腳本,並整合 Lorebook、上下文建構等功能。技能內容包含關鍵的 TypeScript 類型安全規則(如顯式參數註解、物件迭代型別轉換)以及 API 命名空間(如 storage、hooks、ui、document、editor 等)的詳細參考,並提供標準腳本模板和 UI 組件系統的最佳實踐。

相容平台~Claude Code✓Codex CLI~Cursor
npx skills add MidMeantime/nai-scripting-skill

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

NovelAI 腳本與 TypeScript 技能指南

此技能提供開發 NovelAI 使用者腳本及與 NovelAI 文字生成引擎互動的完整說明、API 參考、型別規則與架構指南。


1. 快速參考與文檔檔案

所有深入文檔與型別定義位於 references/ 目錄中:

  • TypeScript 指南 — 避免在 Web Worker 解譯器中發生執行時崩潰的關鍵規則、型別模式、事件處理與型別轉換。
  • 腳本 API 完整文檔 — 所有 api.v1 命名空間(Storage、UI、Document、Hooks、Generation、Lorebook、TTS 等)的完整參考。
  • 文字生成文檔 — NovelAI 編輯器與網站功能的背景知識(模型、上下文設定、取樣器、Lorebook UI、ATTG、文字冒險)。腳本程式碼通常不需要;當腳本需要推理編輯器如何構建上下文或某個設定的含義時查閱。
  • 腳本型別定義(.d.ts) — 官方 TypeScript 定義(來自 https://novelai.net/scripting/types/script-types.d.ts)。這是屬性名稱與允許值的權威來源。 當與文字文檔衝突時,請信任型別定義。
  • JSX 指南 + JSX 型別 — 基於 Preact 的 jsx UI 部分,用於自訂視覺元素(表格、SVG/Canvas 圖表、即時小工具):全域變數(h、render、鉤子、createStore)、元素/屬性白名單、事件、Canvas 以及如何進行型別檢查。僅選擇性使用:僅在使用者明確要求時才使用 JSX。 否則使用標準部分構建 UI,即使是複雜佈局也至少提及 JSX 可能有所幫助。
  • 實驗型別 — api.v1.editor.block() 和 api.v1.document.transaction(),受使用者 experimentalScriptingApi 設定限制(請參閱 API 文檔第 18 節)。除非使用者表示已啟用,否則不要使用。
  • UI 組件系統 — 如何構建可重複使用、可擴展的 UI:Token、樣式預設、型別化組件工廠、儲存綁定欄位、渲染/刷新策略、可重複使用的對話框。對於任何包含 UI 的腳本,請將其用作預設結構。
  • 腳本標頭與配置範例 — /*--- ... ---*/ 腳本標頭,包含所有配置選項型別(字串、多行字串、數字、布林值、選取)。
  • 官方範例:https://github.com/NovelAI/novelai-script-examples。*腳本演示*場景以及如*尋找與取代*、*筆記*和*智慧摘要器*等腳本展示了真實世界模式。

2. 核心架構與執行環境

  1. 隔離的 Web Worker 執行:
    • 腳本在 Web Worker 內部的隔離 JavaScript 解譯器中執行。
    • 腳本無法存取任意的 DOM 元素或發出原始網路請求。
    • 所有與 NovelAI 的互動都通過版本化的 api.v1 非同步介面進行。
    • 全域 setTimeout/clearTimeout 不可用。請使用 await api.v1.timers.setTimeout(...)、api.v1.timers.clearTimeout(...) 和 api.v1.timers.sleep(ms)。
  2. 腳本是暫時的:
    • 當故事切換、使用者腳本模態框關閉或切換時,腳本可能會被重新載入或卸載。
    • 切勿在記憶體全域變數中儲存持久化狀態。 始終使用 api.v1.storage 持久化狀態。
  3. 靜默 Promise 拒絕:
    • Worker 中未處理的拒絕 Promise 不會列印到控制台或 UI。
    • 始終使用 try/catch 包裝非同步呼叫,或附加 .catch((err) => ...) 處理器。
  4. 緊急恢復:
    • 如果腳本導致不穩定,請在 NovelAI URL 附加 ?noscript=true 以在載入時停用腳本執行。

3. NovelAI 腳本的關鍵 TypeScript 指南

Web Worker 解譯器需要乾淨、嚴格註解的 TypeScript 以避免編譯/執行時錯誤:

A. 明確的參數註解

始終為標準函數和箭頭回呼中的參數註明型別:

// ❌ 錯誤
function processEntry(id, data) { ... }
items.filter(item => item.active);

// ✅ 正確
function processEntry(id: string, data: any): void { ... }
items.filter((item: any) => item.active);

B. 物件迭代型別轉換

Object.entries() 和 Object.values() 必須明確進行型別轉換:

// ✅ 正確
for (const [key, value] of Object.entries(myObj) as [string, any][]) { ... }
for (const val of Object.values(myObj) as any[]) { ... }

C. 陣列位移的 undefined 檢查

Array.prototype.shift() 返回 T | undefined:

// ✅ 正確
const item = queue.shift();
if (!item) return;

D. 僅在非同步上下文中使用 Await

呼叫非同步 API 方法的 UI 建構器和輔助函數必須是 async:

// ✅ 正確
async function loadUserData(): Promise<void> {
    const hasData = await api.v1.storage.has('userData');
    if (hasData) {
        const data = await api.v1.storage.get('userData');
    }
}

4. 關鍵 API 命名空間 (api.v1)

命名空間 / 方法用途
api.v1.storage持久化鍵值儲存:get、getOrDefault、set、setIfAbsent、has、remove、list(也有 storyStorage、historyStorage、tempStorage)。
api.v1.hooks註冊事件生命週期鉤子(onGenerationRequested、onBeforeContextBuild、onContextBuilt、onResponse、onGenerationEnd、onScriptsLoaded、onLorebookEntrySelected、onTextAdventureInput、onHistoryNavigated、onDocumentConvertedToText)。每個腳本每個鉤子只能有一個回呼;再次註冊會取代它。
api.v1.ui建立 UI 擴充(scriptPanel、sidebarPanel、lorebookPanel、toolbarButton、contextMenuButton、toolboxOption)、模態框、視窗、提示訊息;使用 updateParts() 按 id 變更單個部分。
api.v1.document按章節(段落)ID 讀取和編輯故事文件:scan()、sectionIds()、textFromSelection()、append()、appendParagraph(s)()、insertParagraph(s)After()、updateParagraph(s)()、removeParagraph(s)(),以及 document.history。
api.v1.editorgenerate()、isBlocked()、selection.get(),以及 editor.decorations(高亮、標記、小工具)。
api.v1.memory / systemPrompt / prefill取得或設定故事的記憶、系統提示和預填文字(設定需要 storyEdit)。
api.v1.generate()使用自訂參數和上下文執行直接文字生成。
api.v1.generateWithStory()使用目前故事上下文和設定執行生成。
api.v1.lorebook查詢、更新、建立或刪除 Lorebook 條目和類別。
api.v1.an取得或設定作者註記文字。
api.v1.buildContext()在上下文發送到 AI 之前存取和檢查其組合。
api.v1.configawait api.v1.config.get('name') 讀取在腳本標頭中聲明的使用者可配置值(見下文)。
api.v1.permissionshas() / request() / list()。在使用受限 API 之前,請求 documentEdit、storyEdit、lorebookEdit、editorDecorations、clipboardWrite、fileInput、fileDownload(以及 presetEdit)。

腳本標頭與配置

每個 .naiscript 以 /*--- ... ---*/ 註解中的 YAML 標頭開頭(compatibilityVersion: naiscript-1.0、id、name、version、author、description、memoryLimit、可選的 config)。config 條目(name、prettyName、type、default、description)會顯示在使用者腳本模態框的配置標籤中,因此使用者無需編輯程式碼即可變更它們。型別為 string(可選 multiline: true)、number(可選 min/max/decimal)、boolean 和 select(options: [{ label, value }];預設為第一個選項)。請參閱 script_config_examples.ts。在執行時使用回退讀取值:

const openOnCreate: boolean = (await api.v1.config.get('open_on_create')) ?? true;

5. 標準腳本模板

建立新的 NovelAI 腳本時,請遵循此結構:

(async () => {
    try {
        const STORAGE_KEY = "my_custom_script_data";

        // 1. 初始化持久化狀態
        const state = await api.v1.storage.getOrDefault(STORAGE_KEY, { enabled: true, count: 0 });
        await api.v1.storage.setIfAbsent(STORAGE_KEY, state);

        // 2. 註冊生命週期鉤子
        // onGenerationRequested 在上下文構建之前執行;它只能停止生成或準備狀態。
        api.v1.hooks.register('onGenerationRequested', async (params: any) => {
            if (!state.enabled) return;
            // 在此為後續鉤子準備狀態,或返回 { stopGeneration: true } 以阻止它
        });

        // 若要修改模型看到的內容,請使用 onBeforeContextBuild(記憶、lorebook、AN...)
        // 或 onContextBuilt(返回 { messages } 與編輯後的訊息陣列)。
        api.v1.hooks.register('onContextBuilt', async (params: any) => {
            if (!state.enabled || params.dryRun) return;
            return { messages: params.messages };
        });

        // 3. 註冊 UI — 始終使用 update(),而非 register()
        api.v1.hooks.register('onScriptsLoaded', async () => {
            await api.v1.ui.update([{
                type: 'sidebarPanel' as const,
                id: 'my-script-panel',
                name: '自訂工具',
                content: [
                    { type: 'text' as const, text: `狀態:${state.enabled ? '啟用' : '停用'}` } as any
                ]
            }]);
        });

    } catch (err: any) {
        api.v1.error("腳本初始化錯誤:", err);
    }
})();

6. 樣式、主題 Token 與工廠函數

NovelAI 腳本在隔離的 Web Worker 中執行,無法存取 DOM。切勿使用 document.createElement、document.head、cloneNode 或任何直接操作頁面的瀏覽器 API。所有 UI 都通過傳遞給 api.v1.ui.update() 的宣告式 UI 部分物件構建。

任何包含 UI 的腳本的預設架構:遵循 NovelAI_UI_Component_System.md。 它提供一個可重複使用、可擴展的分層結構,並對官方型別進行型別檢查:

  1. 命名空間與 Token:NS + id()、COLOR 主題 Token 對應、SPACE/RADIUS 常數
  2. 結合 sx() 的樣式預設
  3. 基於 api.v1.ui.part.* 的組件工廠,帶有選項物件和變體
  4. field() 綁定,保持 storageKey 前綴與儲存 API 同步
  5. 一個 render(),搭配 updateParts/{{templates}} 進行小幅刷新
  6. 可重複使用的 confirm()/notify() 對話框

下面的程式碼片段是相同概念的簡化版本。如果使用者要求 JSX,請參閱 NovelAI_JSX_Guide.md。JSX 樣式使用普通 CSS 和 var(--theme-*),而非 Token 名稱。不要自行切換到 JSX。

樣式物件預設

在腳本頂部定義型別化的樣式物件,並將其展開到 UI 部分上:

const CARD_STYLE: Record<string, string> = {
    backgroundColor: 'bg1',      // 主題 Token
    border: '1px solid bg2',     // 主題 Token
    borderRadius: '6px',
    padding: '10px',
    margin: '6px 0',
};

const DANGER_STYLE: Record<string, string> = {
    ...CARD_STYLE,
    borderColor: 'warning',
};

// 透過展開應用於 UI 部分:
{ type: 'container' as const, style: CARD_STYLE, content: [...] } as any

主題 Token

請勿硬編碼十六進位顏色,直接在 style 物件中使用 NovelAI 主題 Token 字串作為值。在渲染時,每個 Token 都會被替換為使用者活躍主題的值,作為行內字面值(例如 'bg1' 變成 rgb(...) 代碼)。這些不是 CSS 變數:沒有 --theme-* 自訂屬性可與 var() 一起使用,而且網站上其他地方的自訂 CSS 也無法引用它們。若要在腳本內取得原始主題值,請使用 api.v1.theme.get()。(例外:根據官方型別,JSX UI 部分在其自身的 Shadow DOM 內會獲得 --theme-* 自訂屬性。)

Token角色
'bg0'最深/最外層背景
'bg1'預設面板背景
'bg2'凸起表面/邊框/按鈕背景
'bg3'高亮/選中的表面
'textHeadings'高對比度的標題和標題文字
'textMain'預設正文文字
'textDisabled'停用/減弱文字
'textPlaceholder'輸入框預留位置文字
'textHighlight'搜尋/高亮顏色
'warning'警告/警示狀態
'textPrompt'、'textUser'、'textAI'、'textEdit'故事文字顏色
'lowIntensityColor'、'midIntensityColor'、'highIntensityColor'低/中/高機率顏色(如 Token 機率所使用的)
'default'正文字型(在 fontFamily 中使用)
'headings'標題字型(在 fontFamily 中使用)
'code'等寬程式碼字型
'field'表單/輸入欄位字型

⚠️ 關於 'primary' 的注意事項:NovelAI 沒有 'primary' 或 'error' 主題 Token。使用 color: 'primary' 會產生無效的 CSS,瀏覽器會忽略它。對於活動/高亮表面,請使用 'bg3';對於高對比度強調,請使用 'textHeadings';或提供明確的十六進位/rgba 顏色字串(例如 '#90caf9')。

工廠函數(取代 cloneNode / 模板)

在腳本頂部編寫工廠函數,返回預先設定樣式和配置的 UI 部分物件。這可以避免在整個腳本中重複行內樣式物件。

// 可重複使用的樣式化按鈕工廠
function makeButton(
    text: string,
    onClick: () => Promise<void>

…

[為翻譯而截斷]

相關技能