feat: Add 'Always in Context' checkbox and enhance compendium entry resolution logic

This commit is contained in:
aomukai 2025-11-16 02:57:30 +09:00
parent bf1811cd6c
commit ba12698908
4 changed files with 54 additions and 14 deletions

View file

@ -338,7 +338,7 @@
<div class="scene-item"
:class="{ 'active': currentScene && currentScene.id === scene.id }"
@click="loadScene(scene.id)"
:title="`${scene.title}: ${scene.summary ? (scene.summaryStale ? 'Summary outdated' : 'Has summary') : 'No summary yet'}`"
:title="`${scene.title}${scene.povCharacter ? ' (POV: ' + scene.povCharacter + ')' : ''}\n${scene.summary ? (scene.summaryStale ? 'Summary outdated' : 'Has summary') : 'No summary yet'}`"
:aria-label="`${scene.title}: ${scene.summary ? (scene.summaryStale ? 'Summary outdated' : 'Has summary') : 'No summary yet'}`"
style="display:flex;justify-content:space-between;align-items:center;">
<div style="display:flex;align-items:center;gap:8px;">
@ -1432,6 +1432,22 @@
<!-- Image removal handled by clicking the image itself -->
</div>
</div>
<!-- Always in Context checkbox -->
<div
style="margin-top:12px;padding:12px;background:rgba(74,158,255,0.05);border:1px solid rgba(74,158,255,0.2);border-radius:6px;">
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
<input type="checkbox" x-model="currentCompEntry.alwaysInContext"
style="cursor:pointer;">
<span style="font-size:14px;color:rgba(255,255,255,0.9);">Always include in
prose prompts</span>
</label>
<p
style="margin:6px 0 0 26px;font-size:12px;color:rgba(255,255,255,0.5);line-height:1.4;">
When enabled, this entry will be automatically included in all prose
generation prompts (useful for style guides, world rules, etc.)
</p>
</div>
</div>
</template>
</div>

View file

@ -245,29 +245,47 @@
/**
* Parse beatInput for @[Title] mentions and return resolved compendium rows
* Also includes entries marked with alwaysInContext flag
* @param {Object} app - Alpine app instance
* @param {string} beatText - Beat text to parse
* @returns {Promise<Array>} Array of compendium entries
*/
async resolveCompendiumEntriesFromBeat(app, beatText) {
try {
if (!beatText) return [];
const ids = new Set();
// Parse @[Title] mentions and look up IDs from our mapping
const reMention = /@\[([^\]]+)\]/g;
let m;
while ((m = reMention.exec(beatText)) !== null) {
const title = m[1];
if (app.beatCompendiumMap[title]) {
ids.add(app.beatCompendiumMap[title]);
// First, add all entries marked as "always in context" for the current project
if (app.currentProject && app.currentProject.id) {
try {
const alwaysInContext = await db.compendium
.where('projectId')
.equals(app.currentProject.id)
.filter(e => e.alwaysInContext === true)
.toArray();
for (const entry of alwaysInContext) {
ids.add(entry.id);
}
} catch (e) {
console.warn('Failed to fetch alwaysInContext entries:', e);
}
}
// Also support legacy formats for backward compatibility
const reLegacy = /\[\[comp:([^\]]+)\]\]/g;
while ((m = reLegacy.exec(beatText)) !== null) {
if (m[1]) ids.add(m[1]);
// Parse @[Title] mentions and look up IDs from our mapping
if (beatText) {
const reMention = /@\[([^\]]+)\]/g;
let m;
while ((m = reMention.exec(beatText)) !== null) {
const title = m[1];
if (app.beatCompendiumMap[title]) {
ids.add(app.beatCompendiumMap[title]);
}
}
// Also support legacy formats for backward compatibility
const reLegacy = /\[\[comp:([^\]]+)\]\]/g;
while ((m = reLegacy.exec(beatText)) !== null) {
if (m[1]) ids.add(m[1]);
}
}
const out = [];

View file

@ -112,7 +112,8 @@
title: app.currentCompEntry.title || '',
body: app.currentCompEntry.body || '',
tags: JSON.parse(JSON.stringify(app.currentCompEntry.tags || [])),
imageUrl: app.currentCompEntry.imageUrl || null
imageUrl: app.currentCompEntry.imageUrl || null,
alwaysInContext: app.currentCompEntry.alwaysInContext || false
};
await window.Compendium.updateEntry(app.currentCompEntry.id, updates);
await this.loadCompendiumCategory(app, app.currentCompCategory);

View file

@ -52,6 +52,11 @@
wordCount
};
// Track mentioned compendium entries from beat field
if (app.beatCompendiumMap && Object.keys(app.beatCompendiumMap).length > 0) {
scenePatch.mentionedEntries = Object.values(app.beatCompendiumMap);
}
// If the content changed and there was an existing summary, mark the summary stale
try {
const contentChanged = prevContent && (prevContent.text || '') !== (scene.content || '');