Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Search across all your Logseq blocks using text embeddings. Instead of matching exact keywords, semantic search finds blocks that are **conceptually similar** to your query.

Blocks are embedded with their full context — page name, page properties, and parent block hierarchy — so searching for a page topic or parent heading surfaces relevant child blocks.
Each block is embedded content-first, followed by its context — page name, page and block properties, and parent block hierarchy — so searching for a page topic or parent heading surfaces relevant child blocks.

## Requirements

Expand Down Expand Up @@ -89,6 +89,8 @@ Semantic search matches meaning, not exact words. To get the best results:
| Batch Size | `50` | Number of texts per API request |
| Top K Results | `20` | Maximum number of search results |
| Auto-index on Load | `true` | Automatically index when the graph loads |
| Page Properties | `tags, alias, category, type, ...` | Comma-separated page properties to include in embedding context |
| Block Properties | `type, status, priority, tags, ...` | Comma-separated block properties to include in embedding text |

## Support

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "logseq-plugin-semantic-search",
"version": "0.1.6",
"version": "0.2.0",
"description": "Search your Logseq notes by meaning, not just keywords",
"main": "dist/index.html",
"scripts": {
Expand Down
4 changes: 0 additions & 4 deletions src/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
getMetadata,
setMetadata,
getEmbeddingCount,
invalidateEmbeddingCache,
} from "./storage";
import { getSettings } from "./settings";

Expand Down Expand Up @@ -407,9 +406,6 @@ export async function indexBlocks(
await setMetadata("blockCount", count);
await setMetadata("lastIndexed", Date.now());

// Ensure search cache is fresh after indexing
invalidateEmbeddingCache();

if (!abort.signal.aborted && total > 0) {
logseq.UI.showMsg(`Indexed ${total} blocks`);
}
Expand Down
11 changes: 7 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,18 @@ async function main() {
}
});

async function syncGraphName(): Promise<void> {
const graph = await logseq.App.getCurrentGraph();
if (graph?.name) setGraphName(graph.name);
}

// Auto-index on load
const settings = getSettings();
if (settings.autoIndexOnLoad) {
// Delay to let Logseq finish loading
setTimeout(async () => {
try {
const graph = await logseq.App.getCurrentGraph();
if (graph?.name) setGraphName(graph.name);
await syncGraphName();
await indexBlocks();
} catch (err) {
console.error("Auto-indexing failed:", err);
Expand All @@ -72,8 +76,7 @@ async function main() {

// Re-index on graph change
logseq.App.onCurrentGraphChanged(async () => {
const graph = await logseq.App.getCurrentGraph();
if (graph?.name) setGraphName(graph.name);
await syncGraphName();
const s = getSettings();
if (s.autoIndexOnLoad) {
setTimeout(() => {
Expand Down
17 changes: 7 additions & 10 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,13 @@ export interface PluginSettings {
blockProperties: string;
}

const settingsDefaults: PluginSettings = Object.fromEntries(
settingsSchema
.filter((s) => s.type !== "heading")
.map((s) => [s.key, s.default]),
) as unknown as PluginSettings;

export function getSettings(): PluginSettings {
const s = logseq.settings as Partial<PluginSettings> | undefined;
return {
apiEndpoint: s?.apiEndpoint ?? "http://localhost:11434",
apiFormat: s?.apiFormat ?? "ollama",
embeddingModel: s?.embeddingModel ?? "nomic-embed-text",
batchSize: s?.batchSize ?? 50,
topK: s?.topK ?? 20,
autoIndexOnLoad: s?.autoIndexOnLoad ?? true,
pageProperties: s?.pageProperties ?? "tags, alias, category, type, description, summary, author, topic, area, project, status, priority, platform",
blockProperties: s?.blockProperties ?? "type, status, priority, tags, source, url, author",
};
return { ...settingsDefaults, ...s };
}
4 changes: 0 additions & 4 deletions src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,6 @@ export function invalidateEmbeddingCache(): void {
embeddingCache = null;
}

export function evictEmbeddingCache(): void {
embeddingCache = null;
}

export async function deleteEmbeddings(blockIds: string[]): Promise<void> {
if (blockIds.length === 0) return;
const db = await openDB();
Expand Down
6 changes: 3 additions & 3 deletions src/ui.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { debounce } from "./utils";
import { embedTexts } from "./embeddings";
import { getCachedEmbeddings, getEmbeddingCount, evictEmbeddingCache } from "./storage";
import { getCachedEmbeddings, getEmbeddingCount, invalidateEmbeddingCache } from "./storage";
import { searchEmbeddings, type SearchResult } from "./search";
import { indexBlocks, indexingState, acquireSearchPriority, releaseSearchPriority } from "./indexer";
import { getSettings } from "./settings";
Expand Down Expand Up @@ -196,7 +196,7 @@ function hideModal(): void {
// Evict embedding cache after idle period
if (evictTimer) clearTimeout(evictTimer);
evictTimer = setTimeout(() => {
evictEmbeddingCache();
invalidateEmbeddingCache();
evictTimer = undefined;
}, CACHE_EVICT_MS);
}
Expand Down Expand Up @@ -427,7 +427,7 @@ export function showModal(): void {
evictTimer = undefined;
}
// Invalidate cache so first search loads fresh from IDB
evictEmbeddingCache();
invalidateEmbeddingCache();
clearResults();
logseq.showMainUI();
if (indexingState.status === "idle") {
Expand Down
2 changes: 1 addition & 1 deletion styles/search-modal.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
background: var(--ls-primary-background-color, #fff);
color: var(--ls-primary-text-color, #333);
border-radius: 8px;
width: 600px;
width: 900px;
max-width: 90vw;
max-height: 70vh;
display: flex;
Expand Down
Loading