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:
eezd committed 2026-10-16 20:17:24 +08:00
1 parent a11e0ec498
commit 55f0ee174e
13 files changed
+792 -20

No files matched your search

+11 -3
View File
@@ -1,6 +1,6 @@
// RawMark 服务入口。配置全部来自环境变量:
//
// RAWMARK_DATA 数据目录,默认 /data(存放 rawmark.db 与默认仓库)
// RAWMARK_DATA 数据目录,默认 /data(存放 rawmark.db、可重建的搜索索引 index.db 与默认仓库)
// RAWMARK_VAULT 笔记仓库目录,默认 $RAWMARK_DATA/vault;可指向已有的 Typora 笔记目录
// RAWMARK_ADDR 监听地址,默认 :8080
package main
@@ -22,6 +22,7 @@ import (
"rawmark/internal/api"
"rawmark/internal/auth"
"rawmark/internal/events"
"rawmark/internal/search"
"rawmark/internal/store"
"rawmark/internal/vault"
"rawmark/internal/watch"
@@ -80,13 +81,20 @@ func run(addr string) error {
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
idx, err := search.Open(filepath.Join(dataDir, "index.db"), v)
if err != nil {
return err
}
defer idx.Close()
go idx.Run(ctx)
hub := events.NewHub()
if err := watch.Start(ctx, v, hub); err != nil {
if err := watch.Start(ctx, v, hub, idx.Update); err != nil {
return fmt.Errorf("启动文件监听失败: %w", err)
}
go cleanupSessions(ctx, st)
srv := api.New(v, am, hub)
srv := api.New(v, am, hub, idx)
srv.OnSetup = func() { os.Remove(codeFile) }
httpServer := &http.Server{
Addr: addr,