调整策划项目命名
策划入口使用带随机短码的策划项目名称 保留游戏与素材入口的现有命名链路 补充自动建项测试与技术方案说明
This commit is contained in:
@@ -332,6 +332,7 @@ fn automatic_local_game_projects_root(app: &tauri::AppHandle) -> Result<PathBuf,
|
||||
pub(crate) fn create_automatic_local_game_project_at(
|
||||
projects_root: &Path,
|
||||
requested_name: Option<&str>,
|
||||
planning: bool,
|
||||
) -> Result<InitLocalProjectResult, String> {
|
||||
let requested_name = requested_name
|
||||
.map(normalize_game_creation_project_name)
|
||||
@@ -354,9 +355,14 @@ pub(crate) fn create_automatic_local_game_project_at(
|
||||
for _ in 0..16 {
|
||||
let workspace_id = uuid::Uuid::new_v4().simple().to_string();
|
||||
let short_id = &workspace_id[..8];
|
||||
let project_name = requested_name
|
||||
.clone()
|
||||
.unwrap_or_else(|| format!("GameAgent 项目 {short_id}"));
|
||||
let project_name = requested_name.clone().unwrap_or_else(|| {
|
||||
let prefix = if planning {
|
||||
"策划项目"
|
||||
} else {
|
||||
"GameAgent 项目"
|
||||
};
|
||||
format!("{prefix} {short_id}")
|
||||
});
|
||||
let project_root = projects_root.join(format!("gameagent-{short_id}"));
|
||||
match fs::create_dir(&project_root) {
|
||||
Ok(()) => {
|
||||
@@ -395,10 +401,12 @@ pub(crate) fn create_automatic_local_game_project_at(
|
||||
pub(crate) fn create_automatic_local_game_project(
|
||||
app: tauri::AppHandle,
|
||||
name: Option<String>,
|
||||
planning: Option<bool>,
|
||||
) -> Result<InitLocalProjectResult, String> {
|
||||
create_automatic_local_game_project_at(
|
||||
&automatic_local_game_projects_root(&app)?,
|
||||
name.as_deref(),
|
||||
planning.unwrap_or(false),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1411,15 +1411,26 @@ fn project_picker_uses_the_closest_existing_parent_for_a_suggested_new_path() {
|
||||
fn automatic_local_game_project_allocates_unique_initialized_workspaces() {
|
||||
let projects_root = unique_project_path();
|
||||
|
||||
let first = create_automatic_local_game_project_at(&projects_root, None)
|
||||
let first = create_automatic_local_game_project_at(&projects_root, None, false)
|
||||
.expect("create first automatic workspace");
|
||||
let second = create_automatic_local_game_project_at(&projects_root, Some("自定义项目名"))
|
||||
let second = create_automatic_local_game_project_at(&projects_root, Some("自定义项目名"), false)
|
||||
.expect("create second automatic workspace");
|
||||
let planning = create_automatic_local_game_project_at(&projects_root, None, true)
|
||||
.expect("create planning workspace");
|
||||
|
||||
assert_ne!(first.project_path, second.project_path);
|
||||
assert!(first.manifest.name.starts_with("GameAgent 项目 "));
|
||||
assert_eq!(second.manifest.name, "自定义项目名");
|
||||
for result in [first, second] {
|
||||
let planning_directory = Path::new(&planning.project_path)
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.expect("planning directory name");
|
||||
let short_id = planning_directory.strip_prefix("gameagent-").unwrap();
|
||||
assert_eq!(short_id.len(), 8);
|
||||
assert_eq!(planning.manifest.name, format!("策划项目 {short_id}"));
|
||||
assert_ne!(planning.project_path, first.project_path);
|
||||
assert_ne!(planning.project_path, second.project_path);
|
||||
for result in [first, second, planning] {
|
||||
let root = PathBuf::from(&result.project_path);
|
||||
assert_eq!(root.parent(), Some(projects_root.as_path()));
|
||||
assert!(root
|
||||
@@ -1437,7 +1448,7 @@ fn automatic_local_game_project_allocates_unique_initialized_workspaces() {
|
||||
fn automatic_local_game_project_accepts_only_a_safe_custom_name() {
|
||||
let projects_root = unique_project_path();
|
||||
|
||||
let result = create_automatic_local_game_project_at(&projects_root, Some(" 星轨夜航 "))
|
||||
let result = create_automatic_local_game_project_at(&projects_root, Some(" 星轨夜航 "), false)
|
||||
.expect("create named automatic workspace");
|
||||
assert_eq!(result.manifest.name, "星轨夜航");
|
||||
|
||||
@@ -1447,7 +1458,7 @@ fn automatic_local_game_project_accepts_only_a_safe_custom_name() {
|
||||
&"长".repeat(GAME_CREATION_PROJECT_NAME_MAX_CHARS + 1),
|
||||
] {
|
||||
assert!(
|
||||
create_automatic_local_game_project_at(&projects_root, Some(name)).is_err(),
|
||||
create_automatic_local_game_project_at(&projects_root, Some(name), false).is_err(),
|
||||
"unsafe name must fail: {name:?}"
|
||||
);
|
||||
}
|
||||
@@ -1589,7 +1600,7 @@ fn automatic_local_game_project_rejects_symlinked_projects_root() {
|
||||
fs::create_dir_all(&target).expect("create automatic workspace target");
|
||||
symlink(&target, &projects_root).expect("symlink automatic workspace root");
|
||||
|
||||
let error = create_automatic_local_game_project_at(&projects_root, None)
|
||||
let error = create_automatic_local_game_project_at(&projects_root, None, false)
|
||||
.expect_err("symlinked automatic workspace root must fail");
|
||||
|
||||
assert!(error.contains("不能包含符号链接"));
|
||||
|
||||
@@ -598,6 +598,7 @@ export function useHomeProjectCreation({
|
||||
'create_automatic_local_game_project',
|
||||
{
|
||||
name: suggestedName,
|
||||
planning: startMode === 'planning',
|
||||
},
|
||||
);
|
||||
try {
|
||||
|
||||
@@ -26,7 +26,8 @@ npm 游戏的可预览产物固定为对应 package 目录下的 `dist/index.htm
|
||||
|
||||
- `.agent/manifest.json` 的 `name` 仍是本地项目显示名唯一事实源;项目组页行尾更多菜单提供行内重命名,保存必须走 Tauri 受控命令、项目写锁、manifest 写锁与既有 ACL/权限校验。重命名只更新 manifest,不改变项目目录、`projectId`、项目类型、任务、资源、版本或远端同步状态;保存成功后当前项目上下文、窗口标题和最近项目检查结果必须回读新 manifest 并保持一致。
|
||||
- 项目名称统一 trim 后非空、最多 80 个字符且不得包含控制字符。空名称、超长、控制字符或未初始化项目必须失败关闭,原 manifest 保持可用;失败提示只展示安全错误,不泄露宿主路径之外的新内部信息。
|
||||
- 首页自动创建工作区前允许一次受限 LLM 名称提炼:输入只包含用户文本需求和附件元数据摘要,输出只允许一个简短中文项目名。Rust 侧统一规范化并在空值、控制字符、超长或多行格式时返回失败;前端把有效名称传给自动建项命令,失败时继续使用现有 `GameAgent 项目 {短ID}` 默认名并照常进入创作,不重试、不阻断、不额外消耗 Provider 请求。
|
||||
- 首页自动创建游戏或素材工作区前允许一次受限 LLM 名称提炼:输入只包含用户文本需求,输出只允许一个简短中文项目名。Rust 侧统一规范化并在空值、控制字符、超长或多行格式时返回失败;前端把有效名称传给自动建项命令,失败时继续使用现有 `GameAgent 项目 {短ID}` 默认名并照常进入创作,不重试、不阻断、不额外消耗 Provider 请求。
|
||||
- 首页“做方案”直接创建 `策划项目 {短ID}`,短 ID 沿用 Rust 生成的八位随机码,与项目目录的后缀一致。该入口不调用 LLM 命名;项目显示名持久化到 manifest,项目目录与身份规则沿用现有自动建项链路。
|
||||
- 手动“新建项目”继续默认使用所选文件夹名,不额外调用 LLM;项目创建后用户可通过项目组页重命名修正显示名。自动建项命令的自定义名称必须走同一校验,未提供名称时保持现有默认名,保证旧调用与失败回退路径不变。
|
||||
|
||||
## 2026-09-02 客户端会话恢复可观测性与超时兜底
|
||||
|
||||
Reference in New Issue
Block a user