Skip to content
Merged

Dev #38

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
eab0966
feat: add folder picker for SillyTavern path in settings form
dmitryplyaskin Dec 29, 2025
da28f03
feat: add scan cards from sillytavern folder
dmitryplyaskin Dec 29, 2025
2ec8ef2
feat: add save/load filters
dmitryplyaskin Dec 29, 2025
3507ab2
feat: new watcher and sync st folder path
dmitryplyaskin Dec 29, 2025
36cfcbc
feat: add play to st card native with ext
dmitryplyaskin Dec 29, 2025
30fe133
feat: add hide card
dmitryplyaskin Dec 29, 2025
dc2a97f
feat: add fav filter
dmitryplyaskin Dec 30, 2025
601e3c4
feat(docker)
Dec 31, 2025
a4a6e9a
Merge pull request #36 from htruane/main
dmitryplyaskin Dec 31, 2025
8c8bebc
feat: add fav icon
dmitryplyaskin Dec 31, 2025
8a480d2
feat: add fact card action button
dmitryplyaskin Dec 31, 2025
791a7b4
fix: fav icon
dmitryplyaskin Jan 2, 2026
78d2a69
feat: save st card
dmitryplyaskin Jan 2, 2026
faa8469
fix: sync st
dmitryplyaskin Jan 2, 2026
807c885
fix: modal zIndex
dmitryplyaskin Jan 2, 2026
bccdd2e
feat: add scan st chats
dmitryplyaskin Jan 3, 2026
88eb51b
feat: add sort by chats and new filters: by st user and st chats count
dmitryplyaskin Jan 3, 2026
75e4ca0
fix: filters
dmitryplyaskin Jan 3, 2026
7165c86
feat: add metadata
dmitryplyaskin Jan 3, 2026
aa57c01
fix: profile filter
dmitryplyaskin Jan 3, 2026
da2ec2f
feat: add date chat new sort
dmitryplyaskin Jan 3, 2026
2b46297
up
dmitryplyaskin Jan 3, 2026
b19f2fd
feat: add show chats
dmitryplyaskin Jan 4, 2026
908ab9b
fix: chat dates
dmitryplyaskin Jan 4, 2026
d3e3f61
fix: show preloader
dmitryplyaskin Jan 4, 2026
f405c71
feat: add delete card chats
dmitryplyaskin Jan 4, 2026
9b3b173
feat: add setting to bulk tags editor
dmitryplyaskin Jan 4, 2026
a3903f2
fix: filters reset bug
dmitryplyaskin Jan 5, 2026
b7b957c
Merge pull request #37 from dmitryplyaskin/feat/silly-integration
dmitryplyaskin Jan 5, 2026
49cd14a
fix: update readme
dmitryplyaskin Jan 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Stage 1: Build Client
FROM node:24-alpine AS client-builder

RUN corepack enable && corepack prepare yarn@4.12.0 --activate

WORKDIR /app

COPY SillyInnkeeper/package.json ./
COPY SillyInnkeeper/client ./client

WORKDIR /app/client

RUN yarn install --immutable
RUN yarn build

# Stage 2: Build Server
FROM node:24-alpine AS server-builder

RUN corepack enable && corepack prepare yarn@4.12.0 --activate

# Install build dependencies for native modules
RUN apk add --no-cache python3 make g++

WORKDIR /app/server

COPY SillyInnkeeper/server ./

RUN yarn install
RUN yarn build

# Stage 3: Production Runtime
FROM node:24-alpine

# Install runtime dependencies
RUN apk add --no-cache \
shadow \
python3 \
make \
g++ \
su-exec

RUN corepack enable && corepack prepare yarn@4.12.0 --activate

WORKDIR /app

# Copy server package files and install production dependencies
COPY SillyInnkeeper/server/package.json SillyInnkeeper/server/yarn.lock ./
RUN NODE_ENV=production yarn install

# Copy built artifacts
COPY --from=server-builder /app/server/dist ./server/dist
COPY --from=client-builder /app/client/dist ./client/dist

# Create data directory
RUN mkdir -p /app/data && chown node:node /app/data

