This commit is contained in:
Tomas Dvorak
2025-10-22 20:35:22 +02:00
parent e47059385c
commit e6bc2eedb3
55 changed files with 1751 additions and 1378 deletions
Binary file not shown.
+3
View File
@@ -7,6 +7,8 @@ require (
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1
github.com/google/uuid v1.5.0 github.com/google/uuid v1.5.0
github.com/mattn/go-sqlite3 v1.14.19 github.com/mattn/go-sqlite3 v1.14.19
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef
) )
require ( require (
@@ -31,6 +33,7 @@ require (
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.7.0 // indirect golang.org/x/arch v0.7.0 // indirect
golang.org/x/crypto v0.21.0 // indirect golang.org/x/crypto v0.21.0 // indirect
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
golang.org/x/net v0.22.0 // indirect golang.org/x/net v0.22.0 // indirect
golang.org/x/sys v0.18.0 // indirect golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
+8
View File
@@ -63,6 +63,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -82,14 +86,18 @@ golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+65 -12
View File
@@ -7,6 +7,7 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"time" "time"
@@ -369,13 +370,43 @@ func getLogoWithMetadata(c *gin.Context) {
// List all logos // List all logos
func listLogos(c *gin.Context) { func listLogos(c *gin.Context) {
rows, err := db.Query(` q := strings.TrimSpace(c.Query("q"))
SELECT id, club_name, club_city, club_type, club_website, sortParam := c.DefaultQuery("sort", "name")
has_svg, has_png, primary_format, limitStr := c.Query("limit")
created_at, updated_at pageStr := c.Query("page")
FROM logos
ORDER BY club_name base := "SELECT id, club_name, club_city, club_type, club_website, has_svg, has_png, primary_format, created_at, updated_at FROM logos"
`) where := ""
args := []interface{}{}
if q != "" {
where = " WHERE LOWER(club_name) LIKE ? OR LOWER(club_city) LIKE ? OR id LIKE ?"
like := "%" + strings.ToLower(q) + "%"
args = append(args, like, like, "%"+q+"%")
}
order := " ORDER BY club_name"
if sortParam == "recent" {
order = " ORDER BY datetime(updated_at) DESC, datetime(created_at) DESC"
}
limitClause := ""
if limitStr != "" {
if limit, err := strconv.Atoi(limitStr); err == nil && limit > 0 {
limitClause = " LIMIT ?"
args = append(args, limit)
if pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil {
if page < 1 {
page = 1
}
offset := (page - 1) * limit
limitClause += " OFFSET ?"
args = append(args, offset)
}
}
}
}
query := base + where + order + limitClause
rows, err := db.Query(query, args...)
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "database error"}) c.JSON(http.StatusInternalServerError, gin.H{"error": "database error"})
return return
@@ -392,8 +423,7 @@ func listLogos(c *gin.Context) {
for rows.Next() { for rows.Next() {
var logo LogoMetadata var logo LogoMetadata
var hasSVG, hasPNG int var hasSVG, hasPNG int
if err := rows.Scan(
err := rows.Scan(
&logo.ID, &logo.ID,
&logo.ClubName, &logo.ClubName,
&logo.ClubCity, &logo.ClubCity,
@@ -404,14 +434,12 @@ func listLogos(c *gin.Context) {
&logo.PrimaryFormat, &logo.PrimaryFormat,
&logo.CreatedAt, &logo.CreatedAt,
&logo.UpdatedAt, &logo.UpdatedAt,
) ); err != nil {
if err != nil {
continue continue
} }
logo.HasSVG = hasSVG == 1 logo.HasSVG = hasSVG == 1
logo.HasPNG = hasPNG == 1 logo.HasPNG = hasPNG == 1
if logo.HasPNG { if logo.HasPNG {
logo.LogoURL = fmt.Sprintf("%s/logos/%s?format=png", baseURL, logo.ID) logo.LogoURL = fmt.Sprintf("%s/logos/%s?format=png", baseURL, logo.ID)
} else if logo.HasSVG { } else if logo.HasSVG {
@@ -424,6 +452,31 @@ func listLogos(c *gin.Context) {
c.JSON(http.StatusOK, logos) c.JSON(http.StatusOK, logos)
} }
func deleteLogo(c *gin.Context) {
id := c.Param("id")
if id == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "logo ID is required"})
return
}
if _, err := uuid.Parse(id); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid UUID format"})
return
}
_, err := db.Exec("DELETE FROM logos WHERE id = ?", id)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "database error"})
return
}
pngPath := filepath.Join("./logos/png", id+".png")
svgPath := filepath.Join("./logos/svg", id+".svg")
os.Remove(pngPath)
os.Remove(svgPath)
c.JSON(http.StatusOK, gin.H{"success": true, "id": id})
}
func uploadLogo(c *gin.Context) { func uploadLogo(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
if id == "" { if id == "" {
+67 -10
View File
@@ -9,6 +9,9 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"github.com/srwiley/oksvg"
"github.com/srwiley/rasterx"
) )
// ConvertSVGToPNG converts an SVG file to PNG format // ConvertSVGToPNG converts an SVG file to PNG format
@@ -24,8 +27,12 @@ func ConvertSVGToPNG(svgPath, pngPath string, width int) error {
return nil return nil
} }
// If no converter available, copy SVG as fallback and log warning // Try pure-Go conversion
return fmt.Errorf("no SVG converter available (install ImageMagick or Inkscape)") if err := convertWithGoRenderer(svgPath, pngPath, width); err == nil {
return nil
}
return fmt.Errorf("no SVG converter available (install ImageMagick or Inkscape, or ensure Go renderer deps)")
} }
// ConvertPDFToPNG converts a PDF file to PNG format // ConvertPDFToPNG converts a PDF file to PNG format
@@ -39,14 +46,14 @@ func ConvertPDFToPNG(pdfPath, pngPath string, width int) error {
fmt.Sprintf("%s[0]", pdfPath), // Only first page fmt.Sprintf("%s[0]", pdfPath), // Only first page
pngPath, pngPath,
) )
var stderr bytes.Buffer var stderr bytes.Buffer
cmd.Stderr = &stderr cmd.Stderr = &stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return fmt.Errorf("PDF conversion failed (install ImageMagick and Ghostscript): %v - %s", err, stderr.String()) return fmt.Errorf("PDF conversion failed (install ImageMagick and Ghostscript): %v - %s", err, stderr.String())
} }
return nil return nil
} }
@@ -58,14 +65,14 @@ func convertWithImageMagick(svgPath, pngPath string, width int) error {
svgPath, svgPath,
pngPath, pngPath,
) )
var stderr bytes.Buffer var stderr bytes.Buffer
cmd.Stderr = &stderr cmd.Stderr = &stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return fmt.Errorf("imagemagick conversion failed: %v - %s", err, stderr.String()) return fmt.Errorf("imagemagick conversion failed: %v - %s", err, stderr.String())
} }
return nil return nil
} }
@@ -76,10 +83,10 @@ func convertWithInkscape(svgPath, pngPath string, width int) error {
fmt.Sprintf("--export-width=%d", width), fmt.Sprintf("--export-width=%d", width),
svgPath, svgPath,
) )
var stderr bytes.Buffer var stderr bytes.Buffer
cmd.Stderr = &stderr cmd.Stderr = &stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return fmt.Errorf("inkscape conversion failed: %v - %s", err, stderr.String()) return fmt.Errorf("inkscape conversion failed: %v - %s", err, stderr.String())
} }
@@ -87,6 +94,56 @@ func convertWithInkscape(svgPath, pngPath string, width int) error {
return nil return nil
} }
func convertWithGoRenderer(svgPath, pngPath string, width int) error {
f, err := os.Open(svgPath)
if err != nil {
return fmt.Errorf("open svg: %w", err)
}
defer f.Close()
icon, err := oksvg.ReadIconStream(f)
if err != nil {
return fmt.Errorf("parse svg: %w", err)
}
vb := icon.ViewBox
targetW := width
if targetW <= 0 {
targetW = int(vb.W)
if targetW <= 0 {
targetW = 512
}
}
var targetH int
if vb.W != 0 {
targetH = int(float64(targetW) * (vb.H / vb.W))
} else {
targetH = targetW
}
if targetH <= 0 {
targetH = targetW
}
icon.SetTarget(0, 0, float64(targetW), float64(targetH))
rgba := image.NewRGBA(image.Rect(0, 0, targetW, targetH))
scanner := rasterx.NewScannerGV(targetW, targetH, rgba, rgba.Bounds())
raster := rasterx.NewDasher(targetW, targetH, scanner)
icon.Draw(raster, 1.0)
out, err := os.Create(pngPath)
if err != nil {
return fmt.Errorf("create png: %w", err)
}
defer out.Close()
if err := png.Encode(out, rgba); err != nil {
os.Remove(pngPath)
return fmt.Errorf("encode png: %w", err)
}
return nil
}
// OptimizePNG optimizes a PNG file (basic implementation) // OptimizePNG optimizes a PNG file (basic implementation)
func OptimizePNG(pngPath string) error { func OptimizePNG(pngPath string) error {
// Open the file // Open the file
+1
View File
@@ -88,6 +88,7 @@ func setupRoutes(r *gin.Engine) {
logos.GET("/:id", getLogo) logos.GET("/:id", getLogo)
logos.GET("/:id/json", getLogoWithMetadata) logos.GET("/:id/json", getLogoWithMetadata)
logos.POST("/:id", uploadLogo) logos.POST("/:id", uploadLogo)
logos.DELETE("/:id", deleteLogo)
} }
} }
+1
View File
@@ -15,6 +15,7 @@
<a href="/" class="text-2xl font-bold gradient-text">🇨🇿 České Kluby Loga</a> <a href="/" class="text-2xl font-bold gradient-text">🇨🇿 České Kluby Loga</a>
<div class="flex gap-4"> <div class="flex gap-4">
<a href="/" class="nav-link px-4 py-2 rounded-lg hover:bg-dark-border transition-smooth">Domů</a> <a href="/" class="nav-link px-4 py-2 rounded-lg hover:bg-dark-border transition-smooth">Domů</a>
<a href="/logos.html" class="nav-link px-4 py-2 rounded-lg hover:bg-dark-border transition-smooth">Všechna Loga</a>
<a href="/api-docs.html" class="nav-link px-4 py-2 rounded-lg hover:bg-dark-border transition-smooth">API Docs</a> <a href="/api-docs.html" class="nav-link px-4 py-2 rounded-lg hover:bg-dark-border transition-smooth">API Docs</a>
<a href="/admin.html" class="nav-link px-4 py-2 rounded-lg hover:bg-dark-border transition-smooth">Admin</a> <a href="/admin.html" class="nav-link px-4 py-2 rounded-lg hover:bg-dark-border transition-smooth">Admin</a>
</div> </div>
+67
View File
@@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="cs" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Všechna Loga - České Kluby Loga API</title>
<link rel="stylesheet" href="/src/style.css">
</head>
<body class="bg-dark-bg text-white min-h-screen">
<nav class="border-b border-dark-border bg-dark-card/50 backdrop-blur-sm sticky top-0 z-50">
<div class="container mx-auto px-6 py-4">
<div class="flex items-center justify-between">
<a href="/" class="text-2xl font-bold gradient-text">České Kluby Loga</a>
<div class="flex gap-4">
<a href="/" class="nav-link px-4 py-2 rounded-lg hover:bg-dark-border transition-smooth">Domů</a>
<a href="/logos.html" class="nav-link px-4 py-2 rounded-lg bg-accent-blue/20 transition-smooth">Všechna Loga</a>
<a href="/api-docs.html" class="nav-link px-4 py-2 rounded-lg hover:bg-dark-border transition-smooth">API Docs</a>
<a href="/admin.html" class="nav-link px-4 py-2 rounded-lg hover:bg-dark-border transition-smooth">Admin</a>
</div>
</div>
</div>
</nav>
<header class="border-b border-dark-border bg-dark-card">
<div class="container mx-auto px-6 py-8">
<h1 class="text-3xl font-bold gradient-text mb-2">Všechna Loga</h1>
<p class="text-gray-400">Procházejte všechna dostupná loga, vyhledávejte a spravujte</p>
</div>
</header>
<main class="container mx-auto px-6 py-12">
<div class="mb-6 flex flex-col md:flex-row gap-3 md:items-center">
<input
type="text"
id="allLogoSearch"
placeholder="Hledat mezi všemi logy..."
class="w-full md:max-w-lg bg-dark-card border border-dark-border rounded-lg px-4 py-3 text-white focus:outline-none focus:border-accent-blue transition-smooth"
>
<div class="text-xs text-gray-500">20 log na stránku • řazeno: nejnovější</div>
</div>
<div id="allLoading" class="text-center py-12">
<div class="spinner mx-auto"></div>
<p class="mt-4 text-gray-400">Načítání log...</p>
</div>
<div id="allEmpty" class="text-center py-16 hidden">
<div class="text-6xl mb-4"></div>
<p class="text-xl text-gray-400 mb-4">Žádná loga nenalezena</p>
</div>
<div id="allLogoGrid" class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6 gap-6"></div>
<div class="text-center mt-10">
<button id="loadMoreBtn" class="px-6 py-3 bg-dark-card border border-dark-border rounded-lg hover:bg-dark-border transition-smooth hidden">Načíst další</button>
</div>
</main>
<footer class="border-t border-dark-border mt-20">
<div class="container mx-auto px-6 py-8 text-center text-gray-400">
<p>🇨🇿 České Kluby Loga API</p>
</div>
</footer>
<script type="module" src="/src/logos.js"></script>
</body>
</html>
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\autoprefixer\bin\autoprefixer" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\autoprefixer\bin\autoprefixer" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\baseline-browser-mapping\dist\cli.js" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\baseline-browser-mapping\dist\cli.js" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\browserslist\cli.js" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\browserslist\cli.js" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\cssesc\bin\cssesc" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\cssesc\bin\cssesc" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\esbuild\bin\esbuild" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\esbuild\bin\esbuild" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\glob\dist\esm\bin.mjs" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\glob\dist\esm\bin.mjs" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\jiti\bin\jiti.js" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\jiti\bin\jiti.js" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\nanoid\bin\nanoid.cjs" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\nanoid\bin\nanoid.cjs" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\which\bin\node-which" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\which\bin\node-which" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\resolve\bin\resolve" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\resolve\bin\resolve" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\rollup\dist\bin\rollup" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\rollup\dist\bin\rollup" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\sucrase\bin\sucrase-node" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\sucrase\bin\sucrase-node" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\sucrase\bin\sucrase" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\sucrase\bin\sucrase" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\tailwindcss\lib\cli.js" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\tailwindcss\lib\cli.js" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\tailwindcss\lib\cli.js" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\tailwindcss\lib\cli.js" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\update-browserslist-db\cli.js" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\update-browserslist-db\cli.js" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\vite\bin\vite.js" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\vite\bin\vite.js" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\yaml\bin.mjs" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\yaml\bin.mjs" %*
+15 -15
View File
@@ -1,15 +1,15 @@
# Installation # Installation
> `npm install --save @types/estree` > `npm install --save @types/estree`
# Summary # Summary
This package contains type definitions for estree (https://github.com/estree/estree). This package contains type definitions for estree (https://github.com/estree/estree).
# Details # Details
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/estree. Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/estree.
### Additional Details ### Additional Details
* Last updated: Fri, 06 Jun 2025 00:04:33 GMT * Last updated: Fri, 06 Jun 2025 00:04:33 GMT
* Dependencies: none * Dependencies: none
# Credits # Credits
These definitions were written by [RReverser](https://github.com/RReverser). These definitions were written by [RReverser](https://github.com/RReverser).
+7 -7
View File
@@ -1,8 +1,8 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2015 Dmitry Ivanov Copyright (c) 2015 Dmitry Ivanov
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+11 -11
View File
@@ -1,11 +1,11 @@
A JSON with color names and its values. Based on http://dev.w3.org/csswg/css-color/#named-colors. A JSON with color names and its values. Based on http://dev.w3.org/csswg/css-color/#named-colors.
[![NPM](https://nodei.co/npm/color-name.png?mini=true)](https://nodei.co/npm/color-name/) [![NPM](https://nodei.co/npm/color-name.png?mini=true)](https://nodei.co/npm/color-name/)
```js ```js
var colors = require('color-name'); var colors = require('color-name');
colors.red //[255,0,0] colors.red //[255,0,0]
``` ```
<a href="LICENSE"><img src="https://upload.wikimedia.org/wikipedia/commons/0/0c/MIT_logo.svg" width="120"/></a> <a href="LICENSE"><img src="https://upload.wikimedia.org/wikipedia/commons/0/0c/MIT_logo.svg" width="120"/></a>
+152 -152
View File
@@ -1,152 +1,152 @@
'use strict' 'use strict'
module.exports = { module.exports = {
"aliceblue": [240, 248, 255], "aliceblue": [240, 248, 255],
"antiquewhite": [250, 235, 215], "antiquewhite": [250, 235, 215],
"aqua": [0, 255, 255], "aqua": [0, 255, 255],
"aquamarine": [127, 255, 212], "aquamarine": [127, 255, 212],
"azure": [240, 255, 255], "azure": [240, 255, 255],
"beige": [245, 245, 220], "beige": [245, 245, 220],
"bisque": [255, 228, 196], "bisque": [255, 228, 196],
"black": [0, 0, 0], "black": [0, 0, 0],
"blanchedalmond": [255, 235, 205], "blanchedalmond": [255, 235, 205],
"blue": [0, 0, 255], "blue": [0, 0, 255],
"blueviolet": [138, 43, 226], "blueviolet": [138, 43, 226],
"brown": [165, 42, 42], "brown": [165, 42, 42],
"burlywood": [222, 184, 135], "burlywood": [222, 184, 135],
"cadetblue": [95, 158, 160], "cadetblue": [95, 158, 160],
"chartreuse": [127, 255, 0], "chartreuse": [127, 255, 0],
"chocolate": [210, 105, 30], "chocolate": [210, 105, 30],
"coral": [255, 127, 80], "coral": [255, 127, 80],
"cornflowerblue": [100, 149, 237], "cornflowerblue": [100, 149, 237],
"cornsilk": [255, 248, 220], "cornsilk": [255, 248, 220],
"crimson": [220, 20, 60], "crimson": [220, 20, 60],
"cyan": [0, 255, 255], "cyan": [0, 255, 255],
"darkblue": [0, 0, 139], "darkblue": [0, 0, 139],
"darkcyan": [0, 139, 139], "darkcyan": [0, 139, 139],
"darkgoldenrod": [184, 134, 11], "darkgoldenrod": [184, 134, 11],
"darkgray": [169, 169, 169], "darkgray": [169, 169, 169],
"darkgreen": [0, 100, 0], "darkgreen": [0, 100, 0],
"darkgrey": [169, 169, 169], "darkgrey": [169, 169, 169],
"darkkhaki": [189, 183, 107], "darkkhaki": [189, 183, 107],
"darkmagenta": [139, 0, 139], "darkmagenta": [139, 0, 139],
"darkolivegreen": [85, 107, 47], "darkolivegreen": [85, 107, 47],
"darkorange": [255, 140, 0], "darkorange": [255, 140, 0],
"darkorchid": [153, 50, 204], "darkorchid": [153, 50, 204],
"darkred": [139, 0, 0], "darkred": [139, 0, 0],
"darksalmon": [233, 150, 122], "darksalmon": [233, 150, 122],
"darkseagreen": [143, 188, 143], "darkseagreen": [143, 188, 143],
"darkslateblue": [72, 61, 139], "darkslateblue": [72, 61, 139],
"darkslategray": [47, 79, 79], "darkslategray": [47, 79, 79],
"darkslategrey": [47, 79, 79], "darkslategrey": [47, 79, 79],
"darkturquoise": [0, 206, 209], "darkturquoise": [0, 206, 209],
"darkviolet": [148, 0, 211], "darkviolet": [148, 0, 211],
"deeppink": [255, 20, 147], "deeppink": [255, 20, 147],
"deepskyblue": [0, 191, 255], "deepskyblue": [0, 191, 255],
"dimgray": [105, 105, 105], "dimgray": [105, 105, 105],
"dimgrey": [105, 105, 105], "dimgrey": [105, 105, 105],
"dodgerblue": [30, 144, 255], "dodgerblue": [30, 144, 255],
"firebrick": [178, 34, 34], "firebrick": [178, 34, 34],
"floralwhite": [255, 250, 240], "floralwhite": [255, 250, 240],
"forestgreen": [34, 139, 34], "forestgreen": [34, 139, 34],
"fuchsia": [255, 0, 255], "fuchsia": [255, 0, 255],
"gainsboro": [220, 220, 220], "gainsboro": [220, 220, 220],
"ghostwhite": [248, 248, 255], "ghostwhite": [248, 248, 255],
"gold": [255, 215, 0], "gold": [255, 215, 0],
"goldenrod": [218, 165, 32], "goldenrod": [218, 165, 32],
"gray": [128, 128, 128], "gray": [128, 128, 128],
"green": [0, 128, 0], "green": [0, 128, 0],
"greenyellow": [173, 255, 47], "greenyellow": [173, 255, 47],
"grey": [128, 128, 128], "grey": [128, 128, 128],
"honeydew": [240, 255, 240], "honeydew": [240, 255, 240],
"hotpink": [255, 105, 180], "hotpink": [255, 105, 180],
"indianred": [205, 92, 92], "indianred": [205, 92, 92],
"indigo": [75, 0, 130], "indigo": [75, 0, 130],
"ivory": [255, 255, 240], "ivory": [255, 255, 240],
"khaki": [240, 230, 140], "khaki": [240, 230, 140],
"lavender": [230, 230, 250], "lavender": [230, 230, 250],
"lavenderblush": [255, 240, 245], "lavenderblush": [255, 240, 245],
"lawngreen": [124, 252, 0], "lawngreen": [124, 252, 0],
"lemonchiffon": [255, 250, 205], "lemonchiffon": [255, 250, 205],
"lightblue": [173, 216, 230], "lightblue": [173, 216, 230],
"lightcoral": [240, 128, 128], "lightcoral": [240, 128, 128],
"lightcyan": [224, 255, 255], "lightcyan": [224, 255, 255],
"lightgoldenrodyellow": [250, 250, 210], "lightgoldenrodyellow": [250, 250, 210],
"lightgray": [211, 211, 211], "lightgray": [211, 211, 211],
"lightgreen": [144, 238, 144], "lightgreen": [144, 238, 144],
"lightgrey": [211, 211, 211], "lightgrey": [211, 211, 211],
"lightpink": [255, 182, 193], "lightpink": [255, 182, 193],
"lightsalmon": [255, 160, 122], "lightsalmon": [255, 160, 122],
"lightseagreen": [32, 178, 170], "lightseagreen": [32, 178, 170],
"lightskyblue": [135, 206, 250], "lightskyblue": [135, 206, 250],
"lightslategray": [119, 136, 153], "lightslategray": [119, 136, 153],
"lightslategrey": [119, 136, 153], "lightslategrey": [119, 136, 153],
"lightsteelblue": [176, 196, 222], "lightsteelblue": [176, 196, 222],
"lightyellow": [255, 255, 224], "lightyellow": [255, 255, 224],
"lime": [0, 255, 0], "lime": [0, 255, 0],
"limegreen": [50, 205, 50], "limegreen": [50, 205, 50],
"linen": [250, 240, 230], "linen": [250, 240, 230],
"magenta": [255, 0, 255], "magenta": [255, 0, 255],
"maroon": [128, 0, 0], "maroon": [128, 0, 0],
"mediumaquamarine": [102, 205, 170], "mediumaquamarine": [102, 205, 170],
"mediumblue": [0, 0, 205], "mediumblue": [0, 0, 205],
"mediumorchid": [186, 85, 211], "mediumorchid": [186, 85, 211],
"mediumpurple": [147, 112, 219], "mediumpurple": [147, 112, 219],
"mediumseagreen": [60, 179, 113], "mediumseagreen": [60, 179, 113],
"mediumslateblue": [123, 104, 238], "mediumslateblue": [123, 104, 238],
"mediumspringgreen": [0, 250, 154], "mediumspringgreen": [0, 250, 154],
"mediumturquoise": [72, 209, 204], "mediumturquoise": [72, 209, 204],
"mediumvioletred": [199, 21, 133], "mediumvioletred": [199, 21, 133],
"midnightblue": [25, 25, 112], "midnightblue": [25, 25, 112],
"mintcream": [245, 255, 250], "mintcream": [245, 255, 250],
"mistyrose": [255, 228, 225], "mistyrose": [255, 228, 225],
"moccasin": [255, 228, 181], "moccasin": [255, 228, 181],
"navajowhite": [255, 222, 173], "navajowhite": [255, 222, 173],
"navy": [0, 0, 128], "navy": [0, 0, 128],
"oldlace": [253, 245, 230], "oldlace": [253, 245, 230],
"olive": [128, 128, 0], "olive": [128, 128, 0],
"olivedrab": [107, 142, 35], "olivedrab": [107, 142, 35],
"orange": [255, 165, 0], "orange": [255, 165, 0],
"orangered": [255, 69, 0], "orangered": [255, 69, 0],
"orchid": [218, 112, 214], "orchid": [218, 112, 214],
"palegoldenrod": [238, 232, 170], "palegoldenrod": [238, 232, 170],
"palegreen": [152, 251, 152], "palegreen": [152, 251, 152],
"paleturquoise": [175, 238, 238], "paleturquoise": [175, 238, 238],
"palevioletred": [219, 112, 147], "palevioletred": [219, 112, 147],
"papayawhip": [255, 239, 213], "papayawhip": [255, 239, 213],
"peachpuff": [255, 218, 185], "peachpuff": [255, 218, 185],
"peru": [205, 133, 63], "peru": [205, 133, 63],
"pink": [255, 192, 203], "pink": [255, 192, 203],
"plum": [221, 160, 221], "plum": [221, 160, 221],
"powderblue": [176, 224, 230], "powderblue": [176, 224, 230],
"purple": [128, 0, 128], "purple": [128, 0, 128],
"rebeccapurple": [102, 51, 153], "rebeccapurple": [102, 51, 153],
"red": [255, 0, 0], "red": [255, 0, 0],
"rosybrown": [188, 143, 143], "rosybrown": [188, 143, 143],
"royalblue": [65, 105, 225], "royalblue": [65, 105, 225],
"saddlebrown": [139, 69, 19], "saddlebrown": [139, 69, 19],
"salmon": [250, 128, 114], "salmon": [250, 128, 114],
"sandybrown": [244, 164, 96], "sandybrown": [244, 164, 96],
"seagreen": [46, 139, 87], "seagreen": [46, 139, 87],
"seashell": [255, 245, 238], "seashell": [255, 245, 238],
"sienna": [160, 82, 45], "sienna": [160, 82, 45],
"silver": [192, 192, 192], "silver": [192, 192, 192],
"skyblue": [135, 206, 235], "skyblue": [135, 206, 235],
"slateblue": [106, 90, 205], "slateblue": [106, 90, 205],
"slategray": [112, 128, 144], "slategray": [112, 128, 144],
"slategrey": [112, 128, 144], "slategrey": [112, 128, 144],
"snow": [255, 250, 250], "snow": [255, 250, 250],
"springgreen": [0, 255, 127], "springgreen": [0, 255, 127],
"steelblue": [70, 130, 180], "steelblue": [70, 130, 180],
"tan": [210, 180, 140], "tan": [210, 180, 140],
"teal": [0, 128, 128], "teal": [0, 128, 128],
"thistle": [216, 191, 216], "thistle": [216, 191, 216],
"tomato": [255, 99, 71], "tomato": [255, 99, 71],
"turquoise": [64, 224, 208], "turquoise": [64, 224, 208],
"violet": [238, 130, 238], "violet": [238, 130, 238],
"wheat": [245, 222, 179], "wheat": [245, 222, 179],
"white": [255, 255, 255], "white": [255, 255, 255],
"whitesmoke": [245, 245, 245], "whitesmoke": [245, 245, 245],
"yellow": [255, 255, 0], "yellow": [255, 255, 0],
"yellowgreen": [154, 205, 50] "yellowgreen": [154, 205, 50]
}; };
+28 -28
View File
@@ -1,28 +1,28 @@
{ {
"name": "color-name", "name": "color-name",
"version": "1.1.4", "version": "1.1.4",
"description": "A list of color names and its values", "description": "A list of color names and its values",
"main": "index.js", "main": "index.js",
"files": [ "files": [
"index.js" "index.js"
], ],
"scripts": { "scripts": {
"test": "node test.js" "test": "node test.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git@github.com:colorjs/color-name.git" "url": "git@github.com:colorjs/color-name.git"
}, },
"keywords": [ "keywords": [
"color-name", "color-name",
"color", "color",
"color-keyword", "color-keyword",
"keyword" "keyword"
], ],
"author": "DY <dfcreative@gmail.com>", "author": "DY <dfcreative@gmail.com>",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/colorjs/color-name/issues" "url": "https://github.com/colorjs/color-name/issues"
}, },
"homepage": "https://github.com/colorjs/color-name" "homepage": "https://github.com/colorjs/color-name"
} }
+60 -60
View File
@@ -1,60 +1,60 @@
declare module 'Fraction'; declare module 'Fraction';
export interface NumeratorDenominator { export interface NumeratorDenominator {
n: number; n: number;
d: number; d: number;
} }
type FractionConstructor = { type FractionConstructor = {
(fraction: Fraction): Fraction; (fraction: Fraction): Fraction;
(num: number | string): Fraction; (num: number | string): Fraction;
(numerator: number, denominator: number): Fraction; (numerator: number, denominator: number): Fraction;
(numbers: [number | string, number | string]): Fraction; (numbers: [number | string, number | string]): Fraction;
(fraction: NumeratorDenominator): Fraction; (fraction: NumeratorDenominator): Fraction;
(firstValue: Fraction | number | string | [number | string, number | string] | NumeratorDenominator, secondValue?: number): Fraction; (firstValue: Fraction | number | string | [number | string, number | string] | NumeratorDenominator, secondValue?: number): Fraction;
}; };
export default class Fraction { export default class Fraction {
constructor (fraction: Fraction); constructor (fraction: Fraction);
constructor (num: number | string); constructor (num: number | string);
constructor (numerator: number, denominator: number); constructor (numerator: number, denominator: number);
constructor (numbers: [number | string, number | string]); constructor (numbers: [number | string, number | string]);
constructor (fraction: NumeratorDenominator); constructor (fraction: NumeratorDenominator);
constructor (firstValue: Fraction | number | string | [number | string, number | string] | NumeratorDenominator, secondValue?: number); constructor (firstValue: Fraction | number | string | [number | string, number | string] | NumeratorDenominator, secondValue?: number);
s: number; s: number;
n: number; n: number;
d: number; d: number;
abs(): Fraction; abs(): Fraction;
neg(): Fraction; neg(): Fraction;
add: FractionConstructor; add: FractionConstructor;
sub: FractionConstructor; sub: FractionConstructor;
mul: FractionConstructor; mul: FractionConstructor;
div: FractionConstructor; div: FractionConstructor;
pow: FractionConstructor; pow: FractionConstructor;
gcd: FractionConstructor; gcd: FractionConstructor;
lcm: FractionConstructor; lcm: FractionConstructor;
mod(n?: number | string | Fraction): Fraction; mod(n?: number | string | Fraction): Fraction;
ceil(places?: number): Fraction; ceil(places?: number): Fraction;
floor(places?: number): Fraction; floor(places?: number): Fraction;
round(places?: number): Fraction; round(places?: number): Fraction;
inverse(): Fraction; inverse(): Fraction;
simplify(eps?: number): Fraction; simplify(eps?: number): Fraction;
equals(n: number | string | Fraction): boolean; equals(n: number | string | Fraction): boolean;
compare(n: number | string | Fraction): number; compare(n: number | string | Fraction): number;
divisible(n: number | string | Fraction): boolean; divisible(n: number | string | Fraction): boolean;
valueOf(): number; valueOf(): number;
toString(decimalPlaces?: number): string; toString(decimalPlaces?: number): string;
toLatex(excludeWhole?: boolean): string; toLatex(excludeWhole?: boolean): string;
toFraction(excludeWhole?: boolean): string; toFraction(excludeWhole?: boolean): string;
toContinued(): number[]; toContinued(): number[];
clone(): Fraction; clone(): Fraction;
} }
+46 -46
View File
@@ -1,46 +1,46 @@
# read-cache [![Build Status](https://travis-ci.org/TrySound/read-cache.svg?branch=master)](https://travis-ci.org/TrySound/read-cache) # read-cache [![Build Status](https://travis-ci.org/TrySound/read-cache.svg?branch=master)](https://travis-ci.org/TrySound/read-cache)
Reads and caches the entire contents of a file until it is modified. Reads and caches the entire contents of a file until it is modified.
## Install ## Install
``` ```
$ npm i read-cache $ npm i read-cache
``` ```
## Usage ## Usage
```js ```js
// foo.js // foo.js
var readCache = require('read-cache'); var readCache = require('read-cache');
readCache('foo.js').then(function (contents) { readCache('foo.js').then(function (contents) {
console.log(contents); console.log(contents);
}); });
``` ```
## API ## API
### readCache(path[, encoding]) ### readCache(path[, encoding])
Returns a promise that resolves with the file's contents. Returns a promise that resolves with the file's contents.
### readCache.sync(path[, encoding]) ### readCache.sync(path[, encoding])
Returns the content of the file. Returns the content of the file.
### readCache.get(path[, encoding]) ### readCache.get(path[, encoding])
Returns the content of cached file or null. Returns the content of cached file or null.
### readCache.clear() ### readCache.clear()
Clears the contents of the cache. Clears the contents of the cache.
## License ## License
MIT © [Bogdan Chadkin](mailto:trysound@yandex.ru) MIT © [Bogdan Chadkin](mailto:trysound@yandex.ru)
+78 -78
View File
@@ -1,78 +1,78 @@
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var pify = require('pify'); var pify = require('pify');
var stat = pify(fs.stat); var stat = pify(fs.stat);
var readFile = pify(fs.readFile); var readFile = pify(fs.readFile);
var resolve = path.resolve; var resolve = path.resolve;
var cache = Object.create(null); var cache = Object.create(null);
function convert(content, encoding) { function convert(content, encoding) {
if (Buffer.isEncoding(encoding)) { if (Buffer.isEncoding(encoding)) {
return content.toString(encoding); return content.toString(encoding);
} }
return content; return content;
} }
module.exports = function (path, encoding) { module.exports = function (path, encoding) {
path = resolve(path); path = resolve(path);
return stat(path).then(function (stats) { return stat(path).then(function (stats) {
var item = cache[path]; var item = cache[path];
if (item && item.mtime.getTime() === stats.mtime.getTime()) { if (item && item.mtime.getTime() === stats.mtime.getTime()) {
return convert(item.content, encoding); return convert(item.content, encoding);
} }
return readFile(path).then(function (data) { return readFile(path).then(function (data) {
cache[path] = { cache[path] = {
mtime: stats.mtime, mtime: stats.mtime,
content: data content: data
}; };
return convert(data, encoding); return convert(data, encoding);
}); });
}).catch(function (err) { }).catch(function (err) {
cache[path] = null; cache[path] = null;
return Promise.reject(err); return Promise.reject(err);
}); });
}; };
module.exports.sync = function (path, encoding) { module.exports.sync = function (path, encoding) {
path = resolve(path); path = resolve(path);
try { try {
var stats = fs.statSync(path); var stats = fs.statSync(path);
var item = cache[path]; var item = cache[path];
if (item && item.mtime.getTime() === stats.mtime.getTime()) { if (item && item.mtime.getTime() === stats.mtime.getTime()) {
return convert(item.content, encoding); return convert(item.content, encoding);
} }
var data = fs.readFileSync(path); var data = fs.readFileSync(path);
cache[path] = { cache[path] = {
mtime: stats.mtime, mtime: stats.mtime,
content: data content: data
}; };
return convert(data, encoding); return convert(data, encoding);
} catch (err) { } catch (err) {
cache[path] = null; cache[path] = null;
throw err; throw err;
} }
}; };
module.exports.get = function (path, encoding) { module.exports.get = function (path, encoding) {
path = resolve(path); path = resolve(path);
if (cache[path]) { if (cache[path]) {
return convert(cache[path].content, encoding); return convert(cache[path].content, encoding);
} }
return null; return null;
}; };
module.exports.clear = function () { module.exports.clear = function () {
cache = Object.create(null); cache = Object.create(null);
}; };
+70 -70
View File
@@ -1246,76 +1246,76 @@ yargsParser.camelCase = camelCase;
yargsParser.decamelize = decamelize; yargsParser.decamelize = decamelize;
yargsParser.looksLikeNumber = looksLikeNumber; yargsParser.looksLikeNumber = looksLikeNumber;
/****************************************************************************** /******************************************************************************
Copyright (c) Microsoft Corporation. Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted. purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */ ***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
function __addDisposableResource(env, value, async) { function __addDisposableResource(env, value, async) {
if (value !== null && value !== void 0) { if (value !== null && value !== void 0) {
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
var dispose, inner; var dispose, inner;
if (async) { if (async) {
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
dispose = value[Symbol.asyncDispose]; dispose = value[Symbol.asyncDispose];
} }
if (dispose === void 0) { if (dispose === void 0) {
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
dispose = value[Symbol.dispose]; dispose = value[Symbol.dispose];
if (async) inner = dispose; if (async) inner = dispose;
} }
if (typeof dispose !== "function") throw new TypeError("Object not disposable."); if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } }; if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
env.stack.push({ value: value, dispose: dispose, async: async }); env.stack.push({ value: value, dispose: dispose, async: async });
} }
else if (async) { else if (async) {
env.stack.push({ async: true }); env.stack.push({ async: true });
} }
return value; return value;
} }
var _SuppressedError = typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var _SuppressedError = typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message); var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
}; };
function __disposeResources(env) { function __disposeResources(env) {
function fail(e) { function fail(e) {
env.error = env.hasError ? new _SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; env.error = env.hasError ? new _SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
env.hasError = true; env.hasError = true;
} }
var r, s = 0; var r, s = 0;
function next() { function next() {
while (r = env.stack.pop()) { while (r = env.stack.pop()) {
try { try {
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next); if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
if (r.dispose) { if (r.dispose) {
var result = r.dispose.call(r.value); var result = r.dispose.call(r.value);
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
} }
else s |= 1; else s |= 1;
} }
catch (e) { catch (e) {
fail(e); fail(e);
} }
} }
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve(); if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
if (env.hasError) throw env.error; if (env.hasError) throw env.error;
} }
return next(); return next();
} }
const toZeroIfInfinity = value => Number.isFinite(value) ? value : 0; const toZeroIfInfinity = value => Number.isFinite(value) ? value : 0;
+77 -77
View File
@@ -263,83 +263,83 @@ pify$1.all = pify$1;
var pifyExports = pify$2.exports; var pifyExports = pify$2.exports;
var fs = require$$0__default; var fs = require$$0__default;
var path$3 = require$$0; var path$3 = require$$0;
var pify = pifyExports; var pify = pifyExports;
var stat = pify(fs.stat); var stat = pify(fs.stat);
var readFile = pify(fs.readFile); var readFile = pify(fs.readFile);
var resolve = path$3.resolve; var resolve = path$3.resolve;
var cache = Object.create(null); var cache = Object.create(null);
function convert(content, encoding) { function convert(content, encoding) {
if (Buffer.isEncoding(encoding)) { if (Buffer.isEncoding(encoding)) {
return content.toString(encoding); return content.toString(encoding);
} }
return content; return content;
} }
readCache$1.exports = function (path, encoding) { readCache$1.exports = function (path, encoding) {
path = resolve(path); path = resolve(path);
return stat(path).then(function (stats) { return stat(path).then(function (stats) {
var item = cache[path]; var item = cache[path];
if (item && item.mtime.getTime() === stats.mtime.getTime()) { if (item && item.mtime.getTime() === stats.mtime.getTime()) {
return convert(item.content, encoding); return convert(item.content, encoding);
} }
return readFile(path).then(function (data) { return readFile(path).then(function (data) {
cache[path] = { cache[path] = {
mtime: stats.mtime, mtime: stats.mtime,
content: data content: data
}; };
return convert(data, encoding); return convert(data, encoding);
}); });
}).catch(function (err) { }).catch(function (err) {
cache[path] = null; cache[path] = null;
return Promise.reject(err); return Promise.reject(err);
}); });
}; };
readCache$1.exports.sync = function (path, encoding) { readCache$1.exports.sync = function (path, encoding) {
path = resolve(path); path = resolve(path);
try { try {
var stats = fs.statSync(path); var stats = fs.statSync(path);
var item = cache[path]; var item = cache[path];
if (item && item.mtime.getTime() === stats.mtime.getTime()) { if (item && item.mtime.getTime() === stats.mtime.getTime()) {
return convert(item.content, encoding); return convert(item.content, encoding);
} }
var data = fs.readFileSync(path); var data = fs.readFileSync(path);
cache[path] = { cache[path] = {
mtime: stats.mtime, mtime: stats.mtime,
content: data content: data
}; };
return convert(data, encoding); return convert(data, encoding);
} catch (err) { } catch (err) {
cache[path] = null; cache[path] = null;
throw err; throw err;
} }
}; };
readCache$1.exports.get = function (path, encoding) { readCache$1.exports.get = function (path, encoding) {
path = resolve(path); path = resolve(path);
if (cache[path]) { if (cache[path]) {
return convert(cache[path].content, encoding); return convert(cache[path].content, encoding);
} }
return null; return null;
}; };
readCache$1.exports.clear = function () { readCache$1.exports.clear = function () {
cache = Object.create(null); cache = Object.create(null);
}; };
var readCacheExports = readCache$1.exports; var readCacheExports = readCache$1.exports;
+26 -31
View File
@@ -44,27 +44,19 @@ const browseBtn = document.getElementById('browseBtn')
let allLogos = [] let allLogos = []
// Load logos async function loadRecentLogos() {
async function loadLogos() {
try { try {
const response = await fetch(`${API_BASE_URL}/logos`) const response = await fetch(`${API_BASE_URL}/logos?sort=recent&limit=8`)
if (!response.ok) throw new Error('Failed to fetch recent logos')
if (!response.ok) {
throw new Error('Failed to fetch logos')
}
allLogos = await response.json() allLogos = await response.json()
loadingState.classList.add('hidden') loadingState.classList.add('hidden')
if (allLogos.length === 0) { if (allLogos.length === 0) {
emptyState.classList.remove('hidden') emptyState.classList.remove('hidden')
} else { } else {
displayLogos(allLogos) displayLogos(allLogos)
} }
} catch (error) { } catch (error) {
console.error('Error loading logos:', error) console.error('Error loading recent logos:', error)
loadingState.classList.add('hidden') loadingState.classList.add('hidden')
emptyState.classList.remove('hidden') emptyState.classList.remove('hidden')
} }
@@ -116,20 +108,26 @@ function displayLogos(logos) {
} }
// Filter logos // Filter logos
function filterLogos(query) { async function filterLogos(query) {
const filtered = allLogos.filter(logo => const q = query.trim()
logo.club_name.toLowerCase().includes(query.toLowerCase()) || if (!q) {
(logo.club_city && logo.club_city.toLowerCase().includes(query.toLowerCase())) displayLogos(allLogos)
) return
}
displayLogos(filtered) try {
const resp = await fetch(`${API_BASE_URL}/logos?q=${encodeURIComponent(q)}&limit=50`)
if (filtered.length === 0 && query) { if (!resp.ok) throw new Error('Search failed')
logoGrid.innerHTML = ` const results = await resp.json()
<div class="col-span-full text-center py-16"> displayLogos(results)
<p class="text-xl text-gray-400">No logos found matching "${query}"</p> if (results.length === 0) {
</div> logoGrid.innerHTML = `
` <div class="col-span-full text-center py-16">
<p class="text-xl text-gray-400">No logos found matching "${q}"</p>
</div>
`
}
} catch (e) {
console.warn('Search error:', e.message)
} }
} }
@@ -158,10 +156,7 @@ if (gallerySearch) {
// Browse button - scroll to gallery // Browse button - scroll to gallery
if (browseBtn) { if (browseBtn) {
browseBtn.addEventListener('click', () => { browseBtn.addEventListener('click', () => {
document.getElementById('logoGallery').scrollIntoView({ window.location.href = '/logos.html'
behavior: 'smooth',
block: 'start'
})
}) })
} }
@@ -202,7 +197,7 @@ console.log('🇨🇿 Czech Clubs Logos API - Home')
console.log('Backend API:', API_BASE_URL) console.log('Backend API:', API_BASE_URL)
// Load logos on page load // Load logos on page load
loadLogos() loadRecentLogos()
// Show welcome notification // Show welcome notification
setTimeout(() => { setTimeout(() => {
+132
View File
@@ -0,0 +1,132 @@
import './style.css'
const API_BASE_URL = '/api'
const grid = document.getElementById('allLogoGrid')
const loading = document.getElementById('allLoading')
const empty = document.getElementById('allEmpty')
const loadMoreBtn = document.getElementById('loadMoreBtn')
const searchInput = document.getElementById('allLogoSearch')
let page = 1
const limit = 20
let query = ''
let isLoading = false
let hasMore = true
async function loadPage(reset = false) {
if (isLoading) return
if (reset) {
page = 1
hasMore = true
grid.innerHTML = ''
empty.classList.add('hidden')
loading.classList.remove('hidden')
} else {
loadMoreBtn.disabled = true
loadMoreBtn.textContent = 'Načítání...'
}
isLoading = true
try {
const url = new URL(`${API_BASE_URL}/logos`, window.location.origin)
url.searchParams.set('sort', 'recent')
url.searchParams.set('limit', String(limit))
url.searchParams.set('page', String(page))
if (query) url.searchParams.set('q', query)
const resp = await fetch(url.toString().replace(window.location.origin, ''))
if (!resp.ok) throw new Error('Failed to fetch logos')
const data = await resp.json()
if (reset) loading.classList.add('hidden')
if (Array.isArray(data) && data.length > 0) {
appendCards(data)
page += 1
if (data.length < limit) {
hasMore = false
loadMoreBtn.classList.add('hidden')
} else {
loadMoreBtn.classList.remove('hidden')
}
} else {
if (reset) empty.classList.remove('hidden')
hasMore = false
loadMoreBtn.classList.add('hidden')
}
} catch (_) {
if (reset) {
loading.classList.add('hidden')
empty.classList.remove('hidden')
}
} finally {
isLoading = false
loadMoreBtn.disabled = false
loadMoreBtn.textContent = 'Načíst další'
}
}
function appendCards(items) {
const html = items.map(logo => {
const logoUrl = `${API_BASE_URL}/logos/${logo.id}`
return `
<div class="logo-card bg-dark-card rounded-xl p-4 border border-dark-border hover:border-accent-blue transition-smooth group" data-id="${logo.id}">
<div class="aspect-square bg-dark-bg rounded-lg flex items-center justify-center mb-3 overflow-hidden cursor-pointer">
<img src="${logoUrl}" alt="${logo.club_name}" class="max-w-full max-h-full object-contain p-2 group-hover:scale-110 transition-transform duration-300" loading="lazy" onerror="this.parentElement.innerHTML='<svg class=\'w-8 h-8 text-gray-500\' fill=\'none\' stroke=\'currentColor\' viewBox=\'0 0 24 24\'><path stroke-linecap=\'round\' stroke-linejoin=\'round\' stroke-width=\'2\' d=\'M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\'></path></svg>'">
</div>
<div class="flex items-center gap-3">
<div class="flex-1 min-w-0 cursor-pointer">
<h3 class="font-semibold text-sm truncate mb-0.5">${logo.club_name}</h3>
<p class="text-xs text-gray-400 truncate">${logo.club_type || 'fotbal'}</p>
</div>
<button class="delete-logo px-3 py-1.5 text-xs bg-red-600 rounded hover:bg-red-500 transition-smooth">Smazat</button>
</div>
</div>
`
}).join('')
grid.insertAdjacentHTML('beforeend', html)
}
grid.addEventListener('click', async (e) => {
const delBtn = e.target.closest('.delete-logo')
if (delBtn) {
const card = delBtn.closest('.logo-card')
const id = card.dataset.id
const ok = confirm('Smazat toto logo?')
if (!ok) return
delBtn.disabled = true
delBtn.textContent = 'Mazání...'
try {
const resp = await fetch(`${API_BASE_URL}/logos/${id}`, { method: 'DELETE' })
if (!resp.ok) throw new Error('Delete failed')
card.remove()
if (!grid.children.length) empty.classList.remove('hidden')
} catch (_) {
delBtn.textContent = 'Smazat'
delBtn.disabled = false
alert('Mazání selhalo')
}
return
}
const card = e.target.closest('.logo-card')
if (card && !e.target.closest('.delete-logo')) {
const id = card.dataset.id
window.location.href = `/logo.html?id=${id}`
}
})
let searchTimer
searchInput.addEventListener('input', () => {
clearTimeout(searchTimer)
searchTimer = setTimeout(() => {
query = searchInput.value.trim()
loadPage(true)
}, 300)
})
loadMoreBtn.addEventListener('click', () => {
if (hasMore) loadPage(false)
})
loadPage(true)
+2 -1
View File
@@ -22,7 +22,8 @@ export default defineConfig({
main: resolve(__dirname, 'index.html'), main: resolve(__dirname, 'index.html'),
admin: resolve(__dirname, 'admin.html'), admin: resolve(__dirname, 'admin.html'),
apiDocs: resolve(__dirname, 'api-docs.html'), apiDocs: resolve(__dirname, 'api-docs.html'),
logo: resolve(__dirname, 'logo.html') logo: resolve(__dirname, 'logo.html'),
logos: resolve(__dirname, 'logos.html')
} }
} }
} }
+5
View File
@@ -1,3 +1,8 @@
module club module club
go 1.25.1 go 1.25.1
require (
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
)
+6
View File
@@ -0,0 +1,6 @@
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\nodemon\bin\nodemon.js" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\nodemon\bin\nodemon.js" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\touch\bin\nodetouch.js" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\touch\bin\nodetouch.js" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\prebuild-install\bin.js" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\prebuild-install\bin.js" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\rc\cli.js" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\rc\cli.js" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\semver\bin\semver.js" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\semver\bin\semver.js" %*
+17 -17
View File
@@ -1,17 +1,17 @@
@ECHO off @ECHO off
GOTO start GOTO start
:find_dp0 :find_dp0
SET dp0=%~dp0 SET dp0=%~dp0
EXIT /b EXIT /b
:start :start
SETLOCAL SETLOCAL
CALL :find_dp0 CALL :find_dp0
IF EXIST "%dp0%\node.exe" ( IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe" SET "_prog=%dp0%\node.exe"
) ELSE ( ) ELSE (
SET "_prog=node" SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;% SET PATHEXT=%PATHEXT:;.JS;=;%
) )
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\svgo\bin\svgo" %* endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\svgo\bin\svgo" %*
+7 -7
View File
@@ -1,8 +1,8 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2015 Dmitry Ivanov Copyright (c) 2015 Dmitry Ivanov
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+11 -11
View File
@@ -1,11 +1,11 @@
A JSON with color names and its values. Based on http://dev.w3.org/csswg/css-color/#named-colors. A JSON with color names and its values. Based on http://dev.w3.org/csswg/css-color/#named-colors.
[![NPM](https://nodei.co/npm/color-name.png?mini=true)](https://nodei.co/npm/color-name/) [![NPM](https://nodei.co/npm/color-name.png?mini=true)](https://nodei.co/npm/color-name/)
```js ```js
var colors = require('color-name'); var colors = require('color-name');
colors.red //[255,0,0] colors.red //[255,0,0]
``` ```
<a href="LICENSE"><img src="https://upload.wikimedia.org/wikipedia/commons/0/0c/MIT_logo.svg" width="120"/></a> <a href="LICENSE"><img src="https://upload.wikimedia.org/wikipedia/commons/0/0c/MIT_logo.svg" width="120"/></a>
+152 -152
View File
@@ -1,152 +1,152 @@
'use strict' 'use strict'
module.exports = { module.exports = {
"aliceblue": [240, 248, 255], "aliceblue": [240, 248, 255],
"antiquewhite": [250, 235, 215], "antiquewhite": [250, 235, 215],
"aqua": [0, 255, 255], "aqua": [0, 255, 255],
"aquamarine": [127, 255, 212], "aquamarine": [127, 255, 212],
"azure": [240, 255, 255], "azure": [240, 255, 255],
"beige": [245, 245, 220], "beige": [245, 245, 220],
"bisque": [255, 228, 196], "bisque": [255, 228, 196],
"black": [0, 0, 0], "black": [0, 0, 0],
"blanchedalmond": [255, 235, 205], "blanchedalmond": [255, 235, 205],
"blue": [0, 0, 255], "blue": [0, 0, 255],
"blueviolet": [138, 43, 226], "blueviolet": [138, 43, 226],
"brown": [165, 42, 42], "brown": [165, 42, 42],
"burlywood": [222, 184, 135], "burlywood": [222, 184, 135],
"cadetblue": [95, 158, 160], "cadetblue": [95, 158, 160],
"chartreuse": [127, 255, 0], "chartreuse": [127, 255, 0],
"chocolate": [210, 105, 30], "chocolate": [210, 105, 30],
"coral": [255, 127, 80], "coral": [255, 127, 80],
"cornflowerblue": [100, 149, 237], "cornflowerblue": [100, 149, 237],
"cornsilk": [255, 248, 220], "cornsilk": [255, 248, 220],
"crimson": [220, 20, 60], "crimson": [220, 20, 60],
"cyan": [0, 255, 255], "cyan": [0, 255, 255],
"darkblue": [0, 0, 139], "darkblue": [0, 0, 139],
"darkcyan": [0, 139, 139], "darkcyan": [0, 139, 139],
"darkgoldenrod": [184, 134, 11], "darkgoldenrod": [184, 134, 11],
"darkgray": [169, 169, 169], "darkgray": [169, 169, 169],
"darkgreen": [0, 100, 0], "darkgreen": [0, 100, 0],
"darkgrey": [169, 169, 169], "darkgrey": [169, 169, 169],
"darkkhaki": [189, 183, 107], "darkkhaki": [189, 183, 107],
"darkmagenta": [139, 0, 139], "darkmagenta": [139, 0, 139],
"darkolivegreen": [85, 107, 47], "darkolivegreen": [85, 107, 47],
"darkorange": [255, 140, 0], "darkorange": [255, 140, 0],
"darkorchid": [153, 50, 204], "darkorchid": [153, 50, 204],
"darkred": [139, 0, 0], "darkred": [139, 0, 0],
"darksalmon": [233, 150, 122], "darksalmon": [233, 150, 122],
"darkseagreen": [143, 188, 143], "darkseagreen": [143, 188, 143],
"darkslateblue": [72, 61, 139], "darkslateblue": [72, 61, 139],
"darkslategray": [47, 79, 79], "darkslategray": [47, 79, 79],
"darkslategrey": [47, 79, 79], "darkslategrey": [47, 79, 79],
"darkturquoise": [0, 206, 209], "darkturquoise": [0, 206, 209],
"darkviolet": [148, 0, 211], "darkviolet": [148, 0, 211],
"deeppink": [255, 20, 147], "deeppink": [255, 20, 147],
"deepskyblue": [0, 191, 255], "deepskyblue": [0, 191, 255],
"dimgray": [105, 105, 105], "dimgray": [105, 105, 105],
"dimgrey": [105, 105, 105], "dimgrey": [105, 105, 105],
"dodgerblue": [30, 144, 255], "dodgerblue": [30, 144, 255],
"firebrick": [178, 34, 34], "firebrick": [178, 34, 34],
"floralwhite": [255, 250, 240], "floralwhite": [255, 250, 240],
"forestgreen": [34, 139, 34], "forestgreen": [34, 139, 34],
"fuchsia": [255, 0, 255], "fuchsia": [255, 0, 255],
"gainsboro": [220, 220, 220], "gainsboro": [220, 220, 220],
"ghostwhite": [248, 248, 255], "ghostwhite": [248, 248, 255],
"gold": [255, 215, 0], "gold": [255, 215, 0],
"goldenrod": [218, 165, 32], "goldenrod": [218, 165, 32],
"gray": [128, 128, 128], "gray": [128, 128, 128],
"green": [0, 128, 0], "green": [0, 128, 0],
"greenyellow": [173, 255, 47], "greenyellow": [173, 255, 47],
"grey": [128, 128, 128], "grey": [128, 128, 128],
"honeydew": [240, 255, 240], "honeydew": [240, 255, 240],
"hotpink": [255, 105, 180], "hotpink": [255, 105, 180],
"indianred": [205, 92, 92], "indianred": [205, 92, 92],
"indigo": [75, 0, 130], "indigo": [75, 0, 130],
"ivory": [255, 255, 240], "ivory": [255, 255, 240],
"khaki": [240, 230, 140], "khaki": [240, 230, 140],
"lavender": [230, 230, 250], "lavender": [230, 230, 250],
"lavenderblush": [255, 240, 245], "lavenderblush": [255, 240, 245],
"lawngreen": [124, 252, 0], "lawngreen": [124, 252, 0],
"lemonchiffon": [255, 250, 205], "lemonchiffon": [255, 250, 205],
"lightblue": [173, 216, 230], "lightblue": [173, 216, 230],
"lightcoral": [240, 128, 128], "lightcoral": [240, 128, 128],
"lightcyan": [224, 255, 255], "lightcyan": [224, 255, 255],
"lightgoldenrodyellow": [250, 250, 210], "lightgoldenrodyellow": [250, 250, 210],
"lightgray": [211, 211, 211], "lightgray": [211, 211, 211],
"lightgreen": [144, 238, 144], "lightgreen": [144, 238, 144],
"lightgrey": [211, 211, 211], "lightgrey": [211, 211, 211],
"lightpink": [255, 182, 193], "lightpink": [255, 182, 193],
"lightsalmon": [255, 160, 122], "lightsalmon": [255, 160, 122],
"lightseagreen": [32, 178, 170], "lightseagreen": [32, 178, 170],
"lightskyblue": [135, 206, 250], "lightskyblue": [135, 206, 250],
"lightslategray": [119, 136, 153], "lightslategray": [119, 136, 153],
"lightslategrey": [119, 136, 153], "lightslategrey": [119, 136, 153],
"lightsteelblue": [176, 196, 222], "lightsteelblue": [176, 196, 222],
"lightyellow": [255, 255, 224], "lightyellow": [255, 255, 224],
"lime": [0, 255, 0], "lime": [0, 255, 0],
"limegreen": [50, 205, 50], "limegreen": [50, 205, 50],
"linen": [250, 240, 230], "linen": [250, 240, 230],
"magenta": [255, 0, 255], "magenta": [255, 0, 255],
"maroon": [128, 0, 0], "maroon": [128, 0, 0],
"mediumaquamarine": [102, 205, 170], "mediumaquamarine": [102, 205, 170],
"mediumblue": [0, 0, 205], "mediumblue": [0, 0, 205],
"mediumorchid": [186, 85, 211], "mediumorchid": [186, 85, 211],
"mediumpurple": [147, 112, 219], "mediumpurple": [147, 112, 219],
"mediumseagreen": [60, 179, 113], "mediumseagreen": [60, 179, 113],
"mediumslateblue": [123, 104, 238], "mediumslateblue": [123, 104, 238],
"mediumspringgreen": [0, 250, 154], "mediumspringgreen": [0, 250, 154],
"mediumturquoise": [72, 209, 204], "mediumturquoise": [72, 209, 204],
"mediumvioletred": [199, 21, 133], "mediumvioletred": [199, 21, 133],
"midnightblue": [25, 25, 112], "midnightblue": [25, 25, 112],
"mintcream": [245, 255, 250], "mintcream": [245, 255, 250],
"mistyrose": [255, 228, 225], "mistyrose": [255, 228, 225],
"moccasin": [255, 228, 181], "moccasin": [255, 228, 181],
"navajowhite": [255, 222, 173], "navajowhite": [255, 222, 173],
"navy": [0, 0, 128], "navy": [0, 0, 128],
"oldlace": [253, 245, 230], "oldlace": [253, 245, 230],
"olive": [128, 128, 0], "olive": [128, 128, 0],
"olivedrab": [107, 142, 35], "olivedrab": [107, 142, 35],
"orange": [255, 165, 0], "orange": [255, 165, 0],
"orangered": [255, 69, 0], "orangered": [255, 69, 0],
"orchid": [218, 112, 214], "orchid": [218, 112, 214],
"palegoldenrod": [238, 232, 170], "palegoldenrod": [238, 232, 170],
"palegreen": [152, 251, 152], "palegreen": [152, 251, 152],
"paleturquoise": [175, 238, 238], "paleturquoise": [175, 238, 238],
"palevioletred": [219, 112, 147], "palevioletred": [219, 112, 147],
"papayawhip": [255, 239, 213], "papayawhip": [255, 239, 213],
"peachpuff": [255, 218, 185], "peachpuff": [255, 218, 185],
"peru": [205, 133, 63], "peru": [205, 133, 63],
"pink": [255, 192, 203], "pink": [255, 192, 203],
"plum": [221, 160, 221], "plum": [221, 160, 221],
"powderblue": [176, 224, 230], "powderblue": [176, 224, 230],
"purple": [128, 0, 128], "purple": [128, 0, 128],
"rebeccapurple": [102, 51, 153], "rebeccapurple": [102, 51, 153],
"red": [255, 0, 0], "red": [255, 0, 0],
"rosybrown": [188, 143, 143], "rosybrown": [188, 143, 143],
"royalblue": [65, 105, 225], "royalblue": [65, 105, 225],
"saddlebrown": [139, 69, 19], "saddlebrown": [139, 69, 19],
"salmon": [250, 128, 114], "salmon": [250, 128, 114],
"sandybrown": [244, 164, 96], "sandybrown": [244, 164, 96],
"seagreen": [46, 139, 87], "seagreen": [46, 139, 87],
"seashell": [255, 245, 238], "seashell": [255, 245, 238],
"sienna": [160, 82, 45], "sienna": [160, 82, 45],
"silver": [192, 192, 192], "silver": [192, 192, 192],
"skyblue": [135, 206, 235], "skyblue": [135, 206, 235],
"slateblue": [106, 90, 205], "slateblue": [106, 90, 205],
"slategray": [112, 128, 144], "slategray": [112, 128, 144],
"slategrey": [112, 128, 144], "slategrey": [112, 128, 144],
"snow": [255, 250, 250], "snow": [255, 250, 250],
"springgreen": [0, 255, 127], "springgreen": [0, 255, 127],
"steelblue": [70, 130, 180], "steelblue": [70, 130, 180],
"tan": [210, 180, 140], "tan": [210, 180, 140],
"teal": [0, 128, 128], "teal": [0, 128, 128],
"thistle": [216, 191, 216], "thistle": [216, 191, 216],
"tomato": [255, 99, 71], "tomato": [255, 99, 71],
"turquoise": [64, 224, 208], "turquoise": [64, 224, 208],
"violet": [238, 130, 238], "violet": [238, 130, 238],
"wheat": [245, 222, 179], "wheat": [245, 222, 179],
"white": [255, 255, 255], "white": [255, 255, 255],
"whitesmoke": [245, 245, 245], "whitesmoke": [245, 245, 245],
"yellow": [255, 255, 0], "yellow": [255, 255, 0],
"yellowgreen": [154, 205, 50] "yellowgreen": [154, 205, 50]
}; };
+28 -28
View File
@@ -1,28 +1,28 @@
{ {
"name": "color-name", "name": "color-name",
"version": "1.1.4", "version": "1.1.4",
"description": "A list of color names and its values", "description": "A list of color names and its values",
"main": "index.js", "main": "index.js",
"files": [ "files": [
"index.js" "index.js"
], ],
"scripts": { "scripts": {
"test": "node test.js" "test": "node test.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git@github.com:colorjs/color-name.git" "url": "git@github.com:colorjs/color-name.git"
}, },
"keywords": [ "keywords": [
"color-name", "color-name",
"color", "color",
"color-keyword", "color-keyword",
"keyword" "keyword"
], ],
"author": "DY <dfcreative@gmail.com>", "author": "DY <dfcreative@gmail.com>",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/colorjs/color-name/issues" "url": "https://github.com/colorjs/color-name/issues"
}, },
"homepage": "https://github.com/colorjs/color-name" "homepage": "https://github.com/colorjs/color-name"
} }
+53 -53
View File
@@ -7,59 +7,59 @@ var path = require('path');
var fs = require('fs'); var fs = require('fs');
var https = require('https'); var https = require('https');
/****************************************************************************** /******************************************************************************
Copyright (c) Microsoft Corporation. Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted. purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */ ***************************************************************************** */
/* global Reflect, Promise */ /* global Reflect, Promise */
function __awaiter(thisArg, _arguments, P, generator) { function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) { return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next()); step((generator = generator.apply(thisArg, _arguments || [])).next());
}); });
} }
function __generator(thisArg, body) { function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; } function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) { function step(op) {
if (f) throw new TypeError("Generator is already executing."); if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try { while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value]; if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) { switch (op[0]) {
case 0: case 1: t = op; break; case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false }; case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue; case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue; case 7: op = _.ops.pop(); _.trys.pop(); continue;
default: default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop(); if (t[2]) _.ops.pop();
_.trys.pop(); continue; _.trys.pop(); continue;
} }
op = body.call(thisArg, _); op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
} }
} }
var packageJson = process$1.env.npm_package_json; var packageJson = process$1.env.npm_package_json;
+46 -2
View File
@@ -34,6 +34,7 @@ const CONFIG = {
}, },
// Directories to scan (relative to project root) // Directories to scan (relative to project root)
scanDirs: [ scanDirs: [
'backend/logos',
'data/logos', 'data/logos',
'frontend/dist', 'frontend/dist',
], ],
@@ -74,7 +75,7 @@ async function getFileSize(filePath) {
*/ */
async function optimizeSvgFile(filePath) { async function optimizeSvgFile(filePath) {
const svg = await fs.readFile(filePath, 'utf8'); const svg = await fs.readFile(filePath, 'utf8');
const result = optimize(svg, { const result = optimizeSvg(svg, {
path: filePath, path: filePath,
multipass: true, multipass: true,
plugins: [ plugins: [
@@ -100,6 +101,45 @@ async function optimizeSvgFile(filePath) {
return true; return true;
} }
async function convertSvgToPng(svgPath) {
try {
const normalized = path.normalize(svgPath);
let pngPath = normalized.replace(/\.svg$/i, '.png');
const mappings = [
{
from: path.join(path.sep, 'data', 'logos', 'svg') + path.sep,
to: path.join(path.sep, 'data', 'logos', 'png') + path.sep,
},
{
from: path.join(path.sep, 'backend', 'logos', 'svg') + path.sep,
to: path.join(path.sep, 'backend', 'logos', 'png') + path.sep,
},
];
for (const { from, to } of mappings) {
if (normalized.includes(from)) {
pngPath = normalized.replace(from, to).replace(/\.svg$/i, '.png');
break;
}
}
await fs.mkdir(path.dirname(pngPath), { recursive: true });
const image = sharp(svgPath, { density: 300 });
await image
.resize({ width: 512, fit: 'inside', withoutEnlargement: true })
.png({ quality: CONFIG.compression.png.quality, effort: CONFIG.compression.png.effort })
.toFile(pngPath);
console.log(chalk.green(` ✓ Converted to PNG: ${pngPath}`));
return true;
} catch (error) {
console.warn(chalk.yellow(` ⚠️ SVG to PNG conversion skipped: ${error.message}`));
return false;
}
}
/** /**
* Optimize PNG/JPG file * Optimize PNG/JPG file
*/ */
@@ -195,7 +235,11 @@ async function processFile(filePath) {
// Optimize based on file type // Optimize based on file type
switch (ext) { switch (ext) {
case 'svg': case 'svg':
optimized = await optimizeSvgFile(filePath); {
const didOptimize = await optimizeSvgFile(filePath);
const didConvert = await convertSvgToPng(filePath);
optimized = didOptimize || didConvert;
}
break; break;
case 'png': case 'png':
case 'jpg': case 'jpg':
+1 -1
View File
@@ -5,7 +5,7 @@
"main": "optimize-assets.js", "main": "optimize-assets.js",
"scripts": { "scripts": {
"optimize": "node optimize-assets.js", "optimize": "node optimize-assets.js",
"optimize:watch": "nodemon --watch ../data --watch ../frontend/dist -e svg,png,pdf --exec npm run optimize" "optimize:watch": "nodemon --watch ../backend/logos --watch ../data --watch ../frontend/dist -e svg,pdf --exec npm run optimize"
}, },
"dependencies": { "dependencies": {
"chalk": "^4.1.2", "chalk": "^4.1.2",
+118 -118
View File
@@ -1,119 +1,119 @@
🇨🇿 Czech Clubs Logos API 🇨🇿 Czech Clubs Logos API
A fullstack project for serving high-quality, transparent background logos of Czech football & futsal clubs. A fullstack project for serving high-quality, transparent background logos of Czech football & futsal clubs.
Logos are mapped by FAČR UUIDs (from facr.tdvorak.dev Logos are mapped by FAČR UUIDs (from facr.tdvorak.dev
) to ensure consistency across projects. ) to ensure consistency across projects.
✨ Features ✨ Features
⚽ Fetch Czech clubs metadata from FAČR Scraper API. ⚽ Fetch Czech clubs metadata from FAČR Scraper API.
🖼️ Upload & store full-quality transparent logos (SVG/PNG). 🖼️ Upload & store full-quality transparent logos (SVG/PNG).
🔄 Reuse FAČR UUID as a unique identifier. 🔄 Reuse FAČR UUID as a unique identifier.
🌐 Serve logos through a simple CDN-style API. 🌐 Serve logos through a simple CDN-style API.
📝 Optional metadata (club name, city, colors, competition). 📝 Optional metadata (club name, city, colors, competition).
📦 Self-hosted with Go backend + CDN storage. 📦 Self-hosted with Go backend + CDN storage.
🔧 Tech Stack 🔧 Tech Stack
Backend: Golang (Gin or Fiber) Backend: Golang (Gin or Fiber)
Storage: Storage:
Local /logos/{id}.svg Local /logos/{id}.svg
Or cloud (Supabase / Cloudflare R2 / S3) Or cloud (Supabase / Cloudflare R2 / S3)
Database: SQLite/Postgres (to link UUID ↔ metadata ↔ logo file) Database: SQLite/Postgres (to link UUID ↔ metadata ↔ logo file)
External API: facr.tdvorak.dev External API: facr.tdvorak.dev
📂 Project Structure 📂 Project Structure
czech-clubs-logos-api/ czech-clubs-logos-api/
│── backend/ │── backend/
│ ├── main.go # Go API entrypoint │ ├── main.go # Go API entrypoint
│ ├── routes.go # API routes │ ├── routes.go # API routes
│ ├── facr_client.go # Client for facr.tdvorak.dev │ ├── facr_client.go # Client for facr.tdvorak.dev
│ ├── handlers/ │ ├── handlers/
│ │ ├── upload.go # Handle logo upload │ │ ├── upload.go # Handle logo upload
│ │ ├── logos.go # Serve logos │ │ ├── logos.go # Serve logos
│ │ └── clubs.go # Proxy FAČR API │ │ └── clubs.go # Proxy FAČR API
│ └── storage/ │ └── storage/
│ └── local.go # Logo saving/loading │ └── local.go # Logo saving/loading
│── logos/ # Stored club logos (UUID.svg/png) │── logos/ # Stored club logos (UUID.svg/png)
│── frontend/ # (optional, for admin upload UI) │── frontend/ # (optional, for admin upload UI)
│── db.sqlite # Database (UUID ↔ metadata) │── db.sqlite # Database (UUID ↔ metadata)
│── go.mod │── go.mod
│── README.md │── README.md
🚀 API Endpoints 🚀 API Endpoints
Search clubs Search clubs
Proxy FAČR search to help find correct UUID: Proxy FAČR search to help find correct UUID:
GET /clubs/search?q=sparta GET /clubs/search?q=sparta
→ proxies facr.tdvorak.dev/club/search?q=sparta → proxies facr.tdvorak.dev/club/search?q=sparta
Get club info Get club info
GET /clubs/:id GET /clubs/:id
→ proxies facr.tdvorak.dev/club/{id} → proxies facr.tdvorak.dev/club/{id}
Upload logo Upload logo
Upload a full-quality SVG/PNG logo mapped to a FAČR UUID: Upload a full-quality SVG/PNG logo mapped to a FAČR UUID:
POST /logos/:id POST /logos/:id
FormData: file=@slavia.svg FormData: file=@slavia.svg
→ Saves to ./logos/{id}.svg → Saves to ./logos/{id}.svg
→ Stores metadata in DB → Stores metadata in DB
Get logo Get logo
Serve logo by FAČR UUID: Serve logo by FAČR UUID:
GET /logos/:id GET /logos/:id
→ returns {id}.svg/png → returns {id}.svg/png
Get logo with metadata Get logo with metadata
GET /logos/:id/json GET /logos/:id/json
{ {
"id": "00000000-0000-0000-0000-000000000000", "id": "00000000-0000-0000-0000-000000000000",
"club": "AC Sparta Praha", "club": "AC Sparta Praha",
"type": "football", "type": "football",
"logo_url": "https://cdn.example.com/logos/00000000-0000-0000-0000-000000000000.svg" "logo_url": "https://cdn.example.com/logos/00000000-0000-0000-0000-000000000000.svg"
} }
📊 Example Workflow 📊 Example Workflow
Search for a club: Search for a club:
GET /clubs/search?q=Slavia GET /clubs/search?q=Slavia
→ returns UUID: 11111111-2222-3333-4444-555555555555 → returns UUID: 11111111-2222-3333-4444-555555555555
Upload logo for club: Upload logo for club:
POST /logos/11111111-2222-3333-4444-555555555555 POST /logos/11111111-2222-3333-4444-555555555555
file=@slavia.svg file=@slavia.svg
Get logo: Get logo:
GET /logos/11111111-2222-3333-4444-555555555555 GET /logos/11111111-2222-3333-4444-555555555555
→ returns full quality transparent SVG → returns full quality transparent SVG
🔮 Future Ideas 🔮 Future Ideas
✍️ Web admin panel for uploading logos. ✍️ Web admin panel for uploading logos.
🎨 Auto background remover (e.g., remove.bg API). 🎨 Auto background remover (e.g., remove.bg API).
🔎 Logo search by club name (maps internally to UUID). 🔎 Logo search by club name (maps internally to UUID).
📦 Publish as NPM package (@czech-football/logos) or Go module. 📦 Publish as NPM package (@czech-football/logos) or Go module.
👉 This way, youll have a FAČR-aware logo CDN that anyone can integrate into websites, apps, or your SportCreative projects. 👉 This way, youll have a FAČR-aware logo CDN that anyone can integrate into websites, apps, or your SportCreative projects.