▍ humdrum codex / glint v1.1.2
license AGPL-3.0
6.1 KB raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package preview

import (
	"reflect"
	"strings"
	"testing"
)

func TestSplitFrontmatter(t *testing.T) {
	md := "---\ncreated: 2026-07-08\nstatus: \"🟢 In progress\"\ntags:\n  - daily\n  - tui\ncategories: [\"[[Projects]]\", Notes]\n---\n\n# Body\n\ntext\n"
	props, body := splitFrontmatter(md)
	want := []prop{
		{"created", "2026-07-08"},
		{"status", "🟢 In progress"},
		{"tags", "daily · tui"},
		{"categories", "[[Projects]] · Notes"},
	}
	if !reflect.DeepEqual(props, want) {
		t.Errorf("props = %#v, want %#v", props, want)
	}
	if body != "\n# Body\n\ntext\n" {
		t.Errorf("body = %q", body)
	}
}

func TestSplitFrontmatterNone(t *testing.T) {
	md := "# Body\n\ntext\n"
	props, body := splitFrontmatter(md)
	if len(props) != 0 || body != md {
		t.Errorf("no frontmatter: props=%v body=%q", props, body)
	}
	// A "---" that isn't on line one is a horizontal rule, not frontmatter.
	md2 := "intro\n\n---\nkey: value\n---\n"
	props, body = splitFrontmatter(md2)
	if len(props) != 0 || body != md2 {
		t.Errorf("mid-document rule treated as frontmatter: props=%v", props)
	}
}

func TestSplitFrontmatterUnterminated(t *testing.T) {
	md := "---\nkey: value\n\n# Body\n"
	props, body := splitFrontmatter(md)
	if len(props) != 0 || body != md {
		t.Errorf("unterminated block should be left alone: props=%v body=%q", props, body)
	}
}

func TestSplitFrontmatterEmptyValues(t *testing.T) {
	md := "---\naliases:\ntitle: Note\n---\nbody\n"
	props, _ := splitFrontmatter(md)
	want := []prop{{"aliases", ""}, {"title", "Note"}}
	if !reflect.DeepEqual(props, want) {
		t.Errorf("props = %#v, want %#v", props, want)
	}
}

// A colon inside a value (a URL, a time) must not split the key twice.
func TestSplitFrontmatterColonInValue(t *testing.T) {
	md := "---\nsource: https://example.com/a\nat: 09:30\n---\nbody\n"
	props, _ := splitFrontmatter(md)
	want := []prop{{"source", "https://example.com/a"}, {"at", "09:30"}}
	if !reflect.DeepEqual(props, want) {
		t.Errorf("props = %#v, want %#v", props, want)
	}
}

func TestHeaderTitleFromFrontmatter(t *testing.T) {
	m := New("")
	m.SetColors(testColors)
	m.SetSize(60, 20)
	m.SetTitle("filename-title")
	if err := m.Render("---\ntitle: Frontmatter Title\ncreated: 2026-07-08\n---\n\nbody\n"); err != nil {
		t.Fatal(err)
	}
	out := strings.Join(stripANSI(m.vp.View()), "\n")
	if !strings.Contains(out, "Frontmatter Title") {
		t.Errorf("frontmatter title not used as doc name:\n%s", out)
	}
	if strings.Contains(out, "filename-title") {
		t.Errorf("filename should lose to frontmatter title:\n%s", out)
	}
	// The title key is consumed by the heading, not repeated as a property.
	if strings.Contains(out, "title ") {
		t.Errorf("title still listed as a property:\n%s", out)
	}
}

