aboutsummaryrefslogtreecommitdiffstats
path: root/internal/web/layout.go
diff options
context:
space:
mode:
authorgrm <grm@eyesin.space>2026-09-18 12:10:26 +0300
committergrm <grm@eyesin.space>2026-09-18 12:10:33 +0300
commit19353a51c869f4b24ef2253d856084b6e6728048 (patch)
tree6d58fe2d284e0d26f491a87924c65b536e979983 /internal/web/layout.go
parente3114ba866a555e49f6f0f8321ed6106a9f135bf (diff)
downloadblogspace-19353a51c869f4b24ef2253d856084b6e6728048.tar.gz
blogspace-19353a51c869f4b24ef2253d856084b6e6728048.tar.bz2
blogspace-19353a51c869f4b24ef2253d856084b6e6728048.zip
Pair headed gizmo announcements two to a row, split by text length
Headed gizmo boxes used to share a row equally, up to three of them. Now placeNotices pairs consecutive ones in the main column and gives the pair one of the fixed splits — 25/75, 33/67, 50/50, 67/33, 75/25 — nearest the ratio of the text each shows, so the wordier box gets the room. The bases add up to 90% so a third box never joins the row, and a side column, too narrow for two, stacks them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
Diffstat (limited to 'internal/web/layout.go')
-rw-r--r--internal/web/layout.go61
1 files changed, 58 insertions, 3 deletions
diff --git a/internal/web/layout.go b/internal/web/layout.go
index 6f5c84f..91728ed 100644
--- a/internal/web/layout.go
+++ b/internal/web/layout.go
@@ -1,9 +1,12 @@
package web
import (
+ "html"
+ "math"
"net/url"
"strings"
"time"
+ "unicode/utf8"
"github.com/gramanas/blogspace/internal/i18n"
"github.com/gramanas/blogspace/internal/store"
@@ -243,17 +246,69 @@ func groupArchive(posts []store.PostRef) []ArchiveYear {
return years
}
+// Notice is an announcement placed on the page: the section plus, for a
+// headed gizmo box sharing a row with the next one, its share of the row.
+type Notice struct {
+ store.Section
+ Width int // 0 = the whole row; else one of rowShares, in percent
+}
+
+// rowShares are the widths two boxes on one row can take: the longer text
+// gets the bigger share, and the pair always adds up to 100.
+var rowShares = []int{25, 33, 50, 67, 75}
+
// placeNotices groups announcements by placement ("left-top", "main-bottom", …).
// Ones meant for a side column that is not laid out go to the main column instead.
-func placeNotices(secs []store.Section, l Layout) map[string][]store.Section {
- out := map[string][]store.Section{}
+// In each group, headed gizmo boxes are paired up, two to a row, and the
+// pair's widths are set by how much text each carries (main column only —
+// a side column stacks them).
+func placeNotices(secs []store.Section, l Layout) map[string][]Notice {
+ out := map[string][]Notice{}
for _, sec := range secs {
col := sec.Column()
if (col == "left" && !l.HasLeft) || (col == "right" && !l.HasRight) {
col = "main"
}
key := col + "-" + sec.Position()
- out[key] = append(out[key], sec)
+ out[key] = append(out[key], Notice{Section: sec})
+ }
+ for key, ns := range out {
+ if strings.HasPrefix(key, "main-") {
+ pairNotices(ns)
+ }
}
return out
}
+
+// pairNotices gives every two consecutive headed gizmo boxes their row shares.
+func pairNotices(ns []Notice) {
+ for i := 0; i+1 < len(ns); i++ {
+ if !ns[i].pairable() || !ns[i+1].pairable() {
+ continue
+ }
+ ns[i].Width, ns[i+1].Width = shareRow(noticeLen(ns[i]), noticeLen(ns[i+1]))
+ i++
+ }
+}
+
+func (n Notice) pairable() bool { return n.Style == "gizmo" && n.Title != "" }
+
+// noticeLen is how much text a box shows: its title and body, tags stripped.
+func noticeLen(n Notice) int {
+ return utf8.RuneCountInString(n.Title) + utf8.RuneCountInString(html.UnescapeString(stripTags.Sanitize(n.BodyHTML)))
+}
+
+// shareRow picks the pair of rowShares nearest to the two lengths' ratio.
+func shareRow(a, b int) (int, int) {
+ if a+b == 0 {
+ return 50, 50
+ }
+ want := float64(a) * 100 / float64(a+b)
+ best := 50
+ for _, s := range rowShares {
+ if math.Abs(float64(s)-want) < math.Abs(float64(best)-want) {
+ best = s
+ }
+ }
+ return best, 100 - best
+}