diff --git a/docs/index.md b/docs/README.md
similarity index 100%
rename from docs/index.md
rename to docs/README.md
diff --git a/docs/docs.go b/docs/docs.go
index b83996b..7cf477e 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -3,126 +3,162 @@ package docs
import (
"bytes"
"embed"
- "fmt"
+ "html/template"
"io"
+ "log"
"net/http"
"strings"
- "github.com/Depado/bfchroma/v2"
- "github.com/alecthomas/chroma/v2/formatters/html"
- bf "github.com/russross/blackfriday/v2"
+ toc "github.com/abhinav/goldmark-toc"
+ chromahtml "github.com/alecthomas/chroma/formatters/html"
+ "github.com/digineo/texd"
+ "github.com/microcosm-cc/bluemonday"
+ "github.com/yuin/goldmark"
+ highlighting "github.com/yuin/goldmark-highlighting"
+ "github.com/yuin/goldmark/extension"
+ "github.com/yuin/goldmark/parser"
+ "github.com/yuin/goldmark/renderer/html"
+ "github.com/yuin/goldmark/text"
"gopkg.in/yaml.v3"
)
-//go:embed docs.yml *.md **/*.md
+//go:embed *.md **/*.md
var sources embed.FS
+//go:embed docs.yml
+var config []byte
+
+//go:embed docs.html
+var rawLayout string
+var tplLayout = template.Must(template.New("layout").Parse(rawLayout))
+
type page struct {
Title string
Breadcrumbs []string
- Body string
+ TOC *toc.TOC
+ CSS []byte
+ Body []byte
File string
Route string
Children []*page
}
+type pageRoutes map[string]*page
-var root = func() page {
- structure, err := sources.Open("docs.yml")
- if err != nil {
- panic(err)
- }
- defer structure.Close()
-
+var root, routes = func() (page, pageRoutes) {
var menu page
- dec := yaml.NewDecoder(structure)
+ dec := yaml.NewDecoder(bytes.NewReader(config))
dec.KnownFields(true)
if err := dec.Decode(&menu); err != nil {
panic(err)
}
- menu.init()
- return menu
+ r := make(pageRoutes)
+ menu.init(r)
+ return menu, r
}()
-func (pg *page) init(crumbs ...string) {
+func (pg *page) init(r pageRoutes, crumbs ...string) {
if pg.File != "" {
- if r := strings.TrimSuffix(pg.File, ".md"); r == "index" {
+ if r := strings.TrimSuffix(pg.File, ".md"); r == "README" {
pg.Route = ""
} else {
pg.Route = "/" + r
}
-
+ r[pg.Route] = pg
pg.parseFile()
}
if pg.Title != "" {
- pg.Breadcrumbs = append(pg.Breadcrumbs, pg.Title)
+ pg.Breadcrumbs = append([]string{pg.Title}, crumbs...)
}
for _, child := range pg.Children {
- child.init(pg.Breadcrumbs...)
+ child.init(r, pg.Breadcrumbs...)
}
}
+var sanitize = func() func(io.Reader) *bytes.Buffer {
+ p := bluemonday.UGCPolicy()
+ p.AllowAttrs("class").Globally()
+ return p.SanitizeReader
+}()
+
func (pg *page) parseFile() {
- body, err := sources.ReadFile(pg.File)
+ raw, err := sources.ReadFile(pg.File)
if err != nil {
panic(err)
}
- r := bfchroma.NewRenderer(
- bfchroma.WithoutAutodetect(),
- bfchroma.ChromaOptions(
- html.WithLineNumbers(true),
+ var css, body bytes.Buffer
+ md := goldmark.New(
+ goldmark.WithParserOptions(
+ parser.WithAutoHeadingID(),
+ ),
+ goldmark.WithRendererOptions(
+ html.WithUnsafe(),
+ ),
+ goldmark.WithExtensions(
+ extension.GFM,
+ highlighting.NewHighlighting(
+ highlighting.WithCSSWriter(&css),
+ highlighting.WithStyle("github"),
+ highlighting.WithFormatOptions(
+ chromahtml.WithLineNumbers(true),
+ chromahtml.WithClasses(true),
+ ),
+ ),
),
- bfchroma.Extend(bf.NewHTMLRenderer(bf.HTMLRendererParameters{
- Flags: bf.CommonHTMLFlags & ^bf.UseXHTML & ^bf.CompletePage,
- })),
- )
- parser := bf.New(
- bf.WithExtensions(bf.CommonExtensions),
- bf.WithRenderer(r),
)
- ast := parser.Parse(body)
- var buf bytes.Buffer
- var inH1 bool
-
- ast.Walk(func(node *bf.Node, entering bool) bf.WalkStatus {
- switch node.Type {
- case bf.Heading:
- inH1 = entering && node.HeadingData.Level == 1 && pg.Title == ""
- case bf.Text:
- if inH1 {
- pg.Title = string(node.Literal)
- }
- case bf.Link:
- if entering && bytes.HasPrefix(node.LinkData.Destination, []byte("./")) {
- node.LinkData.Destination = bytes.TrimSuffix(node.LinkData.Destination, []byte(".md"))
- }
+ doc := md.Parser().Parse(text.NewReader(raw))
+ tree, err := toc.Inspect(doc, raw)
+ if err != nil {
+ panic(err)
+ }
+ if pg.Title == "" {
+ if len(tree.Items) > 0 {
+ pg.Title = string(tree.Items[0].Title)
}
- return r.RenderNode(&buf, node, entering)
- })
-
- pg.Body = buf.String()
-}
-
-func (pg *page) Dump(w io.Writer) {
- fmt.Fprintf(w, "- %s (%s)\n", pg.Title, pg.Route)
- fmt.Fprintln(w, pg.Body)
- fmt.Fprintln(w)
-
- for _, c := range pg.Children {
- c.Dump(w)
}
+ if err := md.Renderer().Render(&body, raw, doc); err != nil {
+ panic(err)
+ }
+ pg.TOC = tree
+ pg.CSS = css.Bytes()
+ pg.Body = sanitize(&body).Bytes()
}
func Handler() http.Handler {
+ type pageVars struct {
+ Version string
+ Title string
+ CSS template.CSS
+ Content template.HTML
+ }
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "text/plain; charset=utf-8")
- w.Header().Set("X-Content-Type-Options", "nosniff")
- w.WriteHeader(http.StatusOK)
+ pg := routes[r.URL.Path]
+ if pg == nil {
+ http.NotFound(w, r)
+ return
+ }
- fmt.Fprintf(w, "%#v\n\n", r.URL)
+ var buf bytes.Buffer
+ err := tplLayout.Execute(&buf, &pageVars{
+ Version: texd.Version(),
+ Title: strings.Join(pg.Breadcrumbs, " ยท "),
+ CSS: template.CSS(pg.CSS),
+ Content: template.HTML(pg.Body),
+ })
+
+ if err != nil {
+ log.Println(err)
+ code := http.StatusInternalServerError
+ http.Error(w, http.StatusText(code), code)
+ return
+ }
- root.Dump(w)
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ w.Header().Set("X-Content-Type-Options", "nosniff")
+ w.WriteHeader(http.StatusOK)
+ _, _ = buf.WriteTo(w)
})
}
diff --git a/docs/docs.html b/docs/docs.html
new file mode 100644
index 0000000..d864afc
--- /dev/null
+++ b/docs/docs.html
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+ {{ .Title }}
+
+
+
+
+
+
+
+
+
+
+ {{ .Content }}
+
+
+
+
diff --git a/docs/docs.yml b/docs/docs.yml
index 34ab7c8..616fd06 100644
--- a/docs/docs.yml
+++ b/docs/docs.yml
@@ -1,5 +1,5 @@
---
-file: index.md
+file: README.md
children:
- file: operation-modes.md
- file: cli-options.md
diff --git a/go.mod b/go.mod
index f52c3a2..b665d46 100644
--- a/go.mod
+++ b/go.mod
@@ -3,22 +3,24 @@ module github.com/digineo/texd
go 1.18
require (
- github.com/Depado/bfchroma/v2 v2.0.0
- github.com/alecthomas/chroma/v2 v2.2.0
+ github.com/abhinav/goldmark-toc v0.2.1
+ github.com/alecthomas/chroma v0.10.0
github.com/bahlo/generic-list-go v0.2.0
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d
github.com/docker/docker v20.10.17+incompatible
github.com/docker/go-units v0.4.0
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
+ github.com/microcosm-cc/bluemonday v1.0.19
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
github.com/opencontainers/image-spec v1.0.2
github.com/prometheus/client_golang v1.12.2
- github.com/russross/blackfriday/v2 v2.1.0
github.com/spf13/afero v1.8.2
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.0
github.com/thediveo/enumflag v0.10.1
+ github.com/yuin/goldmark v1.4.13
+ github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
go.uber.org/zap v1.21.0
gopkg.in/yaml.v3 v3.0.1
)
@@ -26,6 +28,7 @@ require (
require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
+ github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@@ -36,6 +39,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.6 // indirect
+ github.com/gorilla/css v1.0.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/morikuni/aec v1.0.0 // indirect
diff --git a/go.sum b/go.sum
index 40dcd76..0e552d7 100644
--- a/go.sum
+++ b/go.sum
@@ -40,20 +40,21 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/Depado/bfchroma/v2 v2.0.0 h1:IRpN9BPkNwEpR6w1ectIcNWOuhDSLx+8f1pn83fzxx8=
-github.com/Depado/bfchroma/v2 v2.0.0/go.mod h1:wFwW/Pw8Tnd0irzgO9Zxtxgzp3aPS8qBWlyadxujxmw=
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY=
-github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
-github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae h1:zzGwJfFlFGD94CyyYwCJeSuD32Gj9GTaSi5y9hoVzdY=
+github.com/abhinav/goldmark-toc v0.2.1 h1:QJsKKGbdVeCWYMB11hSkNuZLuIzls7Y4KBZfwTkBB90=
+github.com/abhinav/goldmark-toc v0.2.1/go.mod h1:aq1IZ9qN85uFYpowec98iJrFkEHYT4oeFD1SC0qd8d0=
+github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
+github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
+github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
@@ -195,6 +196,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
+github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
@@ -239,6 +242,8 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c=
+github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
@@ -310,7 +315,6 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -352,7 +356,13 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.3/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
+github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg=
+github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
diff --git a/service/assets/texd.js b/service/assets/texd.js
index e683444..8bad096 100644
--- a/service/assets/texd.js
+++ b/service/assets/texd.js
@@ -65,7 +65,7 @@ const app = Vue.createApp({
},
beforeMount() {
- setInterval(this.fetchStatus, 1000)
+ setInterval(this.fetchStatus, 5000)
this.fetchStatus()
},
diff --git a/service/ui.go b/service/ui.go
index c743828..82b7b08 100644
--- a/service/ui.go
+++ b/service/ui.go
@@ -3,6 +3,7 @@ package service
import (
"bytes"
"embed"
+ "io"
"net/http"
)
@@ -13,12 +14,11 @@ var uiHTML []byte
var assets embed.FS
func HandleUI(res http.ResponseWriter, req *http.Request) {
- buf := bytes.NewBuffer(uiHTML)
-
res.Header().Set("Content-Type", mimeTypeHTML)
res.Header().Set("X-Content-Type-Options", "nosniff")
res.WriteHeader(http.StatusOK)
- _, _ = buf.WriteTo(res)
+
+ _, _ = io.Copy(res, bytes.NewReader(uiHTML))
}
func HandleAssets() http.Handler {
diff --git a/service/ui.html b/service/ui.html
index 507a4ee..fa32787 100644
--- a/service/ui.html
+++ b/service/ui.html
@@ -13,9 +13,18 @@
-