ac5823ffe5
Two requests on top of v0.1.9:
1. Drag-to-resize splitter
The previous fixed Bootstrap col-md-5/col-md-7 split locked the
user into 41.66% answer / 58.33% PDF. Replaced with a flex-row
layout containing a 6px drag handle. mousedown spawns a fixed
transparent overlay so the PDF.js iframe doesn't swallow mouse
events while dragging; mouseup persists the chosen ratio (clamped
15-85%) to localStorage as kb-split-pct, so the next page-load and
the next session both come back to the same layout. Both the
ask split view and the search split view share _buildSplitShell +
_wireSplitter — single source of truth for the geometry.
2. Search survives a view switch
Search had the same view-bound promise problem ask had before
v0.1.9. Hoisted the in-flight request to a module-level
_activeSearch and the most recent {query, kind, hits, selectedIdx}
to _lastSearch + sessionStorage. afterRender restores the input,
the kind filter, the hit list (with the same hit highlighted), and
the right-pane preview. Click handler updates selectedIdx so a
round-trip lands on the user's last-clicked hit, not always on the
top-ranked one.
The persistence helpers grew a tiny refactor: _loadJson/_saveJson/_clear
replace the ask-only SS_KEY shim and now back both ask + search keys.
Refs Task Master #5, #6
84 lines
7.6 KiB
JSON
84 lines
7.6 KiB
JSON
{
|
||
"master": {
|
||
"tasks": [
|
||
{
|
||
"id": 1,
|
||
"title": "feat: search-mode split-view with PDF + page jump",
|
||
"description": "Render /kb/search results as a two-column split view: left column is the stack of ranked hit cards (kind, title, section, page label), right column is an iframe viewing the selected hit's PDF scrolled to page_number. Clicking any hit swaps the iframe. Text-only sources (law from Wikisource) fall back to a chunk/text panel with a Wikisource link so the right column never 404s.",
|
||
"status": "done",
|
||
"priority": "high",
|
||
"details": "Before this change renderSearchResults produced plain vertical text cards — users got the chunk body and had to open the Wikisource link (or track down the PDF manually) to verify context. With 5 of 5 PDFs now carrying accurate page_number (shira-hermes e534709 + 1ca1cc0), the search UI can finally deep-link to the right page. Mirrors the ask-mode split view (v0.1.7 uncommitted).",
|
||
"testStrategy": "Search 'תקנה 37' → first hit should be a regulation/circular → right pane loads the PDF at page ~N where the section appears. Click a lower-ranked law hit → right pane swaps to a Wikisource link view. Search 'הגדרות' → hits span multiple sources → each click swaps iframe source.",
|
||
"subtasks": [],
|
||
"dependencies": [],
|
||
"createdAt": "2026-04-24T15:47:00Z"
|
||
},
|
||
{
|
||
"id": 2,
|
||
"title": "fix(kb/chunker): page_number propagation through split + packing (shira-hermes)",
|
||
"description": "Applied in shira-hermes commits e534709 + 1ca1cc0: (1) _split_oversized now re-derives page_number per piece from markers embedded in parent content + a virtual (offset 0, parent.page_number) anchor, (2) chunk_circular tracks absolute paragraph offsets in the original text so each packed sub-chunk reports its real starting page, (3) _as_dicts strips stray \\x00 bytes left behind when _split_oversized slices through a marker sentinel. Reference data: ספר הליקויים now 211 chunks × 210 pages (1-413) vs 209 × 1 before. Not work for this repo, but the search split-view in task #1 depends on it.",
|
||
"status": "done",
|
||
"priority": "high",
|
||
"details": "Lives in espocrm-extensions/shira-hermes. Logged here for traceability because task #1 can't demonstrate correct page jumps without it.",
|
||
"testStrategy": "Query DB: SELECT source_id, MIN/MAX(page_number), COUNT(DISTINCT page_number) FROM kb_chunk GROUP BY source_id — every PDF source should span multiple distinct pages.",
|
||
"subtasks": [],
|
||
"dependencies": [],
|
||
"createdAt": "2026-04-24T15:47:00Z"
|
||
},
|
||
{
|
||
"id": 3,
|
||
"title": "feat(kb/search): LLM query expansion so ranked-by-title docs don't crowd everything else out (shira-hermes)",
|
||
"description": "User reports searching 'מהי תקנה 37' returns only the תקנה 37 circular and the law — never ספר הליקויים, even though that's where the underlying medical content lives. Root cause is a single-shot retrieval where any document whose title contains the query terms wins by a huge margin. Fix: ask the LLM (via ai-gateway) for up to 3 alternative phrasings, retrieve candidates for the original + each variant in parallel, merge by chunk_id keeping best RRF score, then rerank the union against the ORIGINAL query. After fix: src 29 (ספר הליקויים) shows up with 2 hits in the same query.",
|
||
"status": "done",
|
||
"priority": "high",
|
||
"details": "Lives in espocrm-extensions/shira-hermes commit 1441a41. /kb/search now has expand=true by default, agent path (search_insurance_kb tool) keeps expand=false because the agent already does multi-query via tool calls.",
|
||
"testStrategy": "POST /kb/search {query:'מהי תקנה 37', top_k:8} should return hits from at least 2 distinct source_ids, including src 29 (ספר הליקויים) and src 26 (תקנה 37 חוזר).",
|
||
"subtasks": [],
|
||
"dependencies": [],
|
||
"createdAt": "2026-04-25T10:00:00Z"
|
||
},
|
||
{
|
||
"id": 5,
|
||
"title": "feat(kb): drag-to-resize splitter (both ask + search)",
|
||
"description": "Replace the fixed Bootstrap col-md-5/7 split with a flex layout containing a 6px drag handle. mousedown on the handle starts the drag; while dragging an invisible full-screen overlay covers any iframe so the PDF.js viewer doesn't swallow mouse events. The chosen ratio (15-85%) persists to localStorage as kb-split-pct so the layout sticks across sessions. Both modes share the same _buildSplitShell helper so the look stays consistent.",
|
||
"status": "done",
|
||
"priority": "normal",
|
||
"details": "Default ratio = 42% left (matches the previous col-md-5 ≈ 41.66%). Applied to renderSearchResults, renderAskAnswer, and showSearchPreview (which now uses 100%-height containers via flex column).",
|
||
"testStrategy": "Open ask mode, drag the divider; verify PDF iframe doesn't lose mouse focus mid-drag; reload — divider stays at last position. Repeat in search mode.",
|
||
"subtasks": [],
|
||
"dependencies": [],
|
||
"createdAt": "2026-04-25T11:00:00Z"
|
||
},
|
||
{
|
||
"id": 6,
|
||
"title": "fix: search survives view switch (parity with ask in v0.1.9)",
|
||
"description": "Search mode had the same view-bound promise problem as ask did before v0.1.9: navigating to another EspoCRM screen and back blanked the results. Hoisted {query, kind, promise} to a module-level _activeSearch with the same {query, kind, hits, selectedIdx} sessionStorage replay we already built for ask. Selected hit index also persists so the right pane comes back to the same source/page the user had open.",
|
||
"status": "done",
|
||
"priority": "high",
|
||
"details": "Mirrors the v0.1.9 ask fix in _attachSearchHandlers + afterRender. Click handler updates _lastSearch.selectedIdx so a return-trip lands on the user's last-clicked hit, not always the top-ranked one.",
|
||
"testStrategy": "Search 'מהי תקנה 37'; click a result other than the first; navigate to Contacts; navigate back — same hit selected, same PDF page on the right.",
|
||
"subtasks": [],
|
||
"dependencies": [],
|
||
"createdAt": "2026-04-25T11:00:00Z"
|
||
},
|
||
{
|
||
"id": 4,
|
||
"title": "fix: ask survives view switch via module-level promise + sessionStorage",
|
||
"description": "User reports asking Shira a question and switching to a different EspoCRM view loses the in-flight request — coming back shows a blank screen as if nothing was asked. Cause: the Espo.Ajax promise was view-bound, so handlers fired against a detached DOM after re-mount. Fix: hoist {question, promise, startedAt} to a module-level _activeAsk; afterRender re-attaches handlers and resumes the progress UI with the original startedAt; completed answers persist to sessionStorage and replay on remount or a hard reload.",
|
||
"status": "done",
|
||
"priority": "high",
|
||
"details": "All client-side, files/client/custom/modules/knowledge-base/src/views/kb/index.js. State is intentionally session-scoped — kept in sessionStorage so the next browser session starts clean.",
|
||
"testStrategy": "Ask a question; while the spinner shows, click Contacts (or any other navbar item); wait until the request would have finished; click back to בסיס ידע — the answer (or active spinner with correct elapsed-time) should be there.",
|
||
"subtasks": [],
|
||
"dependencies": [],
|
||
"createdAt": "2026-04-25T10:00:00Z"
|
||
}
|
||
],
|
||
"metadata": {
|
||
"created": "2026-04-24T15:47:00Z",
|
||
"updated": "2026-04-25T10:30:00Z",
|
||
"description": "KnowledgeBase extension tasks"
|
||
}
|
||
}
|
||
}
|