fix(Web): 角色列表显示所有角色 + 区分视频/Live2D 模式
问题:切到 live2d 配置后角色列表只显示一个角色;角色卡片用当前配置的
character 信息渲染,而非各配置自己的。
1. routes.rs config_available_route 扩展返回结构
- configs 从字符串数组改为对象数组,每项含 filename + character 元信息
(name/character_type/render_type/live2d_model)
- 读取每个配置文件提取 character 块
2. chat.js loadCharacters 用各配置自己的 character 元信息渲染卡片
- 显示角色名/类型徽章/渲染模式(video/live2d)
- Live2D 角色额外显示模型路径
- 策略说明区分"视频播放+状态机"和"Canvas渲染+嘴部动效"
3. chat.js switchCharacter 改为 await loadConfig() 确保配置加载后再刷新
loadAvailableConfigs 兼容新旧返回结构
This commit is contained in:
@@ -542,23 +542,57 @@ fn config_available_route(
|
||||
.and_then(|state: Arc<HttpState>| async move {
|
||||
let config = state.config();
|
||||
let configs_dir = &config.source_dir;
|
||||
let mut files: Vec<String> = Vec::new();
|
||||
if let Ok(entries) = std::fs::read_dir(configs_dir) {
|
||||
for entry in entries.flatten() {
|
||||
// 每个配置文件提取 character 元信息,供前端渲染角色卡片
|
||||
let mut entries: Vec<serde_json::Value> = Vec::new();
|
||||
if let Ok(read_entries) = std::fs::read_dir(configs_dir) {
|
||||
for entry in read_entries.flatten() {
|
||||
let name = entry.file_name().to_string_lossy().into_owned();
|
||||
if name.ends_with(".json") {
|
||||
files.push(name);
|
||||
if !name.ends_with(".json") {
|
||||
continue;
|
||||
}
|
||||
let path = entry.path();
|
||||
let char_info = match std::fs::read_to_string(&path) {
|
||||
Ok(content) => {
|
||||
match serde_json::from_str::<serde_json::Value>(&content) {
|
||||
Ok(v) => {
|
||||
let c = v.get("character").cloned().unwrap_or_default();
|
||||
serde_json::json!({
|
||||
"name": c.get("name").and_then(|n| n.as_str()).unwrap_or(&name).to_string(),
|
||||
"character_type": c.get("character_type").and_then(|t| t.as_str()).unwrap_or("pet").to_string(),
|
||||
"render_type": c.get("render_type").and_then(|r| r.as_str()).unwrap_or("video").to_string(),
|
||||
"live2d_model": c.get("live2d_model").and_then(|m| m.as_str()).map(|s| s.to_string()),
|
||||
})
|
||||
}
|
||||
Err(_) => serde_json::json!({
|
||||
"name": name.clone(),
|
||||
"character_type": "pet",
|
||||
"render_type": "video",
|
||||
"live2d_model": null,
|
||||
}),
|
||||
}
|
||||
}
|
||||
Err(_) => serde_json::json!({
|
||||
"name": name.clone(),
|
||||
"character_type": "pet",
|
||||
"render_type": "video",
|
||||
"live2d_model": null,
|
||||
}),
|
||||
};
|
||||
entries.push(serde_json::json!({
|
||||
"filename": name,
|
||||
"character": char_info,
|
||||
}));
|
||||
}
|
||||
}
|
||||
files.sort();
|
||||
// 标记当前活跃的配置
|
||||
entries.sort_by(|a, b| {
|
||||
a["filename"].as_str().cmp(&b["filename"].as_str())
|
||||
});
|
||||
let active = config.source_path
|
||||
.file_name()
|
||||
.map(|n| n.to_string_lossy().into_owned())
|
||||
.unwrap_or_default();
|
||||
let result = serde_json::json!({
|
||||
"configs": files,
|
||||
"configs": entries,
|
||||
"active": active,
|
||||
});
|
||||
Ok::<_, Infallible>(json_response(StatusCode::OK, &result))
|
||||
|
||||
Reference in New Issue
Block a user