Add full-text search
- Index notes in a separate, rebuildable SQLite database (index.db) using FTS5 trigram, synced from watcher snapshots so external edits are picked up; terms under 3 characters fall back to LIKE - GET /api/v1/search?q= returns matching notes with the first matching line and a snippet; path matches rank first - Sidebar search panel (Ctrl+Shift+F) with highlighted terms and keyboard navigation; opening a result jumps to and selects the match - Restore scroll position on tab switch with scroll snapshots; restoring scrollTop drifted by a few lines after line heights were re-estimated
This commit is contained in:
1 parent
a11e0ec498
commit
55f0ee174e
13 files changed
+792
-20
No files matched your search
+16
-2
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"rawmark/internal/auth"
|
||||
"rawmark/internal/events"
|
||||
"rawmark/internal/search"
|
||||
"rawmark/internal/vault"
|
||||
)
|
||||
|
||||
@@ -30,12 +31,13 @@ type Server struct {
|
||||
vault *vault.Vault
|
||||
auth *auth.Manager
|
||||
hub *events.Hub
|
||||
index *search.Index
|
||||
// OnSetup 在首次初始化完成后调用(用于删除启动码文件)
|
||||
OnSetup func()
|
||||
}
|
||||
|
||||
func New(v *vault.Vault, a *auth.Manager, hub *events.Hub) *Server {
|
||||
return &Server{vault: v, auth: a, hub: hub}
|
||||
func New(v *vault.Vault, a *auth.Manager, hub *events.Hub, index *search.Index) *Server {
|
||||
return &Server{vault: v, auth: a, hub: hub, index: index}
|
||||
}
|
||||
|
||||
func (s *Server) Handler(static fs.FS) http.Handler {
|
||||
@@ -59,6 +61,7 @@ func (s *Server) Handler(static fs.FS) http.Handler {
|
||||
"POST /api/v1/trash/{id}/restore": s.trashRestore,
|
||||
"DELETE /api/v1/trash/{id}": s.trashPurge,
|
||||
"GET /api/v1/events": s.events,
|
||||
"GET /api/v1/search": s.search,
|
||||
}
|
||||
for pattern, h := range protected {
|
||||
mux.Handle(pattern, s.requireAuth(h))
|
||||
@@ -211,6 +214,17 @@ func (s *Server) tree(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, 200, map[string]any{"entries": entries})
|
||||
}
|
||||
|
||||
const searchLimit = 50
|
||||
|
||||
func (s *Server) search(w http.ResponseWriter, r *http.Request) {
|
||||
results, err := s.index.Search(r.URL.Query().Get("q"), searchLimit)
|
||||
if err != nil {
|
||||
internalError(w, r, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"results": results})
|
||||
}
|
||||
|
||||
func (s *Server) readFile(w http.ResponseWriter, r *http.Request) {
|
||||
data, rev, err := s.vault.Read(r.PathValue("path"))
|
||||
if err != nil {
|
||||
|
||||
Reference in new issue
Block a user