mirror of
https://github.com/AventurasTeam/Aventuras.git
synced 2026-04-26 10:51:24 +00:00
feat: branch and checkpoint aware bg images
This commit is contained in:
parent
ba00458f12
commit
b3b63d1985
5 changed files with 129 additions and 15 deletions
21
src-tauri/migrations/024_background_images.sql
Normal file
21
src-tauri/migrations/024_background_images.sql
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
-- Migration 024: Table-based background images
|
||||
-- Supports per-branch and per-checkpoint backgrounds
|
||||
|
||||
CREATE TABLE IF NOT EXISTS background_images (
|
||||
id TEXT PRIMARY KEY,
|
||||
story_id TEXT NOT NULL,
|
||||
branch_id TEXT, -- NULL for main branch context
|
||||
checkpoint_id TEXT, -- Linked to a specific checkpoint
|
||||
image_data TEXT NOT NULL, -- The image data (URL or base64)
|
||||
created_at INTEGER NOT NULL,
|
||||
|
||||
FOREIGN KEY (story_id) REFERENCES stories(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (checkpoint_id) REFERENCES checkpoints(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Index for fast lookup by story/branch
|
||||
CREATE INDEX IF NOT EXISTS idx_backgrounds_branch ON background_images(story_id, branch_id);
|
||||
|
||||
-- Index for fast lookup by story/checkpoint
|
||||
CREATE INDEX IF NOT EXISTS idx_backgrounds_checkpoint ON background_images(story_id, checkpoint_id);
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
-- Add current_background_image column to stories table
|
||||
ALTER TABLE stories ADD COLUMN current_background_image TEXT;
|
||||
|
|
@ -151,7 +151,7 @@ pub fn run() {
|
|||
Migration {
|
||||
version: 24,
|
||||
description: "story_bg_image",
|
||||
sql: include_str!("../migrations/024_story_bg_image.sql"),
|
||||
sql: include_str!("../migrations/024_background_images.sql"),
|
||||
kind: MigrationKind::Up,
|
||||
}
|
||||
];
|
||||
|
|
|
|||
|
|
@ -164,9 +164,8 @@ class DatabaseService {
|
|||
retry_state,
|
||||
style_review_state,
|
||||
time_tracker
|
||||
current_background_image
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
story.id,
|
||||
story.title,
|
||||
|
|
@ -181,7 +180,6 @@ class DatabaseService {
|
|||
story.retryState ? JSON.stringify(story.retryState) : null,
|
||||
story.styleReviewState ? JSON.stringify(story.styleReviewState) : null,
|
||||
story.timeTracker ? JSON.stringify(story.timeTracker) : null,
|
||||
story.currentBgImage,
|
||||
],
|
||||
)
|
||||
return { ...story, createdAt: now, updatedAt: now }
|
||||
|
|
@ -1670,14 +1668,81 @@ class DatabaseService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Save the current background image for a story.
|
||||
* Get the background image for a specific branch.
|
||||
*/
|
||||
async saveCurrentBackgroundImage(storyId: string, imageData: string | null): Promise<void> {
|
||||
async getBackgroundForBranch(storyId: string, branchId: string | null): Promise<string | null> {
|
||||
const db = await this.getDb()
|
||||
await db.execute('UPDATE stories SET current_background_image = ? WHERE id = ?', [
|
||||
imageData,
|
||||
storyId,
|
||||
])
|
||||
const results = await db.select<{ image_data: string }[]>(
|
||||
'SELECT image_data FROM background_images WHERE story_id = ? AND branch_id IS ? AND checkpoint_id IS NULL ORDER BY created_at DESC LIMIT 1',
|
||||
[storyId, branchId],
|
||||
)
|
||||
return results.length > 0 ? results[0].image_data : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the background image for a specific checkpoint.
|
||||
*/
|
||||
async getBackgroundForCheckpoint(storyId: string, checkpointId: string): Promise<string | null> {
|
||||
const db = await this.getDb()
|
||||
const results = await db.select<{ image_data: string }[]>(
|
||||
'SELECT image_data FROM background_images WHERE story_id = ? AND checkpoint_id = ? LIMIT 1',
|
||||
[storyId, checkpointId],
|
||||
)
|
||||
return results.length > 0 ? results[0].image_data : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a background image for a story/branch/checkpoint.
|
||||
*/
|
||||
async saveBackground(
|
||||
storyId: string,
|
||||
branchId: string | null,
|
||||
checkpointId: string | null,
|
||||
imageData: string | null,
|
||||
): Promise<void> {
|
||||
const db = await this.getDb()
|
||||
|
||||
if (!imageData) {
|
||||
// If clearing, delete entries for this specific context
|
||||
if (checkpointId) {
|
||||
await db.execute('DELETE FROM background_images WHERE story_id = ? AND checkpoint_id = ?', [
|
||||
storyId,
|
||||
checkpointId,
|
||||
])
|
||||
} else {
|
||||
await db.execute(
|
||||
'DELETE FROM background_images WHERE story_id = ? AND branch_id IS ? AND checkpoint_id IS NULL',
|
||||
[storyId, branchId],
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Insert or update
|
||||
const id = crypto.randomUUID()
|
||||
const now = Date.now()
|
||||
|
||||
if (checkpointId) {
|
||||
// Checkpoints always get a new entry or replace existing for that checkpoint
|
||||
await db.execute(
|
||||
'INSERT OR REPLACE INTO background_images (id, story_id, branch_id, checkpoint_id, image_data, created_at) VALUES (?, ?, ?, ?, ?, ?)',
|
||||
[id, storyId, branchId, checkpointId, imageData, now],
|
||||
)
|
||||
} else {
|
||||
// For branches (including main), we update the single "current" record for that branch
|
||||
const existing = await this.getBackgroundForBranch(storyId, branchId)
|
||||
if (existing) {
|
||||
await db.execute(
|
||||
'UPDATE background_images SET image_data = ?, created_at = ? WHERE story_id = ? AND branch_id IS ? AND checkpoint_id IS NULL',
|
||||
[imageData, now, storyId, branchId],
|
||||
)
|
||||
} else {
|
||||
await db.execute(
|
||||
'INSERT INTO background_images (id, story_id, branch_id, checkpoint_id, image_data, created_at) VALUES (?, ?, ?, ?, ?, ?)',
|
||||
[id, storyId, branchId, null, imageData, now],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1752,7 +1817,7 @@ class DatabaseService {
|
|||
styleReviewState: row.style_review_state ? JSON.parse(row.style_review_state) : null,
|
||||
timeTracker: row.time_tracker ? JSON.parse(row.time_tracker) : null,
|
||||
currentBranchId: row.current_branch_id || null,
|
||||
currentBgImage: row.current_background_image || null,
|
||||
currentBgImage: null, // Loaded separately now
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -431,7 +431,7 @@ class StoryStore {
|
|||
await database.cleanupOrphanedEmbeddedImages()
|
||||
|
||||
this.currentStory = story
|
||||
this.currentBgImage = story.currentBgImage
|
||||
this.currentBgImage = await database.getBackgroundForBranch(storyId, story.currentBranchId)
|
||||
|
||||
// Load branch-independent data first
|
||||
const [characters, locations, items, storyBeats, checkpoints, lorebookEntries, branches] =
|
||||
|
|
@ -745,7 +745,12 @@ class StoryStore {
|
|||
this.currentStory.currentBgImage = imageData
|
||||
}
|
||||
|
||||
await database.saveCurrentBackgroundImage(this.currentStory.id, imageData)
|
||||
await database.saveBackground(
|
||||
this.currentStory.id,
|
||||
this.currentStory.currentBranchId,
|
||||
null,
|
||||
imageData,
|
||||
)
|
||||
log('Background image updated and persisted')
|
||||
}
|
||||
|
||||
|
|
@ -1915,6 +1920,18 @@ class StoryStore {
|
|||
|
||||
await database.createCheckpoint(checkpoint)
|
||||
this.checkpoints = [checkpoint, ...this.checkpoints]
|
||||
|
||||
// Save current background for this checkpoint
|
||||
if (this.currentBgImage) {
|
||||
log('Saving background for checkpoint:', name)
|
||||
await database.saveBackground(
|
||||
this.currentStory.id,
|
||||
this.currentStory.currentBranchId,
|
||||
checkpoint.id,
|
||||
this.currentBgImage,
|
||||
)
|
||||
}
|
||||
|
||||
log('Checkpoint created:', name)
|
||||
|
||||
// Emit event
|
||||
|
|
@ -2020,6 +2037,16 @@ class StoryStore {
|
|||
await database.addBranch(branch)
|
||||
this.branches = [...this.branches, branch]
|
||||
|
||||
// Inherit background from checkpoint
|
||||
const checkpointBg = await database.getBackgroundForCheckpoint(
|
||||
this.currentStory.id,
|
||||
checkpointId,
|
||||
)
|
||||
if (checkpointBg) {
|
||||
log('Inheriting background from checkpoint for new branch:', branch.name)
|
||||
await database.saveBackground(this.currentStory.id, branch.id, null, checkpointBg)
|
||||
}
|
||||
|
||||
// Copy world state from checkpoint into database with the new branch_id
|
||||
// This ensures the branch has its own copy of the world state at the fork point
|
||||
log('Copying world state from checkpoint to branch:', branch.name)
|
||||
|
|
@ -2170,6 +2197,9 @@ class StoryStore {
|
|||
this.invalidateWordCountCache()
|
||||
this.invalidateChapterCache()
|
||||
|
||||
// Reload background from database for the branch
|
||||
this.currentBgImage = await database.getBackgroundForBranch(this.currentStory.id, branchId)
|
||||
|
||||
log('Switched to branch:', branchId ?? 'main')
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue