diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_project_history.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_project_history.rs
index 24b3aed74..130c01549 100644
--- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_project_history.rs
+++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_project_history.rs
@@ -5,6 +5,7 @@ use crate::project::{
};
use crate::{LocalConversationMessageRecord, LocalConversationResult};
use serde_json::Value;
+use std::collections::BTreeMap;
use std::fs::{self, File};
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom};
use std::path::{Path, PathBuf};
@@ -91,6 +92,10 @@ fn record(item: &Value) -> Result {
serde_json::to_string(&serde_json::json!({
"type": DIRECT_PROJECT_HISTORY_RECORD_TYPE,
"payload": item,
+ "recordedAt": std::time::SystemTime::now()
+ .duration_since(std::time::UNIX_EPOCH)
+ .map(|duration| duration.as_millis() as u64)
+ .unwrap_or(0),
}))
.map_err(|error| format!("序列化 DirectProject 历史失败:{error}"))
}
@@ -470,6 +475,13 @@ fn direct_project_message_item(role: &str, content: &str, message_id: Option<&st
}
pub(crate) fn read_direct_project_history_items_at(root: &Path) -> Result, String> {
+ Ok(read_direct_project_history_entries_at(root)?
+ .into_iter()
+ .map(|(item, _)| item)
+ .collect())
+}
+
+fn read_direct_project_history_entries_at(root: &Path) -> Result, String> {
let path = history_path(root);
if !prepare_game_creator_private_path_for_read(&path, false, "DirectProject 历史")? {
return Ok(Vec::new());
@@ -507,7 +519,13 @@ pub(crate) fn read_direct_project_history_items_at(root: &Path) -> Result,
limit: usize,
-) -> Result<(Vec, bool), String> {
- let items = read_direct_project_history_items_at(root)?;
+) -> Result<(Vec, bool, BTreeMap), String> {
+ let items = read_direct_project_history_entries_at(root)?;
let end = match before_item_id {
Some(item_id) => items
.iter()
- .position(|item| item.get("id").and_then(Value::as_str) == Some(item_id))
+ .position(|(item, _)| item.get("id").and_then(Value::as_str) == Some(item_id))
.ok_or_else(|| format!("DirectProject 历史中不存在 item:{item_id}"))?,
None => items.len(),
};
let bounded_limit = limit.clamp(1, 200);
let start = end.saturating_sub(bounded_limit);
- Ok((items[start..end].to_vec(), start > 0))
+ let slice = &items[start..end];
+ let timestamps = slice
+ .iter()
+ .filter_map(|(item, at)| {
+ let id = item.get("id").and_then(Value::as_str)?;
+ (*at > 0).then(|| (id.to_string(), *at))
+ })
+ .collect();
+ Ok((
+ slice.iter().map(|(item, _)| item.clone()).collect(),
+ start > 0,
+ timestamps,
+ ))
}
pub(crate) fn read_direct_project_last_item_id_at(root: &Path) -> Result
);
};
- const renderMessage = (message: ChatMessage, index: number) => (
-
-
- {showDesignReasoning && message.reasoningText ? (
-
- 思考过程
- {message.reasoningText}
-
- ) : null}
-
- );
+ const renderMessage = (message: ChatMessage, index: number) => {
+ const sentAt =
+ message.role === 'user' ? directMessageTimestamp(message.updatedAt) : 0;
+ return (
+
+
+ {sentAt > 0 ? (
+
+ ) : null}
+ {showDesignReasoning && message.reasoningText ? (
+
+ 思考过程
+ {message.reasoningText}
+
+ ) : null}
+
+ );
+ };
const submitButton = (