feat: add evidence search and stabilize pdf previews
- add document evidence parsing/search/open routes, Hermes tool wiring, local index settings/status, and the document-evidence skill plus design notes - fix PDF resource tabs by rendering PDFs inline with pdf.js canvases instead of iframe preview pages, release PDF documents on close, and document the fourth-PDF stall bug - keep PDF preview at 2x rendering while removing the previous lazy-load/placeholder direction, and make dev:hot bind loopback defaults externally reachable Verification: - node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js - node scripts/task-dev-hot-plan-test.js - cargo test -p mnote-web --manifest-path rust/Cargo.toml pdf_preview_page_does_not_render_visible_toolbar - cargo test -p mnote-web --manifest-path rust/Cargo.toml document_shell_returns_page_aggregate_snapshot - cargo build -p mnote-web --manifest-path rust/Cargo.toml - browser smoke: sequentially opened the four tea_seed_oil_cosmetic PDFs; fourth PDF rendered 15/15 canvases, iframeCount=0, browser errors=0
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use crate::app::AppState;
|
||||
use crate::app::{open_control_plane_store, AppState};
|
||||
use crate::context::RequestContext;
|
||||
use crate::error::WebError;
|
||||
use crate::page_aggregate::{
|
||||
@@ -2680,6 +2680,21 @@ pub fn load_local_folder_file_tree_children_snapshot(
|
||||
load_local_folder_file_tree_scope_snapshot(root_uri, Some(parent_relative_path), None)
|
||||
}
|
||||
|
||||
pub fn load_local_folder_file_tree_children_snapshot_with_reveal(
|
||||
root_uri: &str,
|
||||
parent_relative_path: &str,
|
||||
reveal_document_id: Option<&str>,
|
||||
) -> Result<ProjectionSnapshot, WebError> {
|
||||
let reveal_relative_path = reveal_document_id
|
||||
.and_then(local_markdown_relative_path_from_document_id)
|
||||
.map(|path| path.replace('\\', "/"));
|
||||
load_local_folder_file_tree_scope_snapshot(
|
||||
root_uri,
|
||||
Some(parent_relative_path),
|
||||
reveal_relative_path.as_deref(),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_local_folder_file_tree_scope_snapshot(
|
||||
root_uri: &str,
|
||||
parent_relative_path: Option<&str>,
|
||||
@@ -2732,16 +2747,15 @@ fn load_local_folder_file_tree_scope_snapshot(
|
||||
&workspace_id,
|
||||
&metadata,
|
||||
)?;
|
||||
if parent_relative_path.is_empty() {
|
||||
append_file_tree_reveal_rows(
|
||||
&canonical_root,
|
||||
reveal_relative_path,
|
||||
&root_source_uri,
|
||||
&workspace_id,
|
||||
&metadata,
|
||||
&mut scan_result.rows,
|
||||
)?;
|
||||
}
|
||||
append_file_tree_reveal_rows(
|
||||
&canonical_root,
|
||||
parent_relative_path,
|
||||
reveal_relative_path,
|
||||
&root_source_uri,
|
||||
&workspace_id,
|
||||
&metadata,
|
||||
&mut scan_result.rows,
|
||||
)?;
|
||||
|
||||
let items = scan_result
|
||||
.rows
|
||||
@@ -3207,7 +3221,13 @@ fn save_local_markdown_page_inner(
|
||||
}
|
||||
|
||||
fn refresh_local_search_index_best_effort(root: &Path, root_uri: &str, workspace_id: &str) {
|
||||
let _ = local_search_index::refresh_local_search_index(root, root_uri, workspace_id);
|
||||
let control_plane = open_control_plane_store();
|
||||
let _ = local_search_index::refresh_local_search_index_for_change_with_store(
|
||||
&control_plane,
|
||||
root,
|
||||
root_uri,
|
||||
workspace_id,
|
||||
);
|
||||
}
|
||||
|
||||
fn local_markdown_conflict_error(
|
||||
@@ -6842,6 +6862,7 @@ fn ancestor_directories_for_relative_path(relative_path: &str) -> Vec<String> {
|
||||
|
||||
fn append_file_tree_reveal_rows(
|
||||
root: &Path,
|
||||
parent_relative_path: &str,
|
||||
reveal_relative_path: Option<&str>,
|
||||
root_source_uri: &str,
|
||||
workspace_id: &str,
|
||||
@@ -6854,10 +6875,30 @@ fn append_file_tree_reveal_rows(
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
let mut ancestors = ancestor_directories_for_relative_path(reveal_relative_path);
|
||||
if let Some(bundle_parent) = same_name_markdown_bundle_parent(reveal_relative_path) {
|
||||
let parent_relative_path = parent_relative_path
|
||||
.trim()
|
||||
.trim_matches('/')
|
||||
.replace('\\', "/");
|
||||
let reveal_relative_path = reveal_relative_path
|
||||
.trim()
|
||||
.trim_matches('/')
|
||||
.replace('\\', "/");
|
||||
if !parent_relative_path.is_empty()
|
||||
&& reveal_relative_path != parent_relative_path
|
||||
&& !reveal_relative_path.starts_with(&format!("{parent_relative_path}/"))
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
let mut ancestors = ancestor_directories_for_relative_path(&reveal_relative_path);
|
||||
if let Some(bundle_parent) = same_name_markdown_bundle_parent(&reveal_relative_path) {
|
||||
ancestors.retain(|ancestor| ancestor != &bundle_parent);
|
||||
}
|
||||
if !parent_relative_path.is_empty() {
|
||||
ancestors.retain(|ancestor| {
|
||||
ancestor != &parent_relative_path
|
||||
&& ancestor.starts_with(&format!("{parent_relative_path}/"))
|
||||
});
|
||||
}
|
||||
if ancestors.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -7047,7 +7088,38 @@ fn resolve_metadata_relative_path(root: &Path, relative_path: &str) -> Result<Pa
|
||||
}
|
||||
|
||||
fn should_ignore_entry(relative_path: &str, file_name: &str) -> bool {
|
||||
if matches!(file_name, ".git" | "node_modules" | ".mnote") {
|
||||
let lower_name = file_name.to_ascii_lowercase();
|
||||
if matches!(
|
||||
lower_name.as_str(),
|
||||
".git"
|
||||
| ".mnote"
|
||||
| ".codegraph"
|
||||
| ".codex"
|
||||
| ".claw"
|
||||
| ".gemini"
|
||||
| ".reasonix"
|
||||
| ".venv"
|
||||
| "__pycache__"
|
||||
| "node_modules"
|
||||
| ".next"
|
||||
| ".turbo"
|
||||
| ".pnpm-store"
|
||||
| ".convex-tmp"
|
||||
| "target"
|
||||
| "dist"
|
||||
| "build"
|
||||
| "tmp"
|
||||
| "temp"
|
||||
| "artifacts"
|
||||
| "test-results"
|
||||
| "pw-tests"
|
||||
| "recycle"
|
||||
| "reference-code"
|
||||
| "services"
|
||||
| "cankao"
|
||||
| "ai-sessions"
|
||||
) || lower_name.starts_with("onlyoffice-")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
relative_path == ".mnote/trash"
|
||||
@@ -9732,6 +9804,18 @@ mod tests {
|
||||
std::fs::write(root.join(".mnote").join("ignored.txt"), "ignored").expect("write ignored");
|
||||
let ignored = local_folder_watch_revision(&root_uri).expect("ignored revision");
|
||||
assert_eq!(initial.revision, ignored.revision);
|
||||
std::fs::create_dir_all(root.join("target")).expect("create target");
|
||||
std::fs::write(root.join("target").join("ignored.md"), "# Ignored\n")
|
||||
.expect("write ignored target md");
|
||||
std::fs::create_dir_all(root.join("reference-code")).expect("create reference-code");
|
||||
std::fs::write(
|
||||
root.join("reference-code").join("ignored.md"),
|
||||
"# Ignored\n",
|
||||
)
|
||||
.expect("write ignored reference md");
|
||||
let ignored_generated =
|
||||
local_folder_watch_revision(&root_uri).expect("ignored generated revision");
|
||||
assert_eq!(initial.revision, ignored_generated.revision);
|
||||
std::fs::write(root.join("docs").join("page.md"), "# Watch Again\n").expect("update md");
|
||||
let updated = local_folder_watch_revision(&root_uri).expect("updated revision");
|
||||
assert_ne!(initial.revision, updated.revision);
|
||||
|
||||
Reference in New Issue
Block a user