Communitygithub.com

MidMeantime/novel-ai-scripting

NovelAI 사용자 스크립트, TypeScript 스크립팅 규칙, API v1 참조, UI 확장, 생성 훅 및 텍스트 생성 설정을 위한 종합 가이드 및 참조 자료입니다. NovelAI 스크립트 작성, 디버깅, 리팩토링, 로어북 통합, 컨텍스트 구성 및 NovelAI TypeScript 코드 검토 시 사용하세요.

novel-ai-scripting란 무엇인가요?

이 스킬은 NovelAI 플랫폼에서 실행되는 사용자 스크립트를 개발, 디버깅, 리팩토링 및 검토하기 위한 완전하고 공식적인 참조 가이드를 제공합니다. Web Worker 샌드박스 환경, 필수 TypeScript 타입 어노테이션 규칙, 그리고 `api.v1` 인터페이스를 통한 핵심 API 네임스페이스(Storage, Document, Hooks, UI, Generation, Lorebook, Editor 등)를 포함한 핵심 아키텍처를 다룹니다. 이 스킬은 스크립트 헤더 구성, 지속적인 UI 구축 방법, 테마 토큰, 팩토리 함수, 스타일 사전 설정을 설명하고, JSX 가이드, UI 컴포넌트 시스템, 공식 타입 정의(.d.ts)와 같은 추가 참조 자료를 가리킵니다. 런타임 충돌을 방지하기 위한 명시적 타입 캐스팅 및 프로미스 에러 처리 규칙, 영구 상태 저장을 위한 `api.v1.storage` 사용법, 그리고 모든 NovelAI 스크립트를 위한 검증된 표준 보일러플레이트 템플릿을 강조합니다. Claude Code, Cursor 등과 같은 도구에서 이 지식을 활용하여 NovelAI 스크립트를 올바르고 효율적으로 작성할 수 있습니다.

지원 대상~Claude Code✓Codex CLI~Cursor
npx skills add MidMeantime/nai-scripting-skill

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

NovelAI 스크립팅 및 TypeScript 스킬 가이드

이 스킬은 NovelAI 사용자 스크립트를 개발하고 NovelAI 텍스트 생성 엔진과 상호 작용하기 위한 포괄적인 지침, API 참조, 타입 규칙 및 아키텍처 가이드라인을 제공합니다.


1. 빠른 참조 및 문서 파일

모든 심층 문서와 타입은 references/ 디렉토리에 있습니다.

  • TypeScript 가이드라인 — Web Worker 인터프리터에서 런타임 충돌을 피하기 위한 중요한 규칙, 타입 패턴, 이벤트 처리 및 캐스팅.
  • 스크립팅 API 전체 문서 — 모든 api.v1 네임스페이스(Storage, UI, Document, Hooks, Generation, Lorebook, TTS 등)에 대한 전체 참조.
  • 텍스트 생성 문서 — NovelAI 편집기 및 웹사이트 기능(모델, 컨텍스트 설정, 샘플러, 로어북 UI, ATTG, 텍스트 어드벤처)에 대한 배경 정보. 스크립트 코드에는 거의 필요하지 않습니다. 스크립트가 편집기가 컨텍스트를 구성하는 방식이나 설정의 의미를 추론해야 할 때 참고하세요.
  • 스크립트 타입 정의 (.d.ts) — 공식 TypeScript 정의(https://novelai.net/scripting/types/script-types.d.ts에서 제공). 속성 이름과 허용 값의 진실 공급원입니다. 일반 문서와 의견이 다를 경우 타입 정의를 신뢰하세요.
  • JSX 가이드 + JSX 타입 — 사용자 정의 시각적 요소(테이블, SVG/캔버스 차트, 라이브 위젯)를 위한 Preact 기반 jsx UI 파트: 전역(h, render, 훅, createStore), 요소/속성 허용 목록, 이벤트, 캔버스 및 타입 검사 방법. 옵트인만 가능: 사용자가 명시적으로 요청한 경우에만 JSX를 사용하세요. 그 외에는 복잡한 레이아웃의 경우에도 표준 파트로 UI를 구축하고, 최대한 JSX가 도움이 될 수 있다고 언급하세요.
  • 실험적 타입 — api.v1.editor.block() 및 api.v1.document.transaction()은 사용자의 experimentalScriptingApi 설정 뒤에 있습니다(API 문서 섹션 18 참조). 사용자가 활성화했다고 말하지 않는 한 사용하지 마세요.
  • UI 컴포넌트 시스템 — 재사용 가능하고 확장 가능한 UI를 구축하는 방법: 토큰, 스타일 사전 설정, 타입이 지정된 컴포넌트 팩토리, 스토리지 바인딩 필드, 렌더링/새로고침 전략, 재사용 가능한 대화상자. UI가 있는 모든 스크립트의 기본 구조로 사용하세요.
  • 스크립트 헤더 및 구성 예제 — 모든 구성 옵션 유형(string, multiline string, number, boolean, select)이 포함된 /*--- ... ---*/ 스크립트 헤더.
  • 공식 예제: https://github.com/NovelAI/novelai-script-examples. Scripting Demo 시나리오와 Find & Replace, Notes, Smart Summarizer와 같은 스크립트는 실제 패턴을 보여줍니다.

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. 무음 프로미스 거부:
    • 작업자에서 처리되지 않은 거부된 프로미스는 콘솔이나 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. 배열 shift에 대한 정의되지 않음 검사

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.uiUI 확장 생성 (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로어북 항목 및 카테고리 쿼리, 업데이트, 생성 또는 삭제.
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(메모리, 로어북, AN...)를 사용하거나
        // onContextBuilt(편집된 메시지 배열과 함께 { messages }를 반환)를 사용합니다.
        api.v1.hooks.register('onContextBuilt', async (params: any) => {
            if (!state.enabled || params.dryRun) return;
            return { messages: params.messages };
        });

        // 3. UI 등록 — 항상 register()가 아닌 update()를 사용합니다.
        api.v1.hooks.register('onScriptsLoaded', async () => {
            await api.v1.ui.update([{
                type: 'sidebarPanel' as const,
                id: 'my-script-panel',
                name: 'Custom Tool',
                content: [
                    { type: 'text' as const, text: `Status: ${state.enabled ? 'Active' : 'Disabled'}` } as any
                ]
            }]);
        });

    } catch (err: any) {
        api.v1.error("Script Initialization Error:", err);
    }
})();

6. 스타일링, 테마 토큰 및 팩토리 함수

NovelAI 스크립트는 DOM에 접근할 수 없는 격리된 Web Worker에서 실행됩니다. document.createElement, document.head, cloneNode 또는 페이지에 직접 닿는 브라우저 API를 절대 사용하지 마세요. 모든 UI는 api.v1.ui.update()에 전달되는 선언적 UI 파트 객체로 빌드됩니다.

UI가 있는 모든 스크립트의 기본 아키텍처: NovelAI_UI_Component_System.md를 따르세요. 공식 타입에 대해 타입 검사를 수행하는 재사용 가능하고 확장 가능한 계층 구조가 있습니다.

  1. 네임스페이스 및 토큰: NS + id(), COLOR 테마-토큰 맵, SPACE/RADIUS 상수
  2. sx()와 결합된 스타일 사전 설정
  3. api.v1.ui.part.*에 구축된 옵션 객체 및 변형이 있는 컴포넌트 팩토리
  4. storageKey 접두사와 스토리지 API를 동기화 상태로 유지하는 field() 바인딩
  5. 하나의 render(), 작은 새로고침을 위한 updateParts/{{templates}}
  6. 재사용 가능한 confirm()/notify() 대화상자

아래 스니펫은 동일한 아이디어의 최소 버전입니다. 사용자가 JSX를 요청하면 NovelAI_JSX_Guide.md를 참조하세요. JSX 스타일은 일반 CSS와 var(--theme-*)를 사용하며 토큰 이름은 사용하지 않습니다. 함부로 JSX로 전환하지 마세요.

스타일 객체 사전 설정

스크립트 상단에 타입이 지정된 스타일 객체를 정의하고 UI 파트에 스프레드하세요.

