Ship floating launcher, automatic discovery and bounded media search improvements

This commit is contained in:
karl committed 2026-10-14 11:44:15 +02:00
1 parent 01279a6b25
commit 521966a2ac
43 files changed
+3165 -267

No files matched your search

+5 -2
View File
@@ -22,6 +22,7 @@ then warms its retained CPU worker before listening on 127.0.0.1:1431.
```powershell
$env:SEARCHI_FORCE_CPU = '1'
$env:SEARCHI_EXTRACT_WORKER = (Resolve-Path target/release/searchi-extract-worker.exe).Path
cargo run --manifest-path tools/desktop-capture/backend/Cargo.toml -- --root .searchi/readme-capture/files --db .searchi/readme-capture/index.sqlite --models .searchi/models/embeddinggemma2 --worker .searchi/runtime/cpu/Release/searchi-gemma-worker.exe --dist apps/desktop/dist --ffmpeg .searchi/media-runtime/bin/ffmpeg.exe --ffprobe .searchi/media-runtime/bin/ffprobe.exe
```
@@ -46,12 +47,14 @@ loaded fonts, image pixels, and genuine worker inference counters in debug mode.
## Artifacts and limits
Outputs under docs/screenshots are search-light.png, search-dark.png,
Outputs under docs/screenshots are launcher-empty.png, search-light.png, search-dark.png,
media-search.png and performance-details.png, plus capture-manifest.json. The
manifest contains actual fixture queries, counts/selection, measured response
metrics, device/model, viewport, PNG SHA256, runner/browser versions, Git base
commit and dirty-state flag. The base commit does not identify uncommitted
source exactly. The ephemeral API token is never saved in the manifest.
source exactly. It also records the actual frontend package version and built
index SHA-256. The compact empty-bar capture has no manufactured results and
uses the genuine ready backend. The ephemeral API token is never saved in the manifest.
Captures are **browser-rendered production UI with the actual CPU engine**.
They omit native window chrome. They do not establish native WebView visual QA,
+16
View File
@@ -1129,11 +1129,16 @@ name = "searchi-core"
version = "0.1.0"
dependencies = [
"anyhow",
"libc",
"lopdf",
"quick-xml",
"rusqlite",
"searchi-media",
"serde",
"serde_json",
"sha2 0.10.9",
"walkdir",
"windows-sys",
"zip",
]
@@ -1192,11 +1197,22 @@ dependencies = [
"searchi-core",
"searchi-media",
"searchi-semantic",
"searchi-speech",
"serde",
"sha2 0.10.9",
"usearch",
]
[[package]]
name = "searchi-speech"
version = "0.1.0"
dependencies = [
"anyhow",
"serde",
"serde_json",
"sha2 0.10.9",
]
[[package]]
name = "serde"
version = "1.0.229"
+12 -2
View File
@@ -44,6 +44,8 @@ struct Filters {
max_duration_ms: Option<u32>,
modified_after_unix_ms: Option<i64>,
modified_before_unix_ms: Option<i64>,
media_artist: Option<String>,
media_title: Option<String>,
}
impl Filters {
fn options(self, root: &Path) -> Result<SearchOptions> {
@@ -63,6 +65,8 @@ impl Filters {
max_duration_ms: self.max_duration_ms,
modified_after_unix_ms: self.modified_after_unix_ms,
modified_before_unix_ms: self.modified_before_unix_ms,
media_artist: self.media_artist,
media_title: self.media_title,
})
}
}
@@ -87,6 +91,10 @@ fn main() -> Result<()> {
let mut lexical = SearchIndex::open(&args.db)?;
validate_database(&args.db, &root)?;
lexical.index_folder(&root)?;
ensure!(
lexical.extracted_source_hash(&root.join("Supplier Agreement.pdf").to_string_lossy())?.is_some(),
"Capture PDF was not genuinely extracted; set SEARCHI_EXTRACT_WORKER to the verified document helper"
);
lexical.prewarm()?;
let tools = FfmpegTools::new(args.ffmpeg, args.ffprobe)?;
let mut hybrid = HybridSearch::open(&args.db, &args.models, &args.worker, 1)?;
@@ -287,7 +295,7 @@ impl App {
json!({"model":"Google EmbeddingGemma 2","ready":true,"model_directory":self.models,"worker_ready":true,"downloading":false,"embedding":false,"device_policy":"cpu","error":null,"downloaded_bytes":864676480u64,"total_bytes":864676480u64,"generation":status.generation,"chunks":status.chunks,"stale":status.stale,"pending":status.pending,"warming":false,"worker_info":self.hybrid.worker_info(),"media_tools_ready":true,"media_setup_error":null,"media_failures":status.failures}),
)
}
"search_measured" | "hybrid_search_measured" => {
"search_measured" | "hybrid_search_measured" | "semantic_search_raw" => {
let query = args["query"].as_str().context("Query required")?;
ensure!(query.len() <= 4096, "Query too large");
let filters: Filters = if args["filters"].is_null() {
@@ -297,7 +305,9 @@ impl App {
};
let options = filters.options(&self.root)?;
let debug = args["debugPerformance"].as_bool().unwrap_or(false);
if name == "search_measured" {
if name == "semantic_search_raw" {
Ok(serde_json::to_value(self.hybrid.search_semantic(query, 50, &options)?)?)
} else if name == "search_measured" {
Ok(serde_json::to_value(
self.lexical
.search_with_options_measured(query, 50, &options, debug)?,
+41 -2
View File
@@ -92,6 +92,28 @@ try {
.evaluate((image) => image.complete && image.naturalWidth > 0),
"Brand asset failed to load",
);
// Capture the actual ready/empty production layout before typing. This is
// browser-rendered UI, not native window chrome or synthesized app results.
await page.waitForFunction(() =>
document.documentElement.dataset.launcherLayout === "compact" &&
document.querySelector("#query")?.value === "",
);
const compactPath = resolve(out, "launcher-empty.png");
const compactBounds = await page.locator("#app").boundingBox();
assert(compactBounds && compactBounds.width <= 720 && compactBounds.height <= 112,
"Ready empty launcher must remain a compact bar");
await page.locator("#app").screenshot({ path: compactPath, animations: "disabled" });
evidence.push({
file: "launcher-empty.png",
sha256: createHash("sha256").update(await readFile(compactPath)).digest("hex"),
query: "",
mode: "empty-ready",
result_count: 0,
selected_filename: null,
theme: "light",
capture_bounds: compactBounds,
state_assertions: { semantic_ready: true, compact_empty_layout: true, native_pixels: false },
});
async function query(text, debug = false) {
const awaited = page.waitForResponse(
(response) => {
@@ -151,6 +173,10 @@ try {
() => !document.querySelector("#preview")?.hidden,
);
if (text === "cancel the supplier partnership") {
assert(
!body.results.some((result) => ["Mountain Lake.jpg", "Night Pier.jpg"].includes(result.filename)),
"Contract results must not contain the unrelated fixture photos",
);
assert.equal(
latestMeasured.first_filename,
"Supplier Agreement.pdf",
@@ -225,9 +251,12 @@ try {
literal_query_marks: true,
actual_page_explanation: true,
why_disclosure_collapsed_by_default: true,
unrelated_photos_removed: true,
};
}
if (text === "mountains reflected in calm water") {
assert(body.results.every((result) => result.match_kind === "semantic_image"),
"Visual search shortlist must not contain the unrelated fixture documents");
assert.equal(
latestMeasured.first_filename,
"Mountain Lake.jpg",
@@ -238,7 +267,7 @@ try {
0,
"Meaning-only photo result must not invent word-level alignment",
);
latestMeasured.ui_checks = { meaning_only_image_has_no_text_marks: true };
latestMeasured.ui_checks = { meaning_only_image_has_no_text_marks: true, unrelated_documents_removed: true };
}
await page.evaluate(
() =>
@@ -249,8 +278,12 @@ try {
}
async function screenshot(name, queryText) {
assert.deepEqual(failures, [], "Frontend page errors occurred");
const pane = await page.locator(".results-pane").boundingBox();
const preview = await page.locator("#preview").boundingBox();
assert(pane && preview && preview.x >= pane.x + pane.width - 1,
"Wide result and preview panes must remain side by side");
const path = resolve(out, `${name}.png`);
await page.screenshot({ path, animations: "disabled", fullPage: false });
await page.locator("#app").screenshot({ path, animations: "disabled" });
const selected = await page.locator("#preview-title").innerText();
assert.equal(
selected,
@@ -272,7 +305,9 @@ try {
matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light",
),
summary: await page.locator("#result-summary").innerText(),
capture_bounds: await page.locator("#app").boundingBox(),
state_assertions: {
wide_split_preview: true,
semantic_ready: true,
hybrid_final: true,
image_visible: name === "media-search",
@@ -366,6 +401,10 @@ try {
{
kind: "browser-rendered-production-ui-real-cpu-engine",
source,
frontend_build: {
version: JSON.parse(await readFile(resolve(repository, "apps/desktop/package.json"), "utf8")).version,
index_sha256: createHash("sha256").update(await readFile(resolve(repository, "apps/desktop/dist/index.html"))).digest("hex"),
},
viewport: { width: 1440, height: 1000 },
runner: { playwright: "1.64.0", chromium: browser.version() },
backend: {