# Environment variables
ENV NODE_ENV=production \
INNKEEPER_HOST=0.0.0.0 \
INNKEEPER_PORT=48912

EXPOSE 48912

# Create entrypoint script
RUN echo '#!/bin/sh' > /entrypoint.sh && \
echo 'PUID=${PUID:-1000}' >> /entrypoint.sh && \
echo 'PGID=${PGID:-1000}' >> /entrypoint.sh && \
echo 'echo "Running as UID:GID $PUID:$PGID"' >> /entrypoint.sh && \
echo 'groupmod -o -g "$PGID" node' >> /entrypoint.sh && \
echo 'usermod -o -u "$PUID" node' >> /entrypoint.sh && \
echo 'chown -R node:node /app/data' >> /entrypoint.sh && \
echo 'exec su-exec node node server/dist/server.js' >> /entrypoint.sh && \
chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

136 changes: 70 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

> 🔗 **SillyTavern Extension**: [ST-Extension-SillyInnkeeper](https://github.com/dmitryplyaskin/ST-Extension-SillyInnkeeper) — Install this extension in SillyTavern to integrate with SillyInnkeeper.

> 📢 **Telegram Channel**: [@SillyInnkeeper](https://t.me/SillyInnkeeper) — news, updates, and project discussions.

[![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://opensource.org/licenses/AGPL-3.0)

![Main img](assets/main.webp)
Expand All @@ -24,21 +26,57 @@ SillyInnkeeper solves all these problems by providing a fast and convenient way

## ✨ Key Features

### Card Library Management
### Full SillyTavern Integration

- **Automatic scanning**: Just specify the folder with cards — the application will find and index all PNG files automatically
- **Automatic updates**: When new cards are added or existing ones are changed, the application will automatically update the information
- **Duplicate management**: Automatic detection of identical cards and convenient management of them
- **View and edit cards**: Full access to character card content from SillyTavern
- **Chat management**: View information about chats associated with cards, filter by user profiles
- **Data synchronization**: Automatic synchronization of metadata between SillyInnkeeper and SillyTavern
- **One-click launch**: The "Play" button instantly imports the card into SillyTavern
- **Automatic import**: The SillyTavern extension automatically receives cards from SillyInnkeeper

### Powerful Search and Filtering

- **Search by name**: Quick search for cards by character name
- **Filter by creator**: Find all cards from a specific author
- **Filter by tags**: Select multiple tags for precise search
- **Filter by date**: Find cards created in a specific period
- **Filter by content**: Find cards with specific fields (e.g., only with system prompt or with alternate greetings)
- **Filter by size**: Search by approximate number of prompt tokens
- **Flexible sorting**: Sort by creation date or by name
- **Text search**: Full-text search through card content with two modes:
- "LIKE" mode — simple substring search
- "FTS" mode — full-text search with relevance ranking
- Search fields: description, personality, scenario, first message, message examples, creator notes, system prompts, post history instructions, alternate greetings, group-only greetings
- **Metadata filtering**:
- By creator (single or multiple)
- By specification version (V1/V2/V3)
- By tags (multiple selection)
- By patterns (customizable rules)
- **Date filtering**: Date range for card creation
- **Size filtering**: Search by approximate number of prompt tokens (min/max)
- **Field presence filtering**: Find cards with or without specific fields:
- Creator notes
- System prompts
- Post history instructions
- Personality
- Scenario
- Message examples
- Character Book (lorebook)
- **Alternate greetings filtering**: Presence and minimum count
- **Source filtering**: Cards from SillyTavern, from folder, or all
- **Status filtering**: Hidden, favorite cards
- **SillyTavern chats filtering**:
- Chat count (equal/greater than/less than)
- User profiles
- Hide cards without chats
- **Flexible sorting**:
- By creation date (newest/oldest first)
- By name (A-Z / Z-A)
- By prompt tokens count (descending/ascending)
- By SillyTavern chats count (descending/ascending)
- By last chat date (newest/oldest first)
- By first chat date (newest/oldest first)
- By relevance (when using full-text search)

### Card Library Management

- **Automatic scanning**: Just specify the folder with cards — the application will find and index all PNG files automatically
- **Automatic updates**: When new cards are added or existing ones are changed, the application will automatically update the information
- **Duplicate management**: Automatic detection of identical cards and convenient management of them

### Convenient Card Viewing

Expand All @@ -50,11 +88,6 @@ SillyInnkeeper solves all these problems by providing a fast and convenient way
- Raw JSON (for advanced users)
- **Image viewing**: Zoom card images with optional blur (censorship)
- **Metadata**: View ID, specification version, creation date, and other useful information

### SillyTavern Integration

- **One-click launch**: The "Play" button instantly imports the card into SillyTavern
- **Automatic import**: The SillyTavern extension automatically receives cards from SillyInnkeeper
- **Card export**: Download PNG files with correct metadata for use in other applications

### User Experience
Expand All @@ -73,8 +106,6 @@ SillyInnkeeper solves all these problems by providing a fast and convenient way
- **Node.js**: version 18.x or higher (recommended 20.x or 24.x)
- **Yarn**: version 4.12.0 (or npm 9.x+)
- **Operating system**: Windows 10/11, Linux, macOS
- **RAM**: minimum 2 GB (recommended 4 GB+ for large collections)
- **Free disk space**: minimum 500 MB for installation + space for database and cache

## 📦 Installation

Expand Down Expand Up @@ -175,8 +206,6 @@ You can configure **host/port** and SillyTavern integration via a root `.env` fi
- `ST_PORT` — SillyTavern port for default CORS allowlist (default `8000`)
- `CORS_ALLOW_ORIGINS` — extra allowed origins (comma-separated), e.g. `http://192.168.1.50:8001`

**Note about LAN**: if you set `INNKEEPER_HOST=0.0.0.0`, open the app from another device using `http://<your-pc-lan-ip>:<INNKEEPER_PORT>` (you may also need to allow inbound traffic in your firewall).

## 🚀 Quick Start

### First Launch
Expand Down Expand Up @@ -204,15 +233,9 @@ You can configure **host/port** and SillyTavern integration via a root `.env` fi

1. Install the [ST-Extension-SillyInnkeeper](https://github.com/dmitryplyaskin/ST-Extension-SillyInnkeeper) extension in SillyTavern

2. In the extension settings, specify the SillyInnkeeper URL:

```
http://127.0.0.1:48912
```
2. In the extension settings, specify the SillyInnkeeper URL: `http://127.0.0.1:48912`

If SillyTavern is opened on a different machine, **do not use `localhost`** — use the Innkeeper machine IP/hostname instead (e.g. `http://192.168.1.10:48912`).

If SillyTavern is not on port 8000, set `ST_PORT` (and/or add its full origin to `CORS_ALLOW_ORIGINS`).
If SillyTavern is opened on a different machine, use the Innkeeper machine IP/hostname instead (e.g. `http://192.168.1.10:48912`).

3. Enable "Auto-connect" for automatic connection

Expand All @@ -232,22 +255,27 @@ The main screen of the application consists of:

1. **Open the filters panel** (the "Filters" button in the top panel)

2. **Use available filters**:
2. **Use search**:
- **Search by name**: Enter the character name for quick search
- **Text search**: Enter text to search through card content
- Choose search mode: "LIKE" (simple search) or "FTS" (full-text search with ranking)
- Select fields to search: description, personality, scenario, first message, message examples, creator notes, system prompts, post history instructions, alternate greetings, group-only greetings

- **Search by name**: Enter the character name
- **Creator**: Select one or more creators
- **Specification version**: Filter by Character Card version (V1/V2/V3)
- **Tags**: Select tags for filtering
3. **Apply filters**:
- **Metadata**: Creator, specification version (V1/V2/V3), tags, patterns
- **Creation date**: Specify a date range
- **Tokens**: Minimum and maximum number of tokens
- **Prompt tokens**: Minimum and maximum number of tokens
- **Alternate greetings**: Presence and minimum count
- **Field presence**: Select fields that should be present/absent
- **Field presence**: Select fields that should be present/absent (creator notes, system prompts, post history instructions, personality, scenario, message examples, Character Book)
- **Source**: All cards, only from SillyTavern, only from folder
- **Status**: Hidden, favorite cards
- **SillyTavern chats**: Chat count (equal/≥/≤), user profiles, hide cards without chats

3. **Choose sorting**: By creation date or by name
4. **Choose sorting**: By creation date, name, token count, chat count, last/first chat date, or by relevance (when using full-text search)

4. **Apply filters**: Results will update automatically
5. **Results update automatically** when filters change

5. **Reset filters**: Use the "Reset" button to clear all filters
6. **Reset filters**: Use the "Reset" button to clear all filters

### Viewing a Card

Expand All @@ -266,12 +294,6 @@ The main screen of the application consists of:
- **Rename**: Change the name of the main file
- **Delete**: Delete the card or duplicate

### Library Management

- **Automatic updates**: When files in the cards folder are changed, the application will automatically update the index
- **Manual scanning**: You can start a rescan through settings
- **Duplicate management**: In the detailed card view, you can select the main file or delete duplicates

## 🔗 SillyTavern Integration

SillyInnkeeper integrates with SillyTavern through the [ST-Extension-SillyInnkeeper](https://github.com/dmitryplyaskin/ST-Extension-SillyInnkeeper) extension.
Expand Down Expand Up @@ -305,31 +327,16 @@ SillyInnkeeper integrates with SillyTavern through the [ST-Extension-SillyInnkee

### Planned Features

1. **Full SillyTavern Integration and Scanning**

- Scanning cards from SillyTavern folder
- Managing and editing cards, chats, lorebooks, etc.
- Two-way synchronization between SillyInnkeeper and SillyTavern

2. **Lorebook Support**
1. **Lorebook Support (Character Book)**

- Viewing and managing lorebooks from cards
- Editing lorebooks
- Export/import lorebooks
- This functionality will be redesigned to improve UX/UI

3. **Multiple Directories Support**

- Support for multiple card libraries
- Switching between libraries
- Unified search across all libraries
- Library management through UI
2. **Native Desktop Application**

4. **Auto-Download and Auto-Import**

- Monitoring downloads folder
- Automatic scanning of new files
- Automatic import into SillyTavern (optional)
- Configuration rules for automatic card organization
- Possible implementation of a native desktop application for improved performance and usability

## 📄 License

Expand All @@ -340,9 +347,6 @@ This project is licensed under [AGPL-3.0](https://opensource.org/licenses/AGPL-3
**Dmitry Plyaskin**

- GitHub: [@dmitryplyaskin](https://github.com/dmitryplyaskin)
- Telegram Channel: [@SillyInnkeeper](https://t.me/SillyInnkeeper)
- Project: [SillyInnkeeper](https://github.com/dmitryplyaskin/SillyInnkeeper)
- SillyTavern Extension: [ST-Extension-SillyInnkeeper](https://github.com/dmitryplyaskin/ST-Extension-SillyInnkeeper)

---

**Note**: SillyInnkeeper is an independent project, not officially affiliated with SillyTavern. It is a community tool for improving work with character cards.
25 changes: 25 additions & 0 deletions client/src/entities/cards/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,28 @@ export {
refreshCardsSilent,
loadCardsFx,
} from "./model";

export {
cardDeleted,
cardUpdated,
closeDeleteCardModal,
closeRenameMainFileModal,
deleteCardConfirmed,
openDeleteCardModal,
openInExplorerRequested,
openRenameMainFileModal,
playInSillyTavernRequested,
renameMainFileConfirmed,
renameMainFileValueChanged,
toggleHiddenRequested,
$deleteCardModal,
$isDeletingCard,
$isOpeningInExplorer,
$isPlayingInSillyTavern,
$isRenamingMainFile,
$isTogglingHidden,
$renameMainFileModal,
} from "./model.actions";

export { CardActionsMenu } from "./ui/card-actions-menu";
export { CardActionsModals } from "./ui/card-actions-modals";
12 changes: 12 additions & 0 deletions client/src/entities/cards/lib/path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export function getFilenameFromPath(filePath: string | null | undefined): string {
const p = (filePath ?? "").trim();
if (!p) return "";
const parts = p.split(/[/\\]+/);
return parts[parts.length - 1] || "";
}

export function stripPngExt(name: string): string {
return name.replace(/\.png$/i, "");
}


Loading