From c5593695770af5fcc858f95afb2ef9ea09443d02 Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Tue, 17 Mar 2026 09:46:53 +0000 Subject: [PATCH 1/7] Document page and block property settings in README Co-Authored-By: Claude Opus 4.6 --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c445f7..3543253 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 From 7e9d745dd4a6d5b2fc19d6a08f81a65d7baa26e1 Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Tue, 17 Mar 2026 09:46:58 +0000 Subject: [PATCH 2/7] Widen search modal to 900px to match native Logseq search Co-Authored-By: Claude Opus 4.6 --- styles/search-modal.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/search-modal.css b/styles/search-modal.css index 15d5d80..6355dfb 100644 --- a/styles/search-modal.css +++ b/styles/search-modal.css @@ -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; From 085b0967c2799d778f1025307b2b552777eda96f Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Tue, 17 Mar 2026 10:15:51 +0000 Subject: [PATCH 3/7] Bump version to 0.2.0 Co-Authored-By: Claude Opus 4.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0c99a55..6db99a0 100644 --- a/package.json +++ b/package.json @@ -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": { From af6958d63aa2cea4844ba9a4a9fe8023af09f5d9 Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Tue, 17 Mar 2026 10:18:42 +0000 Subject: [PATCH 4/7] Remove duplicate evictEmbeddingCache function evictEmbeddingCache was identical to invalidateEmbeddingCache. Replace all usages with invalidateEmbeddingCache and remove the duplicate. Co-Authored-By: Claude Opus 4.6 --- src/storage.ts | 4 ---- src/ui.ts | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/storage.ts b/src/storage.ts index 9e6765e..6d0b5f7 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -122,10 +122,6 @@ export function invalidateEmbeddingCache(): void { embeddingCache = null; } -export function evictEmbeddingCache(): void { - embeddingCache = null; -} - export async function deleteEmbeddings(blockIds: string[]): Promise { if (blockIds.length === 0) return; const db = await openDB(); diff --git a/src/ui.ts b/src/ui.ts index 7f4bf8f..c94b717 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -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"; @@ -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); } @@ -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") { From 5e274729bf38690dd82fe110c341a320c809f2cd Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Tue, 17 Mar 2026 10:19:04 +0000 Subject: [PATCH 5/7] Remove redundant invalidateEmbeddingCache call in indexBlocks putEmbeddings and deleteEmbeddings already call invalidateEmbeddingCache internally, so the explicit call at the end of indexBlocks was unnecessary. Co-Authored-By: Claude Opus 4.6 --- src/indexer.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/indexer.ts b/src/indexer.ts index d8cbb21..1e44992 100644 --- a/src/indexer.ts +++ b/src/indexer.ts @@ -9,7 +9,6 @@ import { getMetadata, setMetadata, getEmbeddingCount, - invalidateEmbeddingCache, } from "./storage"; import { getSettings } from "./settings"; @@ -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`); } From 27fb6163dff004d3777ebadf16e3cabb6e2630ef Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Tue, 17 Mar 2026 10:19:24 +0000 Subject: [PATCH 6/7] Derive settings defaults from schema to avoid duplication The default values for each setting were specified both in settingsSchema and in getSettings(). Now getSettings() derives its defaults from the schema, so each default is defined in one place. Co-Authored-By: Claude Opus 4.6 --- src/settings.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/settings.ts b/src/settings.ts index 06edf9a..094a34b 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -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 | 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 }; } From 1366e4a0338ef2dcfda312b1684e06a1998855e0 Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Tue, 17 Mar 2026 10:19:46 +0000 Subject: [PATCH 7/7] Extract syncGraphName helper to deduplicate graph name fetch The getCurrentGraph/setGraphName pattern was repeated in both the auto-index-on-load and graph-change handlers. Extract it into a shared helper function. Co-Authored-By: Claude Opus 4.6 --- src/main.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main.ts b/src/main.ts index 1820f87..4beede5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -55,14 +55,18 @@ async function main() { } }); + async function syncGraphName(): Promise { + 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); @@ -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(() => {