stage partial

This commit is contained in:
Ross
2026-07-11 17:09:52 +01:00
parent a10dd5bd7b
commit 0bd27c2714
77 changed files with 2158 additions and 20 deletions
+5
View File
@@ -252,6 +252,10 @@ def match_author(authors: List[Dict[str, Any]], q: str) -> bool:
_docid_map = {}
def clear_doc_cache():
global _docid_map
_docid_map = {}
def get_doc_by_id(root: str, identifier: str) -> Tuple[Optional[Dict[str, Any]], Optional[str], Optional[str]]:
global _docid_map
if not _docid_map:
@@ -379,6 +383,7 @@ def check_linked_sections(content: str, root: str = 'docs_md/articles') -> Dict[
def run_search(root: str, qkey: str, qval: str, mode: str = 'exact', targets: List[str] = None, or_queries: List[Dict[str, Any]] = None, expand_links: bool = False) -> List[Dict[str, Any]]:
clear_doc_cache()
out = []
# Normalize queries list
+124
View File
@@ -290,6 +290,37 @@ def search_page() -> None: # build UI
# Render initial query blocks
render_query_blocks()
# Persistent Search History Container
with ui.row().classes('w-full items-center gap-2 mt-2'):
ui.label('Recent Searches:').classes('text-slate-400 text-xs font-semibold')
history_container = ui.row().classes('gap-1 items-center')
def render_history_chips():
history_container.clear()
history = load_history()
if not history:
with history_container:
ui.label('None').classes('text-slate-500 text-xs italic')
return
with history_container:
for item in history[:8]:
def make_click(saved=item['queries']):
def handler():
nonlocal queries
queries.clear()
for sq in saved:
queries.append(json.loads(json.dumps(sq)))
render_query_blocks()
ui.notify("Search query restored!", type='info')
return handler
ui.chip(
item['label'],
clickable=True,
on_click=make_click()
).props('dense color="slate-800" text-color="blue-300" icon="history"').classes('text-xs hover:bg-slate-700')
render_history_chips()
with ui.row().classes('w-full justify-between items-center gap-4 mt-2'):
with ui.row().classes('items-center gap-4'):
@@ -424,6 +455,61 @@ def search_page() -> None: # build UI
table.on('show_links_modal', lambda msg: show_links_modal_handler(msg.args))
table.on('capture_article', lambda msg: asyncio.create_task(trigger_capture_handler(msg.args)))
async def wait_and_process_capture(docid: str, title: str):
import glob
processed = False
for i in range(30):
await asyncio.sleep(1.5)
# Check if JSON files matching the docid exist in input dir
json_files = glob.glob(f"xhr_captured_async/*{docid}*.json")
if json_files and not processed:
with table.client:
ui.notify(f"Captured data found for: {title}. Processing to Markdown...", type='info')
try:
await anyio.to_thread.run_sync(
document_to_markdown.main,
['--input-dir', 'xhr_captured_async', '--output-dir', 'docs_md']
)
processed = True
except Exception as e:
print(f"Error running document_to_markdown: {e}")
# Check if it has been successfully converted/cached
def check_cache():
search_md.clear_doc_cache()
return search_md.get_doc_by_id('docs_md/articles', docid)
fm, content, doc_path = await anyio.to_thread.run_sync(check_cache)
if fm:
# Successfully processed! Reload row data in table!
with table.client:
row_index = -1
for idx, r in enumerate(table.rows):
if r.get('docid') == docid:
row_index = idx
break
if row_index != -1:
linked_fm_title = fm.get('title') or fm.get('pageTitle') or title
updated_row = {
'path': doc_path,
'title': linked_fm_title,
'docid': docid,
'breadcrumbs': fm.get('breadcrumbs'),
'authors': fm.get('authors'),
'pageKeywords': fm.get('pageKeywords'),
'reasons': table.rows[row_index].get('reasons', []),
'snippet': content[:300] + '...' if content else '',
'linked_info': search_md.check_linked_sections(content, root_input.value),
'is_cached': True
}
table.rows[row_index] = updated_row
table.rows = list(table.rows)
ui.notify(f"Successfully cached and loaded: {linked_fm_title}!", type='positive')
else:
ui.notify(f"Cached document: {title}", type='positive')
return
async def trigger_capture_handler(args: dict):
docid = args.get('docid')
title = args.get('title')
@@ -439,11 +525,15 @@ def search_page() -> None: # build UI
except Exception:
return False
def start_capture_poll_task(d_id, t_title):
asyncio.create_task(wait_and_process_capture(d_id, t_title))
with table.client:
ui.notify(f"Checking capture browser status for: {title}...", type='info')
ok = await asyncio.to_thread(check_and_send)
if ok:
ui.notify(f"Requested capture browser to open: {title}", type='positive')
start_capture_poll_task(docid, title)
return
ui.notify("Capture browser not responding. Cleaning up existing capture processes...", type='warning')
@@ -465,6 +555,7 @@ def search_page() -> None: # build UI
ok_again = await asyncio.to_thread(check_and_send)
if ok_again:
ui.notify(f"Capture browser launched successfully. Opened: {title}", type='positive')
start_capture_poll_task(docid, title)
else:
ui.notify("Capture browser process launched, but command port timed out. Try clicking again in a few seconds.", type='warning')
except Exception as e:
@@ -738,6 +829,20 @@ def search_page() -> None: # build UI
status.set_text('Running search...')
search_btn.set_enabled(False)
# Save query configuration to persistent history
active_queries = [q for q in queries if q['qval'].strip()]
if active_queries:
history_label = " OR ".join([f"'{q['qval']}' ({q['mode']})" for q in active_queries])
history_item = {
'label': history_label,
'queries': json.loads(json.dumps(queries))
}
history = load_history()
history = [h for h in history if h['label'] != history_label]
history.insert(0, history_item)
save_history(history[:20])
render_history_chips()
try:
or_queries_param = []
for q in queries:
@@ -984,6 +1089,25 @@ def linkify_references(content: str, root: str = 'docs_md/articles') -> str:
return content
HISTORY_FILE = 'search_history.json'
def load_history() -> list:
try:
if os.path.exists(HISTORY_FILE):
with open(HISTORY_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception:
pass
return []
def save_history(history_list: list):
try:
with open(HISTORY_FILE, 'w', encoding='utf-8') as f:
json.dump(history_list, f, indent=2, ensure_ascii=False)
except Exception:
pass
def is_doc_cached(docid: str) -> bool:
fm, _, _ = search_md.get_doc_by_id('docs_md/articles', docid)
return fm is not None