From de4ffb2fdbb763d8d8d44bd8f87e3fc7e786ad4a Mon Sep 17 00:00:00 2001 From: takenX10 Date: Tue, 17 May 2022 16:47:59 +0200 Subject: [PATCH 1/8] added automatic embed and minify of style.css and made website responsive --- go.mod | 5 +++++ go.sum | 12 ++++++++++++ statik.go | 52 +++++++++++++++++++++++++--------------------------- style.css | 26 +++++++++++++++++++++----- 4 files changed, 63 insertions(+), 32 deletions(-) create mode 100644 go.sum diff --git a/go.mod b/go.mod index 9946ffb..ea31a8f 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,8 @@ module github.com/lucat1/statik go 1.17 + +require ( + github.com/tdewolff/minify/v2 v2.11.2 // indirect + github.com/tdewolff/parse/v2 v2.5.29 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..38cb5fb --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= +github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fsnotify/fsnotify v1.5.3/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/tdewolff/minify/v2 v2.11.2 h1:PpaPWhNlMVjkAKaOj0bbPv6KCVnrm8jbVwG7OtSdAqw= +github.com/tdewolff/minify/v2 v2.11.2/go.mod h1:NxozhBtgUVypPLzQdV96wkIu9J9vAiVmBcKhfC2zMfg= +github.com/tdewolff/parse/v2 v2.5.29 h1:Uf0OtZL9YaUXTuHEOitdo9lD90P0XTwCjZi+KbGChuM= +github.com/tdewolff/parse/v2 v2.5.29/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= +github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/statik.go b/statik.go index 144cd4e..982c940 100644 --- a/statik.go +++ b/statik.go @@ -14,11 +14,15 @@ import ( "sort" "strings" "time" + _"embed" + "github.com/tdewolff/minify/v2" + "github.com/tdewolff/minify/v2/css" ) +//go:embed "style.css" +var style string const ( formatLayout = time.RFC822 - minNameSpace, minNameSpacing, dateSpace, sizeSpace = 50, 10, "30", "8" linkSuffix = ".link" ) @@ -52,26 +56,25 @@ func join(rel string) string { return res } -func header(rel string) string { - path := path.Join(baseUrl.Path + rel) - str := "Index of " + path + "

Index of " + path + "


"
+func header(rel string, css string) string {
+	path := path.Join(baseUrl.Path + rel)	
+	str := "Index of " + path + "

Index of " + path + "


"
 	if rel != "/" {
 		str += "..\n"
 	}
 	return str
 }
 
-func line(name string, nameSpace int, path string, modTime time.Time, isDir bool, size int64, link bool) string {
-	space := strings.Repeat(" ", nameSpace-len(name))
+func line(name string, path string, modTime time.Time, isDir bool, size int64, link bool) string {
 	url := path
 	if !link {
 		url = join(path)
 	}
 	extra := ""
 	if isDir {
-		extra = " class=\"d\""
+		extra = "class=\"d\""
 	}
-	return fmt.Sprintf("%s%s %-"+dateSpace+"s %-"+sizeSpace+"s\n", url, extra, name, space, modTime.Format(formatLayout), bytes(size))
+	return fmt.Sprintf("
%s

%s

%s

", url, extra, name, modTime.Format(formatLayout), bytes(size)) } func footer(date time.Time) string { @@ -99,7 +102,7 @@ func filter(entries []fs.FileInfo) []fs.FileInfo { return filtered } -func generate(dir string) bool { +func generate(dir string, css string) bool { entries, err := ioutil.ReadDir(dir) if err != nil { log.Fatalf("Could not read input directory: %s\n%s\n", dir, err) @@ -132,20 +135,7 @@ func generate(dir string) bool { log.Fatalf("Could not create output index.html: %s\n%s\n", htmlPath, err) } - nameSpace := minNameSpace - for _, entry := range entries { - l := len(entry.Name()) - if strings.HasSuffix(entry.Name(), linkSuffix) { - l -= len(linkSuffix) - } - l += minNameSpacing - - if l > nameSpace { - nameSpace = l - } - } - - content := header(rel) + content := header(rel, css) for _, entry := range entries { pth := path.Join(dir, entry.Name()) // Avoid recursive infinite loop @@ -158,13 +148,13 @@ func generate(dir string) bool { if err != nil { log.Fatalf("Could not read link file: %s\n%s\n", pth, err) } - content += line(entry.Name()[:len(entry.Name())-len(linkSuffix)], nameSpace, string(url), entry.ModTime(), entry.IsDir(), 0, true) + content += line(entry.Name()[:len(entry.Name())-len(linkSuffix)], string(url), entry.ModTime(), entry.IsDir(), 0, true) continue } // Only list directories when recursing and only those which are not empty - if !entry.IsDir() || recursive && generate(pth) { - content += line(entry.Name(), nameSpace, path.Join(rel, entry.Name()), entry.ModTime(), entry.IsDir(), entry.Size(), false) + if !entry.IsDir() || recursive && generate(pth, css) { + content += line(entry.Name(), path.Join(rel, entry.Name()), entry.ModTime(), entry.IsDir(), entry.Size(), false) } // Copy all files over to the web root @@ -249,5 +239,13 @@ func main() { if baseUrl, err = url.Parse(*b); err != nil { log.Fatalf("Could not parse base URL: %s\n%s\n", *b, err) } - generate(baseDir) + m := minify.New() + m.AddFunc("text/css", css.Minify) + out, err := m.String("text/css", style) + if err != nil { + log.Fatalf("Could not minify css") + panic(err) + } + log.Printf("css code minified correctly") + generate(baseDir, out) } diff --git a/style.css b/style.css index 97f887d..39ecd23 100644 --- a/style.css +++ b/style.css @@ -17,7 +17,6 @@ color: var(--f); background: var(--b); } - body { font-size: 16px; font-family: monospace; @@ -25,12 +24,29 @@ body { padding: 1.5rem; line-height: 1.8; } - a { - text-decoration: none; - border-bottom: 1px solid; + word-wrap: break-word; + min-width: 0; + white-space: pre-wrap; + text-underline-position: under; } - .d { color: var(--d); } +div { + display: grid; + align-items: center; + width: 100%; + grid-template: 1fr / 7fr 3fr 2fr; +} +p { + min-width: 0; +} +@media (max-width:880px) { + div{ + grid-template: 1fr / 7fr 3fr; + } + .t{ + display: none; + } +} \ No newline at end of file From 1d2aa6cb2689764136d43183b15636f2a9c9bcaf Mon Sep 17 00:00:00 2001 From: takenX10 Date: Wed, 18 May 2022 12:03:52 +0200 Subject: [PATCH 2/8] one grid to rule them all --- statik.go | 6 +++--- style.css | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/statik.go b/statik.go index 982c940..b3c0e52 100644 --- a/statik.go +++ b/statik.go @@ -58,7 +58,7 @@ func join(rel string) string { func header(rel string, css string) string { path := path.Join(baseUrl.Path + rel) - str := "Index of " + path + "

Index of " + path + "


"
+	str := "Index of " + path + "

Index of " + path + "


" if rel != "/" { str += "..\n" } @@ -74,11 +74,11 @@ func line(name string, path string, modTime time.Time, isDir bool, size int64, l if isDir { extra = "class=\"d\"" } - return fmt.Sprintf("
%s

%s

%s

", url, extra, name, modTime.Format(formatLayout), bytes(size)) + return fmt.Sprintf("%s

%s

%s

", url, extra, name, modTime.Format(formatLayout), bytes(size)) } func footer(date time.Time) string { - return "

Generated by statik on " + date.Format(formatLayout) + "

" + return "

Generated by statik on " + date.Format(formatLayout) + "

" } func copy(src, dest string) { diff --git a/style.css b/style.css index 39ecd23..1b1f45d 100644 --- a/style.css +++ b/style.css @@ -37,14 +37,14 @@ div { display: grid; align-items: center; width: 100%; - grid-template: 1fr / 7fr 3fr 2fr; + grid-template-columns: 7fr 3fr 2fr; } p { min-width: 0; } @media (max-width:880px) { div{ - grid-template: 1fr / 7fr 3fr; + grid-template-columns: 7fr 3fr; } .t{ display: none; From 3b1d64cb83723d5f0c54e68559e50a6803e2b5bb Mon Sep 17 00:00:00 2001 From: takenX10 Date: Thu, 19 May 2022 16:04:11 +0200 Subject: [PATCH 3/8] added templates --- default-components/dotdot.gohtml | 1 + default-components/footer.gohtml | 1 + default-components/header.gohtml | 1 + default-components/line.gohtml | 1 + default-components/style.css | 52 +++++++++++ statik.go | 142 +++++++++++++++++++++++++++---- 6 files changed, 181 insertions(+), 17 deletions(-) create mode 100644 default-components/dotdot.gohtml create mode 100644 default-components/footer.gohtml create mode 100644 default-components/header.gohtml create mode 100644 default-components/line.gohtml create mode 100644 default-components/style.css diff --git a/default-components/dotdot.gohtml b/default-components/dotdot.gohtml new file mode 100644 index 0000000..0c909d6 --- /dev/null +++ b/default-components/dotdot.gohtml @@ -0,0 +1 @@ +..

diff --git a/default-components/footer.gohtml b/default-components/footer.gohtml new file mode 100644 index 0000000..5193c9f --- /dev/null +++ b/default-components/footer.gohtml @@ -0,0 +1 @@ +

Generated by statik on {{ .Date }}

\ No newline at end of file diff --git a/default-components/header.gohtml b/default-components/header.gohtml new file mode 100644 index 0000000..bf1a60d --- /dev/null +++ b/default-components/header.gohtml @@ -0,0 +1 @@ +Index of {{ .Path }}

Index of {{ .Path }}


\ No newline at end of file diff --git a/default-components/line.gohtml b/default-components/line.gohtml new file mode 100644 index 0000000..cb05fc9 --- /dev/null +++ b/default-components/line.gohtml @@ -0,0 +1 @@ +{{ .Name }}

{{ .Time }}

{{ .Size }}

\ No newline at end of file diff --git a/default-components/style.css b/default-components/style.css new file mode 100644 index 0000000..1b1f45d --- /dev/null +++ b/default-components/style.css @@ -0,0 +1,52 @@ +/* NOTE: this stylesheet is compressed and directly embedded into the go code */ +:root { + --b: #fbf1c7; + --f: #282828; + --d: #af3a03; +} + +@media (prefers-color-scheme: dark) { + :root { + --b: #282828; + --f: #fbf1c7; + --d: #fe8019; + } +} + +* { + color: var(--f); + background: var(--b); +} +body { + font-size: 16px; + font-family: monospace; + margin: 0; + padding: 1.5rem; + line-height: 1.8; +} +a { + word-wrap: break-word; + min-width: 0; + white-space: pre-wrap; + text-underline-position: under; +} +.d { + color: var(--d); +} +div { + display: grid; + align-items: center; + width: 100%; + grid-template-columns: 7fr 3fr 2fr; +} +p { + min-width: 0; +} +@media (max-width:880px) { + div{ + grid-template-columns: 7fr 3fr; + } + .t{ + display: none; + } +} \ No newline at end of file diff --git a/statik.go b/statik.go index b3c0e52..37557e5 100644 --- a/statik.go +++ b/statik.go @@ -15,11 +15,31 @@ import ( "strings" "time" _"embed" + "html/template" "github.com/tdewolff/minify/v2" "github.com/tdewolff/minify/v2/css" ) -//go:embed "style.css" -var style string + +type Header struct { + Path string + Mystyle template.CSS +} + +type Footer struct { + Date string +} + +type Dotdot struct { + Path string +} + +type Line struct { + Url string + Name string + Time string + Size string + Extra string +} const ( formatLayout = time.RFC822 @@ -32,6 +52,22 @@ var ( include, exclude *regexp.Regexp = nil, nil empty, recursive, sortEntries, converLinks bool + + //go:embed "default-components/style.css" + styleFile string + //go:embed "default-components/header.gohtml" + headerFile string + //go:embed "default-components/footer.gohtml" + footerFile string + //go:embed "default-components/dotdot.gohtml" + dotdotFile string + //go:embed "default-components/line.gohtml" + lineFile string + + headerTemplate *template.Template + footerTemplate *template.Template + dotdotTemplate *template.Template + lineTemplate *template.Template ) func bytes(b int64) string { @@ -56,17 +92,28 @@ func join(rel string) string { return res } -func header(rel string, css string) string { - path := path.Join(baseUrl.Path + rel) - str := "Index of " + path + "

Index of " + path + "


" +func header(rel string) string { + path := path.Join(baseUrl.Path + rel) + var out strings.Builder + h := Header{ + Path:path, + Mystyle:template.CSS(styleFile)} + if err := headerTemplate.Execute(&out, h); err != nil { + log.Fatalf("could not generate header lines for path: %s", path) + } if rel != "/" { - str += "..\n" + d := Dotdot{join(rel+"/..")} + if err := dotdotTemplate.Execute(&out, d); err != nil { + log.Fatalf("could not generate dotdot line for path: %s", path) + } + } - return str + return out.String() } func line(name string, path string, modTime time.Time, isDir bool, size int64, link bool) string { url := path + var out strings.Builder if !link { url = join(path) } @@ -74,11 +121,21 @@ func line(name string, path string, modTime time.Time, isDir bool, size int64, l if isDir { extra = "class=\"d\"" } - return fmt.Sprintf("%s

%s

%s

", url, extra, name, modTime.Format(formatLayout), bytes(size)) + + l := Line{url, name, modTime.Format(formatLayout), bytes(size), extra} + if err := lineTemplate.Execute(&out, l); err != nil { + log.Fatalf("could not generate line template on path: %s", path) + } + return out.String() } func footer(date time.Time) string { - return "

Generated by statik on " + date.Format(formatLayout) + "

" + var out strings.Builder + f := Footer{date.Format(formatLayout)} + if err := footerTemplate.Execute(&out, f); err != nil { + log.Fatalf("could not generate footer template") + } + return out.String() } func copy(src, dest string) { @@ -102,7 +159,7 @@ func filter(entries []fs.FileInfo) []fs.FileInfo { return filtered } -func generate(dir string, css string) bool { +func generate(dir string) bool { entries, err := ioutil.ReadDir(dir) if err != nil { log.Fatalf("Could not read input directory: %s\n%s\n", dir, err) @@ -135,7 +192,7 @@ func generate(dir string, css string) bool { log.Fatalf("Could not create output index.html: %s\n%s\n", htmlPath, err) } - content := header(rel, css) + content := header(rel) for _, entry := range entries { pth := path.Join(dir, entry.Name()) // Avoid recursive infinite loop @@ -153,7 +210,7 @@ func generate(dir string, css string) bool { } // Only list directories when recursing and only those which are not empty - if !entry.IsDir() || recursive && generate(pth, css) { + if !entry.IsDir() || recursive && generate(pth) { content += line(entry.Name(), path.Join(rel, entry.Name()), entry.ModTime(), entry.IsDir(), entry.Size(), false) } @@ -182,6 +239,11 @@ func main() { s := flag.Bool("sort", true, "Sort files A-z and by type") b := flag.String("b", "http://localhost", "The base URL") l := flag.Bool("l", false, "Convert .link files to anchor tags") + argstyle := flag.String("style", "", "Add a custom style file gohtml") + argfooter := flag.String("footer", "", "Add a custom footer gohtml") + argheader := flag.String("header", "", "Add a custom header gohtml") + argdotdot := flag.String("dotdot", "", "Add a custom dotdot line gohtml") + argline := flag.String("line", "", "Add a custom line gohtml") flag.Parse() args := flag.Args() @@ -195,7 +257,6 @@ func main() { src = args[0] dest = args[1] } - log.Println("Running with parameters:") log.Println("\tInclude:\t", *i) log.Println("\tExclude:\t", *e) @@ -205,6 +266,11 @@ func main() { log.Println("\tSource:\t\t", src) log.Println("\tDestination:\t", dest) log.Println("\tBase URL:\t", *b) + log.Println("\tstyle:\t\t",*argstyle) + log.Println("\tfooter:\t\t",*argfooter) + log.Println("\theader:\t\t",*argheader) + log.Println("\tdotdot:\t\t",*argdotdot) + log.Println("\tline:\t\t",*argline) var err error if include, err = regexp.Compile(*i); err != nil { @@ -239,13 +305,55 @@ func main() { if baseUrl, err = url.Parse(*b); err != nil { log.Fatalf("Could not parse base URL: %s\n%s\n", *b, err) } + var content []byte + + if *argheader != "" { + if content, err = ioutil.ReadFile(*argheader); err != nil { + log.Fatalf("Could not open header file template") + } + headerFile = string(content) + } + if headerTemplate, err = template.New("header").Parse(headerFile); err != nil { log.Fatalf("could not create header template")} + + if *argfooter != "" { + if content, err = ioutil.ReadFile(*argfooter); err != nil { + log.Fatalf("Could not open header file template") + } + footerFile = string(content) + } + if footerTemplate, err = template.New("footer").Parse(footerFile); err != nil { log.Fatalf("could not create footer template")} + + if *argdotdot != "" { + if content, err = ioutil.ReadFile(*argdotdot); err != nil { + log.Fatalf("Could not open header file template") + } + dotdotFile = string(content) + } + if dotdotTemplate, err = template.New("dotdot").Parse(dotdotFile); err != nil { log.Fatalf("could not create dotdot template")} + + if *argline != "" { + if content, err = ioutil.ReadFile(*argline); err != nil { + log.Fatalf("Could not open header file template") + } + lineFile = string(content) + } + if lineTemplate, err = template.New("line").Parse(lineFile); err != nil { log.Fatalf("could not create line template")} + + if *argstyle != "" { + if content, err = ioutil.ReadFile(*argstyle); err != nil { + log.Fatalf("Could not open header file template") + } + styleFile = string(content) + } + + log.Printf("templates created correctly") + m := minify.New() m.AddFunc("text/css", css.Minify) - out, err := m.String("text/css", style) - if err != nil { + if styleFile, err = m.String("text/css", styleFile); err != nil { log.Fatalf("Could not minify css") - panic(err) } log.Printf("css code minified correctly") - generate(baseDir, out) + + generate(baseDir) } From 467a8ea42fd68927fdf226d8e7e1b95ed053e089 Mon Sep 17 00:00:00 2001 From: Luca Date: Fri, 20 May 2022 15:18:39 +0200 Subject: [PATCH 4/8] polish changes and add navigation in the index path --- default-components/dotdot.gohtml | 1 - default-components/footer.gohtml | 1 - default-components/header.gohtml | 1 - default-components/line.gohtml | 1 - default-components/style.css | 52 ------ footer.gohtml | 5 + go.mod | 1 + go.sum | 1 + header.gohtml | 14 ++ line.gohtml | 5 + statik.go | 295 ++++++++++++++----------------- 11 files changed, 155 insertions(+), 222 deletions(-) delete mode 100644 default-components/dotdot.gohtml delete mode 100644 default-components/footer.gohtml delete mode 100644 default-components/header.gohtml delete mode 100644 default-components/line.gohtml delete mode 100644 default-components/style.css create mode 100644 footer.gohtml create mode 100644 header.gohtml create mode 100644 line.gohtml diff --git a/default-components/dotdot.gohtml b/default-components/dotdot.gohtml deleted file mode 100644 index 0c909d6..0000000 --- a/default-components/dotdot.gohtml +++ /dev/null @@ -1 +0,0 @@ -..

diff --git a/default-components/footer.gohtml b/default-components/footer.gohtml deleted file mode 100644 index 5193c9f..0000000 --- a/default-components/footer.gohtml +++ /dev/null @@ -1 +0,0 @@ -

Generated by statik on {{ .Date }}

\ No newline at end of file diff --git a/default-components/header.gohtml b/default-components/header.gohtml deleted file mode 100644 index bf1a60d..0000000 --- a/default-components/header.gohtml +++ /dev/null @@ -1 +0,0 @@ -Index of {{ .Path }}

Index of {{ .Path }}


\ No newline at end of file diff --git a/default-components/line.gohtml b/default-components/line.gohtml deleted file mode 100644 index cb05fc9..0000000 --- a/default-components/line.gohtml +++ /dev/null @@ -1 +0,0 @@ -{{ .Name }}

{{ .Time }}

{{ .Size }}

\ No newline at end of file diff --git a/default-components/style.css b/default-components/style.css deleted file mode 100644 index 1b1f45d..0000000 --- a/default-components/style.css +++ /dev/null @@ -1,52 +0,0 @@ -/* NOTE: this stylesheet is compressed and directly embedded into the go code */ -:root { - --b: #fbf1c7; - --f: #282828; - --d: #af3a03; -} - -@media (prefers-color-scheme: dark) { - :root { - --b: #282828; - --f: #fbf1c7; - --d: #fe8019; - } -} - -* { - color: var(--f); - background: var(--b); -} -body { - font-size: 16px; - font-family: monospace; - margin: 0; - padding: 1.5rem; - line-height: 1.8; -} -a { - word-wrap: break-word; - min-width: 0; - white-space: pre-wrap; - text-underline-position: under; -} -.d { - color: var(--d); -} -div { - display: grid; - align-items: center; - width: 100%; - grid-template-columns: 7fr 3fr 2fr; -} -p { - min-width: 0; -} -@media (max-width:880px) { - div{ - grid-template-columns: 7fr 3fr; - } - .t{ - display: none; - } -} \ No newline at end of file diff --git a/footer.gohtml b/footer.gohtml new file mode 100644 index 0000000..f12c378 --- /dev/null +++ b/footer.gohtml @@ -0,0 +1,5 @@ +
+
+

Generated by statik on {{ .Date.Format "02 Jan 06 15:04 MST" }}

+ + diff --git a/go.mod b/go.mod index ea31a8f..a2f1b87 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/lucat1/statik go 1.17 require ( + github.com/dustin/go-humanize v1.0.0 // indirect github.com/tdewolff/minify/v2 v2.11.2 // indirect github.com/tdewolff/parse/v2 v2.5.29 // indirect ) diff --git a/go.sum b/go.sum index 38cb5fb..31a3cb2 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,6 @@ github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/fsnotify/fsnotify v1.5.3/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= diff --git a/header.gohtml b/header.gohtml new file mode 100644 index 0000000..ba19d1d --- /dev/null +++ b/header.gohtml @@ -0,0 +1,14 @@ + + + + + + Index of {{ .FullPath }} + + +

+ Index of + /{{ .BasePath.Name }}/{{ range $i,$a := .Parts }}{{ .Name }}/{{ end }} +

+
+
diff --git a/line.gohtml b/line.gohtml new file mode 100644 index 0000000..474e12f --- /dev/null +++ b/line.gohtml @@ -0,0 +1,5 @@ +{{ if and .IsDir (eq .Name "..") }} + {{ .Name }}

{{ .Date.Format "02 Jan 06 15:04 MST" }}

{{ .Size }}

+{{ else }} + {{ .Name }}

{{ .Date.Format "02 Jan 06 15:04 MST" }}

{{ .Size }}

+{{ end }} diff --git a/statik.go b/statik.go index 37557e5..d693cab 100644 --- a/statik.go +++ b/statik.go @@ -1,8 +1,12 @@ package main import ( + "bytes" + _ "embed" "flag" "fmt" + "html/template" + "io" "io/fs" "io/ioutil" "log" @@ -14,128 +18,72 @@ import ( "sort" "strings" "time" - _"embed" - "html/template" + + "github.com/dustin/go-humanize" "github.com/tdewolff/minify/v2" "github.com/tdewolff/minify/v2/css" + "github.com/tdewolff/minify/v2/html" + "github.com/tdewolff/minify/v2/js" ) -type Header struct { - Path string - Mystyle template.CSS +type Dir struct { + Name string + URL string } -type Footer struct { - Date string +type Header struct { + BasePath Dir + Parts []Dir + FullPath string + Stylesheet template.CSS } -type Dotdot struct { - Path string +type Footer struct { + Date time.Time } type Line struct { - Url string - Name string - Time string - Size string - Extra string + IsDir bool + Name string + URL string + Size string + Date time.Time } -const ( - formatLayout = time.RFC822 - linkSuffix = ".link" -) +const linkSuffix = ".link" var ( baseDir, outDir string - baseUrl *url.URL = nil + baseURL *url.URL = nil include, exclude *regexp.Regexp = nil, nil empty, recursive, sortEntries, converLinks bool - //go:embed "default-components/style.css" - styleFile string - //go:embed "default-components/header.gohtml" - headerFile string - //go:embed "default-components/footer.gohtml" - footerFile string - //go:embed "default-components/dotdot.gohtml" - dotdotFile string - //go:embed "default-components/line.gohtml" - lineFile string - - headerTemplate *template.Template - footerTemplate *template.Template - dotdotTemplate *template.Template - lineTemplate *template.Template -) + //go:embed "style.css" + style string + //go:embed "header.gohtml" + rawHeader string + //go:embed "line.gohtml" + rawLine string + //go:embed "footer.gohtml" + rawFooter string -func bytes(b int64) string { - const unit = 1000 - if b < unit { - return fmt.Sprintf("%d B", b) - } - div, exp := int64(unit), 0 - for n := b / unit; n >= unit; n /= unit { - div *= unit - exp++ - } - return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp]) -} + header, footer, line *template.Template +) // joins the baseUrl path with the given relative path and returns the url as a string -func join(rel string) string { - cpy := baseUrl.Path - baseUrl.Path = path.Join(baseUrl.Path, rel) - res := baseUrl.String() - baseUrl.Path = cpy +func withBaseURL(rel string) string { + cpy := baseURL.Path + baseURL.Path = path.Join(baseURL.Path, rel) + res := baseURL.String() + baseURL.Path = cpy return res } -func header(rel string) string { - path := path.Join(baseUrl.Path + rel) - var out strings.Builder - h := Header{ - Path:path, - Mystyle:template.CSS(styleFile)} - if err := headerTemplate.Execute(&out, h); err != nil { - log.Fatalf("could not generate header lines for path: %s", path) - } - if rel != "/" { - d := Dotdot{join(rel+"/..")} - if err := dotdotTemplate.Execute(&out, d); err != nil { - log.Fatalf("could not generate dotdot line for path: %s", path) - } - - } - return out.String() -} - -func line(name string, path string, modTime time.Time, isDir bool, size int64, link bool) string { - url := path - var out strings.Builder - if !link { - url = join(path) - } - extra := "" - if isDir { - extra = "class=\"d\"" - } - - l := Line{url, name, modTime.Format(formatLayout), bytes(size), extra} - if err := lineTemplate.Execute(&out, l); err != nil { - log.Fatalf("could not generate line template on path: %s", path) - } - return out.String() -} - -func footer(date time.Time) string { - var out strings.Builder - f := Footer{date.Format(formatLayout)} - if err := footerTemplate.Execute(&out, f); err != nil { - log.Fatalf("could not generate footer template") +func gen(tmpl *template.Template, data interface{}, out io.Writer) { + if err := tmpl.Execute(out, data); err != nil { + log.Fatalf("could not generate template for the %s section:\n%s\n", tmpl.Name(), err) } - return out.String() } func copy(src, dest string) { @@ -159,7 +107,7 @@ func filter(entries []fs.FileInfo) []fs.FileInfo { return filtered } -func generate(dir string) bool { +func generate(m *minify.M, dir string, parts []string) bool { entries, err := ioutil.ReadDir(dir) if err != nil { log.Fatalf("Could not read input directory: %s\n%s\n", dir, err) @@ -178,21 +126,43 @@ func generate(dir string) bool { }) } - if !strings.HasSuffix(dir, "/") { - dir += "/" - } - rel := strings.Replace(dir, baseDir, "", 1) - out := path.Join(outDir, rel) - if err := os.Mkdir(out, os.ModePerm); err != nil { - log.Fatalf("Could not create output *sub*directory: %s\n%s\n", out, err) + rel := path.Join(parts...) + outDir := path.Join(outDir, rel) + if err := os.Mkdir(outDir, os.ModePerm); err != nil { + log.Fatalf("Could not create output *sub*directory: %s\n%s\n", outDir, err) } - htmlPath := path.Join(out, "index.html") + htmlPath := path.Join(outDir, "index.html") html, err := os.OpenFile(htmlPath, os.O_RDWR|os.O_CREATE, 0666) if err != nil { log.Fatalf("Could not create output index.html: %s\n%s\n", htmlPath, err) } - content := header(rel) + out := new(bytes.Buffer) + // Generate the header and the double dots back anchor when appropriate + p, url := []Dir{}, "" + for _, part := range parts { + url = path.Join(url, part) + p = append(p, Dir{Name: part, URL: withBaseURL(url)}) + } + fmt.Println(parts) + gen(header, Header{ + BasePath: Dir{ + Name: strings.TrimPrefix(strings.TrimSuffix(baseURL.Path, "/"), "/"), + URL: baseURL.String(), + }, + Parts: p, + FullPath: path.Join(baseURL.Path+rel) + "/", + Stylesheet: template.CSS(style), + }, out) + if len(parts) != 0 { + gen(line, Line{ + IsDir: true, + Name: "..", + URL: withBaseURL(path.Join(rel, "..")), + Size: humanize.Bytes(0), + }, out) + } + for _, entry := range entries { pth := path.Join(dir, entry.Name()) // Avoid recursive infinite loop @@ -200,29 +170,41 @@ func generate(dir string) bool { continue } + data := Line{ + IsDir: entry.IsDir(), + Name: entry.Name(), + URL: withBaseURL(path.Join(rel, entry.Name())), + Size: humanize.Bytes(uint64(entry.Size())), + Date: entry.ModTime(), + } if strings.HasSuffix(pth, linkSuffix) { + data.Name = data.Name[:len(data.Name)-len(linkSuffix)] + data.Size = humanize.Bytes(0) + url, err := ioutil.ReadFile(pth) if err != nil { log.Fatalf("Could not read link file: %s\n%s\n", pth, err) } - content += line(entry.Name()[:len(entry.Name())-len(linkSuffix)], string(url), entry.ModTime(), entry.IsDir(), 0, true) + data.URL = string(url) + gen(line, data, out) continue } // Only list directories when recursing and only those which are not empty - if !entry.IsDir() || recursive && generate(pth) { - content += line(entry.Name(), path.Join(rel, entry.Name()), entry.ModTime(), entry.IsDir(), entry.Size(), false) + if !entry.IsDir() || recursive && generate(m, pth, append(parts, entry.Name())) { + gen(line, data, out) } // Copy all files over to the web root if !entry.IsDir() { - copy(pth, path.Join(out, entry.Name())) + copy(pth, path.Join(outDir, entry.Name())) } } - content += footer(time.Now()) - if n, err := html.Write([]byte(content)); err != nil || n != len(content) { + gen(footer, Footer{Date: time.Now()}, out) + if err := m.Minify("text/html", html, out); err != nil { log.Fatalf("Could not write to index.html: %s\n%s\n", htmlPath, err) } + fmt.Println(out.String()) if err := html.Close(); err != nil { log.Fatalf("Could not write to close index.html: %s\n%s\n", htmlPath, err) } @@ -231,6 +213,22 @@ func generate(dir string) bool { return !empty } +func loadTemplate(name string, path string, def *string, dest **template.Template) { + var ( + content []byte + err error + ) + if path != "" { + if content, err = ioutil.ReadFile(path); err != nil { + log.Fatalf("Could not read %s template file %s:\n%s\n", name, path, err) + } + *def = string(content) + } + if *dest, err = template.New(name).Parse(*def); err != nil { + log.Fatalf("Could not parse %s template:\n%s\n", name, path, err) + } +} + func main() { i := flag.String("i", ".*", "A regex pattern to include files into the listing") e := flag.String("e", "\\.git(hub)?", "A regex pattern to exclude files from the listing") @@ -239,11 +237,10 @@ func main() { s := flag.Bool("sort", true, "Sort files A-z and by type") b := flag.String("b", "http://localhost", "The base URL") l := flag.Bool("l", false, "Convert .link files to anchor tags") - argstyle := flag.String("style", "", "Add a custom style file gohtml") - argfooter := flag.String("footer", "", "Add a custom footer gohtml") - argheader := flag.String("header", "", "Add a custom header gohtml") - argdotdot := flag.String("dotdot", "", "Add a custom dotdot line gohtml") - argline := flag.String("line", "", "Add a custom line gohtml") + argstyle := flag.String("style", "", "Use a custom stylesheet file") + argfooter := flag.String("footer", "", "Use a custom footer template") + argheader := flag.String("header", "", "Use a custom header template") + argline := flag.String("line", "", "Use a custom line template") flag.Parse() args := flag.Args() @@ -266,11 +263,10 @@ func main() { log.Println("\tSource:\t\t", src) log.Println("\tDestination:\t", dest) log.Println("\tBase URL:\t", *b) - log.Println("\tstyle:\t\t",*argstyle) - log.Println("\tfooter:\t\t",*argfooter) - log.Println("\theader:\t\t",*argheader) - log.Println("\tdotdot:\t\t",*argdotdot) - log.Println("\tline:\t\t",*argline) + log.Println("\tStyle:\t\t", *argstyle) + log.Println("\tFooter:\t\t", *argfooter) + log.Println("\tHeader:\t\t", *argheader) + log.Println("\tline:\t\t", *argline) var err error if include, err = regexp.Compile(*i); err != nil { @@ -302,58 +298,25 @@ func main() { log.Fatalf("Could not remove output directory previous contents: %s\n%s\n", outDir, err) } } - if baseUrl, err = url.Parse(*b); err != nil { + if baseURL, err = url.Parse(*b); err != nil { log.Fatalf("Could not parse base URL: %s\n%s\n", *b, err) } - var content []byte - - if *argheader != "" { - if content, err = ioutil.ReadFile(*argheader); err != nil { - log.Fatalf("Could not open header file template") - } - headerFile = string(content) - } - if headerTemplate, err = template.New("header").Parse(headerFile); err != nil { log.Fatalf("could not create header template")} - - if *argfooter != "" { - if content, err = ioutil.ReadFile(*argfooter); err != nil { - log.Fatalf("Could not open header file template") - } - footerFile = string(content) - } - if footerTemplate, err = template.New("footer").Parse(footerFile); err != nil { log.Fatalf("could not create footer template")} - - if *argdotdot != "" { - if content, err = ioutil.ReadFile(*argdotdot); err != nil { - log.Fatalf("Could not open header file template") - } - dotdotFile = string(content) - } - if dotdotTemplate, err = template.New("dotdot").Parse(dotdotFile); err != nil { log.Fatalf("could not create dotdot template")} - - if *argline != "" { - if content, err = ioutil.ReadFile(*argline); err != nil { - log.Fatalf("Could not open header file template") - } - lineFile = string(content) - } - if lineTemplate, err = template.New("line").Parse(lineFile); err != nil { log.Fatalf("could not create line template")} - + + loadTemplate("header", *argheader, &rawHeader, &header) + loadTemplate("line", *argline, &rawLine, &line) + loadTemplate("footer", *argfooter, &rawFooter, &footer) + if *argstyle != "" { + var content []byte if content, err = ioutil.ReadFile(*argstyle); err != nil { - log.Fatalf("Could not open header file template") + log.Fatalf("Could not read stylesheet file %s:\n%s\n", *argstyle, err) } - styleFile = string(content) + style = string(content) } - log.Printf("templates created correctly") - m := minify.New() m.AddFunc("text/css", css.Minify) - if styleFile, err = m.String("text/css", styleFile); err != nil { - log.Fatalf("Could not minify css") - } - log.Printf("css code minified correctly") - - generate(baseDir) + m.AddFunc("text/html", html.Minify) + m.AddFunc("application/javascript", js.Minify) + generate(m, baseDir, []string{}) } From c192594dcf57666adf39e841e8e88ffcd4a7f61b Mon Sep 17 00:00:00 2001 From: Luca Date: Fri, 20 May 2022 15:20:37 +0200 Subject: [PATCH 5/8] remove debug logs --- statik.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/statik.go b/statik.go index d693cab..de75ec0 100644 --- a/statik.go +++ b/statik.go @@ -4,7 +4,6 @@ import ( "bytes" _ "embed" "flag" - "fmt" "html/template" "io" "io/fs" @@ -144,7 +143,6 @@ func generate(m *minify.M, dir string, parts []string) bool { url = path.Join(url, part) p = append(p, Dir{Name: part, URL: withBaseURL(url)}) } - fmt.Println(parts) gen(header, Header{ BasePath: Dir{ Name: strings.TrimPrefix(strings.TrimSuffix(baseURL.Path, "/"), "/"), @@ -204,7 +202,6 @@ func generate(m *minify.M, dir string, parts []string) bool { if err := m.Minify("text/html", html, out); err != nil { log.Fatalf("Could not write to index.html: %s\n%s\n", htmlPath, err) } - fmt.Println(out.String()) if err := html.Close(); err != nil { log.Fatalf("Could not write to close index.html: %s\n%s\n", htmlPath, err) } From 82067a73c96dad717d69cef0038442c7bc80aedc Mon Sep 17 00:00:00 2001 From: takenX10 Date: Fri, 20 May 2022 15:52:42 +0200 Subject: [PATCH 6/8] changed padding --- style.css | 104 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 54 insertions(+), 50 deletions(-) diff --git a/style.css b/style.css index 1b1f45d..90312ee 100644 --- a/style.css +++ b/style.css @@ -1,52 +1,56 @@ /* NOTE: this stylesheet is compressed and directly embedded into the go code */ :root { - --b: #fbf1c7; - --f: #282828; - --d: #af3a03; -} - -@media (prefers-color-scheme: dark) { - :root { - --b: #282828; - --f: #fbf1c7; - --d: #fe8019; - } -} - -* { - color: var(--f); - background: var(--b); -} -body { - font-size: 16px; - font-family: monospace; - margin: 0; - padding: 1.5rem; - line-height: 1.8; -} -a { - word-wrap: break-word; - min-width: 0; - white-space: pre-wrap; - text-underline-position: under; -} -.d { - color: var(--d); -} -div { - display: grid; - align-items: center; - width: 100%; - grid-template-columns: 7fr 3fr 2fr; -} -p { - min-width: 0; -} -@media (max-width:880px) { - div{ - grid-template-columns: 7fr 3fr; - } - .t{ - display: none; - } -} \ No newline at end of file + --b: #fbf1c7; + --f: #282828; + --d: #af3a03; + } + + @media (prefers-color-scheme: dark) { + :root { + --b: #282828; + --f: #fbf1c7; + --d: #fe8019; + } + } + + * { + color: var(--f); + background: var(--b); + } + body { + font-size: 16px; + font-family: monospace; + margin: 0; + padding: 1.5rem; + line-height: 1.8; + } + a { + word-wrap: break-word; + min-width: 0; + white-space: pre-wrap; + text-underline-position: under; + } + .d { + color: var(--d); + } + div { + display: grid; + align-items: center; + width: 100%; + grid-template-columns: 7fr 3fr 2fr; + } + p { + min-width: 0; + margin: 0.6rem; + } + @media (max-width:880px) { + div{ + grid-template-columns: 7fr 3fr; + } + .t{ + display: none; + } + p{ + margin: 1.8rem; + } + } \ No newline at end of file From fc7c9f74f3e31092a08851ce251dc35c25f2cd84 Mon Sep 17 00:00:00 2001 From: Luca Date: Sat, 21 May 2022 11:59:31 +0200 Subject: [PATCH 7/8] fix double slash --- header.gohtml | 2 +- statik.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/header.gohtml b/header.gohtml index ba19d1d..54f7b36 100644 --- a/header.gohtml +++ b/header.gohtml @@ -8,7 +8,7 @@

Index of - /{{ .BasePath.Name }}/{{ range $i,$a := .Parts }}{{ .Name }}/{{ end }} + {{ if not (eq (len .Root.Name) 0) }}/{{ .Root.Name }}{{ end }}/{{ range $i,$a := .Parts }}{{ .Name }}/{{ end }}


diff --git a/statik.go b/statik.go index de75ec0..9624e88 100644 --- a/statik.go +++ b/statik.go @@ -31,7 +31,7 @@ type Dir struct { } type Header struct { - BasePath Dir + Root Dir Parts []Dir FullPath string Stylesheet template.CSS @@ -144,7 +144,7 @@ func generate(m *minify.M, dir string, parts []string) bool { p = append(p, Dir{Name: part, URL: withBaseURL(url)}) } gen(header, Header{ - BasePath: Dir{ + Root: Dir{ Name: strings.TrimPrefix(strings.TrimSuffix(baseURL.Path, "/"), "/"), URL: baseURL.String(), }, @@ -256,7 +256,7 @@ func main() { log.Println("\tExclude:\t", *e) log.Println("\tRecursive:\t", *r) log.Println("\tEmpty:\t\t", *emp) - log.Println("\tConvert links:\t\t", *l) + log.Println("\tConvert links:\t", *l) log.Println("\tSource:\t\t", src) log.Println("\tDestination:\t", dest) log.Println("\tBase URL:\t", *b) From a4fc229fd6a88428c8e1a21f8a5999101ca7ec24 Mon Sep 17 00:00:00 2001 From: Luca Date: Sat, 21 May 2022 12:31:17 +0200 Subject: [PATCH 8/8] golf some bytes on the css --- header.gohtml | 2 +- line.gohtml | 8 +++- style.css | 110 +++++++++++++++++++++++++++----------------------- 3 files changed, 67 insertions(+), 53 deletions(-) diff --git a/header.gohtml b/header.gohtml index 54f7b36..9ff154d 100644 --- a/header.gohtml +++ b/header.gohtml @@ -11,4 +11,4 @@ {{ if not (eq (len .Root.Name) 0) }}/{{ .Root.Name }}{{ end }}/{{ range $i,$a := .Parts }}{{ .Name }}/{{ end }}
-
+
diff --git a/line.gohtml b/line.gohtml index 474e12f..9bd758e 100644 --- a/line.gohtml +++ b/line.gohtml @@ -1,5 +1,9 @@ {{ if and .IsDir (eq .Name "..") }} - {{ .Name }}

{{ .Date.Format "02 Jan 06 15:04 MST" }}

{{ .Size }}

+ {{ .Name }} +

{{ .Date.Format "02 Jan 06 15:04 MST" }}

+

{{ .Size }}

{{ else }} - {{ .Name }}

{{ .Date.Format "02 Jan 06 15:04 MST" }}

{{ .Size }}

+ {{ .Name }} +

{{ .Date.Format "02 Jan 06 15:04 MST" }}

+

{{ .Size }}

{{ end }} diff --git a/style.css b/style.css index 90312ee..bee64e3 100644 --- a/style.css +++ b/style.css @@ -1,56 +1,66 @@ /* NOTE: this stylesheet is compressed and directly embedded into the go code */ :root { - --b: #fbf1c7; - --f: #282828; - --d: #af3a03; + --b: #fbf1c7; + --f: #282828; + --d: #af3a03; +} + +* { + color: var(--f); + background: var(--b); +} + +body { + font-size: 16px; + font-family: monospace; + margin: 0; + padding: 1.5rem; + line-height: 1.8; +} + +a { + word-wrap: break-word; + min-width: 0; + white-space: pre-wrap; + text-underline-position: under; +} + +.d { + color: var(--d); +} + +.g { + display: grid; + width: 100%; + grid-template-columns: 7fr 3fr 2fr; +} + +.g * { + margin: 0.5rem; +} + +.g *:nth-child(3n + 3) { + text-align: right; +} + +@media (prefers-color-scheme: dark) { + :root { + --b: #282828; + --f: #fbf1c7; + --d: #fe8019; } - - @media (prefers-color-scheme: dark) { - :root { - --b: #282828; - --f: #fbf1c7; - --d: #fe8019; - } +} + +@media (max-width: 880px) { + .g { + grid-template-columns: 7fr 3fr; } - - * { - color: var(--f); - background: var(--b); + + .g * { + margin: 1rem; } - body { - font-size: 16px; - font-family: monospace; - margin: 0; - padding: 1.5rem; - line-height: 1.8; + + .g *:nth-child(3n + 2) { + display: none; } - a { - word-wrap: break-word; - min-width: 0; - white-space: pre-wrap; - text-underline-position: under; - } - .d { - color: var(--d); - } - div { - display: grid; - align-items: center; - width: 100%; - grid-template-columns: 7fr 3fr 2fr; - } - p { - min-width: 0; - margin: 0.6rem; - } - @media (max-width:880px) { - div{ - grid-template-columns: 7fr 3fr; - } - .t{ - display: none; - } - p{ - margin: 1.8rem; - } - } \ No newline at end of file +}