func TestHeaderPropsAreTightAndRuleFree(t *testing.T) {
	m := New("")
	m.SetColors(testColors)
	m.SetSize(60, 30)
	m.SetTitle("My Note")
	if err := m.Render("---\ncreated: 2026-07-08\nstatus: In progress\ntags:\n  - daily\n---\n\nbody text\n"); err != nil {
		t.Fatal(err)
	}
	lines := stripANSI(m.vp.View())
	find := func(want string) int {
		for i, ln := range lines {
			if strings.HasPrefix(strings.TrimSpace(ln), want) {
				return i
			}
		}
		t.Fatalf("line starting %q not found in:\n%s", want, strings.Join(lines, "\n"))
		return -1
	}
	title := find("My Note")
	created := find("created")
	// Doc name sits above the properties, with one blank line between.
	if created != title+2 {
		t.Errorf("properties start at %d, want %d (one blank line under the title)", created, title+2)
	}
	// Properties are consecutive: no blank lines between them.
	if got := strings.TrimSpace(lines[created+1]); !strings.HasPrefix(got, "status") {
		t.Errorf("line after created = %q, want status", got)
	}
	if got := strings.TrimSpace(lines[created+2]); !strings.HasPrefix(got, "tags") {
		t.Errorf("line after status = %q, want tags", got)
	}
	// The --- fences never render as horizontal rules.
	for _, ln := range lines {
		if strings.HasPrefix(strings.TrimSpace(ln), "---") {
			t.Errorf("frontmatter fence rendered as a rule:\n%s", strings.Join(lines, "\n"))
			break
		}
	}
	// Values are aligned in a column, keys padded to the widest key.
	for _, ln := range lines[created : created+3] {
		if !strings.Contains(ln, "  2026-07-08") && !strings.Contains(ln, "  In progress") && !strings.Contains(ln, "  daily") {
			t.Errorf("property value not in an aligned column: %q", ln)
		}
	}
}

func TestHeaderTitleBarIsStyled(t *testing.T) {
	m := New("")
	m.SetColors(testColors)
	m.SetSize(60, 20)
	m.SetTitle("My Note")
	if err := m.Render("body\n"); err != nil {
		t.Fatal(err)
	}
	raw := m.vp.View()
	line := strings.Split(raw, "\n")[0]
	if !strings.Contains(line, "My Note") {
		t.Fatalf("title not on the first line: %q", line)
	}
	// The heading color paints the bar background, with legible text on top.
	if !strings.Contains(line, "48;2;209;77;65") {
		t.Errorf("title bar missing the heading background: %q", line)
	}
	if !strings.Contains(line, "38;2;255;252;240") {
		t.Errorf("title bar missing legible foreground: %q", line)
	}
	// The bar spans the full preview width, like a filled H1 block.
	if w := len(stripANSI(line)[0]); w != 60 {
		t.Errorf("title bar width = %d, want 60", w)
	}
}

func TestHeaderAbsentWithoutTitleOrProps(t *testing.T) {
	m := New("")
	m.SetColors(testColors)
	m.SetSize(60, 20)
	if err := m.Render("# Heading\n\nbody\n"); err != nil {
		t.Fatal(err)
	}
	if h := m.renderHeader("", nil); h != "" {
		t.Errorf("renderHeader with nothing to show = %q", h)
	}
	// The document opens on glamour's own H1, not a doc-name bar above it.
	lines := stripANSI(m.vp.View())
	if got := strings.TrimSpace(lines[0]); got != "" {
		t.Errorf("untitled doc grew a header: %q", got)
	}
	if got := strings.TrimSpace(lines[1]); got != "Heading" {
		t.Errorf("body H1 should be the first thing rendered, got %q", got)
	}
}

func TestHeaderWrapsLongValues(t *testing.T) {
	m := New("")
	m.SetColors(testColors)
	m.SetSize(40, 20)
	m.SetTitle("N")
	if err := m.Render("---\ntags: alpha beta gamma delta epsilon zeta eta theta\n---\nbody\n"); err != nil {
		t.Fatal(err)
	}
	for _, ln := range stripANSI(m.vp.View()) {
		if w := len([]rune(ln)); w > 40 {
			t.Errorf("line exceeds width 40 (%d): %q", w, ln)
		}
	}
}