You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: DESIGN.md
+2-3Lines changed: 2 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -604,15 +604,14 @@ Source: `Switch` and `DescriptionSwitch` from `@cherrystudio/ui` (`packages/ui/s
604
604
-**Top chrome height**: `var(--app-top-chrome-height)` = 44px. Use this for the main window tab bar and any standalone macOS window top drag area that should visually align with the main app chrome.
605
605
-**Navbar content height**: `var(--navbar-height)` defaults to `var(--app-top-chrome-height)` for the fixed top-menu layout. Only override it for inner content calculations that intentionally do not include a top navbar.
606
606
- Settings-style floating windows with a transparent macOS shell must keep the outer top inset tied to `var(--app-top-chrome-height)` instead of hard-coded pixel classes such as `h-11` or `h-[50px]`.
607
-
-**Settings window sizing** (standalone settings window only): sized to 80% of the main window with a hard floor of 760×560 and a 1280px max width, centered on the main window. The 760×560 floor keeps the ~200px sidebar plus the detail column usable when the user shrinks the main window; the 1280px ceiling prevents 2K/4K displays from stretching settings into empty space. Canonical implementation: `SettingsWindowService` in `src/main/services/SettingsWindowService.ts`.
608
607
609
608
### Settings Panel Layout
610
609
611
-
Settings pages (both the in-app `/settings` route and the standalone settings window) share the same two-column shape:
610
+
Settings pages use the same two-column shape:
612
611
613
612
| Column | Width | Composition |
614
613
|---|---|---|
615
-
| Left submenu |`var(--settings-width)` (200px in the standalone window, 250px default in `responsive.css`) |`PageHeader` (title) → `Scrollbar` → `MenuList` of grouped `MenuItem` rows |
614
+
| Left submenu |`var(--settings-width)` (250px default in `responsive.css`) |`PageHeader` (title) → `Scrollbar` → `MenuList` of grouped `MenuItem` rows |
Copy file name to clipboardExpand all lines: docs/references/knowledge/experiment/knowledge-technical-design.md
+2-4Lines changed: 2 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -157,9 +157,7 @@ A vector base's `knowledge_base.embeddingModelId` / `dimensions` must be valid t
157
157
158
158
### 6.1 search() wiring and retrieval tuning
159
159
160
-
`searchMode` / `hybridAlpha` / `documentCount` / `threshold` are all **base-level configuration** (`knowledge_base` columns) for now; `search()` reads them from the base row (result cap `documentCount ?? 10`).
161
-
162
-
> **Decision note (2026-06-10)**: `hybridAlpha` describes whether a base's corpus leans lexical or semantic — a stable property of the base, not something the model should guess per call — so it stays a base column with the RagConfig slider (configurable only in hybrid mode; cleared when `searchMode` moves away). `threshold` only applies to relevance-scored hits (vector mode, or after rerank) and is a no-op for BM25/RRF ranking scores (`applyRelevanceThreshold` in `utils/search.ts`). Researched and decided, but **deferred to a later PR**: `topK` / `threshold` become per-call knobs (`KnowledgeSearchOptions`, exposed through `kb__search` arguments and REST `top_k`), and the `documentCount` column is removed with them. That refactor was implemented during PR A's development and then deliberately carved out to keep PR A reviewable; it will be re-done on top of the merged PR A in the per-call-tuning PR — the paragraph above records the agreed design so nothing depends on any developer-local state.
160
+
`documentCount` and `threshold` are the base-level retrieval tuning columns left; `search()` reads `documentCount` from the base row as the result cap (`documentCount ?? 10`) and applies `threshold` as a relevance cutoff on scored hits (`applyRelevanceThreshold` in `utils/search.ts` — it filters vector-mode or post-rerank relevance scores and is a no-op for BM25/RRF ranking scores). Retrieval mode is derived at runtime: bases with a completed vector config use hybrid retrieval, and bases without an embedding model fall back to BM25.
163
161
164
162
### 6.2 Legacy result shape mapping
165
163
@@ -185,5 +183,5 @@ A vector base's `knowledge_base.embeddingModelId` / `dimensions` must be valid t
185
183
- Startup-recovery cross-cancellation: a crash-recovered delete-subtree job and the `recoverDeletingItems` re-enqueue get different idempotency keys and cancel each other via roots-intersection (`jobTouchesSubtree`); cancel only jobs whose roots are fully covered by the current job's roots.
186
184
- Hybrid search runs its two lanes as independent read snapshots; a rebuild committing between them can transiently return both copies of a chunk — close with a shared read transaction or a second dedupe by material id + unit index.
187
185
- The per-base index driver's `close()` no longer needs to take a write mutex: better-sqlite3 uses one synchronous, persistent connection and per-base writes are serialized by `KnowledgeLockManager.withBaseMutationLock(baseId)`, so there is no driver-level async write mutex to race — shutdown safety rests on JobManager draining before the store service stops.
188
-
- Retrieval-surface follow-ups (PR C): the `searchMode``default`→`vector` rename is externally visible through the gateway's pass-through base entity, and a permanent open failure (legacy layout) currently maps to a retryable 503.
186
+
- Retrieval-surface follow-ups (PR C): permanent open failure for a legacy layout currently maps to a retryable 503.
189
187
- PR A's full test matrix and risk notes live in this repo's test suites (`src/main/features/knowledge/**/__tests__`) and the PR #15973 description.
Copy file name to clipboardExpand all lines: docs/references/knowledge/knowledge-service.md
+5-4Lines changed: 5 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -165,7 +165,7 @@ Current persisted `knowledge_base` columns include:
165
165
166
166
-`groupId`: nullable group assignment; `null` means ungrouped.
167
167
-`embeddingModelId`: the embedding model; `null` for BM25-only bases.
168
-
-`dimensions`: positive embedding vector width for vector-capable bases; `null` for BM25-only completed bases (no embedding model) and for failed migrated bases with unknown dimensions. On a completed base it is paired with `embeddingModelId` — both set, or both `null`with `searchMode` forced to `bm25` (enforced by the DB CHECK and the entity schema).
168
+
-`dimensions`: positive embedding vector width for vector-capable bases; `null` for BM25-only completed bases (no embedding model) and for failed migrated bases with unknown dimensions. On a completed base it is paired with `embeddingModelId` — both set, or both `null`for BM25-only retrieval (enforced by the DB CHECK and the entity schema).
169
169
-`status`: `completed` for runnable bases, `failed` for recoverable base-level migration failures.
170
170
-`error`: nullable `KnowledgeBaseErrorCode`; currently `missing_embedding_model` for recoverable failed bases.
171
171
@@ -260,11 +260,12 @@ Search is executed by `KnowledgeService.search(baseId, query)`:
260
260
261
261
1. Reject failed bases.
262
262
2. Reject queries without searchable tokens.
263
-
3.Resolve the base's `searchMode` (`vector` / `bm25` / `hybrid`) and embed the query — skipped for `bm25`, which is lexical only.
264
-
4. Call `KnowledgeIndexStore.search` on the base's per-base index store with an over-fetched candidate limit (`topK × overfetch`, capped). The store runs the BM25 lane (`search_text_fts`, with a LIKE fallback for short CJK tokens), the brute-force vector lane, or fuses both with RRF (`hybridAlpha`).
263
+
3.Derive the retrieval mode from the base config and embed the query only for embedding-backed bases. Bases without an embedding model search BM25 only; embedding-backed bases use hybrid retrieval.
264
+
4. Call `KnowledgeIndexStore.search` on the base's per-base index store with an over-fetched candidate limit (`topK × overfetch`, capped). The store runs the BM25 lane (`search_text_fts`, with a LIKE fallback for short CJK tokens) or fuses BM25 and brute-force vector results with RRF.
265
265
5. Filter results whose source items are missing, outside the base, or `deleting`, then trim to `documentCount ?? 10`.
266
266
6. Rerank when `base.rerankModelId` is configured.
267
-
7. Apply relevance threshold (a no-op for `ranking`-kind scores) and assign ranks.
267
+
7. Apply `threshold` only to results whose `scoreKind` is `relevance`; BM25/hybrid `ranking` scores pass through.
@@ -199,6 +199,19 @@ WindowManager exposes four lifecycle methods, arranged in two layers:
199
199
200
200
**Why `destroy()` is not a consumer API.** On non-pooled windows (default and singleton) `close()` falls through to the same `destroyWindow()` call — there is no behavioral difference. On pooled windows, `destroy()` bypasses the pool, which is almost never what a consumer actually wants; the correct API for "stop the whole pool" is `suspendPool(type)`, which destroys idle windows and prevents further recycling without touching in-use windows.
201
201
202
+
### Consumer-loaded windows (`htmlPath: ''`)
203
+
204
+
A registry entry with `htmlPath: ''` is **consumer-loaded**: WM wires the window (preload, behavior, bounds, lifecycle) but loads no content — the domain service loads it after `open()`. For hidden, one-shot surfaces rendering *generated* content (print / PDF, offscreen render).
205
+
206
+
```typescript
207
+
const id =wm.open(WindowType.MyPrintSurface) // WM wires; loads nothing
208
+
const win =wm.getWindow(id) // the sanctioned handle to load into
209
+
awaitwin?.webContents.loadURL(generatedHtmlDataUrl) // consumer owns content + show + close()
210
+
// ... await 'did-finish-load', e.g. webContents.printToPDF(), then wm.close(id)
211
+
```
212
+
213
+
`getWindow(id)` is the one exception to "consumers only call `open()` / `close()`" — use it only for `webContents` loading (payload encoding is the consumer's call). Main-initiated `loadURL` / `loadFile` is not blocked by WM's navigation guards (those only intercept renderer-initiated navigation).
214
+
202
215
### Domain-Key-to-WindowId Mapping
203
216
204
217
For window types that are keyed by domain data (e.g., a topic-specific window), the domain service maintains its own mapping:
0 commit comments