This commit is contained in:
Kurvaz 2026-01-03 04:42:51 -07:00
parent 3137baba9d
commit 279b5250fc
6 changed files with 14265 additions and 28 deletions

7062
src-tauri/Adventure.avt Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -540,8 +540,8 @@
lorebookContext = entryResult.contextBlock;
// Store retrieval result for debug panel
ui.setLastLorebookRetrieval(entryResult);
// Update activation data with recorded activations
ui.updateActivationData(activationTracker);
// Update activation data with recorded activations (and persist to database)
ui.updateActivationData(activationTracker, story.currentStory?.id);
log('Lorebook retrieval complete', {
tier1: entryResult.tier1.length,
tier2: entryResult.tier2.length,

View file

@ -533,7 +533,12 @@
// Create Story
async function createStory() {
// Sanity checks
if (!storyTitle.trim()) return;
if (!generatedOpening) {
openingError = 'Please generate an opening scene first';
return;
}
const wizardData: WizardData = {
mode: selectedMode,
@ -552,16 +557,6 @@
openingGuidance: selectedMode === 'creative-writing' && openingGuidance.trim() ? openingGuidance.trim() : undefined,
};
// Generate opening if not already done
if (!generatedOpening) {
await generateOpeningScene();
}
if (!generatedOpening) {
openingError = 'Failed to generate opening scene';
return;
}
// Prepare story data
const storyData = scenarioService.prepareStoryData(wizardData, generatedOpening);
@ -1241,6 +1236,13 @@
{/if}
</div>
<!-- Hint when no protagonist is defined -->
{#if !protagonist && !showManualInput}
<p class="text-xs text-surface-500 italic">
Tip: While optional, having a protagonist helps the AI create more personalized story content.
</p>
{/if}
<!-- Supporting Characters (Creative Mode Only) -->
{#if selectedMode === 'creative-writing'}
<div class="space-y-3 pt-4 border-t border-surface-700">
@ -1522,19 +1524,26 @@
{/if}
{#if storyTitle.trim()}
<button
class="btn btn-secondary flex items-center gap-2"
onclick={generateOpeningScene}
disabled={isGeneratingOpening}
>
{#if isGeneratingOpening}
<Loader2 class="h-4 w-4 animate-spin" />
Generating Opening...
{:else}
<PenTool class="h-4 w-4" />
{generatedOpening ? 'Regenerate Opening' : 'Generate Opening Scene'}
<div class="flex items-center gap-3">
<button
class="btn btn-secondary flex items-center gap-2"
onclick={generateOpeningScene}
disabled={isGeneratingOpening}
>
{#if isGeneratingOpening}
<Loader2 class="h-4 w-4 animate-spin" />
Generating Opening...
{:else}
<PenTool class="h-4 w-4" />
{generatedOpening ? 'Regenerate Opening' : 'Generate Opening Scene'}
{/if}
</button>
{#if !generatedOpening && !isGeneratingOpening}
<span class="text-sm text-amber-400">Required to begin story</span>
{/if}
</button>
</div>
{:else}
<p class="text-sm text-surface-500">Enter a title to generate the opening scene</p>
{/if}
{#if openingError}
@ -1595,7 +1604,8 @@
<button
class="btn btn-primary flex items-center gap-2"
onclick={createStory}
disabled={!storyTitle.trim() || isGeneratingOpening}
disabled={!storyTitle.trim() || isGeneratingOpening || !generatedOpening}
title={!generatedOpening ? 'Generate an opening scene first' : ''}
>
<Play class="h-4 w-4" />
Begin Story

View file

@ -298,8 +298,8 @@ class StoryStore {
checkpoints: checkpoints.length,
});
// Clear activation data from previous story (stickiness tracking is story-specific)
ui.clearActivationData();
// Load persisted activation data for this story (stickiness tracking)
await ui.loadActivationData(storyId);
// Clear retry backup from previous story
ui.clearRetryBackup();

View file

@ -44,6 +44,13 @@ interface PersistedSuggestions {
suggestions: StorySuggestion[];
}
// Persisted activation data structure (for lorebook stickiness)
interface PersistedActivationData {
storyId: string;
activationData: Record<string, number>;
storyPosition: number;
}
// UI State using Svelte 5 runes
class UIStore {
activePanel = $state<ActivePanel>('story');
@ -596,6 +603,9 @@ class UIStore {
// Activation tracking methods for lorebook stickiness
// Track the current story ID for activation persistence
private currentActivationStoryId: string | null = null;
/**
* Create an activation tracker for the current story position.
* The tracker maintains references to our state so activations are persisted.
@ -611,11 +621,17 @@ class UIStore {
* Update activation data after retrieval completes.
* Called with the tracker that was modified during retrieval.
*/
updateActivationData(tracker: SimpleActivationTracker) {
updateActivationData(tracker: SimpleActivationTracker, storyId?: string) {
this.activationData = tracker.getActivationData();
// Prune old activations (beyond max stickiness of 10 turns)
tracker.pruneOldActivations(10);
this.activationData = tracker.getActivationData();
// Persist to database
const targetStoryId = storyId || this.currentActivationStoryId;
if (targetStoryId) {
this.saveActivationData(targetStoryId);
}
}
/**
@ -624,6 +640,55 @@ class UIStore {
clearActivationData() {
this.activationData = {};
this.currentStoryPosition = 0;
this.currentActivationStoryId = null;
}
/**
* Save activation data to the database for persistence.
*/
saveActivationData(storyId: string) {
this.currentActivationStoryId = storyId;
const data: PersistedActivationData = {
storyId,
activationData: { ...this.activationData },
storyPosition: this.currentStoryPosition,
};
database.setSetting('lorebook_activation', JSON.stringify(data)).catch(err => {
console.warn('[UI] Failed to persist activation data:', err);
});
}
/**
* Load activation data from the database for a story.
* Called when a story is loaded.
*/
async loadActivationData(storyId: string) {
try {
const data = await database.getSetting('lorebook_activation');
if (data) {
const parsed: PersistedActivationData = JSON.parse(data);
// Only restore if it's for the same story
if (parsed.storyId === storyId) {
this.activationData = parsed.activationData;
this.currentStoryPosition = parsed.storyPosition;
this.currentActivationStoryId = storyId;
console.log('[UI] Restored activation data for story:', storyId, {
entriesCount: Object.keys(parsed.activationData).length,
storyPosition: parsed.storyPosition,
});
return;
}
}
// No matching data found, start fresh
this.activationData = {};
this.currentStoryPosition = 0;
this.currentActivationStoryId = storyId;
} catch (err) {
console.warn('[UI] Failed to load persisted activation data:', err);
this.activationData = {};
this.currentStoryPosition = 0;
this.currentActivationStoryId = storyId;
}
}
/**