const CARD_STYLE: Record<string, string> = {
    backgroundColor: 'bg1',      // 테마 토큰
    border: '1px solid bg2',     // 테마 토큰
    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

테마 토큰

16진수 색상을 하드코딩하는 대신 style 객체의 값으로 NovelAI 테마 토큰 문자열을 직접 사용하세요. 렌더링 시 각 토큰은 사용자의 활성 테마 값으로 리터럴 인라인 값(예: 'bg1'은 rgb(...) 코드가 됨)으로 대체됩니다. 이는 CSS 변수가 아닙니다: var()와 함께 사용할 --theme-* 사용자 정의 속성이 없으며, 사이트의 다른 곳에서 사용자 정의 CSS가 이를 참조할 수 없습니다. 스크립트 내에서 원시 테마 값이 필요한 경우 api.v1.theme.get()을 사용하세요. (예외: 공식 타입에 따르면 JSX UI 파트는 자체 섀도우 DOM 내부에 --theme-* 사용자 정의 속성을 가져옵니다.)

토큰역할
'bg0'가장 깊은 / 최외곽 배경
'bg1'기본 패널 배경
'bg2'올려진 표면 / 테두리 / 버튼 배경
'bg3'강조 표시된 / 선택된 표면
'textHeadings'고대비 제목 및 제목 텍스트
'textMain'기본 본문 텍스트
'textDisabled'비활성화 / 음소거된 텍스트
'textPlaceholder'입력 플레이스홀더 텍스트
'textHighlight'검색 / 강조 색상
'warning'경고 / 알림 상태
'textPrompt', 'textUser', 'textAI', 'textEdit'스토리 텍스트 색상
'lowIntensityColor', 'midIntensityColor', 'highIntensityColor'저/중/고 확률 색상 (토큰 확률에 사용되는 대로)
'default'본문 글꼴 모음 (fontFamily에서 사용)
'headings'제목 글꼴 모음 (fontFamily에서 사용)
'code'고정폭 코드 글꼴 모음
'field'양식 / 입력 필드 글꼴 모음

⚠️ 'primary'에 대한 참고 사항: NovelAI에는 'primary' 또는 'error' 테마 토큰이 없습니다. color: 'primary'를 사용하면 브라우저가 무시하는 잘못된 CSS가 생성됩니다. 활성/강조 표면에는 'bg3'를, 고대비 악센트에는 'textHeadings'를 사용하거나 명시적인 16진수/rgba 색상 문자열(예: '#90caf9')을 제공하세요.

팩토리 함수 (cloneNode / 템플릿 대체)

스크립트 상단에 미리 스타일이 지정되고 미리 구성된 UI 파트 객체를 반환하는 팩토리 함수를 작성하세요. 이렇게 하면 스크립트 전체에 인라인 스타일 객체가 반복되는 것을 방지할 수 있습니다.

// 재사용 가능한 스타일 버튼 팩토리
function makeButton(
    text: string,
    onClick: () => Promise<void>

…

7. UI 파트 객체

UI는 api.v1.ui.update()에 전달되는 UI 파트 객체 배열로 구성됩니다. 각 파트에는 type 속성이 있습니다.

공통 속성

  • id: string (선택 사항, updateParts에 필요)
  • style: Record<string, string> (선택 사항, 인라인 스타일, 테마 토큰 사용)
  • hidden: boolean (선택 사항)

파트 유형

유형속성설명
'container'content, direction ('row' / 'column')다른 파트를 위한 레이아웃 컨테이너
'text'text텍스트 표시
'button'label, onClick (() => Promise<void>)클릭 가능한 버튼
'input'value, onInput, placeholder, multiline텍스트 입력
'select'options, value, onChange드롭다운 선택
'checkbox'checked, onChange체크박스
'image'url, alt, width, height이미지 표시
'separator'—수평 구분선
'markdown'markdown마크다운 렌더링 (READ-ONLY, 베어 공식)
'html'htmlHTML 렌더링 (READ-ONLY, 베어 공식)
'trigger'onTrigger자동화에 유용한 푸시-풀 트리거

8. 일반적인 패턴 및 문제 해결

훅에서 수정된 컨텍스트 반환

onContextBuilt에서 AI에 전달되는 메시지를 수정하려면 수정된 메시지와 함께 { messages } 객체를 반환하세요.

api.v1.hooks.register('onContextBuilt', async (params: any) => {
    if (params.dryRun) return;
    const messages = (params.messages ?? []).map((msg: any) => {
        if (msg.role === 'system') {
            return { ...msg, content: msg.content + '\n\n[Custom instruction added by script]' };
        }
        return msg;
    });
    return { messages };
});

스토리지 지연 및 Promise.all

여러 스토리지 키가 필요한 경우 Promise.all을 사용하여 병렬로 가져오세요.

const [data1, data2] = await Promise.all([
    api.v1.storage.get('key1'),
    api.v1.storage.get('key2'),
]);

관례: 스토리지 키 접두사

스토리지 키가 충돌하지 않도록 스크립트 이름을 접두사로 사용하세요.

const STORAGE_PREFIX = 'my_script_';
const configKey = `${STORAGE_PREFIX}config`;
const dataKey = `${STORAGE_PREFIX}data`;

9. 공식 참조

  • Official Examples: https://github.com/NovelAI/novelai-script-examples
  • Official Types (Source of Truth): https://novelai.net/scripting/types/script-types.d.ts
  • This Skill's References: See the references/ directory for all detailed documentation files.

10. 이 스킬 사용에 대한 중요 규칙

  1. always-read-the-references-first: 코드를 작성하기 전에 항상 관련 references/ 파일을 읽으십시오. 이 가이드는 개념의 개요를 제공합니다. 구체적인 API 시그니처, 타입, 옵션 및 세부 동작은 참조 파일에 있습니다.
  2. trust-the-types-when-unsure: 일반 문서와 .d.ts 파일이 충돌하는 경우 항상 .d.ts 파일의 타입 정의를 신뢰하십시오. 타입 정의가 진실의 근원입니다.
  3. default-to-standard-parts: UI를 구축할 때 JSX가 아닌 표준 UI 파트를 기본으로 사용하십시오. 사용자가 JSX를 요청하거나 JSX가 크게 단순화할 명확한 이점이 있는 경우에만 JSX를 사용하십시오.
  4. use-the-component-system: UI가 있는 모든 스크립트에 대해 NovelAI_UI_Component_System.md에 설명된 컴포넌트 아키텍처를 따르십시오. 이는 공식 타입과 호환되며 확장 가능하고 유지 관리 가능한 UI를 제공합니다.
  5. no-dom-access: 스크립트 내에서 document.* 또는 window.* API에 절대 접근하지 마십시오. 모든 상호 작용은 api.v1 인터페이스를 통과해야 합니다.
  6. always-wrap-async: 스크립트의 최상위 및 모든 비동기 함수를 try/catch로 감싸거나 적절한 오류 핸들러를 연결하여 무음 프로미스 거부를 방지하십시오.
  7. use-storage-persistently: 상태를 유지해야 하는 경우 항상 api.v1.storage를 사용하십시오. 전역 변수에 영구 데이터를 저장하지 마십시오. 스크립트는 언제든지 다시 로드되거나 언로드될 수 있습니다.
  8. explicit-types: Web Worker 인터프리터는 엄격한 타입이 필요합니다. 함수 매개변수, 콜백 및 객체 반환에 항상 명시적 타입 주석을 추가하십시오.
  9. check-experimental: 실험적 API(api.v1.editor.block(), api.v1.document.transaction())를 사용하기 전에 사용자가 설정에서 experimentalScriptingApi를 활성화했는지 확인하십시오. 활성화하지 않은 경우 사용하지 마십시오.
  10. review-the-boilerplate: 새 스크립트를 시작할 때 이 가이드의 표준 보일러플레이트를 검토하여 프로젝트에 올바르게 적용하고 있는지 확인하십시오.

11. 참조 파일 요약

파일목적
NovelAI_TYPESCRIPT_GUIDELINES.md충돌 방지를 위한 핵심 TypeScript 규칙
NovelAI_Scripting_API_Complete_Documentation.md모든 api.v1 네임스페이스에 대한 전체 참조
NovelAI_Text_Generation_Documentation.mdNovelAI 편집기 기능 (모델, 설정)
NovelAI_Script_Types.d.ts공식 타입 정의 (진실의 근원)
NovelAI_JSX_Guide.mdJSX UI 파트 가이드
NovelAI_JSX_Types.d.tsJSX UI 파트에 대한 타입
NovelAI_UI_Component_System.mdUI 구축을 위한 컴포넌트 시스템
NovelAI_Script_Types_Experimental.d.ts실험적 API 타입
script_config_examples.ts스크립트 헤더 구성 예제

이 가이드가 NovelAI 스크립팅 요구 사항에 대한 포괄적인 참조 역할을 하기를 바랍니다. 코드 생성, 디버깅 또는 리팩토링을 시작하기 전에 항상 관련 references/ 파일을 참조하여 가장 정확하고 최신의 정보를 얻으십시오.

관련 스킬