oops forgot to commit for a whole day again. i spent 10 hours playing with css though, so no loss.
sophuwu sophie@skisiel.com
Mon, 10 Feb 2025 01:10:54 +0100
5 files changed,
281 insertions(+),
93 deletions(-)
M
README.md
→
README.md
@@ -12,28 +12,36 @@ - Filter by page function or section: 1=commands, 3=C/C++ Refs, 5,7=config/format, 8=sudo commands, etc.
- Able to correctly interpret and display incorrectly formatted man pages, to a degree. - Auto updates man pages when new packages are installed or removed using standard installation methods. -## Dependencies -Ubuntu/Debian dependency installation: `sudo apt-get install mandoc -y` +### Contents +- [Installation Using Apt](#installation-using-apt) +- [Compiling From Source](#compiling-from-source) +- [Using As Systemd Service](#using-as-systemd-service) +- [Accessing the Web Interface](#accessing-the-web-interface) +- [Example Usage](#example-usage) -Golang installation instructions at [go.dev](https://go.dev/doc/install). +# Installation Using Apt -lazy script to install go 1.23.1 updated 7-SEP-2024 +Simply run the following commands to add my repository and install the package. This will install the latest release and automatically update the server when new versions are released. +This will also make the server available as a systemd service, and start it automatically. You may still want to configure a user for the service as some manuals may be in user home directories. +This isn't common on most systems, so the default configuration should work out of the box in most cases. ```bash -#!/bin/bash -# delete incompatible versions -[ -d /usr/local/go ] && sudo rm -rf '/usr/local/go' ; -# downlaod compatible version -which wget || sudo apt-get install wget -y && wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz ; -# install into system -[ -f go1.23.1.linux-amd64.tar.gz ] && sudo tar -C /usr/local -xzf go1.23.1.linux-amd64.tar.gz ; -# add to bin -[ -f /usr/local/go/bin/go ] && sudo ln -s /usr/local/go/bin/go /bin/go ; -# hope it works -go version ; +curl https://cdn.sophuwu.site/deb/addrepo.sh | sudo sh +sudo apt update +sudo apt install manhttpd ``` +# Compiling From Source + +## Dependencies +If you are not installing from apt, you will need to install the mandoc package. +* Ubuntu/Debian dependency installation: `sudo apt-get install mandoc -y` + +If you wish to compile from source, you will need Go installed.\ +* Golang installation instructions at [go.dev](https://go.dev/doc/install). + + ## Compiling The Binary ```sh@@ -47,7 +55,7 @@ # install the binary into the system
sudo install ./build/manhttpd /usr/local/bin/manhttpd ``` -## Using As Systemd Service: +# Using As Systemd Service: The provided service file should work on most systems, but you may need to edit it to fit your needs.\ It will open a http server on port 8082 available through all network interfaces.\@@ -94,7 +102,7 @@ sudo systemctl reload-or-restart manhttpd.service
sudo systemctl status manhttpd.service ``` -## Accessing the Web Interface +# Accessing the Web Interface Open your web browser and navigate to `http://localhost:8082` if you are running the server locally or the remote server's IP address or hostname.\ To search with regex, you can use the search bar at the top of the page with `-r` at the beginning of the search term.\@@ -124,6 +132,7 @@
## Help and Support I don't know how this git pull thing works. I will try if I see any issues. I've never collaborated on code before. If you have any suggestions, or questions about anything I've written, I would be happy to hear your thoughts.\ -contant info: [skisiel.com](https://skisiel.com) or [sophuwu.site](https://sophuwu.site) - +contant info: +* discord: [@sophuwu](https://discord.com/users/sophuwu) +* email: [sophie@sophuwu.site](mailto:sophie@sophuwu.site)
M
dark_theme.css
→
dark_theme.css
@@ -1,7 +1,75 @@
-@import url("https://fonts.googleapis.com/css2?family=Comfortaa:wght@300..700&family=Victor+Mono:ital,wght@0,100..700;1,100..700&display=swap"); + + @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap'); + +:root{ + /* + --theme: <integer 0-5>; + * sets the theme color, 4 is the default. 0 white, 1-3 add yellow filter. 4 is dark, 5 changes header+link colors. + --contrast: <bool 0/1>; + * sets high contrast, 0 is the default. + --font: string; + * sets the font, 'JetBrains Mono' will be used from google fonts if not set. + --font-size: <float><unit>; + * sets the font size, 18px is the default. + */ + + --col: var(--theme, 4); + --cont: calc(70% + (30% * var(--contrast,0))); + --dark-bg: #262833; + + --sepia: color-mix(in srgb, white, yellow calc( 10% * (var(--col) + 2) * (min(1,var(--col))))); + --bg-color: color-mix(in srgb, var(--sepia), var(--dark-bg) calc(0% + (100% * (max(0,calc(var(--col) - 3)))))); + --st-color: rgb(from var(--bg-color) calc(255 - r) calc(255 - g) calc(255 - b)); + --fg-color: color-mix(in srgb, var(--bg-color), var(--st-color) var(--cont)); + --it-color: color-mix(in srgb, var(--bg-color), var(--fg-color) var(--cont)); + + --hl-color: color-mix(in srgb, #8d53a6, #ff4500 calc(0% + (100% * (max(0,calc(var(--col) - 4)))))); + --rf-color: color-mix(in srgb, #ff7597, #4c86ce calc(0% + (100% * (max(0,calc(var(--col) - 4)))))); + --rf-hover: color-mix(in srgb, var(--rf-color), var(--st-color) 40%); + + --dimmer: rgba(0,0,0,69); + + + font-variant-ligatures: none!important; + --font-family: var(--font, 'JetBrains Mono'); + + --x-font-size: 18px; + @media (min-width: 2000px) { + --x-font-size: 22px; + } + @media (max-width: 2000px) and (min-width: 1500px) { + --x-font-size: 20px; + } + @media (max-width: 1500px) and (min-width: 1200px) { + --x-font-size: 18px; + } + @media (max-width: 1200px) and (min-width: 800px) { + --x-font-size: 16px; + } + @media (max-width: 800px) and (min-width: 600px) { + --x-font-size: 14px; + } + @media (max-width: 600px) and (min-width: 400px) { + --x-font-size: 12px; + } + @media (max-width: 400px) { + --x-font-size: 10px; + } + font-size: var(--font-size, var(--x-font-size)); + +} *{ - color: #c7c7c7; - font-family: 'Comfortaa', helvetica, sans-serif; + color: var(--fg-color); + font-family: var(--font-family), monospace; + line-height: 1em; + padding: 0; +} +body, html { + margin: 0; + padding: 0; + height: 100%; + width: 100%; + background-color: var(--bg-color); } table.head, table.foot { width: inherit;@@ -26,39 +94,22 @@ font-weight: bold;
} code.Nm, .Fl, .Cm, .Ic, code.In, .Fd, .Fn, .Cd { font-weight: bold; - font-family: 'Victor Mono', monospace !important; } -code, pre { - font-family: 'Victor Mono', monospace !important; -} -i, em { - color: #7f7f7f; +i, em, hr { + color: var(--it-color); } dt, b, strong { - color: #f0f0f0; + color: var(--st-color); } hr { - color: #363636; margin: 5px 0; } form{ padding:0; margin:0; } -.flexy { - display: flex; - flex-direction: row; - justify-content: center; -} -body, html { - margin: 0; - padding: 0; - height: 100%; - background-color: #1f1e23; - color: #c7c7c7; -} h1, h2, h3, h4, h5, h6 { - color: #8d53a6; + color: var(--hl-color); padding: 0; margin: 0; }@@ -71,44 +122,81 @@ margin: 2px 10px;
padding: 5px 0; } a { - color: #ff7597; - text-decoration: underline; + color: var(--rf-color); + text-decoration: none; + font-weight: bold; } a:hover { - color: #db335d; + color: var(--rf-hover); text-decoration: underline; } -input, textarea { - background-color: #343434; - border: 1px solid #c7c7c7; - padding: 4px; - border-radius: 5px; - margin: 2px; +body{ + display: flex; + flex-direction: column; + align-content: space-between; + align-items: center; } - -input[type="text"], textarea { - height: inherit; - width: 300px; +header { + width: calc( 100% - 1em ); + padding: 0.5lh 0; + margin: 0; + background-color: var(--bg-color); + border-bottom: var(--fg-color) 1px solid; + display: flex; + flex-direction: row; + align-content: center; + justify-content: center; + + form, div{ + display: contents; + * { + background-color: inherit; + border: 1px var(--fg-color); + font-size: 1rem; + margin: 0!important; + padding: 0.1em 1ch; + border-style: solid none solid solid ; + } + :first-child { + border-radius: 1ch 0 0 1ch; + border-style: solid none solid solid; + margin-left: auto!important;; + } + :last-child { + border-radius: 0 1ch 1ch 0; + border-style: solid; + margin-right: auto!important;; + } + input:not([type="submit"]){ + font-size: 0.9em; + width: 100%; + max-width: 50ch; + } + h3, input[type="submit"], button { + min-width: fit-content; + } + input[type="submit"], button { + font-weight: bold; + cursor: pointer; + } + :is(input[type="submit"], button):hover { + background: var(--dimmer); + } + /*h3{*/ + /* border: none 0;*/ + /* font-size: 1.2rem;*/ + /* padding: 0.1em 1ch;*/ + /*}*/ + } + * { + height: inherit; + } } -input[type="submit"] { - background-color: #343434; - font-weight: bold; - cursor: pointer; -} -.flexy{ - position: fixed; - z-index: 5; - width: 100%; - padding: 3px 0; - margin: 0; - top: 0; - background-color: #1f1e23; - border-bottom: #7f7f7f 1px solid; -} .content{ - width: calc(100% - 10px); - transform: translateY(40px); + width: calc( 100% - 1em ); margin: 0; + overflow-y: scroll; padding: 0; -}+} +
M
index.html
→
index.html
@@ -3,16 +3,32 @@ <html lang="en">
<head> <meta charset="utf-8"> <title>{{ title }}@{{ hostname }}</title> - <link rel="stylesheet" type="text/css" href="style.css"> + <!--link rel="stylesheet" type="text/css" href="style.css"--> + <style id="styleCss"> + {{ cssContent }} + </style> + <script> + {{ jsContent }} + </script> </head> <body> - <div class="flexy"> - <h3 style="margin: 2px;">Man Pages:</h3> - <FORM method="post"> - <INPUT TYPE="text" name="q" autocomplete="off"> - <INPUT TYPE="submit" VALUE="Search"> - </FORM> - </div> + + <header> + <div> + <h3>ManWeb</h3> + <h3>@{{ hostname }}</h3> + </div> + <form method="post"> + <h3>Find:</h3> + <input type="text" name="q" autocomplete="off"> + <input type="submit" value="Search"> + </form> + <div> + <button id="helpButt">Help</button> + <button id="SetButt">Settings</button> + </div> + </header> + <div class="content"> {{ content }} </div>
M
main.go
→
main.go
@@ -16,7 +16,10 @@ //go:embed index.html
var index string //go:embed dark_theme.css -var css []byte +var css string + +//go:embed scripts.js +var scripts string //go:embed favicon.ico var favicon []byte@@ -30,6 +33,9 @@ }
func GetCFG() { CFG.Hostname = os.Getenv("HOSTNAME") + if CFG.Hostname == "" { + CFG.Hostname, _ = os.Hostname() + } index = strings.ReplaceAll(index, "{{ hostname }}", CFG.Hostname) b, e := exec.Command("which", "mandoc").Output() if e != nil || len(b) == 0 {@@ -51,16 +57,21 @@ CFG.Addr = "0.0.0.0"
} } -func CssHandle(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/css; charset=utf-8") - w.Header().Set("Content-Length", fmt.Sprint(len(css))) - w.WriteHeader(http.StatusOK) - w.Write(css) +// func CssHandle(w http.ResponseWriter, r *http.Request) { +// w.Header().Set("Content-Type", "text/css; charset=utf-8") +// w.Header().Set("Content-Length", fmt.Sprint(len(css))) +// w.WriteHeader(http.StatusOK) +// w.Write(css) +// } + +func init() { + index = strings.ReplaceAll(index, "{{ jsContent }}", scripts) + index = strings.ReplaceAll(index, "{{ cssContent }}", css) } func main() { GetCFG() - http.HandleFunc("/style.css", CssHandle) + // http.HandleFunc("/style.css", CssHandle) http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "image/x-icon") w.Header().Set("Content-Length", fmt.Sprint(len(favicon)))@@ -167,10 +178,10 @@ }
func indexHandler(w http.ResponseWriter, r *http.Request) { path := filepath.Base(r.URL.Path) path = strings.TrimSuffix(path, "/") - if path == "style.css" { - CssHandle(w, r) - return - } + // if path == "style.css" { + // CssHandle(w, r) + // return + // } if r.Method == "POST" { searchHandler(w, r)
A
scripts.js
@@ -0,0 +1,64 @@
+ /* + --theme: <integer 0-5>; + * sets the theme color, 4 is the default. 0 white, 1-3 add yellow filter. 4 is dark, 5 changes header+link colors. + --contrast: <bool 0/1>; + * sets high contrast, 0 is the default. + --font: string; + * sets the font, 'JetBrains Mono' will be used from google fonts if not set. + --font-size: <float><unit>; + * sets the font size, 18px is the default. + */ + +const CSSRx = /^(((\d+(\.\d+)?)(%|p[xtc]|r?em|ex|ch|v(min|max|w|h)|([cm]m)|in))|(((?<x>x{1,3}-)?(small|larg)e?((?<=\k<x>)r)?)|medium|normal))$/; + + +let funcmap = { + "--theme": function (value) { + let v = parseInt(value); + return (v < 0 || v > 5) + }, + "--contrast": function (value) { + let v = parseInt(value); + return (v < 0 || v > 1) + }, + "--font": function (value) { + return (value.length < 1) + }, + "--font-size": function (value) { + return !CSSRx.test(value) + } +}; + + +function SaveValue(key, value) { + let fn = funcmap[key] + if (typeof(fn) != "function"||fn(value)) { + return false; + } + localStorage.setItem(key, value); + return true; +} + +function DeleteValue(key) { + localStorage.removeItem(key); +} + +// var style = document.createElement('style'); +// style.innerText += document.styleSheets[0].cssRules[0].cssText; +// const styleCss = style.innerText; +// style.id = "styleCss"; +// document.head.appendChild(style); + +const style = document.getElementById("styleCss"); +const styleCss = style.innerText; + +function setStyle() { + let tmp = ":root{\n"; + let ar = ["--theme", "--contrast", "--font", "--font-size"]; + for (let key of ar) { + let value = localStorage.getItem(key); + if (value) tmp += `${key}:${value};` + "\n"; + } + tmp += "}"; + style.innerText = tmp + styleCss; +}