Skip to content

Commit 1f8188f

Browse files
PyAgniclaude
andcommitted
refactor: replace YAML front matter with title heading and metadata table
- Title is now a # heading at the top of the file - Note body follows the title - Metadata (ID, created, modified, account, shared) is rendered as a Markdown table at the bottom, separated by a horizontal rule - Shared field shows "Yes"/"No" instead of true/false Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent cc80a72 commit 1f8188f

2 files changed

Lines changed: 70 additions & 45 deletions

File tree

internal/filesystem/writer.go

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,23 @@ func NewFSNoteWriter(basePath, notesSubdir string, frontMatter bool, attachmentD
5656
}
5757
}
5858

59-
// frontMatterTemplate is the YAML front matter template for note files.
60-
var frontMatterTemplate = template.Must(template.New("frontmatter").Parse(`---
61-
title: "{{.Title}}"
62-
id: "{{.ID}}"
63-
created: {{.Created}}
64-
modified: {{.Modified}}
65-
account: "{{.Account}}"
66-
shared: {{.Shared}}
59+
// metadataTableTemplate renders a Markdown table with note metadata,
60+
// placed at the bottom of the file after a horizontal rule.
61+
var metadataTableTemplate = template.Must(template.New("metadata").Parse(`
6762
---
6863
64+
| ID | Created | Modified | Account | Shared |
65+
|----|---------|----------|---------|--------|
66+
| {{.ID}} | {{.Created}} | {{.Modified}} | {{.Account}} | {{.Shared}} |
6967
`))
7068

71-
// frontMatterData holds the template data for front matter rendering.
72-
type frontMatterData struct {
73-
Title string
69+
// metadataData holds the template data for the metadata table.
70+
type metadataData struct {
7471
ID string
7572
Created string
7673
Modified string
7774
Account string
78-
Shared bool
75+
Shared string
7976
}
8077

8178
// notesDir returns the absolute path to the notes directory.
@@ -104,28 +101,38 @@ func (w *FSNoteWriter) WriteNote(ctx context.Context, note *model.Note) (string,
104101
fileName := note.SanitizedFileName() + ".md"
105102
fullPath := filepath.Join(dirPath, fileName)
106103

107-
// Build file content.
104+
// Build file content: title heading, body, then metadata table at bottom.
108105
var content strings.Builder
109106

107+
// Title as a top-level heading.
108+
content.WriteString("# ")
109+
content.WriteString(note.Name)
110+
content.WriteString("\n\n")
111+
112+
// Note body.
113+
content.WriteString(note.BodyMarkdown)
114+
115+
// Metadata table at the bottom after a divider.
110116
if w.frontMatter {
111-
data := frontMatterData{
112-
Title: escapeFrontMatterString(note.Name),
117+
shared := "No"
118+
if note.Shared {
119+
shared = "Yes"
120+
}
121+
data := metadataData{
113122
ID: note.ID,
114-
Created: note.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
115-
Modified: note.ModifiedAt.Format("2006-01-02T15:04:05Z07:00"),
123+
Created: note.CreatedAt.Format("2006-01-02 15:04:05"),
124+
Modified: note.ModifiedAt.Format("2006-01-02 15:04:05"),
116125
Account: note.Account,
117-
Shared: note.Shared,
126+
Shared: shared,
118127
}
119128

120129
var buf bytes.Buffer
121-
if err := frontMatterTemplate.Execute(&buf, data); err != nil {
122-
return "", fmt.Errorf("rendering front matter for %q: %w", note.Name, err)
130+
if err := metadataTableTemplate.Execute(&buf, data); err != nil {
131+
return "", fmt.Errorf("rendering metadata for %q: %w", note.Name, err)
123132
}
124133
content.WriteString(buf.String())
125134
}
126135

127-
content.WriteString(note.BodyMarkdown)
128-
129136
if err := os.WriteFile(fullPath, []byte(content.String()), 0644); err != nil {
130137
return "", fmt.Errorf("writing note file %q: %w", fullPath, err)
131138
}
@@ -249,11 +256,6 @@ func (w *FSNoteWriter) SaveAttachment(ctx context.Context, notePath string, atta
249256
return relPath, nil
250257
}
251258

252-
// escapeFrontMatterString escapes double quotes in strings for YAML front matter.
253-
func escapeFrontMatterString(s string) string {
254-
return strings.ReplaceAll(s, `"`, `\"`)
255-
}
256-
257259
// removeEmptyDirs walks a directory tree bottom-up and removes empty directories.
258260
func removeEmptyDirs(root string) error {
259261
return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {

internal/filesystem/writer_test.go

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,16 @@ func TestFSNoteWriter_WriteNote_BasicNote(t *testing.T) {
4545
content, err := os.ReadFile(filepath.Join(base, relPath))
4646
require.NoError(t, err)
4747

48-
assert.Contains(t, string(content), "---")
49-
assert.Contains(t, string(content), `title: "My Note"`)
50-
assert.Contains(t, string(content), `account: "iCloud"`)
51-
assert.Contains(t, string(content), "Hello world")
48+
s := string(content)
49+
// Title as heading at the top.
50+
assert.True(t, strings.HasPrefix(s, "# My Note\n"))
51+
// Body content.
52+
assert.Contains(t, s, "Hello world")
53+
// Metadata table at the bottom with divider.
54+
assert.Contains(t, s, "\n---\n")
55+
assert.Contains(t, s, "| ID | Created | Modified | Account | Shared |")
56+
assert.Contains(t, s, "| x-coredata://test-id |")
57+
assert.Contains(t, s, "| iCloud |")
5258
}
5359

5460
func TestFSNoteWriter_WriteNote_WithSubdir(t *testing.T) {
@@ -61,7 +67,7 @@ func TestFSNoteWriter_WriteNote_WithSubdir(t *testing.T) {
6167
assert.Equal(t, filepath.Join("notes", "Work", "Test.md"), relPath)
6268
}
6369

64-
func TestFSNoteWriter_WriteNote_NoFrontMatter(t *testing.T) {
70+
func TestFSNoteWriter_WriteNote_NoMetadata(t *testing.T) {
6571
w, base := newTestWriter(t, "", false)
6672

6773
note := newTestNote("Simple", "Notes", "Just content\n")
@@ -71,8 +77,12 @@ func TestFSNoteWriter_WriteNote_NoFrontMatter(t *testing.T) {
7177
content, err := os.ReadFile(filepath.Join(base, relPath))
7278
require.NoError(t, err)
7379

74-
assert.NotContains(t, string(content), "---")
75-
assert.Equal(t, "Just content\n", string(content))
80+
s := string(content)
81+
// Title heading is always present.
82+
assert.True(t, strings.HasPrefix(s, "# Simple\n"))
83+
assert.Contains(t, s, "Just content")
84+
// No metadata table.
85+
assert.NotContains(t, s, "| ID |")
7686
}
7787

7888
func TestFSNoteWriter_WriteNote_NestedFolders(t *testing.T) {
@@ -199,25 +209,38 @@ func TestFSNoteWriter_SaveAttachment_NilData(t *testing.T) {
199209
assert.Empty(t, relPath)
200210
}
201211

202-
func TestEscapeFrontMatterString(t *testing.T) {
203-
assert.Equal(t, `Hello \"World\"`, escapeFrontMatterString(`Hello "World"`))
204-
assert.Equal(t, "No quotes", escapeFrontMatterString("No quotes"))
212+
func TestFSNoteWriter_WriteNote_MetadataTableContent(t *testing.T) {
213+
w, base := newTestWriter(t, "", true)
214+
215+
note := newTestNote("My Title", "Notes", "Body\n")
216+
note.Shared = true
217+
relPath, err := w.WriteNote(context.Background(), &note)
218+
require.NoError(t, err)
219+
220+
content, err := os.ReadFile(filepath.Join(base, relPath))
221+
require.NoError(t, err)
222+
223+
s := string(content)
224+
// Title heading at top.
225+
assert.True(t, strings.HasPrefix(s, "# My Title\n"))
226+
// Body before divider.
227+
assert.Contains(t, s, "Body\n")
228+
// Metadata table with correct values.
229+
assert.Contains(t, s, "2026-03-18 16:00:00")
230+
assert.Contains(t, s, "2026-03-18 17:00:00")
231+
assert.Contains(t, s, "| iCloud |")
232+
assert.Contains(t, s, "| Yes |")
205233
}
206234

207-
func TestFSNoteWriter_WriteNote_FrontMatterContent(t *testing.T) {
235+
func TestFSNoteWriter_WriteNote_MetadataSharedNo(t *testing.T) {
208236
w, base := newTestWriter(t, "", true)
209237

210-
note := newTestNote("Title With \"Quotes\"", "Notes", "Body\n")
238+
note := newTestNote("Test", "Notes", "Content\n")
211239
relPath, err := w.WriteNote(context.Background(), &note)
212240
require.NoError(t, err)
213241

214242
content, err := os.ReadFile(filepath.Join(base, relPath))
215243
require.NoError(t, err)
216244

217-
s := string(content)
218-
assert.True(t, strings.HasPrefix(s, "---\n"))
219-
assert.Contains(t, s, `title: "Title With \"Quotes\""`)
220-
assert.Contains(t, s, "created: 2026-03-18T16:00:00Z")
221-
assert.Contains(t, s, "modified: 2026-03-18T17:00:00Z")
222-
assert.Contains(t, s, "shared: false")
245+
assert.Contains(t, string(content), "| No |")
223246
}

0 commit comments

Comments
 (0)