refactor: unified EditorPanel for notes and files, SaveFileText backend

- Create EditorPanel.svelte: shared editor component for notes + files
  - Markdown: edit/preview/split modes via MarkdownEditor + MarkdownPreview
  - Plain text: monospace textarea
  - Header: title, mode switcher, external open button
  - Footer: save + close buttons, dirty indicator
- Rewrite NoteEditorPanel.svelte as thin wrapper over EditorPanel
- FilesTab: browser/editor modes (editor replaces file list, not appended)
- Add SaveFileText Wails binding: writes text files atomically (tmp+rename)
- Add WriteText to files.Service with SHA256 update
- Remove separate TextFileEditor.svelte (replaced by EditorPanel)
- Note context: SaveNote API, noteId/title props
- File context: SaveFileText API, fileId prop
- Linked .md (CheckFileAction=note) → note editor tab
- Unlinked .md / text files → EditorPanel in FilesTab
- Images/PDF/binary → FilePreviewModal only

Co-Authored-By: OWL (Hermes Agent) <hermes@nousresearch.com>
This commit is contained in:
2026-06-16 10:40:09 +08:00
co-authored by OWL
parent 383a9546df
commit 120ff7d6fe
9 changed files with 566 additions and 463 deletions
+22
View File
@@ -330,6 +330,28 @@ func (s *Service) ReadText(id string) (string, error) {
return string(b), nil
}
// WriteText writes text content to a file on disk and updates the record.
func (s *Service) WriteText(rec *Record, content string) error {
abs, err := s.absPathSafe(rec)
if err != nil {
return err
}
tmp := abs + ".tmp"
if err := os.WriteFile(tmp, []byte(content), 0o640); err != nil {
return fmt.Errorf("write temp: %w", err)
}
if err := os.Rename(tmp, abs); err != nil {
os.Remove(tmp)
return fmt.Errorf("rename: %w", err)
}
now := time.Now().UTC().Format(time.RFC3339)
sum := sha256.Sum256([]byte(content))
_, err = s.db.Exec(
`UPDATE files SET size=?, sha256=?, updated_at=?, missing=? WHERE id=?`,
len(content), fmt.Sprintf("%x", sum[:]), now, 0, rec.ID)
return err
}
// ReadBase64 reads a file and returns a data URI (base64-encoded).
func (s *Service) ReadBase64(id string) (string, error) {
rec, err := s.Get(id)