~ols/yeet

4c8c3b3f74ba0d2e59c8229601582b40617b04cf — honk 2 years ago bceed1f
bring in line with upstream
M .gitignore => .gitignore +1 -0
@@ 2,3 2,4 @@
memes
emus
honk
log

M activity.go => activity.go +20 -2
@@ 451,6 451,19 @@ func extractattrto(obj junk.Junk) string {
	return ""
}

func firstofmany(obj junk.Junk, key string) string {
	if val, _ := obj.GetString(key); val != "" {
		return val
	}
	if arr, _ := obj.GetArray(key); len(arr) > 0 {
		val, ok := arr[0].(string)
		if ok {
			return val
		}
	}
	return ""
}

func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
	depth := 0
	maxdepth := 10


@@ 476,7 489,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {

	xonkxonkfn = func(item junk.Junk, origin string, isUpdate bool) *Honk {
		id, _ := item.GetString("id")
		what, _ := item.GetString("type")
		what := firstofmany(item, "type")
		dt, ok := item.GetString("published")
		if !ok {
			dt = time.Now().Format(time.RFC3339)


@@ 504,6 517,11 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
			log.Printf("eradicating %s", xid)
			eradicatexonk(user.ID, xid)
			return nil
		case "Remove":
			xid, _ = item.GetString("object")
			targ, _ := obj.GetString("target")
			log.Printf("remove %s from %s", obj, targ)
			return nil
		case "Tombstone":
			xid, _ = item.GetString("id")
			if xid == "" {


@@ 1452,7 1470,7 @@ func junkuser(user *WhatAbout) junk.Junk {
		if ava := user.Options.Avatar; ava != "" {
			a["url"] = ava
		} else {
			a["url"] = fmt.Sprintf("https://%s/a?a=%s", serverName, url.QueryEscape(user.URL))
			a["url"] = fmt.Sprintf("https://https://ols.wtf/avatar?seed=%s", serverName, url.QueryEscape(user.URL))
		}
		j["icon"] = a
	} else {

M avatar.go => avatar.go +8 -0
@@ 21,6 21,7 @@ import (
	"crypto/sha512"
	"fmt"
	"image"
	"image/color"
	"image/png"
	"log"
	"net/http"


@@ 29,6 30,7 @@ import (
	"strings"

	"github.com/gorilla/mux"
	adorable "github.com/ipsn/go-adorable"
)

var avatarcolors = [4][4]byte{


@@ 67,6 69,12 @@ func loadAvatarColors() {
}

func genAvatar(name string) []byte {
	scheme := color.RGBA{R: 254, G: 246, B: 228, A: 255}
	img := adorable.PseudoRandomWithColor([]byte(name), scheme)
	return img
}

func oldgenAvatar(name string) []byte {
	h := sha512.New()
	h.Write([]byte(name))
	s := h.Sum(nil)

A build.sh => build.sh +5 -0
@@ 0,0 1,5 @@
#!/bin/sh

go build && \
pkill honk && \
sleep 2 ; date ; ./honk -datadir ../honkdata >> log 2>&1 &

M go.mod => go.mod +1 -0
@@ 5,6 5,7 @@ go 1.13
require (
	github.com/andybalholm/cascadia v1.2.0
	github.com/gorilla/mux v1.8.0
	github.com/ipsn/go-adorable v1.0.0
	github.com/mattn/go-runewidth v0.0.9
	golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
	golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect

M go.sum => go.sum +5 -0
@@ 2,6 2,10 @@ github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5
github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/ipsn/go-adorable v1.0.0 h1:bIyOri2h+p3hIlxcgcGRI9yt1wQQlCo3LysIA7LQqqo=
github.com/ipsn/go-adorable v1.0.0/go.mod h1:7QZ95Q2rEvA2kdteyy/QxWO1D84NG7CFGVi3faM/Bos=
github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08 h1:5MnxBC15uMxFv5FY/J/8vzyaBiArCOkMdFT9Jsw78iY=
github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/yuin/goldmark v1.3.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=


@@ 21,6 25,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190309122539-980fc434d28e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=

M hoot.go => hoot.go +38 -72
@@ 20,7 20,6 @@ import (
	"io"
	"log"
	"net/http"
	"os"
	"regexp"
	"strings"



@@ 31,78 30,10 @@ import (

var tweetsel = cascadia.MustCompile("p.tweet-text")
var linksel = cascadia.MustCompile("a.tweet-timestamp")
var replyingto = cascadia.MustCompile(".ReplyingToContextBelowAuthor")
var authorregex = regexp.MustCompile("twitter.com/([^/]+)")

var re_hoots = regexp.MustCompile(`hoot: ?https://\S+`)

func hootextractor(r io.Reader, url string, seen map[string]bool) string {
	root, err := html.Parse(r)
	if err != nil {
		log.Printf("error parsing hoot: %s", err)
		return url
	}
	divs := tweetsel.MatchAll(root)

	url = strings.Replace(url, "mobile.twitter.com", "twitter.com", -1)

	var wanted string
	wantmatch := authorregex.FindStringSubmatch(url)
	if len(wantmatch) == 2 {
		wanted = wantmatch[1]
	}
	var buf strings.Builder

	fmt.Fprintf(&buf, "%s\n", url)
	var htf htfilter.Filter
	htf.Imager = func(node *html.Node) string {
		return ""
	}
	for i, div := range divs {
		twp := div.Parent.Parent.Parent
		link := url
		alink := linksel.MatchFirst(twp)
		if alink == nil {
			if i != 0 {
				log.Printf("missing link")
				continue
			}
		} else {
			link = "https://twitter.com" + htfilter.GetAttr(alink, "href")
		}
		replto := replyingto.MatchFirst(twp)
		if replto != nil {
			continue
		}
		authormatch := authorregex.FindStringSubmatch(link)
		if len(authormatch) < 2 {
			log.Printf("no author?: %s", link)
			continue
		}
		author := authormatch[1]
		if wanted == "" {
			wanted = author
		}
		if author != wanted {
			continue
		}
		text := htf.NodeText(div)
		text = strings.Replace(text, "\n", " ", -1)
		text = strings.Replace(text, "pic.twitter.com", "https://pic.twitter.com", -1)

		if seen[text] {
			continue
		}

		fmt.Fprintf(&buf, "> @%s: %s\n", author, text)
		seen[text] = true
	}
	return buf.String()
}

func hooterize(noise string) string {
	seen := make(map[string]bool)

	hootfetcher := func(hoot string) string {
		url := hoot[5:]
		if url[0] == ' ' {


@@ 128,10 59,45 @@ func hooterize(noise string) string {
			log.Printf("error getting %s: %d", url, resp.StatusCode)
			return hoot
		}
		ld, _ := os.Create("lasthoot.html")
		r := io.TeeReader(resp.Body, ld)
		return hootextractor(r, url, seen)
		return hootextractor(resp.Body, url)
	}

	return re_hoots.ReplaceAllStringFunc(noise, hootfetcher)
}

func hootextractor(r io.Reader, url string) string {
        root, err := html.Parse(r)
        if err != nil {
                log.Printf("error parsing hoot: %s", err)
                return url
        }
        divs := tweetsel.MatchAll(root)

        url = strings.Replace(url, "mobile.twitter.com", "twitter.com", -1)

        var buf strings.Builder

        fmt.Fprintf(&buf, "🐦 %s\n", url)
        var htf htfilter.Filter
        htf.Imager = func(node *html.Node) string {
                return ""
        }
	twp := divs[0].Parent.Parent.Parent
	link := url
	alink := linksel.MatchFirst(twp)
	if alink == nil {
		link = "https://twitter.com" + htfilter.GetAttr(alink, "href")
	}
	authormatch := authorregex.FindStringSubmatch(link)
	if len(authormatch) < 2 {
		log.Printf("no author?: %s", link)
	}
	author := authormatch[1]
	text := htf.NodeText(divs[0])
	text = strings.Replace(text, "\n", " ", -1)
	text = strings.Replace(text, "pic.twitter.com", "https://pic.twitter.com", -1)

	fmt.Fprintf(&buf, "> @%s: %s", author, text)

        return buf.String()
}

M views/header.html => views/header.html +36 -60
@@ 1,71 1,47 @@
<!doctype html>
<html>
<html lang="en">
<head>
<title>honk</title>
<link href="/style.css{{ .StyleParam }}" rel="stylesheet">
{{ if .LocalStyleParam }}
<link href="/local.css{{ .LocalStyleParam }}" rel="stylesheet">
{{ end }}
<style>
{{ .UserStyle }}
</style>
<link href="/icon.png" rel="icon">
<meta name="theme-color" content="#305">
<meta name="viewport" content="width=device-width">
<meta charset="utf-8">
<title>yeet</title>
<link href="/style.css" rel="stylesheet">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<link rel="apple-touch-icon" sizes="180x180" href="https://ols.wtf/sunne/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="https://ols.wtf/sunne/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="https://ols.wtf/sunne/favicon-16x16.png">
<link rel="manifest" href="https://ols.wtf/sunne/site.webmanifest">
<link rel="mask-icon" href="https://ols.wtf/sunne/safari-pinned-tab.svg" color="#f582ae">
<link rel="shortcut icon" href="https://ols.wtf/sunne/favicon.ico">
<meta name="msapplication-TileColor" content="#104e8b">
<meta name="msapplication-config" content="https://ols.wtf/sunne/browserconfig.xml">
<meta name="theme-color" content="#F582AE">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css">
<meta name="viewport" content="width=device-width,initial-scale:1.0">
</head>
<body>
<header>
	<div class="mast"><img src="/logo.svg"><p>yeet</p></div>
	<nav>
		<a id="homelink" href="/"><i class="fas fa-home"></i></a>
{{ if .UserInfo }}
<details id="topmenu">
<summary>menu<span> {{ .UserInfo.Username }}</span></summary>
<ul>
<li><a id="homelink" href="/">home</a>
<li><a id="atmelink" href="/atme">@me</a>
<li><a id="firstlink" href="/first">first</a>
<li style="list-style-type:none; margin-left:-1em">
<details>
<summary>combos</summary>
<ul>
{{ range .Combos }}
<li><a class="combolink" href="/c/{{ . }}">{{ . }}</a>
<a id="atmelink" href="/atme"><i class="fas fa-at"></i></a>
<a href="/chatter"><i class="fas fa-inbox"></i></a>
<a id="savedlink" href="/saved"><i class="fas fa-save"></i></a>
<a href="/honkers"><i class="fas fa-user-friends"></i></a>
<a href="/hfcs"><i class="fas fa-filter"></i></a>
<a href="/c"><i class="fas fa-layer-group"></i></a>
{{ end }}
</ul>
</details>
<li><a href="/chatter">chatter</a>
<li><a href="/o">tags</a>
<li><a href="/events">events</a>
<li><a id="longagolink" href="/longago">long ago</a>
<li><a id="savedlink" href="/saved">saved</a>
<li><a href="/honkers">honkers</a>
<li><a href="/hfcs">filters</a>
<li><a href="/account">account</a>
<li style="list-style-type:none; margin-left:-1em">
<details>
<summary>more stuff</summary>
<ul>
<li><a href="/{{ .UserSep }}/{{ .UserInfo.Username }}">my honks</a>
<li><a href="/about">about</a>
<li><a href="/front">front</a>
<li><a href="/funzone">funzone</a>
<li><a href="/xzone">xzone</a>
</ul>
</details>
<li><a href="/help/honk.1.html">help</a>
<li>
<form action="/q" method="GET">
<input type="text" name="q" autocomplete=off size=10 placeholder="search">
</form>
</ul>
</details>
<p id="topspacer"></p>
{{ else }}
<span><a id="homelink" href="/">home</a></span>
<span><a href="/o">tags</a></span>
<span><a href="/events">events</a></span>
<span><a href="/about">about</a></span>
<a href="/o"><i class="fas fa-tags"></i></a>
<a href="/events"><i class="fas fa-calendar-week"></i></a>
<a href="/about"><i class="fas fa-info-circle"></i></a>
{{ if .ShowRSS }}
<span><a href="/rss">rss</a></span>
<a href="/rss"><i class="fas fa-rss"></i></a>
{{ end }}
<span><a href="/login">login</a></span>
{{ if .UserInfo }}
<a href="/{{ .UserSep }}/{{ .UserInfo.Username }}"><i class="fas fa-address-card"></i></a>
<a href="/account"><i class="fas fa-user-cog"></i></a>
{{ else }}
<a href="/login"><i class="fas fa-sign-in-alt"></i></a>
{{ end }}
	</nav>
</header>

M views/honk.html => views/honk.html +10 -22
@@ 10,7 10,7 @@
{{ else }}
<a href="{{ .Honker }}" rel=noreferrer>
{{ end }}
<img alt="" src="/a?a={{ .Honker}}">
<img class="avatar" alt="" src="/a?a={{ .Honker}}">
{{ if $bonkcsrf }} </a> {{ end }}
{{ if .Oonker }}
{{ if $bonkcsrf }}


@@ 18,7 18,7 @@
{{ else }}
<a href="{{ .Oonker }}" rel=noreferrer>
{{ end }}
<img alt="" src="/a?a={{ .Oonker}}">
<img class="avatar" alt="" src="/a?a={{ .Oonker}}">
{{ if $bonkcsrf }} </a> {{ end }}
{{ end }}
<p>


@@ 27,7 27,7 @@
{{ else }}
<a href="{{ .Honker }}" rel=noreferrer>{{ .Username }}</a>
{{ end }}
<span class="clip"><a href="{{ .URL }}" rel=noreferrer>{{ .What }}</a> {{ .Date.Local.Format "02 Jan 2006 15:04 -0700" }}</span>
<span class="clip"><a href="{{ .URL }}" rel=noreferrer>{{ .What }}</a> {{ .Date.Local.Format "02 Jan 2006 15:04" }}</span>
{{ if .Oonker }}
<br>
<span style="margin-left: 1em;" class="clip">


@@ 56,7 56,7 @@ in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
<p>{{ .HTPrecis }}
<p>{{ .HTML }}
{{ with .Time }}
<p>Time: {{ .StartTime.Local.Format "03:04PM EDT Mon Jan 02"}}
<p>Time: {{ .StartTime.Local.Format "02 Jan 2006 15:04" }}
{{ if .Duration }}<br>Duration: {{ .Duration }}{{ end }}
{{ end }}
{{ with .Place }}


@@ 97,20 97,14 @@ in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
<p>
{{ if .Honk.Public }}
{{ if .Honk.IsBonked }}
<button onclick="return unbonk(this, '{{ .Honk.XID }}');">unbonk</button>
<button onclick="return unbonk(this, '{{ .Honk.XID }}');">unboost</button>
{{ else }}
<button onclick="return bonk(this, '{{ .Honk.XID }}');">bonk</button>
<button onclick="return bonk(this, '{{ .Honk.XID }}');">boost</button>
{{ end }}
{{ else }}
<button disabled>nope</button>
{{ end }}
<button onclick="return showhonkform(this, '{{ .Honk.XID }}', '{{ .Honk.Handles }}');"><a href="/newhonk?rid={{ .Honk.XID }}">honk back</a></button>
<button onclick="return showhonkform(this, '{{ .Honk.XID }}', '{{ .Honk.Handles }}');"><a href="/newhonk?rid={{ .Honk.XID }}">reply</a></button>
<button onclick="return muteit(this, '{{ .Honk.Convoy }}');">mute</button>
<button onclick="return showelement('evenmore{{ .Honk.ID }}')">even more</button>
</div>
<div id="evenmore{{ .Honk.ID }}" style="display:none">
<p>
<button onclick="return zonkit(this, '{{ .Honk.XID }}');">zonk</button>
<button onclick="return zonkit(this, '{{ .Honk.XID }}');">delete</button>
{{ if .Honk.IsAcked }}
<button onclick="return flogit(this, 'deack', '{{ .Honk.XID }}');">deack</button>
{{ else }}


@@ 127,15 121,9 @@ in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
<button onclick="return flogit(this, 'untag', '{{ .Honk.XID }}');">untag me</button>
{{ end }}
<button><a href="/edit?xid={{ .Honk.XID }}">edit</a></button>
{{ if not (eq .Badonk "none") }}
{{ if .Honk.IsReacted }}
<button disabled>badonked</button>
{{ else }}
<button onclick="return flogit(this, 'react', '{{ .Honk.XID }}');">{{ .Badonk }}</button>
{{ end }}
{{ end }}
</p>
</div>
</details>
<p>
</p>
{{ end }}
</article>

M views/honkers.html => views/honkers.html +2 -2
@@ 3,7 3,7 @@
<div class="info">
<p>
<form action="/submithonker" method="POST">
<h3>add new honker</h3>
<h3>follow someone new</h3>
<input type="hidden" name="CSRF" value="{{ .HonkerCSRF }}">
<p><label for=url>url:</label><br>
<input tabindex=1 type="text" name="url" value="" autocomplete=off>


@@ 16,7 16,7 @@
<p><label for="notes">notes:</label><br>
<textarea tabindex=1 name="notes">
</textarea>
<p><button tabindex=1 name="add honker" value="add honker">add honker</button>
<p><button tabindex=1 name="add honker" value="add honker" class="yeet">follow</button>
</form>
</div>
{{ $honkercsrf := .HonkerCSRF }}

M views/honkform.html => views/honkform.html +3 -3
@@ 1,10 1,10 @@
<p id="honkformhost">
<button id="honkingtime" onclick="return showhonkform();" {{ if .IsPreview }}style="display:none"{{ end }}><a href="/newhonk">it's honking time</a></button>
<button id="honkingtime" onclick="return showhonkform();" {{ if .IsPreview }}style="display:none"{{ end }}><a href="/newhonk">yeet?</a></button>
<form id="honkform" action="/honk" method="POST" enctype="multipart/form-data" {{ if not .IsPreview }}style="display: none"{{ end }}>
<input type="hidden" name="CSRF" value="{{ .HonkCSRF }}">
<input type="hidden" name="updatexid" id="updatexidinput" value = "{{ .UpdateXID }}">
<input type="hidden" name="rid" id="ridinput" value="{{ .InReplyTo }}">
<h3>let's make some noise</h3>
<h3>less thinking, more typing</h3>
<p>
<details>
<summary>more options</summary>


@@ 41,7 41,7 @@
<p>
<textarea name="noise" id="honknoise">{{ .Noise }}</textarea>
<p class="buttonarray">
<button>it's gonna be honked</button>
<button class="yeet">yeet!</button>
<button name="preview" value="preview">preview</button>
<button type=button name="cancel" value="cancel" onclick="cancelhonking()">cancel</button>
</form>

A views/logo.svg => views/logo.svg +73 -0
@@ 0,0 1,73 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   sodipodi:docname="sunnespace.svg"
   version="1.0"
   inkscape:version="1.0beta2 (2b71d25, 2019-12-03)"
   sodipodi:version="0.32"
   id="svg1897"
   height="197.33333"
   width="197.33333">
  <defs
     id="defs1899" />
  <sodipodi:namedview
     inkscape:document-rotation="0"
     inkscape:window-maximized="0"
     showgrid="false"
     inkscape:window-y="23"
     inkscape:window-x="-5"
     inkscape:window-height="943"
     inkscape:window-width="1580"
     height="185px"
     width="185px"
     inkscape:current-layer="g2129"
     inkscape:document-units="px"
     inkscape:cy="92.637834"
     inkscape:cx="90.29487"
     inkscape:zoom="2"
     inkscape:pageshadow="2"
     inkscape:pageopacity="0.0"
     borderopacity="1.0"
     bordercolor="#666666"
     pagecolor="#ffffff"
     id="base" />
  <metadata
     id="metadata1902">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     id="layer1"
     inkscape:groupmode="layer"
     inkscape:label="Calque 1">
    <g
       transform="matrix(0.754398,0,0,0.754398,-22.570176,-20.020139)"
       id="g2129"
       style="display:inline;opacity:1">
      <circle
         r="124.93404"
         cy="157.32651"
         cx="160.70674"
         id="path840"
         style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#f582ae;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.58646;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1" />
      <path
         inkscape:connector-curvature="0"
         style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#104e8b;fill-opacity:1;fill-rule:nonzero;stroke:#0d3e6f;stroke-width:3.49167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
         d="m 121.29655,62.156009 c -2.57787,10.081743 0.26361,18.381162 5.19057,26.201383 3.80987,6.047171 6.31311,12.491558 6.19402,19.415538 -1.73307,0.97726 -3.38567,2.03063 -4.98092,3.16418 L 87.882882,84.490015 114.61422,123.86807 c -0.48028,0.66312 -0.94821,1.35235 -1.39874,2.0343 -7.58192,-3.41551 -14.699289,-4.09276 -21.481478,-2.7808 -9.587603,1.85462 -17.727401,-0.15335 -26.227127,-5.21921 5.306059,8.95171 13.222321,12.80899 22.235913,14.85486 6.983925,1.58518 13.296712,4.36681 18.114252,9.3661 -0.5242,1.8838 -0.95712,3.78915 -1.28629,5.72335 l -46.867744,9.50402 46.762074,8.94257 c 0.12853,0.80453 0.2834,1.60668 0.44768,2.40984 -7.778539,2.94302 -13.308884,7.48551 -17.177798,13.21026 -5.468044,8.09087 -12.605158,12.42878 -22.197454,14.85689 10.081746,2.57788 18.367266,-0.29718 26.187484,-5.22412 6.047179,-3.80987 12.491558,-6.31313 19.415518,-6.19403 0.97726,1.73306 2.03064,3.38567 3.16419,4.98092 l -26.433186,39.85091 39.397716,-26.77882 c 0.658,0.47625 1.32421,0.96569 2.00074,1.41263 -3.4155,7.58193 -4.07887,14.73287 -2.76691,21.51507 1.85461,9.58761 -0.16722,17.69389 -5.2331,26.19357 8.95172,-5.306 12.80901,-13.22229 14.85486,-22.23593 1.58518,-6.98394 4.36681,-13.29672 9.3661,-18.11425 1.88759,0.52569 3.78513,0.95641 5.72334,1.28628 l 9.50403,46.86776 8.94257,-46.76209 c 0.80453,-0.12851 1.60668,-0.28337 2.40985,-0.44767 2.94134,7.78086 7.48682,13.30978 13.21025,17.17781 8.09086,5.46804 12.42879,12.60518 14.85689,22.19743 2.57788,-10.0817 -0.29717,-18.36724 -5.22411,-26.18744 -3.81729,-6.059 -6.29256,-12.48383 -6.16047,-19.42948 1.72172,-0.97229 3.3618,-2.02361 4.94736,-3.15027 l 39.85088,26.43321 -26.74524,-39.41163 c 0.46993,-0.65002 0.93774,-1.31878 1.37908,-1.98684 7.58408,3.42537 14.72938,4.07953 21.51505,2.76691 9.58758,-1.8547 17.69387,0.1673 26.19355,5.2331 -5.30604,-8.95171 -13.2084,-12.77543 -22.22201,-14.82129 -6.97607,-1.58338 -13.28167,-4.41857 -18.09457,-9.41357 0.52291,-1.88057 0.92411,-3.77867 1.25272,-5.70944 l 46.88165,-9.47046 -46.776,-8.97613 c -0.12851,-0.80452 -0.28339,-1.60668 -0.44767,-2.40986 7.78568,-2.9403 13.30834,-7.48472 17.1778,-13.21024 5.46803,-8.09086 12.61908,-12.39523 22.21135,-14.82333 -10.08175,-2.57787 -18.38117,0.26361 -26.20137,5.19055 -6.05893,3.81728 -12.48377,6.29257 -19.42943,6.16047 -0.97232,-1.72172 -2.02364,-3.3618 -3.1503,-4.94735 l 26.44711,-39.817349 -39.42552,26.711699 c -0.65004,-0.46994 -1.31877,-0.93773 -1.98683,-1.37908 3.42076,-7.57974 4.09274,-14.69931 2.78079,-21.481509 -1.85465,-9.587597 0.15338,-17.727407 5.21921,-26.227117 -8.9517,5.306055 -12.77543,13.208413 -14.82129,22.222006 -1.58694,6.991768 -4.4171,13.32506 -9.43322,18.14205 -1.87482,-0.52067 -3.76513,-0.97262 -5.68978,-1.30018 l -9.47048,-46.881648 -8.97615,46.775978 c -0.8045,0.12852 -1.60666,0.28339 -2.40984,0.44769 -2.94249,-7.780602 -7.48463,-13.308286 -13.21024,-17.177813 -8.09087,-5.468043 -12.39522,-12.619047 -14.82333,-22.211348 z"
         id="path2140" />
    </g>
  </g>
</svg>

M views/style.css => views/style.css +72 -66
@@ 1,21 1,37 @@
/* html { */
/* 	--bg-page: #fff; */
/* 	--bg-dark: #000; */
/* 	--fg: #000; */
/* 	--hl: #0f0; */
/* 	--fg-subtle: #ff0; */
/* 	--fg-limited: #f00; */
/* } */

html {
	--bg-page: #306;
	--bg-dark: #002;
	--fg: #dcf;
	--hl: #dcf;
	--fg-subtle: #a9c;
	--fg-limited: #a79;
	--bg-page: #192734;
	--bg-dark: #15202B;
	--fg: #FFF;
	--hl: #556;
	--fg-subtle: rgba(185, 185, 186, 0.8);
	--fg: #FEF6E4;--bg-dark: rgba(0,0,0,0.2); --bg-page:#104E8B;--hl:rgba(0,0,0,0.2);--accent:#F582AE;--fg-limited:#F582AE;--fg-subtle:rgba(254,246,228, 0.7);
	padding:3em 1em;
}


body {
	background: var(--bg-page);
	color: var(--fg);
	font-size: 1em;
	word-wrap: break-word;
	font-family: sans-serif, "Noto Color Emoji";
	line-height: 1.2;
	font-family: -apple-system,blinkmacsystemfont, "Helvetica Neue", "Helvetica", "Segoe UI", roboto, oxygen-sans, ubuntu, cantarell, sans-serif;
	text-rendering: optimizeLegibility;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	font-size: 1.1rem;
	line-height: 1.6rem;
	overscroll-behavior-y: contain;
	max-width:70em; margin: 0 auto;
}

pre, code {
	white-space: pre-wrap;
}


@@ 35,9 51,6 @@ table {
a {
	color: var(--fg);
}
form, input, textarea {
	font-family: monospace, "Noto Color Emoji";
}
p {
	margin-top: 1em;
	margin-bottom: 1em;


@@ 52,53 65,53 @@ input {
#honkform input {
	font-size: 0.8em;
}

body > header {
	margin: 1em auto;
	font-size: 1.5em;
	color: var(--fg);
}
body > header span {
	margin-left: 2em;

body > header .mast {
	display:flex;align-items:center;gap:1em;
}
body > header p {
	padding: 1em;
.mast img{width:50px;height:50px;border-radius:50%;}
.mast p{flex:1 auto;font-weight:bold;}

body > header nav {
	margin-top:2em;display:flex;flex-wrap:wrap;justify-content:center;gap:1rem;
}
header > details {
	background: var(--bg-page);
	padding: 1em 1em 1em 1em;
	position: fixed;
	top: 0;
	left: 0;
	display: inline;
	max-height: calc(100% - 1em);
	overflow: auto;
	opacity: 0.7;
	overscroll-behavior: contain;
}
header > details[open] {
	padding: 1em 1em 0em 1em;
	background: var(--bg-dark);
	border: 1px solid var(--hl);
	margin-bottom: 1em;
	opacity: 1.0;

body > header ul {
	list-style: none;
	margin: 0;
	padding: 0;
}
header > details summary span {
	display: none;

body > header li {
	padding: 0 0.2rem;
}
header > details[open] summary span {
	display: inline;

body > header a {
	color: var(--fg);
	text-decoration: none;
	font-weight: bold;
	border-bottom: solid 3px var(--accent);
	padding:0.3rem 0.8rem;
}
header > details li {
	margin: 1em 0em 1em 0em;

body > header a:hover {
	color:var(--bg-page);border-color:var(--fg);background:var(--accent);
}

main {
	max-width: 1200px;
	margin: auto;
	font-size: 1.5em;
	padding: 1rem;
}
hr {
	border-color: var(--hl);
}
.info {
	margin: 0 auto;
	max-width: 640px;
	background: var(--bg-dark);
	border: 1px solid var(--hl);
	margin-bottom: 1em;


@@ 113,13 126,17 @@ label {
}
label.button, button, select {
	font-size: 16px;
	font-family: monospace;
	color: var(--fg);
	background: var(--bg-page);
	border: 1px solid var(--hl);
	padding: 0.5em;
	white-space: nowrap;
}
button.yeet {
	background: var(--accent);
	font-weight: bold;
	color: var(--bg-page);
}
.buttonarray {
	margin-top: -2.0em;
}


@@ 134,6 151,7 @@ form {
	margin-top: 1em;
}
textarea {
	font-family: -apple-system,blinkmacsystemfont, "Helvetica Neue", "Helvetica", "Segoe UI", roboto, oxygen-sans, ubuntu, cantarell, sans-serif;
	padding: 0.5em;
	font-size: 1em;
	background: var(--bg-page);


@@ 169,10 187,10 @@ input[type=file] {
}

.honk {
	max-width:640px;
	margin: auto;
	background: var(--bg-dark);
	border: 1px solid var(--hl);
	border-radius: 1em;
	margin-bottom: 1em;
	padding-left: 1em;
	padding-right: 1em;


@@ 217,17 235,17 @@ input[type=file] {
		}
.honk	header	img {
			float: left;
			background: var(--fg);
			margin-right: 1em;
			width: 64px;
			height: 64px;
			width: 47px;
			height: 47px;
			border-radius:50%;
			border: solid 3px var(--accent);
			padding:5px;
		}
.honk	header	p {
			margin-top: 0px;
		}
.honk	.actions button {
		margin-left: 4em;
		margin-top: 2em;
	}
.honk	.noise {
		line-height: 1.4;
	}


@@ 295,20 313,8 @@ img.emu {
	margin: -2px;
	object-fit: contain;
}
@media screen and (max-width: 740px) {
	body {
		font-size: 12px;
	}
	.honk header {
		height: 52px;
	}
	.honk header img {
		width: 48px;
		height: 48px;
	}
	details summary {
		outline: none;
	}
@media (max-width:768px){
	nav,.mast{justify-content:center!important;padding:0;}
}
@media print {
	#topmenu, #topspacer, #infobox, #refreshbox, .actions {

M web.go => web.go +10 -5
@@ 206,7 206,7 @@ func showrss(w http.ResponseWriter, r *http.Request) {
		}
		desc := string(honk.HTML)
		if t := honk.Time; t != nil {
			desc += fmt.Sprintf(`<p>Time: %s`, t.StartTime.Local().Format("03:04PM EDT Mon Jan 02"))
			desc += fmt.Sprintf(`<p>Time: %s`, t.StartTime.Local().Format("02 Jan 2006 15:04"))
			if t.Duration != 0 {
				desc += fmt.Sprintf(`<br>Duration: %s`, t.Duration)
			}


@@ 1396,8 1396,10 @@ func edithonkpage(w http.ResponseWriter, r *http.Request) {
	templinfo["SavedPlace"] = honk.Place
	if tm := honk.Time; tm != nil {
		templinfo["ShowTime"] = ";"
		templinfo["StartTime"] = tm.StartTime.Format("2006-01-02 03:04")
		templinfo["Duration"] = tm.Duration
		templinfo["StartTime"] = tm.StartTime.Format("2006-01-02 15:04")
		if tm.Duration != 0 {
			templinfo["Duration"] = tm.Duration
		}
	}
	templinfo["ServerMessage"] = "honk edit 2"
	templinfo["IsPreview"] = true


@@ 1711,8 1713,10 @@ func submithonk(w http.ResponseWriter, r *http.Request) *Honk {
		templinfo["SavedFile"] = donkxid
		if tm := honk.Time; tm != nil {
			templinfo["ShowTime"] = ";"
			templinfo["StartTime"] = tm.StartTime.Format("2006-01-02 03:04")
			templinfo["Duration"] = tm.Duration
			templinfo["StartTime"] = tm.StartTime.Format("2006-01-02 15:04")
			if tm.Duration != 0 {
				templinfo["Duration"] = tm.Duration
			}
		}
		templinfo["IsPreview"] = true
		templinfo["UpdateXID"] = updatexid


@@ 2458,6 2462,7 @@ func serve() {
	posters.HandleFunc("/inbox", serverinbox)

	getters.HandleFunc("/style.css", serveasset)
	getters.HandleFunc("/logo.svg", serveasset)
	getters.HandleFunc("/local.css", serveasset)
	getters.HandleFunc("/honkpage.js", serveasset)
	getters.HandleFunc("/about", servehtml)