Complete list of available dependencies and their usage patterns for SillyTavern development.
pnpm only - Do not use npm or yarn.
pnpm install
pnpm add lodash
pnpm add -D @types/lodashCore:
vue@3.5.24- Progressive JavaScript frameworkvue-router@4.6.3- Official router (usecreateMemoryHistory()for iframe)pinia@3.0.4- State management
Vue Composition Utilities:
@vueuse/core@13.9.0- Collection of composition APIs@vueuse/components@14.0.0- Renderless components@vueuse/integrations@13.9.0- Third-party library integrations@vueuse/shared@13.9.0- Shared utilities
Additional Vue Libraries:
vue-final-modal@4.5.5- Modal/dialog managementvue3-pixi@1.0.0-beta.3- PixiJS integration for Vue
Usage:
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import { createRouter, createMemoryHistory } from 'vue-router';
import { useMouse, useLocalStorage } from '@vueuse/core';
// Router MUST use createMemoryHistory for iframe
const router = createRouter({
history: createMemoryHistory(),
routes: [/* ... */],
});
$(() => {
const app = createApp(App);
app.use(createPinia());
app.use(router);
app.mount('#app');
});Core:
react@19.2.0- UI libraryreact-dom@19.2.0- DOM rendering
Usage:
import React from 'react';
import ReactDOM from 'react-dom/client';
$(() => {
const root = ReactDOM.createRoot(document.getElementById('app')!);
root.render(<App />);
});Core:
pixi.js@8.14.1- 2D WebGL renderer@pixi/react@8.0.0-beta.25- React renderer for PixiJS
Use when: Heavy multimedia assets, game-like interfaces, animation-intensive UIs
Usage:
import { Application, Assets } from 'pixi.js';
import { Stage, Sprite } from '@pixi/react';
// Preload assets
Assets.addBundle('game', {
bgm: '/audio/bgm.mp3',
sprite: '/img/sprite.png',
});
await Assets.loadBundle('game');Core:
jquery@3.7.1- DOM manipulationjqueryui@1.11.1- UI interactions (draggable, resizable, etc.)
Script vs Frontend Interface:
// In scripts: $ operates on SillyTavern page
window.$ = window.parent.$;
$('body'); // SillyTavern <body>
// In frontend interfaces: $ operates on iframe
$('body'); // Iframe <body>Common usage:
// Event binding
$('#button').on('click', handler);
// DOM manipulation
$('<div>').addClass('my-class').appendTo('body');
// AJAX
$.ajax({
url: '/api/data',
method: 'POST',
data: { key: 'value' },
success: (data) => console.log(data),
});
// Draggable (jQueryUI)
$('#element').draggable({
containment: 'parent',
handle: '.drag-handle',
});Package: lodash@4.17.21
Common functions:
import _ from 'lodash';
// Deep clone
const copy = _.cloneDeep(obj);
// Get nested value safely
const value = _.get(obj, 'path.to.value', defaultValue);
// Set nested value
_.set(obj, 'path.to.value', newValue);
// Debounce
const debouncedFn = _.debounce(fn, 300);
// Throttle
const throttledFn = _.throttle(fn, 1000);
// Array operations
_.uniq([1, 2, 2, 3]); // [1, 2, 3]
_.chunk([1, 2, 3, 4], 2); // [[1, 2], [3, 4]]Package: zod@4.1.12
Usage:
import { z } from 'zod';
// Define schema with defaults
const Settings = z.object({
volume: z.number().min(0).max(1).default(0.5),
autoPlay: z.boolean().default(true),
theme: z.enum(['light', 'dark']).default('dark'),
tags: z.array(z.string()).default([]),
}).prefault({}); // Auto-fill missing fields with defaults
type Settings = z.infer<typeof Settings>;
// Parse (throws on error)
const settings = Settings.parse(data);
// Safe parse
const result = Settings.safeParse(data);
if (!result.success) {
console.error(z.prettifyError(result.error));
return;
}
// Use parsed data
const { volume, autoPlay } = result.data;Package: klona@2.0.6
Purpose: Remove Vue Proxy layer before saving to Tavern variables
Usage:
import { klona } from 'klona';
import { watchEffect, ref } from 'vue';
const settings = ref({ score: 0 });
// Auto-save without Proxy
watchEffect(() => {
replaceVariables(klona(settings.value), { type: 'chat' });
});Package: gsap@3.13.0
Use for: All animations, including typewriter effects
Common patterns:
import gsap from 'gsap';
// Typewriter effect
gsap.to('#text', {
duration: 2,
text: {
value: "New text appears character by character",
delimiter: ""
},
ease: "none"
});
// Fade in/out
gsap.to('#element', {
duration: 0.5,
opacity: 0,
onComplete: () => $('#element').remove()
});
// Timeline
const tl = gsap.timeline();
tl.to('#a', { x: 100, duration: 1 })
.to('#b', { y: 50, duration: 0.5 })
.to('#c', { rotation: 360, duration: 1 });
// Scroll trigger (requires gsap/ScrollTrigger)
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
gsap.to('#element', {
scrollTrigger: {
trigger: '#element',
start: 'top center',
end: 'bottom center',
scrub: true
},
x: 500
});Package: toastr@2.1.4
Usage:
import toastr from 'toastr';
// Success
toastr.success('Operation completed');
// Info
toastr.info('Loading...');
// Warning
toastr.warning('Are you sure?');
// Error
toastr.error('Something went wrong');
// Options
toastr.options = {
closeButton: true,
progressBar: true,
positionClass: 'toast-top-right',
timeOut: 3000,
};Package: yaml@2.8.1
Usage:
import YAML from 'yaml';
// Parse
const data = YAML.parse(yamlString);
// Stringify
const yamlString = YAML.stringify(data);Package: dedent@1.7.0
Purpose: Clean indentation in multi-line strings
Usage:
import dedent from 'dedent';
const prompt = dedent`
You are a helpful assistant.
Please respond in a friendly manner.
Keep answers concise.
`;
// Removes leading whitespace while preserving relative indentationPackage: tailwindcss@4.1.17
Setup:
// Create tailwind.css
// Content: @import 'tailwindcss';
import './tailwind.css';Usage in HTML:
<div class="flex items-center justify-center p-4 bg-gray-100 rounded-lg">
<button class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
Click me
</button>
</div>Package: sass@1.94.0
Usage:
// In TypeScript
import './styles.scss';
// In Vue component
<style lang="scss" scoped>
.my-component {
$primary-color: #3498db;
background: $primary-color;
&:hover {
background: darken($primary-color, 10%);
}
}
</style>// Import file content as string
import htmlContent from './file.html?raw';
import jsonContent from './data.json?raw';
// Import compiled files
import jsContent from './script.ts?raw'; // Gets compiled JS
import cssContent from './style.scss?raw'; // Gets compiled CSS// Uses html-loader for minification
import html from './template.html';// Uses remark-loader for parsing
import markdown from './content.md';import Component from './Component.vue';Package: typescript@6.0.0-dev.20250807
Config: tsconfig.json
Core: webpack@5.102.1 + webpack-cli@6.0.1
Loaders:
ts-loader@9.5.4- TypeScriptvue-loader@17.4.2- Vue SFCsass-loader@16.0.6- Sass/SCSScss-loader@7.1.2- CSSstyle-loader@4.0.0- CSS injectionhtml-loader@5.1.0- HTMLremark-loader@6.0.0- Markdown
Build commands:
pnpm run build:dev # Development build
pnpm run build # Production build
pnpm run watch # Watch modePackage: eslint@9.39.1
Plugins:
@typescript-eslint/parser@8.46.4eslint-plugin-vue@10.5.1eslint-plugin-pinia@0.4.1eslint-plugin-better-tailwindcss@3.7.10
Commands:
pnpm run lint
pnpm run lint:fixPackage: prettier@3.6.2
Command:
pnpm run formatAll packages are tested together in this environment. Do not arbitrarily upgrade versions as it may cause compatibility issues.
Critical constraints:
⚠️ Browser-only environment (no Node.js APIs)⚠️ Vue Router must usecreateMemoryHistory()for iframe⚠️ Scripts' jQuery operates on parent window⚠️ Vue reactive data needsklona()before saving