refactor: split editor mount and sidebar page ai

This commit is contained in:
lix-2026
2026-05-26 04:01:52 +08:00
parent c65135f7ed
commit a8bc87173f
10 changed files with 3124 additions and 2973 deletions
+45 -43
View File
@@ -215,6 +215,8 @@ mod tests {
include_str!("../../../browser/sidebar-workspace-runtime.js");
const SIDEBAR_PAGE_TREE_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-page-tree-runtime.js");
const SIDEBAR_PAGE_AI_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-page-ai-runtime.js");
const FILETREE_CONTEXT_MENU_RUNTIME_JS: &str =
include_str!("../../../browser/filetree-context-menu-runtime.js");
const FILETREE_DND_RUNTIME_JS: &str = include_str!("../../../browser/filetree-dnd-runtime.js");
@@ -352,7 +354,7 @@ mod tests {
#[test]
fn page_ai_fast_path_is_not_local_first_main_path() {
assert!(
SIDEBAR_TREE_RUNTIME_JS
SIDEBAR_PAGE_AI_RUNTIME_JS
.contains("if (currentSourceKind() === 'local_folder') return false;"),
"local-first 页面 AI 不应继续加厚 page-ai fast-path;本地编辑应走受控文件工具"
);
@@ -361,15 +363,15 @@ mod tests {
#[test]
fn page_ai_local_source_passes_file_reference_fields_to_agent_run() {
assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("function currentRootUri()"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("sourceKind: currentSourceKind()"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("rootUri: currentRootUri()"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("sourceKind: currentSourceKind()"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("rootUri: currentRootUri()"));
assert!(SIDEBAR_WORKSPACE_RUNTIME_JS
.contains("if (sourceKind && !payload.sourceKind) payload.sourceKind = sourceKind"));
assert!(SIDEBAR_WORKSPACE_RUNTIME_JS
.contains("if (rootUri && !payload.rootUri) payload.rootUri = rootUri"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("pageContext: scopedContext.pageContext"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageContext: scopedContext.pageContext"));
assert!(
SIDEBAR_TREE_RUNTIME_JS
SIDEBAR_PAGE_AI_RUNTIME_JS
.contains("if (currentSourceKind() === 'local_folder') return false;"),
"local source 不应进入 page-ai fast-path,后端会把 run 收敛为文件引用 scope"
);
@@ -377,70 +379,70 @@ mod tests {
#[test]
fn page_ai_uses_backend_acp_session_runtime_store() {
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function pageAiLoadBackendSessions"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("/api/hermes/client/sessions?"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("params.set('source', 'acp')"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function pageAiLoadBackendSessionDetail"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function pageAiSearchBackendSessions"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-page-ai-session-rename"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-page-ai-session-delete"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-page-ai-session-resume"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-page-ai-session-search"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("pageAiUsageSummary"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("usage.updated"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("thought.delta"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("permission.requested"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-page-ai-permission-action=\"allow\""));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-page-ai-permission-action=\"deny\""));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function pageAiHidePermissionDialog"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("if (!message.resolved)"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiLoadBackendSessions"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("/api/hermes/client/sessions?"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("params.set('source', 'acp')"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiLoadBackendSessionDetail"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiSearchBackendSessions"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("data-page-ai-session-rename"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("data-page-ai-session-delete"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("data-page-ai-session-resume"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("data-page-ai-session-search"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiUsageSummary"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("usage.updated"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("thought.delta"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("permission.requested"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("data-page-ai-permission-action=\"allow\""));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("data-page-ai-permission-action=\"deny\""));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiHidePermissionDialog"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("if (!message.resolved)"));
assert!(
!SIDEBAR_TREE_RUNTIME_JS.contains("(item.resolved ? ' disabled' : '')"),
!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("(item.resolved ? ' disabled' : '')"),
"已决 ACP permission 事件不能继续展示假审批按钮"
);
assert!(
SIDEBAR_TREE_RUNTIME_JS.contains("session.info.updated"),
SIDEBAR_PAGE_AI_RUNTIME_JS.contains("session.info.updated"),
"ACP SessionInfoUpdate 事件应通过 session.info.updated SSE 转发到前端"
);
assert!(
SIDEBAR_TREE_RUNTIME_JS.contains("plan.updated"),
SIDEBAR_PAGE_AI_RUNTIME_JS.contains("plan.updated"),
"ACP PlanUpdate 事件应通过 plan.updated SSE 转发到前端"
);
assert!(
SIDEBAR_TREE_RUNTIME_JS.contains("data-page-ai-plan"),
SIDEBAR_PAGE_AI_RUNTIME_JS.contains("data-page-ai-plan"),
"plan 消息应渲染为 data-page-ai-plan 标记的轻量系统状态面板"
);
assert!(
SIDEBAR_TREE_RUNTIME_JS.contains("执行计划 · "),
SIDEBAR_PAGE_AI_RUNTIME_JS.contains("执行计划 · "),
"plan 面板标题应显示执行计划和步数"
);
}
#[test]
fn page_ai_session_ui_labels_local_shared_and_cloud_storage() {
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function pageAiSessionStorageLabel"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("local_private"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("local_shared"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("convex_acp_runtime_store"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("本地私有"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("共享会话"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("云端会话"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("sessionStorage:"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("permissionLevel:"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("shareId:"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiSessionStorageLabel"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("local_private"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("local_shared"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("convex_acp_runtime_store"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("本地私有"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("共享会话"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("云端会话"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("sessionStorage:"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("permissionLevel:"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("shareId:"));
}
#[test]
fn page_ai_acp_runtime_defaults_to_reasonix_and_keeps_hermes_switch() {
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("var PAGE_AI_SESSION_STORAGE_VERSION = 3"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("var PAGE_AI_SESSION_STORAGE_VERSION = 3"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("pageAiAcpRuntime: 'reasonix'"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function pageAiNormalizeAcpRuntimes"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("return ['reasonix', 'hermes']"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("pageAiNormalizeAcpRuntimes(acpRuntimes)"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiNormalizeAcpRuntimes"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("return ['reasonix', 'hermes']"));
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiNormalizeAcpRuntimes(acpRuntimes)"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("pageAiAcpRuntimeSelect.value || 'reasonix'"));
assert!(SIDEBAR_TREE_RUNTIME_JS
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS
.contains("activeAcpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix'"));
assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("默认 (Hermes HTTP)"));
assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("默认 (Hermes HTTP)"));
}
#[test]