Unify tracked-state quick actions into a single Track State action (#72)

* feature(world-state): unify tracked-state quick actions into a single Track State action

* refactor(world-state): address PR review - drop unused character prop, rename defaultInsert, remove comment noise
This commit is contained in:
veguAI
2026-07-06 00:30:30 +03:00
committed by GitHub
parent 81f5f96241
commit 73f6199979
3 changed files with 89 additions and 47 deletions

View File

@@ -9,6 +9,7 @@
- "MistralAI Client: Added a Concurrent Inference toggle so batch operations can dispatch multiple requests in parallel. Off by default."
- "Text-Generation-WebUI Client: Added an 'API handles prompt template' toggle. When enabled, requests go to the chat/completions API and text-generation-webui applies the model's prompt template; response pre-filling (coercion) keeps working via the API's continue_ mechanism, and reasoning-model thinking is captured automatically without needing a reasoning pattern. Off by default."
- "llama.cpp Client: Added an 'API handles prompt template' toggle. When enabled, prompts are rendered through the server's /apply-template endpoint using the model's built-in chat template; response pre-filling (coercion) and reasoning keep working, and thinking-capable templates are asked not to think when reasoning is disabled. Off by default."
- "Track State: The World State scene tool menu's two separate 'Add tracked world state' and 'Add tracked character state' actions are now a single 'Track state' action. The modal itself lets you choose the target — the world or a specific character — before describing what to track, and the context attachment default and 'Require character active' option adapt to the choice."
fixes:
- "OpenRouter Client: A failed model/provider list fetch at startup no longer sticks until the server is restarted — later config saves and client status refreshes now retry it. Setting the API key for the first time during initial setup also correctly triggers the provider fetch."
- "Generation Error Dialog: When several generations failed at the same time (e.g. an API throttle hitting both a summarization and a background task), only the most recent error could be answered — the earlier generation was never resumed and its result (such as a scene summary) was silently lost. Error dialogs are now queued and answered one after another, and pending dialogs are cancelled cleanly when the scene is unloaded or the frontend disconnects."

View File

@@ -7,16 +7,12 @@
{{ description }}
</v-alert>
<v-alert v-if="character" density="compact" icon="mdi-account" variant="text" color="grey" class="mb-2">
{{ character }}
</v-alert>
<v-select
v-if="characters.length > 0 && !character"
v-model="selectedCharacter"
:items="characters"
label="Character"
prepend-inner-icon="mdi-account"
v-if="showTargetSelector"
v-model="selectedTarget"
:items="targetItems"
label="Track state for"
prepend-inner-icon="mdi-target"
></v-select>
<v-text-field
@@ -64,13 +60,15 @@
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary" variant="text" prepend-icon="mdi-text-box-plus" @click="create" :disabled="!question || (characters.length > 0 && !effectiveCharacter)">Create</v-btn>
<v-btn color="primary" variant="text" prepend-icon="mdi-text-box-plus" @click="create" :disabled="!question || !hasTargetSelection">Create</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
const WORLD_TARGET = '__world__';
export default {
name: 'QuickCreateStateReinforcement',
props: {
@@ -82,10 +80,6 @@ export default {
type: String,
default: '',
},
character: {
type: String,
default: '',
},
characters: {
type: Array,
default: () => [],
@@ -94,7 +88,17 @@ export default {
type: Array,
required: true,
},
defaultInsert: {
// When true the target selector offers "World" alongside the characters,
// so a single modal can create either a world or a character state.
allowWorld: {
type: Boolean,
default: false,
},
characterDefaultInsert: {
type: String,
default: 'never',
},
worldDefaultInsert: {
type: String,
default: 'never',
},
@@ -108,33 +112,75 @@ export default {
dialog: false,
question: '',
interval: this.defaultInterval,
insert: this.defaultInsert,
insert: this.allowWorld ? this.worldDefaultInsert : this.characterDefaultInsert,
instructions: '',
requireActive: true,
selectedCharacter: '',
selectedTarget: this.allowWorld ? WORLD_TARGET : '',
}
},
emits: ['create'],
computed: {
showTargetSelector() {
return this.allowWorld || this.characters.length > 0;
},
targetItems() {
let items = this.characters.map(name => ({
title: name,
value: name,
props: { prependIcon: 'mdi-account' },
}));
if (this.allowWorld) {
items.unshift({
title: 'World',
value: WORLD_TARGET,
props: { prependIcon: 'mdi-earth' },
});
}
return items;
},
// The world is the target when it is explicitly selected, or when there
// are no characters to choose from at all.
isWorldTarget() {
return this.selectedTarget === WORLD_TARGET || this.characters.length === 0;
},
effectiveCharacter() {
return this.character || this.selectedCharacter;
return this.selectedTarget === WORLD_TARGET ? '' : this.selectedTarget;
},
hasTargetSelection() {
if (!this.showTargetSelector) {
return true;
}
return !!this.selectedTarget;
},
availableInsertionModes() {
if (this.effectiveCharacter) {
return this.insertionModes;
// conversation-context only makes sense inside a character's own context.
if (this.isWorldTarget) {
return this.insertionModes.filter(mode => mode.value !== 'conversation-context');
}
return this.insertionModes.filter(mode => mode.value !== 'conversation-context');
return this.insertionModes;
},
},
watch: {
// Keep the insertion mode aligned with the selected target so switching
// between World and a character resets to that target's sensible default
// (and never leaves an invalid mode like conversation-context on world).
isWorldTarget() {
this.insert = this.insertDefaultForTarget();
},
},
methods: {
insertDefaultForTarget() {
return this.isWorldTarget ? this.worldDefaultInsert : this.characterDefaultInsert;
},
open() {
this.dialog = true;
this.question = '';
this.interval = this.defaultInterval;
this.insert = this.defaultInsert;
this.instructions = '';
this.requireActive = true;
this.selectedCharacter = '';
this.selectedTarget = this.allowWorld ? WORLD_TARGET : '';
this.insert = this.insertDefaultForTarget();
this.$nextTick(() => {
if (this.$refs.questionInput && this.$refs.questionInput.focus) {
this.$refs.questionInput.focus();

View File

@@ -15,16 +15,10 @@
<v-list-item-subtitle>{{ primaryModifierLabel }}+click to wipe and start fresh</v-list-item-subtitle>
</v-list-item>
<!-- add tracked world state -->
<v-list-item density="compact" prepend-icon="mdi-cube-scan" @click="$refs.createWorldState.open()">
<v-list-item-title>Add tracked world state</v-list-item-title>
<v-list-item-subtitle>Track and auto-update a world state</v-list-item-subtitle>
</v-list-item>
<!-- add tracked character state -->
<v-list-item density="compact" prepend-icon="mdi-account-search" @click="$refs.createCharacterState.open()">
<v-list-item-title>Add tracked character state</v-list-item-title>
<v-list-item-subtitle>Track and auto-update a character state</v-list-item-subtitle>
<!-- track state -->
<v-list-item density="compact" prepend-icon="mdi-cube-scan" @click="$refs.trackState.open()">
<v-list-item-title>Track state</v-list-item-title>
<v-list-item-subtitle>Track and auto-update a world or character state</v-list-item-subtitle>
</v-list-item>
<!-- player character submenu -->
@@ -121,22 +115,15 @@
/>
<QuickCreateStateReinforcement
ref="createWorldState"
title="Add Tracked World State"
description="Create a tracked world state that the AI will automatically monitor and update at a regular interval. Once created, the state can be found and managed in the World State Manager under the World States tab."
:insertion-modes="insertionModes"
default-insert="never"
@create="createWorldStateReinforcement"
/>
<QuickCreateStateReinforcement
ref="createCharacterState"
title="Add Tracked Character State"
description="Create a tracked character state that the AI will automatically monitor and update at a regular interval. Once created, the state can be found and managed in the World State Manager under the character's Tracked States tab."
ref="trackState"
title="Track State"
description="Create a tracked state that the AI will automatically monitor and update at a regular interval. Choose whether to track a world state or a specific character's state. Once created, it can be found and managed in the World State Manager."
:insertion-modes="insertionModes"
:characters="allCharacters"
default-insert="sequential"
@create="createCharacterStateReinforcement"
allow-world
character-default-insert="sequential"
world-default-insert="never"
@create="createStateReinforcement"
/>
</template>
@@ -384,6 +371,14 @@ export default {
}));
},
createStateReinforcement(data) {
if (data.character) {
this.createCharacterStateReinforcement(data);
} else {
this.createWorldStateReinforcement(data);
}
},
createWorldStateReinforcement(data) {
this.getWebsocket().send(JSON.stringify({
type: 'world_state_manager',