From c3454bbd5ff6151a64fe3e98293d579efb4a3fc3 Mon Sep 17 00:00:00 2001
From: vegu-ai-tools <152010387+vegu-ai-tools@users.noreply.github.com>
Date: Fri, 3 Apr 2026 16:27:44 +0300
Subject: [PATCH] Refactor advance time scene tool menu
---
CHANGELOG.yaml | 2 +
.../agents/world_state/websocket_handler.py | 25 +++
.../src/components/SceneTools.vue | 44 +----
.../src/components/SceneToolsTime.vue | 153 ++++++++++++++++++
4 files changed, 183 insertions(+), 41 deletions(-)
create mode 100644 talemate_frontend/src/components/SceneToolsTime.vue
diff --git a/CHANGELOG.yaml b/CHANGELOG.yaml
index e98da6cc..968d2d07 100644
--- a/CHANGELOG.yaml
+++ b/CHANGELOG.yaml
@@ -10,6 +10,7 @@
- "Asset Exists Node: Added an allow_partial option that enables startswith matching instead of exact asset ID matching."
- "Prompt From Template Node: Added a dedupe property (default on) to control prompt deduplication. Useful for templates with structured repeated content like beat listings that should not be deduplicated."
- "Gemma 4 Prompt Template: Added a prompt template for Google's Gemma 4 model family, with support for toggling thinking mode via the reasoning tokens setting."
+ - "Advance Time: Moved time tools into a dedicated sub-component with grouped presets (Minutes, Hours, Days, Weeks, Months, Years) and a custom time dialog for arbitrary durations."
fixes:
- "Message Regeneration: Fixed a bug where regenerating a message that fails (e.g., missing agent or function) would permanently remove the original message from the scene. The original message is now restored to both history and UI on failure, and unhandled exceptions during regeneration are caught."
- "Anthropic Client: Fixed generation error caused by missing required max_tokens parameter when response length capping is disabled. Now defaults to the model's API output token limit."
@@ -24,5 +25,6 @@
- "Encryption: Fixed API keys in agent action configs (e.g., OpenAI-compatible TTS, visual backends) being stored in plaintext. The encryption walker now handles the nested AgentActionConfig structure where the key is wrapped in a {value: ...} dict."
- "Agent Config: Stopped persisting unified_api_key fields to config.yaml. These are static reference pointers (e.g., 'openai.api_key') defined in code and never change, so saving them was unnecessary."
- "Contextual Generate: Fixed list-type generation prefilling an empty line after '1.', which caused some LLMs to produce empty lists."
+ - "Advance Time: Fixed toolbar time advancement being completely non-functional due to a missing websocket handler. Also fixed invalid ISO 8601 duration strings for the 1 week and 2 weeks options."
improvements:
- "Prompts Tab Alert: The top-level Prompts tab now shows a warning icon when any active template overrides are outdated, so you can spot them without opening the tab."
\ No newline at end of file
diff --git a/src/talemate/agents/world_state/websocket_handler.py b/src/talemate/agents/world_state/websocket_handler.py
index b40f2091..d028cddc 100644
--- a/src/talemate/agents/world_state/websocket_handler.py
+++ b/src/talemate/agents/world_state/websocket_handler.py
@@ -3,6 +3,7 @@ import pydantic
from talemate.instance import get_agent
from talemate.server.websocket_plugin import Plugin
from talemate.status import set_loading
+from talemate.util.time import amount_unit_to_iso8601_duration
__all__ = [
"WorldStateWebsocketHandler",
@@ -18,6 +19,24 @@ class SummarizeAndPinPayload(pydantic.BaseModel):
num_messages: int = 5
+class AdvanceTimePayload(pydantic.BaseModel):
+ duration: str | None = None
+ amount: int | None = None
+ unit: str | None = None
+
+ @pydantic.model_validator(mode="after")
+ def check_duration_or_amount_unit(self):
+ if not self.duration and (self.amount is None or not self.unit):
+ raise ValueError("Either 'duration' or 'amount' + 'unit' must be provided.")
+ return self
+
+ @property
+ def iso_duration(self) -> str:
+ if self.duration:
+ return self.duration
+ return amount_unit_to_iso8601_duration(self.amount, self.unit)
+
+
class WorldStateWebsocketHandler(Plugin):
router = "world_state_agent"
@@ -47,3 +66,9 @@ class WorldStateWebsocketHandler(Plugin):
message_id, num_messages=payload.num_messages
)
await self.signal_operation_done(allow_auto_save=False)
+
+ @set_loading("Advancing time", cancellable=True, as_async=True)
+ async def handle_advance_time(self, data: dict):
+ payload = AdvanceTimePayload(**data)
+ await self.agent.advance_time(payload.iso_duration)
+ await self.signal_operation_done()
diff --git a/talemate_frontend/src/components/SceneTools.vue b/talemate_frontend/src/components/SceneTools.vue
index 93054d13..18494089 100644
--- a/talemate_frontend/src/components/SceneTools.vue
+++ b/talemate_frontend/src/components/SceneTools.vue
@@ -165,20 +165,7 @@
-
-
-
- mdi-clock
-
-
-
- Advance Time
-
- {{ option.title }}
-
-
-
+
{ return this.canAutoSave ? this.autoSave : "Manually save scene for auto-save to be available"; }},
{"value": "toggleAutoProgress", "title": "Auto Progress", "icon": "mdi-robot", "description": "AI automatically progresses after player turn.", "status": () => { return this.autoProgress }},
],
- advanceTimeOptions: [
- {"value" : "P10Y", "title": "10 years"},
- {"value" : "P5Y", "title": "5 years"},
- {"value" : "P3Y", "title": "3 years"},
- {"value" : "P2Y", "title": "2 years"},
- {"value" : "P1Y", "title": "1 year"},
- {"value" : "P6M", "title": "6 months"},
- {"value" : "P3M", "title": "3 months"},
- {"value" : "P1M", "title": "1 month"},
- {"value" : "P14D:2 Weeks later", "title": "2 weeks"},
- {"value" : "P7D:1 Week later", "title": "1 week"},
- {"value" : "P3D", "title": "3 days"},
- {"value" : "P2D", "title": "2 days"},
- {"value" : "P1D", "title": "1 day"},
- {"value" : "PT12H", "title": "12 hours"},
- {"value" : "PT8H", "title": "8 hours"},
- {"value" : "PT4H", "title": "4 hours"},
- {"value" : "PT2H", "title": "2 hours"},
- {"value" : "PT1H", "title": "1 hour"},
- {"value" : "PT30M", "title": "30 minutes"},
- {"value" : "PT15M", "title": "15 minutes"},
- {"value" : "PT5M", "title": "5 minutes"}
- ],
}
},
inject: [
@@ -460,10 +426,6 @@ export default {
this.$emit('cancel-audio-queue');
},
- advanceTime(duration) {
- this.getWebsocket().send(JSON.stringify({ type: 'world_state_agent', action: 'advance_time', duration: duration }));
- },
-
// Handle incoming messages
handleMessage(data) {
diff --git a/talemate_frontend/src/components/SceneToolsTime.vue b/talemate_frontend/src/components/SceneToolsTime.vue
new file mode 100644
index 00000000..a9a8620d
--- /dev/null
+++ b/talemate_frontend/src/components/SceneToolsTime.vue
@@ -0,0 +1,153 @@
+
+
+
+
+ mdi-clock
+
+
+
+ Advance Time
+
+
+
+
+ {{ group.label }}
+
+
+
+
+ {{ option.title }}
+
+
+
+
+
+
+ Custom...
+
+
+
+
+
+
+
+ Advance Time
+
+
+
+
+
+
+
+
+ Cancel
+ Advance
+
+
+
+
+
+