diff --git a/main.go b/main.go index 6d90470..1c08906 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "net/http" "net/url" "os" + "path/filepath" "sort" "strconv" "strings" @@ -300,6 +301,17 @@ func main() { registerExtension(router, "svg", "image/svg+xml") registerExtension(router, "png", "image/png") + staticDir := os.Getenv("CHARTSRV_STATICDIR") + if staticDir == "" { + staticDir = "static" + } + + router.Get("/*", func(w http.ResponseWriter, r *http.Request) { + workDir, _ := os.Getwd() + fs := http.FileServer(http.Dir(filepath.Join(workDir, staticDir))) + fs.ServeHTTP(w, r) + }) + addr := ":8142" if len(os.Args) > 2 { addr = os.Args[2] diff --git a/static/base.css b/static/base.css new file mode 100644 index 0000000..b964f4d --- /dev/null +++ b/static/base.css @@ -0,0 +1,540 @@ +* { + box-sizing: border-box; } + +html { + line-height: 1.15; + -webkit-text-size-adjust: 100%; } + +body { + margin: 0; } + +main { + display: block; } + +h1 { + font-size: 2em; + margin: 0.67em 0; } + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; } + +pre { + font-family: monospace, monospace; + font-size: 1em; } + +a { + background-color: transparent; } + +abbr[title] { + border-bottom: none; + text-decoration: underline; + text-decoration: underline dotted; } + +b, +strong { + font-weight: bolder; } + +code, +kbd, +samp { + font-family: monospace, monospace; + font-size: 1em; } + +small { + font-size: 80%; } + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; } + +sub { + bottom: -0.25em; } + +sup { + top: -0.5em; } + +img { + border-style: none; } + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0; } + +button, +input { + overflow: visible; } + +button, +select { + text-transform: none; } + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; } + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; } + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; } + +fieldset { + padding: 0.35em 0.75em 0.625em; } + +legend { + box-sizing: border-box; + color: inherit; + display: table; + max-width: 100%; + padding: 0; + white-space: normal; } + +progress { + vertical-align: baseline; } + +textarea { + overflow: auto; } + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; + padding: 0; } + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; } + +[type="search"] { + -webkit-appearance: textfield; + outline-offset: -2px; } + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; } + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit; } + +details { + display: block; } + +summary { + display: list-item; } + +template { + display: none; } + +[hidden] { + display: none; } + +@media (prefers-color-scheme: dark) { + html { + background: #212529; + color: #fff; } } + +body { + font-family: sans-serif; + padding-bottom: 1rem; } + +main, .main { + max-width: 1140px; + margin: 0 auto; } + +table.grid { + width: 100%; + border-collapse: collapse; + margin: 0 -1rem; } + table.grid td { + vertical-align: top; + padding: 0 1rem; + width: 8.3333333333%; } + table.grid td[colspan="1"] { + width: 8.3333333333%; } + table.grid td[colspan="2"] { + width: 16.6666666667%; } + table.grid td[colspan="3"] { + width: 25%; } + table.grid td[colspan="4"] { + width: 33.3333333333%; } + table.grid td[colspan="5"] { + width: 41.6666666667%; } + table.grid td[colspan="6"] { + width: 50%; } + table.grid td[colspan="7"] { + width: 58.3333333333%; } + table.grid td[colspan="8"] { + width: 66.6666666667%; } + table.grid td[colspan="9"] { + width: 75%; } + table.grid td[colspan="10"] { + width: 83.3333333333%; } + table.grid td[colspan="11"] { + width: 91.6666666667%; } + table.grid td[colspan="12"] { + width: 100%; } + @supports (display: flex) { + table.grid { + display: flex; } + table.grid tbody { + display: flex; + flex-grow: 1; + flex-direction: column; } + table.grid tr { + display: flex; } + table.grid td { + display: block; } } + +.dl, article dl { + text-align: left; + margin-left: 0; } + +.dt, article dt { + font-weight: normal; + padding: 0; } + +.blockquote, article blockquote { + margin-left: -1rem; + margin-left: calc(-4px - 1rem); + padding-left: 1rem; + border-left: solid 4px #ced4da; } + @media (prefers-color-scheme: dark) { + .blockquote, article blockquote { + border-left: solid 4px #6c757d; } } +.figure, article figure { + margin: 0; } + .figure img, article figure img { + display: block; + max-width: 80%; + margin: 0 auto; } + .figure figcaption, article figure figcaption { + display: block; + text-align: center; + margin: 0 auto; + font-size: 0.9rem; + max-width: 70%; } + +.aside, article aside { + float: right; + max-width: 40%; + padding-left: 1rem; + margin-left: 1rem; + border-left: solid 4px #ced4da; } + @media (prefers-color-scheme: dark) { + .aside, article aside { + border-left: solid 4px #6c757d; } } +.pre, article pre { + background: #e9ecef; + margin: 0 -1rem; + padding: 1rem; } + @media (prefers-color-scheme: dark) { + .pre, article pre { + background: #131618; } } +.table, article table { + width: 100%; + border-collapse: collapse; } + .table th, article table th { + text-align: left; + border-bottom: solid 1px #131618; } + @media (prefers-color-scheme: dark) { + .table th, article table th { + border-bottom: solid 1px #fff; } } +a, .link, .tabs h1 a, .tabs h2 a, .tabs h3 a, .tabs h4 a, .tabs h5 a { + color: #007bff; } + a:hover, .link:hover, .tabs h1 a:hover, .tabs h2 a:hover, .tabs h3 a:hover, .tabs h4 a:hover, .tabs h5 a:hover { + text-decoration: none; } + a:active, .link:active, .tabs h1 a:active, .tabs h2 a:active, .tabs h3 a:active, .tabs h4 a:active, .tabs h5 a:active { + color: #0062cc; } + a:visited, .link:visited, .tabs h1 a:visited, .tabs h2 a:visited, .tabs h3 a:visited, .tabs h4 a:visited, .tabs h5 a:visited { + color: #004a99; } + @media (prefers-color-scheme: dark) { + a, .link, .tabs h1 a, .tabs h2 a, .tabs h3 a, .tabs h4 a, .tabs h5 a { + color: #3395ff; } + a:active, .link:active, .tabs h1 a:active, .tabs h2 a:active, .tabs h3 a:active, .tabs h4 a:active, .tabs h5 a:active { + color: #006fe6; } + a:visited, .link:visited, .tabs h1 a:visited, .tabs h2 a:visited, .tabs h3 a:visited, .tabs h4 a:visited, .tabs h5 a:visited { + color: #006fe6; } } +h1 small { + font-size: 1.2rem; } + +del { + color: inherit; } + +hr { + border: #ced4da solid 1px; } + @media (prefers-color-scheme: dark) { + hr { + border: #6c757d solid 1px; } } +.align-center { + text-align: center; } + +.align-left { + text-align: left; } + +.align-right { + text-align: right; } + +.block { + display: block !important; } + +.inline { + display: inline !important; } + +.float-left { + float: left; } + +.float-right { + float: right; } + +.text-info { + color: #17a2b8; } + +.text-success { + color: #28a745; } + +.text-danger { + color: #dc3545; } + +.text-muted, form .help, input[disabled] + label { + color: #343a40; } + @media (prefers-color-scheme: dark) { + .text-muted, form .help, input[disabled] + label { + color: #adb5bd; } } +.alert { + padding: 0.5rem; + border: 1px solid transparent; + margin-bottom: 1rem; } + +.alert-danger { + background: #f8d7da; + color: #842029; + border-color: #f5c6cb; } + +.btn, button { + display: inline-block; + padding: .1rem .75rem; + background: #e9ecef; + border: #343a40 1px solid; + font-size: 0.9rem; + font-weight: 400; + line-height: 1.5; + cursor: pointer; + color: #131618; + border-radius: 0; + transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; } + .btn:hover, button:hover { + text-decoration: none; + background: #f8f9fa; + color: #131618; } + @media (prefers-color-scheme: dark) { + .btn, button { + background: #212529; + color: #fff; + border: #495057 1px solid; } + .btn:hover, button:hover { + background: #131618; + color: #fff; } } +.btn-primary { + border: #001933 1px solid; + background: #007bff; + color: #fff; } + .btn-primary:hover { + background: #0069d9; + color: #fff; } + @media (prefers-color-scheme: dark) { + .btn-primary { + background: #0062cc; + color: #fff; + border: #001933 1px solid; } + .btn-primary:hover { + background: #0069d9; + color: #fff; } } +a.btn { + text-decoration: none; + color: #131618; } + @media (prefers-color-scheme: dark) { + a.btn { + color: #fff; } } +a.btn-primary { + color: #fff; } + @media (prefers-color-scheme: dark) { + a.btn-primary { + color: #fff; } } +.btn.block, button.block { + margin-bottom: 0.5rem; } + +.form-field, .form-checkbox { + margin-top: 1rem; } + +label { + display: block; } + +input, textarea, select { + display: block; + width: 100%; + border: 1px solid #888; + padding: .375rem; + font-size: 1rem; + line-height: 1.5; + background-color: #fff; + background-clip: padding-box; + transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; + border-radius: 0; } + input:focus, textarea:focus, select:focus { + outline: 0; + border-color: #80bdff; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + @media (prefers-color-scheme: dark) { + input, textarea, select { + background: #131618; + color: #fff; + border-color: #6c757d; } + input:active, input:focus, textarea:active, textarea:focus, select:active, select:focus { + background: #212529; + color: #fff; } + input[disabled], input[readonly], textarea[disabled], textarea[readonly], select[disabled], select[readonly] { + background: #212529; + color: #ced4da; } } +.has-error input, .has-error textarea, .has-error select { + border-color: #dc3545; } + +.has-error .error { + color: #dc3545; } + +tr:first-child td .form-field:first-child { + margin-top: 0; } + +button { + display: block; } + +button.block { + width: 100%; } + +.form-checkbox input[type="checkbox"], +.form-checkbox input[type="radio"] { + float: left; + width: 1em; + height: 1em; + margin-top: 0.1rem; + margin-right: 0.25rem; + vertical-align: top; } + +.form-checkbox label { + display: inline-block; } + +.form-checkbox.inline { + display: inline-block !important; + margin-right: 1rem; } + +fieldset { + border: none; + padding: 0; + margin-top: 1rem; } + +.inset, .infobox { + background: #f8f9fa; + padding: 1rem; } + @media (prefers-color-scheme: dark) { + .inset, .infobox { + background: #131618; } } +.infobox header { + margin-bottom: 1rem; } + +.infobox :not(header) { + margin: 0; } + +nav, .nav { + max-width: 1140px; + margin: 1rem auto; } + nav ul, .nav ul { + display: inline; + margin: 0 -0.5rem; + padding-left: 0; } + nav li, .nav li { + display: inline; + margin: 0 0.5rem; } + nav a, nav a:visited, .nav a, .nav a:visited { + color: #131618; } + @media (prefers-color-scheme: dark) { + nav a, nav a:visited, .nav a, .nav a:visited { + color: #fff; } } + nav .active a, nav .active a:visited, .nav .active a, .nav .active a:visited { + color: #007bff; } + @media (prefers-color-scheme: dark) { + nav .active a, nav .active a:visited, .nav .active a, .nav .active a:visited { + color: #3395ff; } } + nav .brand a, .nav .brand a { + text-decoration: none; } + nav .right, .nav .right { + float: right; } + +.tabs:not(.tabs-aside) { + border-bottom: solid 3px #ced4da; } + @media (prefers-color-scheme: dark) { + .tabs:not(.tabs-aside) { + border-bottom: solid 3px #131618; } } +.tabs nav { + margin: 0 auto; + position: relative; } + +.tabs h1, .tabs h2, .tabs h3, .tabs h4, .tabs h5 { + display: inline; + margin-right: 1rem; + font-weight: normal; } + +.tabs ul { + display: inline; + margin: 0 -0.5rem; + padding-left: 0; + position: absolute; + bottom: 0; } + +.tabs li { + display: inline; + margin: 0; } + .tabs li a { + padding: 0 1rem; } + .tabs li.active, .tabs li:hover { + background: #ced4da; } + @media (prefers-color-scheme: dark) { + .tabs li.active, .tabs li:hover { + background: #131618; } } + .tabs li.active a, .tabs li:hover a { + color: #131618; } + @media (prefers-color-scheme: dark) { + .tabs li.active a, .tabs li:hover a { + color: #fff; } } +.tabs a { + text-decoration: none; } + +.tabs aside { + background: #ced4da; + padding: 0.2rem 0; } + @media (prefers-color-scheme: dark) { + .tabs aside { + background: #131618; } } + .tabs aside p { + margin: 0 auto; + max-width: 1140px; } + +header + main { + margin-top: 1rem; } diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..e1e6bcf --- /dev/null +++ b/static/index.html @@ -0,0 +1,252 @@ + + + +