feat(markdown-editor): implement wysiswyg markdown editor with live preview

- Build Go backend with Cobra CLI and REST API
  - CRUD operations for markdown files (GET, POST, PUT, DELETE)
  - File storage with flat .md file structure
  - Comprehensive logrus logging with JSON format
  - Static asset serving for frontend

- Build React/TypeScript frontend with Tailwind CSS
  - Markdown editor with live GFM preview
  - File management UI (list, create, open, delete)
  - Theme system (Dark/Light/System) with persistence
  - Responsive design (320px mobile, 1920px desktop)

- Add comprehensive test coverage
  - Backend: API, storage, and logger tests (13 tests passing)
  - Frontend: Editor and App component tests

- Setup Nix development environment with Go, Node.js, and TypeScript
This commit is contained in:
2026-02-05 17:48:23 -05:00
parent 78f33053fb
commit 5b67cb61d2
31 changed files with 2010 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
package logger
import (
"os"
"github.com/sirupsen/logrus"
)
var (
log *logrus.Logger
)
func Init() {
log = logrus.New()
log.SetOutput(os.Stdout)
log.SetFormatter(&logrus.JSONFormatter{
TimestampFormat: "2006-01-02 15:04:05",
})
log.SetLevel(logrus.InfoLevel)
}
func Info(msg string, fields ...interface{}) {
if len(fields) > 0 {
log.WithFields(logrus.Fields{"message": msg}).Info()
} else {
log.Info(msg)
}
}
func Infof(format string, args ...interface{}) {
log.Infof(format, args...)
}
func Debug(msg string, fields ...interface{}) {
if len(fields) > 0 {
log.WithFields(logrus.Fields{"message": msg}).Debug()
} else {
log.Debug(msg)
}
}
func Debugf(format string, args ...interface{}) {
log.Debugf(format, args...)
}
func Warn(msg string, fields ...interface{}) {
if len(fields) > 0 {
log.WithFields(logrus.Fields{"message": msg}).Warn()
} else {
log.Warn(msg)
}
}
func Warnf(format string, args ...interface{}) {
log.Warnf(format, args...)
}
func Error(msg string, fields ...interface{}) {
if len(fields) > 0 {
log.WithFields(logrus.Fields{"message": msg}).Error()
} else {
log.Error(msg)
}
}
func Errorf(format string, args ...interface{}) {
log.Errorf(format, args...)
}
func Fatal(msg string, fields ...interface{}) {
if len(fields) > 0 {
log.WithFields(logrus.Fields{"message": msg}).Fatal()
} else {
log.Fatal(msg)
}
}
func Fatalf(format string, args ...interface{}) {
log.Fatalf(format, args...)
}
func WithField(key string, value interface{}) *logrus.Entry {
return log.WithField(key, value)
}

View File

@@ -0,0 +1,58 @@
package logger
import (
"testing"
)
func TestLoggerInitialization(t *testing.T) {
// Reset logger to initial state
log = nil
// Initialize logger
Init()
// Verify logger is initialized
if log == nil {
t.Fatal("Logger was not initialized")
}
}
func TestLoggerInfo(t *testing.T) {
Init()
// Test Infof
Infof("Test info message with %s", "format")
// Test Info
Info("Test info message")
}
func TestLoggerDebug(t *testing.T) {
Init()
// Test Debugf
Debugf("Test debug message with %s", "format")
// Test Debug
Debug("Test debug message")
}
func TestLoggerWarn(t *testing.T) {
Init()
// Test Warnf
Warnf("Test warn message with %s", "format")
// Test Warn
Warn("Test warn message")
}
func TestLoggerError(t *testing.T) {
Init()
// Test Errorf
Errorf("Test error message with %s", "format")
// Test Error
Error("Test error message")
}