Initial commit: WYSIWYG Markdown Editor - Go backend + React/TypeScript frontend with Tailwind CSS

Backend:
- Cobra CLI with --data-dir, --port, --host flags
- Gin HTTP server with REST API for markdown CRUD operations
- File storage on disk (.md files only)
- Comprehensive logrus logging
- Backend tests with CRUD round-trip verification

Frontend:
- React 18 + TypeScript + Tailwind CSS
- Markdown editor with live GFM preview (react-markdown + remark-gfm)
- File management UI (list, create, open, save, delete)
- Theme switcher with Dark/Light/System modes
- Responsive design
- Frontend tests with vitest

Testing:
- All backend tests pass (go test ./...)
- All frontend tests pass (npm test)
This commit is contained in:
2026-02-05 15:44:06 -05:00
parent c2a225fd29
commit 482d8a448a
37 changed files with 10585 additions and 0 deletions

78
cmd/main.go Normal file
View File

@@ -0,0 +1,78 @@
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"markdown-editor/internal/server"
"markdown-editor/internal/logger"
"github.com/spf13/cobra"
)
var (
rootCmd = &cobra.Command{
Use: "markdown-editor",
Short: "A markdown editor with preview",
Long: "A markdown editor with preview - a WYSIWYG markdown editor server",
Run: runServer,
}
dataDir string
port int
host string
)
func init() {
rootCmd.Flags().StringVar(&dataDir, "data-dir", "./data", "Storage path for markdown files")
rootCmd.Flags().IntVar(&port, "port", 8080, "Server port")
rootCmd.Flags().StringVar(&host, "host", "127.0.0.1", "Bind address")
}
func runServer(cmd *cobra.Command, args []string) {
log := logger.GetLogger()
log.Info("Starting Markdown Editor Server")
log.Infof("Data directory: %s", dataDir)
log.Infof("Port: %d", port)
log.Infof("Host: %s", host)
s := server.NewServer(dataDir, port, host)
// Setup signal handling
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
if err := s.Start(); err != nil {
log.Fatalf("Server error: %v", err)
}
}()
// Wait for signal
sig := <-sigChan
log.Infof("Received signal %v, shutting down...", sig)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := s.Shutdown(ctx); err != nil {
log.Errorf("Shutdown error: %v", err)
os.Exit(1)
}
log.Info("Server stopped")
}
func Execute() error {
return rootCmd.Execute()
}
func main() {
if err := Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

26
cmd/main_test.go Normal file
View File

@@ -0,0 +1,26 @@
package main
import (
"testing"
)
func TestExecute(t *testing.T) {
// Just verify the command structure
cmd := rootCmd
if cmd == nil {
t.Error("rootCmd is nil")
}
if cmd.Use != "markdown-editor" {
t.Errorf("Expected Use 'markdown-editor', got '%s'", cmd.Use)
}
}
func TestRunServer(t *testing.T) {
cmd := rootCmd
if cmd == nil {
t.Error("rootCmd is nil")
}
if cmd.Use != "markdown-editor" {
t.Errorf("Expected Use 'markdown-editor', got '%s'", cmd.Use)
}
}