feat: initial release v0.3.0

- Support 9 chart types: line, bar, pie, scatter, bubble, donut, mixed, polar, radar
- Multi-format output: ANSI, SVG, PNG, Markdown
- Go + Fiber + gonum/plot
- Docker support
- Morandi color palette
This commit is contained in:
2026-04-16 04:33:02 +08:00
commit ba927c2b2f
21 changed files with 2918 additions and 0 deletions

85
internal/service/chart.go Normal file
View File

@@ -0,0 +1,85 @@
package service
import (
"fmt"
"github.com/google/uuid"
"github.com/picoclaw/chart/internal/renderer"
"github.com/picoclaw/chart/internal/types"
)
type ChartService struct {
svgRenderer *renderer.SVGRenderer
pngRenderer *renderer.PNGRenderer
ansiRenderer *renderer.ANSIRenderer
textRenderer *renderer.TextRenderer
chartCache map[string]*types.Chart
}
func NewChartService() *ChartService {
return &ChartService{
svgRenderer: renderer.NewSVGRenderer(),
pngRenderer: renderer.NewPNGRenderer(),
ansiRenderer: renderer.NewANSIRenderer(),
textRenderer: renderer.NewTextRenderer(),
chartCache: make(map[string]*types.Chart),
}
}
func (s *ChartService) CreateChart(req *types.ChartRequest) (*types.ChartResponse, error) {
chart := &types.Chart{
ID: uuid.New().String(),
Type: req.Type,
Title: req.Title,
Data: req.Data,
}
svgData, err := s.svgRenderer.Render(chart)
if err != nil {
return nil, fmt.Errorf("failed to render SVG: %w", err)
}
chart.SVG = svgData
pngData, err := s.pngRenderer.Render(chart)
if err != nil {
return nil, fmt.Errorf("failed to render PNG: %w", err)
}
chart.PNG = pngData
ansiData, err := s.ansiRenderer.Render(chart)
if err != nil {
return nil, fmt.Errorf("failed to render ANSI: %w", err)
}
textData, err := s.textRenderer.Render(chart)
if err != nil {
return nil, fmt.Errorf("failed to render text: %w", err)
}
s.chartCache[chart.ID] = chart
return &types.ChartResponse{
ChartID: chart.ID,
Text: string(textData),
ANSI: string(ansiData),
SVG: string(chart.SVG),
PNGURL: fmt.Sprintf("/api/v1/chart/%s.png", chart.ID),
Markdown: fmt.Sprintf("![%s](/api/v1/chart/%s.png)", chart.Title, chart.ID),
}, nil
}
func (s *ChartService) GetChart(id string) (*types.Chart, error) {
chart, ok := s.chartCache[id]
if !ok {
return nil, fmt.Errorf("chart not found: %s", id)
}
return chart, nil
}
func (s *ChartService) GetChartPNG(id string) ([]byte, error) {
chart, err := s.GetChart(id)
if err != nil {
return nil, err
}
return chart.PNG, nil
}