feat: add pi lab ai management integration

This commit is contained in:
Agent Board
2026-07-04 21:47:42 +08:00
parent 36d027a4a1
commit c8472bb898
62 changed files with 15077 additions and 443 deletions
File diff suppressed because it is too large Load Diff
@@ -167,6 +167,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -73,6 +73,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -112,6 +113,7 @@ mod tests {
enable_legacy_next_compat: false,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -168,6 +170,7 @@ mod tests {
enable_legacy_next_compat: false,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -236,6 +239,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
+3 -8
View File
@@ -164,10 +164,7 @@ pub async fn seed(
Ok(Json(DevSeedResponse { ok: true, results }))
}
fn apply_seed_operation(
state: &AppState,
operation: DevSeedOperation,
) -> Result<Value, WebError> {
fn apply_seed_operation(state: &AppState, operation: DevSeedOperation) -> Result<Value, WebError> {
match operation {
DevSeedOperation::SetupWorkspace {
user_id,
@@ -478,6 +475,7 @@ mod tests {
enable_legacy_next_compat: false,
enable_debug_shell_routes: false,
enable_editor_actor: false,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -573,10 +571,7 @@ mod tests {
assert_eq!(payload["results"][0]["run"]["profile"], "test-profile");
assert_eq!(payload["results"][0]["run"]["acp_runtime"], "reasonix");
assert_eq!(payload["results"][0]["run"]["status"], "running");
assert_eq!(
payload["results"][0]["events"].as_array().unwrap().len(),
1
);
assert_eq!(payload["results"][0]["events"].as_array().unwrap().len(), 1);
// Step 3: getAiRuntimeRun → 回读验证
let (status, payload) = send_seed(
@@ -1196,6 +1196,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -1779,6 +1779,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: true,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -388,6 +388,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
+115
View File
@@ -261,6 +261,88 @@ pub async fn user_access_policy_entry(
Ok(response)
}
pub async fn admin_ai_entry(
State(state): State<AppState>,
Extension(context): Extension<RequestContext>,
) -> Result<Response, WebError> {
if !has_real_auth_context(&state, &context) {
let mut response = Response::builder()
.status(StatusCode::SEE_OTHER)
.header(header::LOCATION, "/auth")
.body(Body::empty())
.map_err(|error| WebError::internal(format!("认证入口跳转响应构造失败: {error}")))?;
stamp_gateway_headers(response.headers_mut(), false);
return Ok(response);
}
if !is_local_access_policy_admin_context(&context) {
return Err(WebError::new(
StatusCode::FORBIDDEN,
"ai_admin_required",
"只有管理员可以访问 AI 管理中心",
)
.with_context(&context));
}
ai_management_response(&state, &context, true)
}
pub async fn user_ai_entry(
State(state): State<AppState>,
Extension(context): Extension<RequestContext>,
) -> Result<Response, WebError> {
if !has_real_auth_context(&state, &context) {
let mut response = Response::builder()
.status(StatusCode::SEE_OTHER)
.header(header::LOCATION, "/auth")
.body(Body::empty())
.map_err(|error| WebError::internal(format!("认证入口跳转响应构造失败: {error}")))?;
stamp_gateway_headers(response.headers_mut(), false);
return Ok(response);
}
ai_management_response(&state, &context, false)
}
fn ai_management_response(
state: &AppState,
context: &RequestContext,
is_admin: bool,
) -> Result<Response, WebError> {
let workspace_name = default_workspace_name_for_context(state, context);
let content = crate::ssr::render_view(leptos::view! {
<crate::ssr::pages::ai_admin::AiManagementPage
workspace_name={workspace_name}
is_admin={is_admin}
/>
});
let title = if is_admin { "AI 管理" } else { "AI 设置" };
let shell = if is_admin {
"admin-ai"
} else {
"user-ai-admin"
};
let mut response = Html(format!(
r#"<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{}</title>
<style>{}</style>
</head>
<body data-mnote-web-owner="mnote-web" data-mnote-shell="{}" data-mnote-actor-id="{}">
{}
</body>
</html>"#,
title,
crate::ssr::MNOTE_CSS,
shell,
escape_html(context.auth.actor_id.as_str()),
content
))
.into_response();
stamp_gateway_headers(response.headers_mut(), false);
Ok(response)
}
pub async fn root_entry(
State(state): State<AppState>,
Extension(context): Extension<RequestContext>,
@@ -579,6 +661,21 @@ pub async fn root_entry(
escape_script_json(&panes_bootstrap_json),
render_editor_island_adapter_script(),
);
let body_extra = if state.config().enable_page_ai_pi_lab {
body_extra
+ &format!(
r#"<script>
(function() {{
var s = document.createElement('script');
s.src = '/api/mnote-browser-runtime/sidebar-page-ai-pi-lab-runtime.js';
s.onload = function() {{ if (window.createSidebarPageAiPiLabRuntime) window.createSidebarPageAiPiLabRuntime({{}}); }};
document.body.appendChild(s);
}})();
</script>"#
)
} else {
body_extra
};
("MNOTE".to_string(), render_workspace_entry(), body_extra)
} else {
match build_page_aggregate_snapshot(
@@ -639,6 +736,21 @@ pub async fn root_entry(
render_document_title_controller_script(),
render_editor_island_adapter_script(),
);
let body_extra = if state.config().enable_page_ai_pi_lab {
body_extra
+ &format!(
r#"<script>
(function() {{
var s = document.createElement('script');
s.src = '/api/mnote-browser-runtime/sidebar-page-ai-pi-lab-runtime.js';
s.onload = function() {{ if (window.createSidebarPageAiPiLabRuntime) window.createSidebarPageAiPiLabRuntime({{}}); }};
document.body.appendChild(s);
}})();
</script>"#
)
} else {
body_extra
};
(title.to_string(), content, body_extra)
}
Err(_) => ("MNOTE".to_string(), render_workspace_entry(), String::new()),
@@ -2554,6 +2666,7 @@ mod tests {
enable_legacy_next_compat,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url,
@@ -3074,6 +3187,7 @@ mod tests {
enable_legacy_next_compat: false,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -3366,6 +3480,7 @@ mod tests {
enable_legacy_next_compat: false,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -166,6 +166,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -1450,7 +1450,9 @@ pub async fn toggle_skill(
return Ok((
StatusCode::OK,
stamp_client_headers(),
Json(json!({"ok": true, "skillKind": "mnote_builtin", "configScope": "user_control_plane"})),
Json(
json!({"ok": true, "skillKind": "mnote_builtin", "configScope": "user_control_plane"}),
),
));
}
let access =
@@ -14553,6 +14555,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -15520,6 +15523,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -15562,6 +15566,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -15991,7 +15996,10 @@ mod tests {
.await
.expect("detail body");
let detail_payload: Value = serde_json::from_slice(&detail_body).expect("detail json");
assert_eq!(detail_payload["persistence"], ACP_RUNTIME_CONTROL_PLANE_STORE);
assert_eq!(
detail_payload["persistence"],
ACP_RUNTIME_CONTROL_PLANE_STORE
);
assert_eq!(
detail_payload["session"]["runs"][0]["sessionId"],
session_id
@@ -16399,6 +16407,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: Some(convex_url),
@@ -16558,6 +16567,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: Some(convex_url),
@@ -16646,6 +16656,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: Some(convex_url),
@@ -16722,6 +16733,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: Some(convex_url),
@@ -16905,6 +16917,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: Some(convex_url),
@@ -1113,6 +1113,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -1180,6 +1181,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -282,6 +282,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -504,6 +504,7 @@ mod tests {
enable_legacy_next_compat: false,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -9630,6 +9630,7 @@ mod tests {
enable_legacy_next_compat: false,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -2147,10 +2147,7 @@ fn clear_local_search_index(root_path: &Path) -> Result<(), WebError> {
Err(error) => {
return Err(WebError::bad_request_code(
"local_search_index_delete_failed",
format!(
"无法删除本地搜索索引文件 {}: {error}",
index_path.display()
),
format!("无法删除本地搜索索引文件 {}: {error}", index_path.display()),
));
}
}
@@ -486,6 +486,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -352,6 +352,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -420,6 +421,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
+55 -3
View File
@@ -1,8 +1,9 @@
mod ai_settings;
mod bridge;
pub(crate) mod command_support;
mod compat;
mod dev_seed;
pub(crate) mod dev_hot;
mod dev_seed;
mod documents;
mod editor;
pub(crate) mod evidence;
@@ -28,6 +29,7 @@ pub(crate) mod onlyoffice_bridge;
mod page_ai_board;
mod page_ai_opencode;
mod page_ai_openhub;
mod page_ai_pi;
mod page_ai_workflow;
mod query_support;
mod resource_trash;
@@ -49,8 +51,7 @@ pub(crate) use local_folder_source::{
control_plane_status_display, decode_local_id_segment, ensure_local_path_read_access,
ensure_local_workspace_access, ensure_local_workspace_read_access_with_state,
ensure_local_workspace_write_access_with_state, local_markdown_conflict_detection_key,
local_workspace_id_from_root_uri, update_local_markdown_title,
write_local_markdown_page_body,
local_workspace_id_from_root_uri, update_local_markdown_title, write_local_markdown_page_body,
};
#[cfg(test)]
pub(crate) use local_search_index::write_local_index_settings;
@@ -84,6 +85,8 @@ pub fn build_router(state: AppState) -> Router {
"/user/access-policy",
get(gateway::user_access_policy_entry),
)
.route("/admin/ai", get(gateway::admin_ai_entry))
.route("/user/ai", get(gateway::user_ai_entry))
.route("/auth", get(gateway::auth_entry).post(gateway::auth_entry))
.route("/search", get(search::shell))
.route("/knowledge", get(gateway::root_entry))
@@ -275,6 +278,10 @@ pub fn build_router(state: AppState) -> Router {
"/api/mnote-browser-runtime/sidebar-page-ai-target-runtime.js",
get(web_shell::sidebar_page_ai_target_runtime_asset),
)
.route(
"/api/mnote-browser-runtime/sidebar-page-ai-pi-lab-runtime.js",
get(web_shell::sidebar_page_ai_pi_lab_runtime_asset),
)
.route(
"/api/mnote-browser-runtime/sidebar-page-settings-runtime.js",
get(web_shell::sidebar_page_settings_runtime_asset),
@@ -588,6 +595,27 @@ pub fn build_router(state: AppState) -> Router {
"/api/page-ai/board/runs/{run_id}/cancel",
post(page_ai_board::cancel_run),
)
.route("/api/page-ai/pi/status", get(page_ai_pi::status))
.route("/api/page-ai/pi/bootstrap", post(page_ai_pi::bootstrap))
.route("/api/page-ai/pi/start", post(page_ai_pi::start))
.route("/api/page-ai/pi/send", post(page_ai_pi::send))
.route("/api/page-ai/pi/abort", post(page_ai_pi::abort))
.route("/api/page-ai/pi/events", get(page_ai_pi::events))
.route("/api/page-ai/pi/sessions", get(page_ai_pi::list_sessions))
.route(
"/api/page-ai/pi/sessions/{session_id}",
get(page_ai_pi::get_session_history),
)
.route(
"/api/page-ai/pi/sessions/{session_id}/events",
get(page_ai_pi::get_session_events),
)
.route("/api/page-ai/pi/tool-call", post(page_ai_pi::tool_call))
.route(
"/api/page-ai/pi/tool-call-bridge",
post(page_ai_pi::tool_call_bridge),
)
.route("/page-ai/pi", get(page_ai_pi::shell))
.route(
"/api/sidebar/shortcuts",
get(sidebar_shortcuts::list_shortcuts).post(sidebar_shortcuts::upsert_shortcut),
@@ -628,6 +656,29 @@ pub fn build_router(state: AppState) -> Router {
"/api/user/access-policy/grants/{grant_id}",
delete(local_folder_source::delete_user_access_grant),
)
.route(
"/api/ai-settings/effective",
get(ai_settings::effective_settings),
)
.route(
"/api/ai-settings/access-scopes",
get(ai_settings::user_access_scopes),
)
.route(
"/api/ai-admin/access-scopes",
get(ai_settings::admin_access_scopes),
)
.route("/api/ai-settings/receipts", get(ai_settings::user_receipts))
.route("/api/ai-admin/receipts", get(ai_settings::admin_receipts))
.route(
"/api/ai-admin/settings",
get(ai_settings::admin_get_settings).put(ai_settings::admin_put_settings),
)
.route("/api/ai-admin/users", get(ai_settings::admin_list_users))
.route(
"/api/ai-admin/users/{user_id}/settings",
get(ai_settings::admin_get_user_settings).put(ai_settings::admin_put_user_settings),
)
.route(
"/api/admin/share-links",
get(local_folder_source::get_share_links).post(local_folder_source::create_share_link),
@@ -1035,6 +1086,7 @@ mod tests {
enable_legacy_next_compat: false,
enable_debug_shell_routes,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -369,6 +369,7 @@ mod tests {
enable_legacy_next_compat: false,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -1998,6 +1998,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
File diff suppressed because it is too large Load Diff
@@ -611,6 +611,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -1163,6 +1163,7 @@ mod tests {
enable_legacy_next_compat: false,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -776,6 +776,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -906,6 +907,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -1517,6 +1519,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -229,6 +229,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -331,6 +332,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -400,6 +402,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -480,6 +483,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
+1
View File
@@ -358,6 +358,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
+3
View File
@@ -2656,6 +2656,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: true,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -3001,6 +3002,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: true,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -3682,6 +3684,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: true,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
+45 -1
View File
@@ -298,6 +298,8 @@ pub async fn document_page_shell(
secondary_requested,
secondary_invalid,
);
let pi_lab_loader_script =
render_page_ai_pi_lab_loader_script(state.config().enable_page_ai_pi_lab);
let body_content = crate::ssr::render_view(leptos::view! {
<DocumentPage
title={title.to_string()}
@@ -339,6 +341,7 @@ pub async fn document_page_shell(
{}
{}
{}
{}
</body>
</html>"#,
escape_html(title),
@@ -368,6 +371,7 @@ pub async fn document_page_shell(
r#"<script type="module" src="{}"></script>"#,
mnote_browser_runtime_src("document-conflict-panel-runtime.js")
),
pi_lab_loader_script,
);
let mut response = Html(html).into_response();
stamp_shell_headers(response.headers_mut(), "document");
@@ -850,6 +854,23 @@ pub(crate) fn render_editor_island_adapter_script() -> String {
)
}
pub(crate) fn render_page_ai_pi_lab_loader_script(enabled: bool) -> String {
if !enabled {
return String::new();
}
format!(
r#"<script>
(function() {{
var s = document.createElement('script');
s.src = '{}';
s.onload = function() {{ if (window.createSidebarPageAiPiLabRuntime) window.createSidebarPageAiPiLabRuntime({{}}); }};
document.body.appendChild(s);
}})();
</script>"#,
mnote_browser_runtime_src("sidebar-page-ai-pi-lab-runtime.js")
)
}
fn leptos_tiptap_runtime_src(asset: &str) -> String {
let base = format!("/api/leptos-tiptap-runtime/{asset}");
if let Some(cache_buster) = crate::routes::dev_hot::dev_hot_cache_buster() {
@@ -2591,6 +2612,20 @@ pub async fn sidebar_page_ai_target_runtime_asset() -> Response {
.unwrap_or_else(|_| Response::new(Body::empty()))
}
pub async fn sidebar_page_ai_pi_lab_runtime_asset() -> Response {
const JS: &str = include_str!("../../browser/sidebar-page-ai-pi-lab-runtime.js");
Response::builder()
.status(StatusCode::OK)
.header(
header::CONTENT_TYPE,
"application/javascript; charset=utf-8",
)
.header(header::CACHE_CONTROL, runtime_asset_cache_control())
.header(HEADER_MNOTE_WEB_OWNER, "mnote-web")
.body(browser_runtime_js_body(JS))
.unwrap_or_else(|_| Response::new(Body::empty()))
}
pub async fn sidebar_page_settings_runtime_asset() -> Response {
const JS: &str = include_str!("../../browser/sidebar-page-settings-runtime.js");
Response::builder()
@@ -3669,6 +3704,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -3726,6 +3762,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -3847,6 +3884,7 @@ mod tests {
enable_legacy_next_compat: false,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: Some("http://127.0.0.1:9".into()),
@@ -3989,6 +4027,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -4729,6 +4768,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -4797,6 +4837,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -4935,7 +4976,8 @@ mod tests {
}
#[tokio::test]
async fn local_folder_page_aggregate_prefers_control_plane_ui_preference_over_default_title_header() {
async fn local_folder_page_aggregate_prefers_control_plane_ui_preference_over_default_title_header(
) {
let root = std::env::temp_dir().join(format!(
"mnote-local-page-aggregate-ui-pref-{}",
std::process::id()
@@ -4954,6 +4996,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,
@@ -5499,6 +5542,7 @@ mod tests {
enable_legacy_next_compat: true,
enable_debug_shell_routes: false,
enable_editor_actor: true,
enable_page_ai_pi_lab: false,
hermes_base_path: "/api/hermes".into(),
compat_next_base_path: "/api/compat/next".into(),
convex_url: None,