Files
libretechandClaude Opus 4.7 df9407dd8d Fork Notesium source and restructure into Go package layout
Initial fork of github.com/alonswartz/notesium into librenotes:
- Source moved to internal/notesium/ (package notesium)
- Thin entry point at cmd/librenotes/main.go
- Module renamed to git.librete.ch/public/librenotes
- main() exposed as notesium.Run()
- LICENSE preserved (MIT), NOTICE added with attribution
- Web assets and completion.bash co-located with embedding code
  to satisfy go:embed path constraints

Closes #3, #34, #35.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 21:52:25 +02:00

143 lines
2.9 KiB
Go

package notesium
import (
"bufio"
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
)
type Link struct {
Filename string
Title string
LineNumber int
}
type Note struct {
Filename string
Title string
IsLabel bool
OutgoingLinks []*Link
IncomingLinks []*Link
Ctime time.Time
Mtime time.Time
Lines int
Words int
Chars int
}
var noteCache map[string]*Note
var fileRegex = regexp.MustCompile(`^[0-9a-f]{8}\.md$`)
var linkRegex = regexp.MustCompile(`\]\(([0-9a-f]{8}\.md)\)`)
func populateCache(dir string) {
if noteCache != nil {
return
}
noteCache = make(map[string]*Note)
files, err := os.ReadDir(dir)
if err != nil {
log.Fatalf("could not read directory: %s\n", err)
}
for _, file := range files {
filename := file.Name()
if !file.IsDir() && fileRegex.MatchString(filename) {
note, err := readNote(dir, filename)
if err != nil {
log.Fatalf("could not read note: %s\n", err)
}
noteCache[filename] = note
}
}
for _, note := range noteCache {
for _, link := range note.OutgoingLinks {
if targetNote, exists := noteCache[link.Filename]; exists {
link.Title = targetNote.Title
targetNote.IncomingLinks = append(targetNote.IncomingLinks, &Link{
Filename: note.Filename,
Title: note.Title,
LineNumber: link.LineNumber,
})
}
}
}
}
func readNote(dir string, filename string) (*Note, error) {
path := filepath.Join(dir, filename)
file, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("could not open file: %s", err)
}
defer file.Close()
info, err := file.Stat()
if err != nil {
return nil, fmt.Errorf("could not get file info: %s", err)
}
mtime := info.ModTime().Truncate(time.Second)
hexTime := strings.TrimSuffix(filename, ".md")
unixTime, err := strconv.ParseInt(hexTime, 16, 64)
if err != nil {
return nil, err
}
ctime := time.Unix(unixTime, 0)
var title string
var isLabel bool
var outgoingLinks []*Link
var lines, words, chars int
scanner := bufio.NewScanner(file)
lineNumber := 0
for scanner.Scan() {
lineNumber++
line := scanner.Text()
if line != "" {
lines++
words += len(strings.Fields(line))
chars += len(line)
}
if title == "" {
title = strings.TrimPrefix(line, "# ")
isLabel = len(strings.Fields(title)) == 1
continue
}
matches := linkRegex.FindAllStringSubmatch(line, -1)
for _, match := range matches {
outgoingLinks = append(outgoingLinks, &Link{LineNumber: lineNumber, Filename: match[1]})
}
}
if err := scanner.Err(); err != nil {
return nil, err
}
if title == "" {
title = "untitled"
}
note := &Note{
Filename: filename,
Title: title,
IsLabel: isLabel,
OutgoingLinks: outgoingLinks,
Ctime: ctime,
Mtime: mtime,
Lines: lines,
Words: words,
Chars: chars,
}
return note, nil
}