customization page
This commit is contained in:
parent
fd40eb276c
commit
f5d5d208fe
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
homepage
|
||||
.env
|
||||
|
225
bookmark.go
225
bookmark.go
@ -5,8 +5,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
Bookmarks = []Bookmark{
|
||||
func DefaultBookmarks() []Bookmark {
|
||||
return []Bookmark{
|
||||
{
|
||||
Title: "DuckDuckGo",
|
||||
Image: "https://duckduckgo.com/assets/logo_homepage.alt.v108.svg",
|
||||
@ -14,130 +14,22 @@ var (
|
||||
Color: "#e37151",
|
||||
},
|
||||
{
|
||||
Title: "Cloud",
|
||||
Image: "https://cloud.tordarus.net/svg/core/logo/logo?color=ffffff&v=1",
|
||||
Link: "https://cloud.tordarus.net/",
|
||||
Color: "#007ec2",
|
||||
Title: "Google Dark",
|
||||
Image: "https://www.google.com/images/branding/googlelogo/2x/googlelogo_light_color_272x92dp.png",
|
||||
Link: "https://google.com/",
|
||||
Color: "#202124",
|
||||
},
|
||||
{
|
||||
Title: "ProtonMail",
|
||||
Image: "https://protonmail.com/images/pm-logo-white.svg",
|
||||
Link: "https://protonmail.com/",
|
||||
Color: "#253163",
|
||||
},
|
||||
{
|
||||
Title: "Anilist",
|
||||
Image: "https://anilist.co/img/icons/icon.svg",
|
||||
Link: "https://anilist.co/",
|
||||
Color: "#2b2d42",
|
||||
IconPadding: "0.5em",
|
||||
},
|
||||
{
|
||||
Title: "Torrents",
|
||||
Image: "/transmission.png",
|
||||
Link: "https://torrents.tordarus.net/",
|
||||
Color: "#e7402c",
|
||||
},
|
||||
{
|
||||
Title: "Nyaa.si",
|
||||
Image: "https://progsoft.net/images/nyaa-v2-icon-a591b5ae622eb38f31c00297cf9b53fd88c058ef.png",
|
||||
Link: "https://nyaa.si/",
|
||||
Color: "#e7402c",
|
||||
ImageSize: "111%",
|
||||
IconPadding: "0em",
|
||||
},
|
||||
{
|
||||
Title: "Adblock",
|
||||
Image: "/pihole.svg",
|
||||
Link: "https://adblock.tordarus.net/",
|
||||
Color: "#272c30",
|
||||
},
|
||||
{
|
||||
Title: "Postbank",
|
||||
Image: "https://wintouch.de/wp-content/uploads/2014/06/Postbank-Icon.png",
|
||||
Link: "https://meine.postbank.de/",
|
||||
Color: "#ffcc00",
|
||||
},
|
||||
{
|
||||
Title: "DKB",
|
||||
Image: "https://vdiv-sa.de/wp-content/uploads/2016/08/DKB_AG_web_RGB.jpg",
|
||||
Link: "https://www.dkb.de/",
|
||||
Title: "Google Light",
|
||||
Image: "https://www.google.de/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png",
|
||||
Link: "https://google.com/",
|
||||
Color: "#ffffff",
|
||||
},
|
||||
{
|
||||
Title: "Finanzen",
|
||||
Image: "https://docs.firefly-iii.org/img/logo.png",
|
||||
Link: "https://finance.tordarus.net",
|
||||
Color: "#ffffff",
|
||||
ImageSize: "cover",
|
||||
IconPadding: "0em",
|
||||
},
|
||||
{
|
||||
Title: "YouTube",
|
||||
Image: "https://upload.wikimedia.org/wikipedia/commons/thumb/4/42/YouTube_light_icon_%282017%29.svg/1280px-YouTube_light_icon_%282017%29.svg.png",
|
||||
Color: "#ff0000",
|
||||
Link: "https://www.youtube.com/",
|
||||
IconPadding: "1.5em",
|
||||
},
|
||||
{
|
||||
Title: "Netflix",
|
||||
Image: "https://1000logos.net/wp-content/uploads/2017/05/emblem-Netflix.jpg",
|
||||
Color: "#000000",
|
||||
Link: "https://www.netflix.com/",
|
||||
},
|
||||
{
|
||||
Title: "Jisho",
|
||||
Image: "https://assets.jisho.org/assets/jisho-logo-v4@2x-7330091c079b9dd59601401b052b52e103978221c8fb6f5e22406d871fcc746a.png",
|
||||
Link: "https://jisho.org/",
|
||||
Color: "#ffffff",
|
||||
},
|
||||
{
|
||||
Title: "Wadoku",
|
||||
Image: "https://www.wadoku.de/img/wadoku_logo.svg",
|
||||
Link: "https://www.wadoku.de/",
|
||||
Color: "#ffffff",
|
||||
},
|
||||
{
|
||||
Title: "Übersetzer",
|
||||
Image: "https://tools.avans.nl/tools/image/wZkdyaMblN.jpg",
|
||||
Link: "https://www.deepl.com/translator",
|
||||
Color: "#042b48",
|
||||
ImageSize: "cover",
|
||||
IconPadding: "0em",
|
||||
},
|
||||
{
|
||||
Title: "WaniKani",
|
||||
Image: "https://assets.wanikani.com/assets/logo--retro-colors-b79775af8773b5a416e4ec3fae02e62d391b7e88e57f9fe0c2e4997a3383b002.png",
|
||||
Link: "https://www.wanikani.com/",
|
||||
Color: "#ffffff",
|
||||
},
|
||||
{
|
||||
Title: "Passwörter",
|
||||
Image: "https://raw.githubusercontent.com/dani-garcia/vaultwarden/main/resources/vaultwarden-logo-white.svg",
|
||||
Link: "https://pw.tordarus.net",
|
||||
Color: "#175ddc",
|
||||
},
|
||||
{
|
||||
Title: "Projekte",
|
||||
Image: "/gitea.png", //"https://git.tordarus.net/assets/img/logo.svg",
|
||||
Link: "https://git.tordarus.net",
|
||||
Color: "#609926",
|
||||
ImageSize: "cover",
|
||||
IconPadding: "0em",
|
||||
},
|
||||
{
|
||||
Title: "Regexr",
|
||||
Image: "https://raw.githubusercontent.com/gskinner/regexr/master/dev/icons/RegExr.svg",
|
||||
Link: "https://regex.tordarus.net",
|
||||
Color: "#101112",
|
||||
ImageSize: "contain",
|
||||
IconPadding: "0em",
|
||||
},
|
||||
{
|
||||
Title: "Wikipedia",
|
||||
Image: "https://upload.wikimedia.org/wikipedia/commons/8/80/Wikipedia-logo-v2.svg",
|
||||
Link: "https://www.wikipedia.org/",
|
||||
Color: "#ffffff",
|
||||
Link: "https://www.wikipedia.org/",
|
||||
},
|
||||
{
|
||||
Title: "Reddit",
|
||||
@ -148,67 +40,64 @@ var (
|
||||
IconPadding: "0em",
|
||||
},
|
||||
{
|
||||
Title: "Tagesschau",
|
||||
Image: "https://m47ch.com/wp-content/uploads/2010/12/Tagesschau.png",
|
||||
Link: "https://www.tagesschau.de/",
|
||||
Color: "#003f8c",
|
||||
Title: "YouTube",
|
||||
Image: "https://upload.wikimedia.org/wikipedia/commons/thumb/4/42/YouTube_light_icon_%282017%29.svg/1280px-YouTube_light_icon_%282017%29.svg.png",
|
||||
Link: "https://www.youtube.com/",
|
||||
Color: "#ff0000",
|
||||
IconPadding: "1.5em",
|
||||
},
|
||||
{
|
||||
Title: "Golem",
|
||||
Image: "https://www.golem.de/staticrl/images/golem-logo-opt2.svg",
|
||||
Link: "https://www.golem.de/",
|
||||
Title: "Netflix",
|
||||
Image: "https://1000logos.net/wp-content/uploads/2017/05/emblem-Netflix.jpg",
|
||||
Link: "https://www.netflix.com/",
|
||||
Color: "#000000",
|
||||
},
|
||||
{
|
||||
Title: "Comico",
|
||||
Image: "https://play-lh.googleusercontent.com/xZ5bfg-XfyEmM1641bXduE27w44OmyWeVspg626wRYF0ejAgZAvsRKP4sECblfmnpg=w512",
|
||||
Link: "https://comico.jp/",
|
||||
Color: "#f40000",
|
||||
Title: "Übersetzer",
|
||||
Image: "https://tools.avans.nl/tools/image/wZkdyaMblN.jpg",
|
||||
Link: "https://www.deepl.com/translator",
|
||||
Color: "#042b48",
|
||||
ImageSize: "cover",
|
||||
IconPadding: "0em",
|
||||
},
|
||||
{
|
||||
Title: "Manganelo",
|
||||
Image: "https://manganato.com/themes/hm/images/logo.png",
|
||||
Link: "https://manganato.com/",
|
||||
Color: "#ffffff",
|
||||
ImageSize: "contain",
|
||||
IconPadding: "0em",
|
||||
Title: "Cloud",
|
||||
Image: "https://cloud.tordarus.net/svg/core/logo/logo?color=ffffff\u0026v=1",
|
||||
Link: "https://cloud.tordarus.net/",
|
||||
Color: "#007ec2",
|
||||
},
|
||||
{
|
||||
Title: "Manga List",
|
||||
Image: "https://www.firstcomicsnews.com/wp-content/uploads/2020/09/manga-logo.png",
|
||||
Link: "https://manga.tordarus.net/",
|
||||
Color: "#be151b",
|
||||
Title: "Passwörter",
|
||||
Image: "https://raw.githubusercontent.com/dani-garcia/vaultwarden/main/resources/vaultwarden-logo-white.svg",
|
||||
Link: "https://pw.tordarus.net",
|
||||
Color: "#175ddc",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type Bookmark struct {
|
||||
Title string
|
||||
Image string
|
||||
ImageSize string
|
||||
IconPadding string
|
||||
Color string
|
||||
Link string
|
||||
HideBorder bool
|
||||
Order int
|
||||
}
|
||||
|
||||
func ParseBookmarks() {
|
||||
// set default values
|
||||
for i := range Bookmarks {
|
||||
if Bookmarks[i].Color == "" {
|
||||
Bookmarks[i].Color = DefaultSettings.Background
|
||||
}
|
||||
if Bookmarks[i].ImageSize == "" {
|
||||
Bookmarks[i].ImageSize = "contain"
|
||||
}
|
||||
}
|
||||
type Bookmark struct {
|
||||
Title string `json:"title"`
|
||||
Image string `json:"image"`
|
||||
ImageSize string `json:"image_size"`
|
||||
IconPadding string `json:"icon_padding"`
|
||||
Color string `json:"color"`
|
||||
Link string `json:"link"`
|
||||
HideBorder bool `json:"hide_border"`
|
||||
Order int `json:"order"`
|
||||
}
|
||||
|
||||
sort.SliceStable(Bookmarks, func(i, j int) bool {
|
||||
return Bookmarks[i].Order < Bookmarks[j].Order
|
||||
})
|
||||
func (b *Bookmark) GetColor() string {
|
||||
if b.Color == "" {
|
||||
b.Color = "transparent"
|
||||
}
|
||||
return b.Color
|
||||
}
|
||||
|
||||
func (b *Bookmark) GetImageSize() string {
|
||||
if b.ImageSize == "" {
|
||||
b.ImageSize = "contain"
|
||||
}
|
||||
return b.ImageSize
|
||||
}
|
||||
|
||||
func (b *Bookmark) SmallLink() string {
|
||||
@ -218,3 +107,13 @@ func (b *Bookmark) SmallLink() string {
|
||||
lnk = strings.TrimSuffix(lnk, "/")
|
||||
return lnk
|
||||
}
|
||||
|
||||
func Reorder(bookmarks []Bookmark) {
|
||||
sort.SliceStable(bookmarks, func(i, j int) bool {
|
||||
return bookmarks[i].Order < bookmarks[j].Order
|
||||
})
|
||||
|
||||
for index := range bookmarks {
|
||||
bookmarks[index].Order = index * 10
|
||||
}
|
||||
}
|
||||
|
262
bookmarks.json
Normal file
262
bookmarks.json
Normal file
@ -0,0 +1,262 @@
|
||||
[
|
||||
{
|
||||
"title": "DuckDuckGo",
|
||||
"image": "https://duckduckgo.com/assets/logo_homepage.alt.v108.svg",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#e37151",
|
||||
"link": "https://duckduckgo.com/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Cloud",
|
||||
"image": "https://cloud.tordarus.net/svg/core/logo/logo?color=ffffff\u0026v=1",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#007ec2",
|
||||
"link": "https://cloud.tordarus.net/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "ProtonMail",
|
||||
"image": "https://protonmail.com/images/pm-logo-white.svg",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#253163",
|
||||
"link": "https://protonmail.com/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Anilist",
|
||||
"image": "https://anilist.co/img/icons/icon.svg",
|
||||
"image_size": "",
|
||||
"icon_padding": "0.5em",
|
||||
"color": "#2b2d42",
|
||||
"link": "https://anilist.co/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Torrents",
|
||||
"image": "/transmission.png",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#e7402c",
|
||||
"link": "https://torrents.tordarus.net/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Nyaa.si",
|
||||
"image": "https://progsoft.net/images/nyaa-v2-icon-a591b5ae622eb38f31c00297cf9b53fd88c058ef.png",
|
||||
"image_size": "111%",
|
||||
"icon_padding": "0em",
|
||||
"color": "#e7402c",
|
||||
"link": "https://nyaa.si/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Adblock",
|
||||
"image": "/pihole.svg",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#272c30",
|
||||
"link": "https://adblock.tordarus.net/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Postbank",
|
||||
"image": "https://wintouch.de/wp-content/uploads/2014/06/Postbank-Icon.png",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#ffcc00",
|
||||
"link": "https://meine.postbank.de/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "DKB",
|
||||
"image": "https://vdiv-sa.de/wp-content/uploads/2016/08/DKB_AG_web_RGB.jpg",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#ffffff",
|
||||
"link": "https://www.dkb.de/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Finanzen",
|
||||
"image": "https://docs.firefly-iii.org/img/logo.png",
|
||||
"image_size": "cover",
|
||||
"icon_padding": "0em",
|
||||
"color": "#ffffff",
|
||||
"link": "https://finance.tordarus.net",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "YouTube",
|
||||
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/42/YouTube_light_icon_%282017%29.svg/1280px-YouTube_light_icon_%282017%29.svg.png",
|
||||
"image_size": "",
|
||||
"icon_padding": "1.5em",
|
||||
"color": "#ff0000",
|
||||
"link": "https://www.youtube.com/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Netflix",
|
||||
"image": "https://1000logos.net/wp-content/uploads/2017/05/emblem-Netflix.jpg",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#000000",
|
||||
"link": "https://www.netflix.com/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Jisho",
|
||||
"image": "https://assets.jisho.org/assets/jisho-logo-v4@2x-7330091c079b9dd59601401b052b52e103978221c8fb6f5e22406d871fcc746a.png",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#ffffff",
|
||||
"link": "https://jisho.org/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Wadoku",
|
||||
"image": "https://www.wadoku.de/img/wadoku_logo.svg",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#ffffff",
|
||||
"link": "https://www.wadoku.de/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Übersetzer",
|
||||
"image": "https://tools.avans.nl/tools/image/wZkdyaMblN.jpg",
|
||||
"image_size": "cover",
|
||||
"icon_padding": "0em",
|
||||
"color": "#042b48",
|
||||
"link": "https://www.deepl.com/translator",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "WaniKani",
|
||||
"image": "https://assets.wanikani.com/assets/logo--retro-colors-b79775af8773b5a416e4ec3fae02e62d391b7e88e57f9fe0c2e4997a3383b002.png",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#ffffff",
|
||||
"link": "https://www.wanikani.com/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Passwörter",
|
||||
"image": "https://raw.githubusercontent.com/dani-garcia/vaultwarden/main/resources/vaultwarden-logo-white.svg",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#175ddc",
|
||||
"link": "https://pw.tordarus.net",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Projekte",
|
||||
"image": "/gitea.png",
|
||||
"image_size": "cover",
|
||||
"icon_padding": "0em",
|
||||
"color": "#609926",
|
||||
"link": "https://git.tordarus.net",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Regexr",
|
||||
"image": "https://raw.githubusercontent.com/gskinner/regexr/master/dev/icons/RegExr.svg",
|
||||
"image_size": "contain",
|
||||
"icon_padding": "0em",
|
||||
"color": "#101112",
|
||||
"link": "https://regex.tordarus.net",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Wikipedia",
|
||||
"image": "https://upload.wikimedia.org/wikipedia/commons/8/80/Wikipedia-logo-v2.svg",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#ffffff",
|
||||
"link": "https://www.wikipedia.org/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Reddit",
|
||||
"image": "https://www.redditinc.com/assets/images/site/reddit-logo.png",
|
||||
"image_size": "cover",
|
||||
"icon_padding": "0em",
|
||||
"color": "#ff4300",
|
||||
"link": "https://www.reddit.com/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Tagesschau",
|
||||
"image": "https://m47ch.com/wp-content/uploads/2010/12/Tagesschau.png",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#003f8c",
|
||||
"link": "https://www.tagesschau.de/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Golem",
|
||||
"image": "https://www.golem.de/staticrl/images/golem-logo-opt2.svg",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#000000",
|
||||
"link": "https://www.golem.de/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Comico",
|
||||
"image": "https://play-lh.googleusercontent.com/xZ5bfg-XfyEmM1641bXduE27w44OmyWeVspg626wRYF0ejAgZAvsRKP4sECblfmnpg=w512",
|
||||
"image_size": "cover",
|
||||
"icon_padding": "0em",
|
||||
"color": "#f40000",
|
||||
"link": "https://comico.jp/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Manganelo",
|
||||
"image": "https://manganato.com/themes/hm/images/logo.png",
|
||||
"image_size": "contain",
|
||||
"icon_padding": "0em",
|
||||
"color": "#ffffff",
|
||||
"link": "https://manganato.com/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Manga List",
|
||||
"image": "https://www.firstcomicsnews.com/wp-content/uploads/2020/09/manga-logo.png",
|
||||
"image_size": "",
|
||||
"icon_padding": "",
|
||||
"color": "#be151b",
|
||||
"link": "https://manga.tordarus.net/",
|
||||
"hide_border": false,
|
||||
"order": 0
|
||||
}
|
||||
]
|
8
go.mod
8
go.mod
@ -2,4 +2,10 @@ module git.tordarus.net/Tordarus/homepage
|
||||
|
||||
go 1.18
|
||||
|
||||
require golang.org/x/text v0.3.7 // indirect
|
||||
require (
|
||||
git.milar.in/milarin/slices v0.0.4
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
golang.org/x/text v0.3.7
|
||||
)
|
||||
|
||||
require github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
|
6
go.sum
6
go.sum
@ -1,2 +1,8 @@
|
||||
git.milar.in/milarin/slices v0.0.4 h1:z92jgsKcnLPLfgXkTVCzH2XXesfXzhe0Osx+PkfCHVI=
|
||||
git.milar.in/milarin/slices v0.0.4/go.mod h1:NOr53AOeur/qscu/FBj3lsFR262PNYBccLYSTCAXRk4=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
|
96
main.go
96
main.go
@ -2,6 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
@ -9,9 +12,13 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
_ "embed"
|
||||
|
||||
"git.milar.in/milarin/slices"
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -27,12 +34,15 @@ var (
|
||||
StaticFS embed.FS
|
||||
|
||||
Templates *template.Template
|
||||
|
||||
SessionStore = sessions.NewCookieStore(Must(hex.DecodeString(os.Getenv("SESSION_KEY"))))
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
gob.Register([]Bookmark{})
|
||||
gob.Register(&Settings{})
|
||||
|
||||
ParseBookmarks()
|
||||
flag.Parse()
|
||||
|
||||
if tmpl, err := template.New("homepage").ParseFS(TemplateFS, "templates/*"); err == nil {
|
||||
Templates = tmpl
|
||||
@ -41,8 +51,11 @@ func main() {
|
||||
}
|
||||
|
||||
http.HandleFunc("/", handler)
|
||||
http.HandleFunc("/customize", customize)
|
||||
http.HandleFunc("/save-changes", saveChanges)
|
||||
http.HandleFunc("/search", search)
|
||||
http.HandleFunc("/style.css", style)
|
||||
http.HandleFunc("/style.css", ProvideFile("static/style.css", "text/css"))
|
||||
http.HandleFunc("/customize.js", ProvideFile("static/customize.js", "application/javascript"))
|
||||
http.HandleFunc("/icons.ttf", ProvideFile("static/icons.ttf", "font/ttf"))
|
||||
http.HandleFunc("/transmission.png", ProvideFile("static/transmission.png", "image/png"))
|
||||
http.HandleFunc("/pihole.svg", ProvideFile("static/pihole.svg", "image/svg+xml"))
|
||||
@ -53,10 +66,12 @@ func main() {
|
||||
}
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
session, _ := SessionStore.Get(r, "settings")
|
||||
|
||||
data := &TmplData{
|
||||
text: GetText(r),
|
||||
Bookmarks: Bookmarks,
|
||||
Settings: DefaultSettings,
|
||||
Bookmarks: GetValueDefault(session, "bookmarks", DefaultBookmarks()),
|
||||
Settings: GetValueDefault(session, "settings", DefaultSettings()),
|
||||
}
|
||||
|
||||
if err := Templates.ExecuteTemplate(w, "index.html", data); err != nil {
|
||||
@ -64,22 +79,6 @@ func handler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func style(w http.ResponseWriter, r *http.Request) {
|
||||
file, err := TemplateFS.Open("templates/style.css")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
w.Header().Add("Content-Type", "text/css")
|
||||
|
||||
if err := Templates.ExecuteTemplate(w, "style.css", DefaultSettings); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func ProvideFile(path, contentType string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
file, err := StaticFS.Open(path)
|
||||
@ -95,6 +94,9 @@ func ProvideFile(path, contentType string) http.HandlerFunc {
|
||||
}
|
||||
|
||||
func search(w http.ResponseWriter, r *http.Request) {
|
||||
session, _ := SessionStore.Get(r, "settings")
|
||||
settings := GetValueDefault(session, "settings", DefaultSettings())
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
fmt.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
@ -109,12 +111,62 @@ func search(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusMovedPermanently)
|
||||
} else {
|
||||
// search string
|
||||
w.Header().Add("Location", fmt.Sprintf("https://duckduckgo.com/?q=%s", query))
|
||||
w.Header().Add("Location", fmt.Sprintf(settings.Search, query))
|
||||
w.WriteHeader(http.StatusMovedPermanently)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func customize(w http.ResponseWriter, r *http.Request) {
|
||||
session, _ := SessionStore.Get(r, "settings")
|
||||
|
||||
text := GetText(r)
|
||||
|
||||
bookmarks := slices.Map(GetValueDefault(session, "bookmarks", DefaultBookmarks()), func(b Bookmark) BookmarkData {
|
||||
return BookmarkData{
|
||||
Bookmark: &b,
|
||||
text: text,
|
||||
}
|
||||
})
|
||||
|
||||
data := &CustomizeData{
|
||||
text: text,
|
||||
Bookmarks: bookmarks,
|
||||
Settings: GetValueDefault(session, "settings", DefaultSettings()),
|
||||
}
|
||||
|
||||
if err := Templates.ExecuteTemplate(w, "customize.html", data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func saveChanges(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
session, _ := SessionStore.Get(r, "settings")
|
||||
|
||||
sessionData := &SessionData{}
|
||||
err := json.NewDecoder(r.Body).Decode(&sessionData)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
Reorder(sessionData.Bookmarks)
|
||||
session.Values["bookmarks"] = sessionData.Bookmarks
|
||||
session.Values["settings"] = sessionData.Settings
|
||||
|
||||
if err := session.Save(r, w); err != nil {
|
||||
fmt.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func ParseURI(uri string) (*url.URL, error) {
|
||||
if !strings.HasPrefix(uri, "http://") && !strings.HasPrefix(uri, "https://") {
|
||||
uri = "https://" + uri
|
||||
|
6
session_data.go
Normal file
6
session_data.go
Normal file
@ -0,0 +1,6 @@
|
||||
package main
|
||||
|
||||
type SessionData struct {
|
||||
Bookmarks []Bookmark `json:"bookmarks"`
|
||||
Settings *Settings `json:"settings"`
|
||||
}
|
24
settings.go
24
settings.go
@ -1,19 +1,17 @@
|
||||
package main
|
||||
|
||||
type Settings struct {
|
||||
Background string
|
||||
IconMargin string
|
||||
IconPadding string
|
||||
IconWidth string
|
||||
IconHeight string
|
||||
BorderRadius string
|
||||
Background string `json:"background_color"`
|
||||
Foreground string `json:"foreground_color"`
|
||||
Search string `json:"search_query"`
|
||||
BorderRadius string `json:"border_radius"`
|
||||
}
|
||||
|
||||
var DefaultSettings = &Settings{
|
||||
Background: "#2b2a33",
|
||||
IconMargin: "0.5em",
|
||||
IconPadding: "1em",
|
||||
IconWidth: "10em",
|
||||
IconHeight: "10em",
|
||||
BorderRadius: "0.15em",
|
||||
func DefaultSettings() *Settings {
|
||||
return &Settings{
|
||||
Background: "#2b2a33",
|
||||
Foreground: "#ffffff",
|
||||
Search: "https://duckduckgo.com/?q=%s",
|
||||
BorderRadius: "1.5%",
|
||||
}
|
||||
}
|
||||
|
135
static/customize.js
Normal file
135
static/customize.js
Normal file
@ -0,0 +1,135 @@
|
||||
window.addEventListener("load", main);
|
||||
|
||||
function main() {
|
||||
const bgColor = document.querySelector("#bg-color");
|
||||
const fgColor = document.querySelector("#fg-color");
|
||||
const borderRadius = document.querySelector("#border-radius");
|
||||
|
||||
bgColor.addEventListener("change", () => document.body.style.backgroundColor = bgColor.value);
|
||||
fgColor.addEventListener("change", () => document.body.style.color = fgColor.value);
|
||||
borderRadius.addEventListener("change", () => document.querySelector(":root").style.setProperty("--bookmark-border-radius", borderRadius.value));
|
||||
|
||||
const save = document.querySelector("#button");
|
||||
save.addEventListener("click", saveChanges);
|
||||
|
||||
const addBookmark = document.querySelector("#add-bookmark");
|
||||
const firstBookmark = document.querySelector(".customizer");
|
||||
|
||||
addBookmark.style.width = `${firstBookmark.offsetWidth}px`;
|
||||
addBookmark.style.height = `${firstBookmark.offsetHeight}px`;
|
||||
addBookmark.style.lineHeight = `${firstBookmark.offsetHeight}px`;
|
||||
|
||||
addBookmark.addEventListener("click", () => {
|
||||
const bookmark = firstBookmark.cloneNode(true);
|
||||
initBookmark(bookmark);
|
||||
clearBookmarkData(bookmark);
|
||||
document.querySelector("#customize-container").insertBefore(bookmark, addBookmark);
|
||||
});
|
||||
|
||||
document.querySelectorAll(".customizer").forEach(initBookmark);
|
||||
}
|
||||
|
||||
function saveChanges() {
|
||||
const bookmarks = [];
|
||||
document.querySelectorAll(".customizer").forEach(element => bookmarks.push(buildDataForBoomark(element)));
|
||||
|
||||
const settings = {
|
||||
background_color: document.querySelector("#bg-color").value,
|
||||
foreground_color: document.querySelector("#fg-color").value,
|
||||
search_query: document.querySelector("#search-string").value,
|
||||
border_radius: document.querySelector("#border-radius").value
|
||||
};
|
||||
|
||||
const data = { bookmarks, settings }
|
||||
|
||||
fetch("/save-changes", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data)
|
||||
}).then(() => {
|
||||
window.location = "/";
|
||||
})
|
||||
}
|
||||
|
||||
function buildDataForBoomark(element) {
|
||||
const form = element.querySelector("form");
|
||||
|
||||
const title = form.querySelector("#bookmark-name");
|
||||
const link = form.querySelector("#bookmark-link");
|
||||
const image = form.querySelector("#bookmark-image");
|
||||
const imageSize = form.querySelector("#bookmark-imagesize");
|
||||
const iconPadding = form.querySelector("#bookmark-iconpadding");
|
||||
const color = form.querySelector("#bookmark-color");
|
||||
const hideBorder = form.querySelector("#bookmark-border");
|
||||
const order = form.querySelector("#bookmark-order");
|
||||
|
||||
return {
|
||||
title: title.value,
|
||||
image: image.value,
|
||||
image_size: imageSize.value,
|
||||
icon_padding: iconPadding.value,
|
||||
color: color.value,
|
||||
link: link.value,
|
||||
hide_border: hideBorder.checked,
|
||||
order: parseInt(order.value)
|
||||
};
|
||||
}
|
||||
|
||||
function clearBookmarkData(bookmark) {
|
||||
const a = bookmark.querySelector(".bookmark");
|
||||
const form = bookmark.querySelector("form");
|
||||
const bg = a.querySelector(".background");
|
||||
|
||||
const title = form.querySelector("#bookmark-name");
|
||||
const link = form.querySelector("#bookmark-link");
|
||||
const image = form.querySelector("#bookmark-image");
|
||||
const imageSize = form.querySelector("#bookmark-imagesize");
|
||||
const iconPadding = form.querySelector("#bookmark-iconpadding");
|
||||
const color = form.querySelector("#bookmark-color");
|
||||
const hideBorder = form.querySelector("#bookmark-border");
|
||||
const order = form.querySelector("#bookmark-order");
|
||||
|
||||
let orderValue = 0;
|
||||
document.querySelectorAll(".customizer").forEach(element => {
|
||||
orderValue = Math.max(orderValue, element.querySelector("#bookmark-order").value);
|
||||
});
|
||||
|
||||
title.value = "";
|
||||
link.value = "";
|
||||
image.value = "";
|
||||
imageSize.value = "contain";
|
||||
iconPadding.value = "";
|
||||
color.value = "#ffffff";
|
||||
hideBorder.value = false;
|
||||
order.value = orderValue + 10;
|
||||
bg.style.backgroundImage = "";
|
||||
a.style.backgroundColor = "#ffffff";
|
||||
}
|
||||
|
||||
function initBookmark(bookmark) {
|
||||
const a = bookmark.querySelector(".bookmark");
|
||||
const del = bookmark.querySelector(".delete");
|
||||
const form = bookmark.querySelector("form");
|
||||
const bg = a.querySelector(".background");
|
||||
|
||||
const title = form.querySelector("#bookmark-name");
|
||||
const link = form.querySelector("#bookmark-link");
|
||||
const image = form.querySelector("#bookmark-image");
|
||||
const imageSize = form.querySelector("#bookmark-imagesize");
|
||||
const iconPadding = form.querySelector("#bookmark-iconpadding");
|
||||
const color = form.querySelector("#bookmark-color");
|
||||
const hideBorder = form.querySelector("#bookmark-border");
|
||||
|
||||
del.addEventListener("click", () => {
|
||||
const question = title.value == "" ? "Do you want to delete this bookmark?" : `Do you want to delete '${title.value}'?`
|
||||
if (confirm(question)) {
|
||||
bookmark.parentElement.removeChild(bookmark);
|
||||
}
|
||||
});
|
||||
|
||||
link.addEventListener("change", () => a.setAttribute("href", link.value));
|
||||
image.addEventListener("change", () => bg.style.backgroundImage = `url(${image.value})`);
|
||||
imageSize.addEventListener("change", () => bg.style.backgroundSize = imageSize.value);
|
||||
iconPadding.addEventListener("change", () => a.style.padding = iconPadding.value);
|
||||
color.addEventListener("change", () => a.style.backgroundColor = color.value);
|
||||
hideBorder.addEventListener("change", () => a.style.boxShadow = hideBorder.value ? "" : "0px 0px 3px black");
|
||||
}
|
315
static/style.css
Normal file
315
static/style.css
Normal file
@ -0,0 +1,315 @@
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url("/icons.ttf");
|
||||
src: local('Material Icons'),
|
||||
local('MaterialIcons-Regular'),
|
||||
url("/icons.ttf") format('truetype');
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
transform: translateY(3px);
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px; /* Preferred icon size */
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
word-wrap: normal;
|
||||
white-space: nowrap;
|
||||
direction: ltr;
|
||||
|
||||
/* Support for all WebKit browsers. */
|
||||
|
||||
/* Support for Safari and Chrome. */
|
||||
text-rendering: optimizeLegibility;
|
||||
|
||||
/* Support for Firefox. */
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Support for IE. */
|
||||
font-feature-settings: 'liga';
|
||||
}
|
||||
|
||||
:root {
|
||||
--bookmark-width: 10em;
|
||||
--bookmark-height: 10em;
|
||||
--bookmark-margin: 0.5em;
|
||||
--bookmark-padding: 1em;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #2b2a33;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
#main-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
#bookmark-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
#search {
|
||||
margin: 3em auto;
|
||||
max-width: calc(100% - 2em);
|
||||
}
|
||||
|
||||
#search form {
|
||||
--input-font-size: 1.2em;
|
||||
--width: 30rem;
|
||||
--height: 2.5rem;
|
||||
|
||||
position: relative;
|
||||
display: block;
|
||||
width: var(--width);
|
||||
height: var(--height);
|
||||
|
||||
max-width: 100%;
|
||||
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border: 1px solid black;
|
||||
border-radius: 0.25em;
|
||||
}
|
||||
|
||||
#search form input[type="search"] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: inline-block;
|
||||
width: calc(100% - var(--height));
|
||||
height: var(--height);
|
||||
border: 0;
|
||||
padding: 0 0.5em;
|
||||
box-sizing: border-box;
|
||||
outline: 0;
|
||||
color: inherit;
|
||||
background-color: transparent;
|
||||
transition: all 0.2s ease-in-out;
|
||||
font-size: var(--input-font-size);
|
||||
}
|
||||
|
||||
#search form label[for="submit"] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: inline-block;
|
||||
width: var(--height);
|
||||
height: var(--height);
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
transition: all 0.2s ease-in-out;
|
||||
font-size: var(--input-font-size);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#search form label[for="submit"] span {
|
||||
transform: translate(0.3em, 0.3em);
|
||||
}
|
||||
|
||||
#search form input[type="submit"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bookmark {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
width: var(--bookmark-width);
|
||||
height: var(--bookmark-height);
|
||||
margin: var(--bookmark-margin);
|
||||
padding: var(--bookmark-padding);
|
||||
border-radius: var(--bookmark-border-radius);
|
||||
}
|
||||
|
||||
.bookmark:hover {
|
||||
transform: scale(1.1, 1.1);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.background {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
border-radius: var(--bookmark-border-radius);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 30em) {
|
||||
.bookmark {
|
||||
width: calc(var(--bookmark-width) * 0.5);
|
||||
height: calc(var(--bookmark-height) * 0.5);
|
||||
padding: calc(var(--bookmark-padding) * 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
#button {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0.25em;
|
||||
right: 0.5em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#customize-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.customizer {
|
||||
margin: 1em;
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 0.25em;
|
||||
}
|
||||
|
||||
.customizer > form {
|
||||
margin: 0.5em;
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
grid-auto-rows: max-content;
|
||||
gap: 0.25em;
|
||||
}
|
||||
|
||||
form label {
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
.customizer > form input {
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid black;
|
||||
color: inherit;
|
||||
padding: 0.25em;
|
||||
border-radius: 0.2em;
|
||||
}
|
||||
|
||||
.customizer > form input:not([type="checkbox"]):not([type="file"]):not([type="color"]):focus {
|
||||
outline-style: solid;
|
||||
outline-color: #aaa;
|
||||
}
|
||||
|
||||
#add-bookmark {
|
||||
margin: 1em;
|
||||
color: white;
|
||||
background-color: darkgreen;
|
||||
text-align: center;
|
||||
border-radius: 0.25em;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s ease-in-out;
|
||||
|
||||
width: 618px;
|
||||
height: 259px;
|
||||
line-height: 259px;
|
||||
}
|
||||
|
||||
#add-bookmark:hover,
|
||||
#add-bookmark:focus {
|
||||
background-color: green;
|
||||
transition: all 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
#add-bookmark .material-icons {
|
||||
font-size: 8em;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.delete {
|
||||
background-color: #cc0000;
|
||||
color: white;
|
||||
position: absolute;
|
||||
bottom: 0.5em;
|
||||
left: 0.5em;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
text-align: center;
|
||||
border-radius: 0.25em;
|
||||
box-shadow: 0px 0px 3px #333;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
.delete:hover,
|
||||
.delete:focus {
|
||||
transform: scale(1.25);
|
||||
transition: all 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
.fixed {
|
||||
position: fixed !important;
|
||||
}
|
||||
|
||||
#top-panel {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
padding: 1em;
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
|
||||
#top-panel h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
#top-panel > form {
|
||||
max-width: 30em;
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
grid-auto-rows: max-content;
|
||||
gap: 0.25em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#top-panel > form input {
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid black;
|
||||
color: inherit;
|
||||
padding: 0.25em;
|
||||
border-radius: 0.2em;
|
||||
}
|
||||
|
||||
#top-panel > form input:not([type="checkbox"]):not([type="file"]):not([type="color"]):focus {
|
||||
outline-style: solid;
|
||||
outline-color: #aaa;
|
||||
}
|
||||
|
||||
#top-panel > form > * {
|
||||
display: block;
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
<a href="{{ .Link }}" class="bookmark" style='background-color: {{ .Color }}; {{ if not .HideBorder }} box-shadow: 0px 0px 3px black; {{ end }}; {{ if not (eq .IconPadding "") }} padding: {{ .IconPadding }}; {{ end }}'>
|
||||
<div class="background" style='background-image: url("{{ .Image }}"); background-size: {{ .ImageSize }}'></div>
|
||||
</a>
|
||||
<a href="{{ .Link }}" class="bookmark" style='background-color: {{ .GetColor }}; {{ if not .HideBorder }} box-shadow: 0px 0px 3px black; {{ end }}; {{ if not (eq .IconPadding "") }} padding: {{ .IconPadding }}; {{ end }}'>
|
||||
<div class="background" style='background-image: url("{{ .Image }}"); background-size: {{ .GetImageSize }}'></div>
|
||||
</a>
|
||||
|
33
templates/customize-bookmark.html
Normal file
33
templates/customize-bookmark.html
Normal file
@ -0,0 +1,33 @@
|
||||
<div class="customizer">
|
||||
<a href="{{ .Link }}" class="bookmark" style='background-color: {{ .GetColor }}; {{ if not .HideBorder }} box-shadow: 0px 0px 3px black; {{ end }}; {{ if not (eq .IconPadding "") }} padding: {{ .IconPadding }}; {{ end }}'>
|
||||
<div class="background" style='background-image: url("{{ .Image }}"); background-size: {{ .GetImageSize }}'></div>
|
||||
</a>
|
||||
|
||||
<a class="delete"><span class="material-icons">delete</span></a>
|
||||
|
||||
<form>
|
||||
<label for="bookmark-name">{{ .Translate "Name" }}</label>
|
||||
<input type="text" id="bookmark-name" name="bookmark-name" value="{{ .Title }}" />
|
||||
|
||||
<label for="bookmark-link">{{ .Translate "Link" }}</label>
|
||||
<input type="url" id="bookmark-link" name="bookmark-link" value="{{ .Link }}" />
|
||||
|
||||
<label for="bookmark-image">{{ .Translate "Image" }}</label>
|
||||
<input type="url" id="bookmark-image" name="bookmark-image" value="{{ .Image }}" />
|
||||
|
||||
<label for="bookmark-imagesize">{{ .Translate "Image size" }}</label>
|
||||
<input list="image-sizes" id="bookmark-imagesize" name="bookmark-imagesize" value="{{ .ImageSize }}" />
|
||||
|
||||
<label for="bookmark-iconpadding">{{ .Translate "Image padding" }}</label>
|
||||
<input list="image-paddings" id="bookmark-iconpadding" name="bookmark-iconpadding" value="{{ .IconPadding }}" />
|
||||
|
||||
<label for="bookmark-color">{{ .Translate "Color" }}</label>
|
||||
<input type="color" id="bookmark-color" name="bookmark-color" value="{{ .Color }}" />
|
||||
|
||||
<label for="bookmark-border">{{ .Translate "Hide border" }}</label>
|
||||
<input type="checkbox" id="bookmark-border" name="bookmark-border" {{ if .HideBorder }} checked {{ end }} />
|
||||
|
||||
<label for="bookmark-order">{{ .Translate "Order priority" }}</label>
|
||||
<input type="number" id="bookmark-order" name="bookmark-order" value="{{ .Order }}" />
|
||||
</form>
|
||||
</div>
|
70
templates/customize.html
Normal file
70
templates/customize.html
Normal file
@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ .Translate "Customize Homepage" }}</title>
|
||||
<link rel="stylesheet" type="text/css" href="/style.css" />
|
||||
<script src="/customize.js"></script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bookmark-border-radius: {{ .Settings.BorderRadius }};
|
||||
}
|
||||
body {
|
||||
background-color: {{ .Settings.Background }};
|
||||
color: {{ .Settings.Foreground }};
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<datalist id="image-sizes">
|
||||
<option value="cover">cover</option>
|
||||
<option value="contain">contain</option>
|
||||
<option value="100%">100%</option>
|
||||
<option value="0px">0px</option>
|
||||
</datalist>
|
||||
<datalist id="image-paddings">
|
||||
<option value="10%">10%</option>
|
||||
<option value="0px">0px</option>
|
||||
<option value="10px">10px</option>
|
||||
<option value="1em">1em</option>
|
||||
</datalist>
|
||||
<datalist id="search-strings">
|
||||
<option value="https://www.google.de/search?q=%s">Google</option>
|
||||
<option value="https://duckduckgo.com/?q=%s">DuckDuckGo</option>
|
||||
<option value="https://www.startpage.com/do/search?query=%s">Startpage</option>
|
||||
<option value="https://www.bing.com/search?q=%s">Bing</option>
|
||||
<option value="https://de.search.yahoo.com/search?p=%s">Yahoo</option>
|
||||
</datalist>
|
||||
<main>
|
||||
<a id="button" class="fixed" href="#">
|
||||
<span class="material-icons">save</span>
|
||||
</a>
|
||||
<div id="top-panel">
|
||||
<h1>{{ .Translate "Settings" }}</h1>
|
||||
<form>
|
||||
<label for="search-string">{{ .Translate "Search engine" }}</label>
|
||||
<input list="search-strings" id="search-string" name="search-string" value="{{ .Settings.Search }}" />
|
||||
|
||||
<label for="bg-color">{{ .Translate "Background color" }}</label>
|
||||
<input type="color" id="bg-color" name="bg-color" value="{{ .Settings.Background }}" />
|
||||
|
||||
<label for="fg-color">{{ .Translate "Foreground color" }}</label>
|
||||
<input type="color" id="fg-color" name="fg-color" value="{{ .Settings.Foreground }}" />
|
||||
|
||||
<label for="border-radius">{{ .Translate "Border radius" }}</label>
|
||||
<input type="text" id="border-radius" name="border-radius" value="{{ .Settings.BorderRadius }}" />
|
||||
</form>
|
||||
</div>
|
||||
<section id="customize-container">
|
||||
{{ range .Bookmarks }}
|
||||
{{ template "customize-bookmark.html" . }}
|
||||
{{ end }}
|
||||
|
||||
<a id="add-bookmark"><span class="material-icons">add</span></a>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
@ -6,11 +6,24 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ .Translate "Homepage" }}</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/style.css" />
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bookmark-border-radius: {{ .Settings.BorderRadius }};
|
||||
}
|
||||
body {
|
||||
background-color: {{ .Settings.Background }};
|
||||
color: {{ .Settings.Foreground }};
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<main id="main-container">
|
||||
<a id="button" href="/customize">
|
||||
<span class="material-icons">settings</span>
|
||||
</a>
|
||||
<section id="search">
|
||||
<form method="post" action="/search">
|
||||
<datalist id="bookmarks">
|
||||
@ -31,4 +44,4 @@
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
@ -1,175 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url("/icons.ttf");
|
||||
src: local('Material Icons'),
|
||||
local('MaterialIcons-Regular'),
|
||||
url("/icons.ttf") format('truetype');
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
transform: translateY(3px);
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px; /* Preferred icon size */
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
word-wrap: normal;
|
||||
white-space: nowrap;
|
||||
direction: ltr;
|
||||
|
||||
/* Support for all WebKit browsers. */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
/* Support for Safari and Chrome. */
|
||||
text-rendering: optimizeLegibility;
|
||||
|
||||
/* Support for Firefox. */
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Support for IE. */
|
||||
font-feature-settings: 'liga';
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: {{ .Background }};
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
#bookmark-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
#search {
|
||||
margin: 3em auto;
|
||||
max-width: calc(100% - 2em);
|
||||
}
|
||||
|
||||
#search form {
|
||||
--input-font-size: 1.2em;
|
||||
--width: 30rem;
|
||||
--height: 2.5rem;
|
||||
|
||||
position: relative;
|
||||
display: block;
|
||||
width: var(--width);
|
||||
height: var(--height);
|
||||
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#search form input[type="search"] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: inline-block;
|
||||
width: calc(100% - var(--height));
|
||||
height: var(--height);
|
||||
border: 1px solid black;
|
||||
padding: 0 0.5em;
|
||||
box-sizing: border-box;
|
||||
outline: 0;
|
||||
background-color: black;
|
||||
color: white;
|
||||
transition: all 0.2s ease-in-out;
|
||||
border-radius: 0.25em 0 0 0.25em;
|
||||
font-size: var(--input-font-size);
|
||||
}
|
||||
|
||||
#search form input[type="search"]:focus {
|
||||
box-shadow: 0px 0px 3px gray;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
#search form input[type="search"]:focus + label[for="submit"] {
|
||||
background-color: #111;
|
||||
box-shadow: 0px 0px 3px gray;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
#search form label[for="submit"] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: inline-block;
|
||||
width: var(--height);
|
||||
height: var(--height);
|
||||
margin-left: 0;
|
||||
border: 1px solid black;
|
||||
box-sizing: border-box;
|
||||
background-color: black;
|
||||
color: white;
|
||||
transition: all 0.2s ease-in-out;
|
||||
border-radius: 0 0.25em 0.25em 0;
|
||||
font-size: var(--input-font-size);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#search form label[for="submit"] span {
|
||||
transform: translate(0.3em, 0.3em);
|
||||
}
|
||||
|
||||
#search form input[type="submit"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bookmark {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
width: {{ .IconWidth }};
|
||||
height: {{ .IconHeight }};
|
||||
margin: {{ .IconMargin }};
|
||||
padding: {{ .IconPadding }};
|
||||
border-radius: {{ .BorderRadius }};
|
||||
}
|
||||
|
||||
.bookmark:hover {
|
||||
transform: scale(1.1, 1.1);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.background {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
border-radius: {{ .BorderRadius }};
|
||||
}
|
||||
|
||||
@media screen and (max-width: 30em) {
|
||||
.bookmark {
|
||||
width: calc({{ .IconWidth }} * 0.5);
|
||||
height: calc({{ .IconHeight }} * 0.5);
|
||||
padding: calc({{ .IconPadding }} * 0.5);
|
||||
}
|
||||
}
|
27
text.go
27
text.go
@ -22,22 +22,37 @@ var (
|
||||
Languages = map[language.Tag]Text{
|
||||
language.English: {
|
||||
Strings: map[string]string{
|
||||
"Homepage": "Homepage",
|
||||
"Search": "Search",
|
||||
"Homepage": "Homepage",
|
||||
"Search": "Search",
|
||||
"Search engine": "Search engine",
|
||||
"Settings": "Settings",
|
||||
"Background color": "Background color",
|
||||
"Foreground color": "Foreground color",
|
||||
"Border radius": "Border radius",
|
||||
},
|
||||
},
|
||||
|
||||
language.German: {
|
||||
Strings: map[string]string{
|
||||
"Homepage": "Homepage",
|
||||
"Search": "Suche",
|
||||
"Homepage": "Homepage",
|
||||
"Search": "Suche",
|
||||
"Search engine": "Suchmaschine",
|
||||
"Settings": "Einstellungen",
|
||||
"Background color": "Hintergrundfarbe",
|
||||
"Foreground color": "Textfarbe",
|
||||
"Border radius": "Randradius",
|
||||
},
|
||||
},
|
||||
|
||||
language.Japanese: {
|
||||
Strings: map[string]string{
|
||||
"Homepage": "ホームページ",
|
||||
"Search": "検索",
|
||||
"Homepage": "ホームページ",
|
||||
"Search": "検索",
|
||||
"Search engine": "検索エンジン",
|
||||
"Settings": "設定",
|
||||
"Background color": "背景色",
|
||||
"Foreground color": "描画色",
|
||||
"Border radius": "境界半径",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
23
tmpl_data.go
23
tmpl_data.go
@ -13,3 +13,26 @@ func (d *TmplData) Translate(str string) string {
|
||||
func (d *TmplData) Language() string {
|
||||
return d.text.Language
|
||||
}
|
||||
|
||||
type CustomizeData struct {
|
||||
text *Text
|
||||
Bookmarks []BookmarkData
|
||||
Settings *Settings
|
||||
}
|
||||
|
||||
func (d *CustomizeData) Translate(str string) string {
|
||||
return d.text.Translate(str)
|
||||
}
|
||||
|
||||
func (d *CustomizeData) Language() string {
|
||||
return d.text.Language
|
||||
}
|
||||
|
||||
type BookmarkData struct {
|
||||
*Bookmark
|
||||
text *Text
|
||||
}
|
||||
|
||||
func (bd *BookmarkData) Translate(str string) string {
|
||||
return bd.text.Translate(str)
|
||||
}
|
||||
|
35
utils.go
Normal file
35
utils.go
Normal file
@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
func Must[T any](value T, err error) T {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func GetValue[K any, T any](session *sessions.Session, key K) (T, bool) {
|
||||
value, ok := session.Values[key]
|
||||
if !ok && value == nil {
|
||||
return *new(T), false
|
||||
}
|
||||
|
||||
castedValue, ok := value.(T)
|
||||
if !ok {
|
||||
return *new(T), false
|
||||
}
|
||||
|
||||
return castedValue, true
|
||||
}
|
||||
|
||||
func GetValueDefault[K any, T any](session *sessions.Session, key K, defaultValue T) T {
|
||||
v, ok := GetValue[K, T](session, key)
|
||||
if !ok {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
Loading…
Reference in New Issue
Block a user