Initial commit: Phase 1+2 prototype

- Go backend with SQLite, JWT auth, file CRUD
- Vue 3 frontend with split/raw/WYSIWYG editor modes
- Markdown preview (marked, GFM)
- Formatting toolbar + keyboard shortcuts
- File tree with search, create, delete
- Light/dark theme toggle
- Admin panel (user management)
- Preferences (timezone, theme, default mode)
- Shared documents section (placeholder)
- Export: PDF, HTML, MD
- Build daemon (Python, stdlib only)
- Build job queue API
- Docker deployment
This commit is contained in:
2026-05-22 19:48:48 +02:00
commit 0c1047d390
26 changed files with 6206 additions and 0 deletions
+66
View File
@@ -0,0 +1,66 @@
package files
import (
"os"
"path/filepath"
"strings"
)
type SearchResult struct {
Path string `json:"path"`
Snippet string `json:"snippet"`
}
// Search finds files containing the query string (case-insensitive).
func Search(dataDir, userID, query string) ([]SearchResult, error) {
root := UserDir(dataDir, userID)
query = strings.ToLower(query)
var results []SearchResult
filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() {
return nil
}
rel, _ := filepath.Rel(root, path)
// Match filename
if strings.Contains(strings.ToLower(info.Name()), query) {
results = append(results, SearchResult{Path: rel, Snippet: "(filename match)"})
return nil
}
// Match content (only for .md and .txt)
ext := strings.ToLower(filepath.Ext(path))
if ext == ".md" || ext == ".txt" {
content, err := os.ReadFile(path)
if err != nil {
return nil
}
lower := strings.ToLower(string(content))
idx := strings.Index(lower, query)
if idx >= 0 {
// Extract snippet
start := idx - 40
if start < 0 {
start = 0
}
end := idx + len(query) + 40
if end > len(content) {
end = len(content)
}
snippet := string(content[start:end])
results = append(results, SearchResult{Path: rel, Snippet: snippet})
}
}
if len(results) >= 50 {
return filepath.SkipAll
}
return nil
})
if results == nil {
results = []SearchResult{}
}
return results, nil
}