This is a dynamic interactive novel game powered by large language models (LLMs). It generates unique story plots, characters, and a visualized story development map in real-time based on the player's chosen story type, offering an immersive reading experience filled with unknowns and choices.
This project is migrating to a full-stack TypeScript architecture, utilizing React Router v7 for SSR/SPA hybrid mode, deployed on Cloudflare Workers. It uses a Hybrid Architecture combining the Ink Engine for structured narrative control and AI Agents for dynamic prose generation.
- 🤖 AI-Augmented Narrative: The core narrative structure is driven by the industry-standard Ink Engine, enabling complex branching logic and state management. AI Agents (DM, Writer) are layered on top to generate rich, dynamic prose and immersive descriptions.
- 🎭 Hybrid Architecture (Ink + AI): Combines the stability and control of handwritten/generated Ink scripts with the infinite creativity of LLMs.
- 🗺️ Visualized Attributes: Real-time tracking of character stats, inventory, and world state, visualized via the frontend.
- 🌿 Branching Narrative: Deep branching story paths managed by the Ink runtime, ensuring logical consistency while allowing for player agency.
- 🎨 Dynamic Writing Style: The AI generates unique writing style descriptions based on the story type and applies them to the entire narrative, enhancing immersion.
- 🌐 SSR and Client Hydration: Supports server-side rendering (SSR) and client hydration for fast loading and consistent user experience.
- 🔒 Frontend-Backend Separation: Strictly separates server-only, shared, and client code to prevent sensitive information leaks.
| Category | Technology |
|---|---|
| Framework | React Router v7 - Isomorphic routing with SSR/SPA support. |
| Build Tool | Vite 7 - Fast build tool supporting SSR and client bundling. |
| Styling | Tailwind CSS v4 - Utility-first CSS framework. |
| Runtime | Bun - Fast JavaScript runtime and package manager. |
| Database | Drizzle ORM - Type-safe ORM for Cloudflare D1. |
| Deployment | Cloudflare Workers - Edge computing platform with D1 database support. |
| Engine | Ink + inkjs - Narrative runtime engine. |
| AI | Vercel AI SDK + OpenAI/Anthropic - For prose generation and DM logic. |
| i18n | i18next - Supports multi-language switching. |
.
├── app/ # React Router app core
│ ├── root.tsx # Main entry and layout
│ ├── routes/ # Route files (loader/action/component)
│ ├── components/ # Frontend components
│ │ ├── ui/ # Basic UI components
│ │ └── views/ # View components
│ ├── pages/ # Page components
│ ├── services/ # Business logic services (to be migrated)
│ └── models/ # Data models (to be migrated)
├── server/ # Server-only code
│ ├── db/ # Database schema and client
│ ├── services/ # Core business logic
│ └── config/ # Environment configuration
├── shared/ # Shared types and schemas
│ ├── types/ # TypeScript type definitions
│ └── schemas/ # Zod schemas
├── scripts/ # Utility scripts
│ └── seed.ts # Database seed data
├── .env.example # Environment variables example
├── drizzle.config.ts # Drizzle configuration
├── oxlint.json # Oxlint configuration
├── package.json # Project dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── vite.config.ts # Vite configuration
└── README.md # This document
Using Bun as the runtime and package manager, you can easily run this project locally.
Prerequisites:
- Bun installed.
Configuration:
-
Install dependencies:
bun install
-
Create environment variables file: The project uses
.envfiles to manage sensitive configurations. We provide an example file.env.example; copy it to create your own config file:cp .env.example .env
-
Edit
.envfile: Open the newly created.envfile and fill in your OpenAI API key, Cloudflare account info, etc.
Launch Steps:
-
Local development:
bun run dev
This starts the development server with hot reloading.
-
Build the project:
bun run build
-
Preview build results:
bun run preview
-
Access the app: Open your browser and visit
http://localhost:5173to start playing.
Database Operations:
- Generate migration files:
bun run db:generate - Local migration:
bun run db:migrate:local - Remote migration:
bun run db:migrate:remote - Seed data:
bun run db:seed
This project follows a strict Git workflow to ensure code quality and branch management.
- NEVER commit directly to main branch
- NEVER merge branches into main directly
- NEVER push to main branch - EVER
- ABSOLUTELY FORBIDDEN ACTIONS:
git push origin mainorgit push maingit merge feature-branchwhile on main- Any direct commits to main branch
All changes to main MUST go through pull requests and code review process.
-
Create a feature branch:
git checkout main git pull origin main git checkout -b feature/your-branch-name
-
Make your changes and commit:
git add . git commit -m "feat: your commit message"
Note: NEVER blindly
git add .- there might be other unrelated files lying around.NEVER use
git commit --amendorgit push --forceunless explicitly asked by the user. -
Lint:
bun run lint
If there are lint errors, fix them.
-
Format:
bun run format
If there are formatting errors, fix them.
-
Push and create PR:
git push -u origin feature/your-branch-name gh pr create --title "Your PR Title" --body "PR description"
-
Wait for review and CI checks before merging.
All user-facing changes must be documented in CHANGELOG.md. The changelog is enforced via GitHub Actions.
IMPORTANT: ALL merged PRs must be documented in the changelog.
Update the changelog for EVERY pull request, including:
- New features or functionality
- Bug fixes
- Breaking changes
- API changes
- Provider additions or updates
- Configuration changes
- Performance improvements
- Dependency updates
- Test changes
- Build configuration changes
- CI/CD changes
- Documentation updates
PRs can bypass changelog requirements with one of these labels:
no-changelog- For exceptional cases (automated bot PRs, reverts of unmerged changes)dependencies- For automated dependency updates (Dependabot, Renovate, etc.)
This project follows Keep a Changelog format:
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]
### Added
- New features go here (#PR_NUMBER)
### Changed
- Changes to existing functionality (#PR_NUMBER)
### Fixed
- Bug fixes (#PR_NUMBER)
### Dependencies
- Dependency updates (#PR_NUMBER)
### Documentation
- Documentation changes (#PR_NUMBER)
### Tests
- Test additions or changes (#PR_NUMBER)
## [1.2.3] - 2025-10-15
### Added
- Feature that was added (#1234)Each entry should:
- Include reference: Add PR number
(#1234)when available; use short commit hash(abc1234)only if no PR exists - Use conventional commit prefix:
feat:,fix:,chore:,docs:,test:,refactor: - Use
!for breaking changes: Add!after scope:feat(api)!:,chore(cli)!: - Include contributor attribution: Add
by @usernamebefore reference when contributor is known - Be concise: One line describing the change
- Be user-focused: Describe what changed, not how
Use these standardized scopes for consistency (based on git history analysis):
- providers - Provider implementations (OpenAI, Anthropic, LocalAI, etc.)
- webui - Web interface and viewer
- cli - Command-line interface
- assertions - Assertion types and grading
- api - Public API changes
- config - Configuration handling
- deps - Dependencies (or use Dependencies section)
- docs - Documentation
- tests - Test infrastructure
- examples - Example configurations
- redteam - Red team features (newer versions)
- site - Documentation site
- Added: New features
- Changed: Changes to existing functionality (refactors, improvements, chores, CI/CD)
- Fixed: Bug fixes
- Dependencies: ALL dependency updates
- Documentation: Documentation additions or updates
- Tests: ALL test additions or changes
- Removed: Removed features (rare, usually breaking)
Good entries:
### Added
- feat(providers): add TrueFoundry LLM Gateway provider (#5839)
- feat(redteam): add test button for request and response transforms in red-team setup UI (#5482)
- feat(cli): add glob pattern support for prompts (a1b2c3d)
- feat(api)!: simplify the API and support unified test suite definitions by @typpo (#14)
### Fixed
- fix(evaluator): support `defaultTest.options.provider` for model-graded assertions (#5931)
- fix(webui): improve UI email validation handling when email is invalid; add better tests (#5932)
- fix(cache): ensure cache directory exists before first use (423f375)
### Changed
- chore(providers): update Alibaba model support (#5919)
- chore(env)!: rename `OPENAI_MAX_TEMPERATURE` to `OPENAI_TEMPERATURE` (4830557)
- refactor(webui): improve EvalOutputPromptDialog with grouped dependency injection (#5845)Bad entries (missing reference, too vague, inconsistent format):
### Added
- Added new feature
- Updated provider
- New feature here- Add to Unreleased section: All new entries go under
## [Unreleased]at the top of the file - Choose correct category: Added, Changed, Fixed, Dependencies, Documentation, Tests
- Include reference: PR number
(#1234)when available, or short commit hash(abc1234)if no PR - Keep conventional commit prefix: feat:, fix:, chore:, docs:, test:
- One line per change: Brief and descriptive
Example workflow:
# 1. Make your changes
# 2. Before creating PR, update CHANGELOG.md
# Add entry under ## [Unreleased] in appropriate category:
- feat(providers): add new provider for XYZ (#PR_NUMBER)
# 3. Commit changelog with your changes
git add CHANGELOG.md
git commit -m "feat(providers): add new provider for XYZ"- Maintainers move entries from Unreleased to versioned sections during releases
- Don't worry about version numbers - focus on the Unreleased section
- If unsure about categorization, use Changed
- ALL dependencies, tests, CI changes must be included (no exemptions)
The diagram below outlines the complete flow from player starting a game to story content presentation:
sequenceDiagram
participant User as User
participant Client as Client (React)
participant Server as Server (Cloudflare Workers)
participant StoryGen as Story Generation Service
participant LLM as AI Large Language Model
participant DB as Cloudflare D1
User->>Client: Select story type, click "Start"
Client->>Server: POST /api/game (via React Router action)
Server->>StoryGen: generate_initial_scene()
StoryGen->>LLM: Request generation of story map (JSON)
LLM-->>StoryGen: Return complete story structure
StoryGen->>DB: Save story metadata (author, title, story map)
StoryGen-->>Server: Return initial scene data
Server-->>Client: Return {game_id, scene, author, title, story_map}
Client->>Mermaid.js: Render story map
Client->>User: Display initial scene and map
User->>Client: Choose branch
Client->>Server: POST /api/game/{game_id}/choice (useFetcher)
Server->>StoryGen: generate_next_scene()
StoryGen->>LLM: Request generation of next scene
LLM-->>StoryGen: Return new scene
StoryGen->>DB: Update game state
StoryGen-->>Server: Return new scene data
Server-->>Client: Return updated data (optimistic UI update)
Client->>User: Display new scene (no page reload)
This project is licensed under the MIT License. See the LICENSE file for details.