feat: integrate Google semantic search and branded desktop launcher
Core CI / core (ubuntu-latest) (push) Failing after 3m27s
Core CI / core (macos-latest) (push) Canceled after 0s
Core CI / core (windows-latest) (push) Canceled after 0s
Core CI / desktop (ubuntu-latest) (push) Successful in 5m45s
Core CI / desktop (macos-latest) (push) Canceled after 0s
Core CI / desktop (windows-latest) (push) Canceled after 0s
Core CI / core (ubuntu-latest) (push) Failing after 3m27s
Core CI / core (macos-latest) (push) Canceled after 0s
Core CI / core (windows-latest) (push) Canceled after 0s
Core CI / desktop (ubuntu-latest) (push) Successful in 5m45s
Core CI / desktop (macos-latest) (push) Canceled after 0s
Core CI / desktop (windows-latest) (push) Canceled after 0s
This commit is contained in:
1 parent
a90df15503
commit
caf6a2f82e
80 files changed
+14971
-837
No files matched your search
@@ -0,0 +1,133 @@
|
||||
/** One active request per lane; intermediate keystrokes collapse to the latest.
|
||||
* Native work already running cannot be cancelled, so revisions guard every
|
||||
* result and retry. Busy responses receive at most one delayed retry. */
|
||||
export class LatestLane<T> {
|
||||
private pending: T | undefined;
|
||||
private running = false;
|
||||
private run: (value: T) => Promise<void>;
|
||||
private current: (value: T) => boolean;
|
||||
private fail: (value: T, error: unknown) => void;
|
||||
private delay: () => Promise<void>;
|
||||
|
||||
constructor(
|
||||
run: (value: T) => Promise<void>,
|
||||
current: (value: T) => boolean,
|
||||
fail: (value: T, error: unknown) => void,
|
||||
delay = () => new Promise<void>((resolve) => setTimeout(resolve, 400)),
|
||||
) {
|
||||
this.run = run;
|
||||
this.current = current;
|
||||
this.fail = fail;
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
submit(value: T) {
|
||||
this.pending = value;
|
||||
if (!this.running) void this.drain();
|
||||
}
|
||||
clear() {
|
||||
this.pending = undefined;
|
||||
}
|
||||
|
||||
private async drain() {
|
||||
this.running = true;
|
||||
try {
|
||||
while (this.pending !== undefined) {
|
||||
const value = this.pending;
|
||||
this.pending = undefined;
|
||||
if (!this.current(value)) continue;
|
||||
for (let attempt = 0; attempt < 2; attempt++) {
|
||||
try {
|
||||
await this.run(value);
|
||||
break;
|
||||
} catch (error) {
|
||||
if (!this.current(value)) break;
|
||||
if (
|
||||
attempt === 0 &&
|
||||
/busy|in progress|currently running|already running/i.test(
|
||||
String(error),
|
||||
)
|
||||
) {
|
||||
await this.delay();
|
||||
if (!this.current(value) || this.pending !== undefined) break;
|
||||
} else {
|
||||
this.fail(value, error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** An unsuccessful automatic build is not retried on every status poll. */
|
||||
export class AutomaticIndexGate {
|
||||
private attempted = new Set<string>();
|
||||
claim(key: string) {
|
||||
if (this.attempted.has(key)) return false;
|
||||
this.attempted.add(key);
|
||||
// Bound memory even across a very long desktop session.
|
||||
if (this.attempted.size > 128)
|
||||
this.attempted.delete(this.attempted.values().next().value!);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export type SemanticPhase =
|
||||
| "preparing"
|
||||
| "indexing"
|
||||
| "pending"
|
||||
| "unavailable"
|
||||
| "no-content"
|
||||
| "complete";
|
||||
|
||||
/** Zero keyword matches are provisional until the semantic lane completes. */
|
||||
export function emptySearchState(
|
||||
lane: "lexical" | "hybrid",
|
||||
phase: SemanticPhase,
|
||||
) {
|
||||
const title =
|
||||
lane === "lexical"
|
||||
? "No keyword matches yet."
|
||||
: "No matches in the current index yet.";
|
||||
if (phase === "preparing")
|
||||
return {
|
||||
title:
|
||||
lane === "lexical" ? title : "No matches in the current index yet.",
|
||||
detail:
|
||||
"Google semantic search is still preparing. This query will also search by meaning when it is ready.",
|
||||
};
|
||||
if (phase === "indexing")
|
||||
return {
|
||||
title:
|
||||
lane === "lexical" ? title : "No matches in the current index yet.",
|
||||
detail:
|
||||
"Semantic indexing is underway. Results may change when your documents finish indexing.",
|
||||
};
|
||||
if (phase === "pending")
|
||||
return {
|
||||
title,
|
||||
detail:
|
||||
"Searching by meaning… Semantic results will appear here when this query finishes.",
|
||||
};
|
||||
if (phase === "unavailable")
|
||||
return {
|
||||
title,
|
||||
detail:
|
||||
"Semantic results are currently unavailable; see the model status above. Try a filename or words from your document, or retry the search.",
|
||||
};
|
||||
if (phase === "no-content")
|
||||
return {
|
||||
title,
|
||||
detail:
|
||||
"No document content is indexed for semantic search yet. Index a folder with readable documents, or search a filename.",
|
||||
};
|
||||
return {
|
||||
title: lane === "hybrid" ? "No matches for this search." : title,
|
||||
detail:
|
||||
"Try another phrase, broaden your filters, or index another folder.",
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user