m4.5: content-aware [[ linking — autocomplete searches note body
The [[ autocomplete only matched note names, so you could only link a note you could name. Now it searches note NAME *and* body, so you can link by recalling any phrase. - new GET /api/notes/link-search?q= — owner-scoped, non-trashed; substring ILIKE on display_title OR body; ranked name-first, then name-prefix, then recency; empty q returns recent notes as suggestions. Deterministic (no semantic/AI search); the FTS index still powers the heavier /search. LIKE wildcards in q are escaped. - editor [[ autocomplete now calls link-search (debounced 120ms) instead of filtering the cached titles index; excludes the note itself; inserts the matched note's display name as [[Name]]. - unit tests for the LIKE-escaping + a link-search auth guard. Second item of M4.5; builds on the display_title work (every note has a name to link to). Command-palette content search is a natural follow-on, left out to keep this focused. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
@@ -3,6 +3,7 @@ import pytest
|
||||
from thoughtsync.app import create_app
|
||||
from thoughtsync.models.note import NOTE_COLORS, Note
|
||||
from thoughtsync.notes import (
|
||||
_escape_like,
|
||||
derive_display_title,
|
||||
is_empty_note,
|
||||
normalize_color,
|
||||
@@ -129,12 +130,26 @@ def test_derive_display_title_caps_length():
|
||||
assert derive_display_title(long, "body") == "x" * 200
|
||||
|
||||
|
||||
def test_escape_like():
|
||||
# LIKE wildcards in user input must be neutralized so they match literally.
|
||||
assert _escape_like("100%") == "100\\%"
|
||||
assert _escape_like("a_b") == "a\\_b"
|
||||
assert _escape_like("c:\\path") == "c:\\\\path"
|
||||
assert _escape_like("plain") == "plain"
|
||||
|
||||
|
||||
async def test_titles_requires_auth(app):
|
||||
client = app.test_client()
|
||||
resp = await client.get("/api/notes/titles")
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
async def test_link_search_requires_auth(app):
|
||||
client = app.test_client()
|
||||
resp = await client.get("/api/notes/link-search?q=hi")
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
async def test_graph_requires_auth(app):
|
||||
client = app.test_client()
|
||||
resp = await client.get("/api/graph")
|
||||
|
||||
Reference in New Issue
Block a user