| -rw-r--r-- | Dockerfile | 2 | ||||
| -rw-r--r-- | internal/frontmatter/parser.go | 1 | ||||
| -rw-r--r-- | internal/lightgallery/block.go | 11 | ||||
| -rw-r--r-- | internal/lightgallery/html_renderer.go | 30 | ||||
| -rw-r--r-- | internal/lightgallery/parser.go | 35 | ||||
| -rw-r--r-- | main.go | 14 | ||||
| -rwxr-xr-x | saya-today-web | bin | 19752119 -> 0 bytes | |||
| -rw-r--r-- | static/input.css | 32 | ||||
| -rw-r--r-- | views/layouts/general-page.html | 2 | ||||
| -rw-r--r-- | views/pages/blog-catalogue.html | 4 | ||||
| -rw-r--r-- | views/pages/blog-page.html | 14 |
11 files changed, 45 insertions, 100 deletions
@@ -16,6 +16,4 @@ COPY --from=build-stage /builddir/views /app/views COPY --from=build-stage /builddir/locale/*.yaml /app/locale/ COPY --from=build-stage /builddir/config/config.yaml /app/config.yaml -RUN apk add --no-cache tzdata - ENTRYPOINT [ "/app/sayana-demo", "-c", "/app/config.yaml" ] diff --git a/internal/frontmatter/parser.go b/internal/frontmatter/parser.go index 3c00a7f..1a521c0 100644 --- a/internal/frontmatter/parser.go +++ b/internal/frontmatter/parser.go @@ -16,7 +16,6 @@ type Metadata struct { Thumbnail string `yaml:"thumbnail"` Tags []string `yaml:"tags"` Geolocation string `yaml:"geolocation"` - Timezone string `yaml:"timezone"` } func ParseFrontmatter(content []byte) (metadata *Metadata, markdown []byte, err error) { diff --git a/internal/lightgallery/block.go b/internal/lightgallery/block.go index 8eb7c9e..dade2ea 100644 --- a/internal/lightgallery/block.go +++ b/internal/lightgallery/block.go @@ -1,21 +1,16 @@ package lightgallery -import ( - "time" - - "github.com/yuin/goldmark/ast" -) +import "github.com/yuin/goldmark/ast" // LightGalleryBlock represents a light gallery block in the AST type LightGalleryBlock struct { ast.BaseBlock - Images []LightGalleryImage - Location *time.Location + Images []LightGalleryImage } type LightGalleryImage struct { URL string - Caption []byte + Caption string } var KindLightGalleryBlock = ast.NewNodeKind("LightGalleryBlock") diff --git a/internal/lightgallery/html_renderer.go b/internal/lightgallery/html_renderer.go index 850cd95..e611f0b 100644 --- a/internal/lightgallery/html_renderer.go +++ b/internal/lightgallery/html_renderer.go @@ -1,13 +1,11 @@ package lightgallery import ( - "bytes" "fmt" "math/rand" "strings" "time" - "github.com/yuin/goldmark" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/renderer" "github.com/yuin/goldmark/renderer/html" @@ -16,18 +14,11 @@ import ( type LightGalleryHTMLRenderer struct { html.Config - md goldmark.Markdown } func NewLightGalleryHTMLRenderer(opts ...html.Option) renderer.NodeRenderer { r := &LightGalleryHTMLRenderer{ Config: html.NewConfig(), - md: goldmark.New( - goldmark.WithRendererOptions( - html.WithHardWraps(), - html.WithXHTML(), - ), - ), } for _, opt := range opts { opt.SetHTMLOption(&r.Config) @@ -62,17 +53,7 @@ func (r *LightGalleryHTMLRenderer) renderLightGallery(w util.BufWriter, source [ imageUrlWithoutExt := strings.Join(imageUrlSegments[:len(imageUrlSegments)-1], ".") imageNameParts := strings.Split(imageUrlWithoutExt, "-") - var captionBuf bytes.Buffer - captionHTML := img.Caption - if err := r.md.Convert(img.Caption, &captionBuf); err == nil { - captionHTML = captionBuf.Bytes() - captionHTML = bytes.TrimPrefix(captionHTML, []byte("<p>")) - captionHTML = bytes.TrimSuffix(captionHTML, []byte("</p>\n")) - captionHTML = bytes.TrimSuffix(captionHTML, []byte("</p>")) - } - dayDate, _ := time.Parse("20060102 150405", imageNameParts[len(imageNameParts)-2]+" "+imageNameParts[len(imageNameParts)-1]) - dayDate = dayDate.In(gallery.Location) dynamicElements = append(dynamicElements, fmt.Sprintf(`{ src: "https://f003.backblazeb2.com/file/sayana-photos/full/%s", @@ -89,7 +70,7 @@ func (r *LightGalleryHTMLRenderer) renderLightGallery(w util.BufWriter, source [ <p class="grow !text-[1vmax]/[0.9] text-left font-spectral text-main-dark">%s</p> <p class="!text-[1vmax]/[0.9] text-right font-spectral text-secondary">%s</p> </div>`+"`"+` - }`, img.URL, img.URL, util.EscapeHTML(captionHTML), imageUrlWithoutExt, imageUrlWithoutExt, captionHTML, dayDate.Format("2006-01-02 15:04:05 -07:00"))) + }`, img.URL, img.URL, img.Caption, imageUrlWithoutExt, imageUrlWithoutExt, img.Caption, dayDate.Format("2006-01-02 15:04:05 -07:00"))) } w.WriteString(fmt.Sprintf(` @@ -136,6 +117,15 @@ window.addEventListener("resize", () => { return ast.WalkContinue, nil } +func escapeHTML(s string) string { + s = strings.ReplaceAll(s, "&", "&") + s = strings.ReplaceAll(s, "<", "<") + s = strings.ReplaceAll(s, ">", ">") + s = strings.ReplaceAll(s, "\"", """) + s = strings.ReplaceAll(s, "'", "'") + return s +} + func generateDivId(length int) string { const charset = "abcdefghijklmnopqrstuvwxyz" seededRand := rand.New(rand.NewSource(time.Now().UnixNano())) // Seed with current time diff --git a/internal/lightgallery/parser.go b/internal/lightgallery/parser.go index daec797..0ce209c 100644 --- a/internal/lightgallery/parser.go +++ b/internal/lightgallery/parser.go @@ -2,9 +2,7 @@ package lightgallery import ( "bytes" - "log/slog" - "regexp" - "time" + "strings" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/parser" @@ -24,52 +22,43 @@ func (p *LightGalleryParser) Trigger() []byte { func (p *LightGalleryParser) Open(parent ast.Node, reader text.Reader, pc parser.Context) (ast.Node, parser.State) { line, _ := reader.PeekLine() - if !bytes.HasPrefix(line, []byte("{Gallery:")) { + if !bytes.HasPrefix(line, []byte("{Gallery}")) { return nil, parser.NoChildren } - r := regexp.MustCompile(`^\{Gallery:([A-Za-z0-9\+\-/]+)\}$`) - trimmed := bytes.TrimSpace(line) - parts := r.FindSubmatch(trimmed) - if len(parts) < 2 { - slog.Warn("invalid gallery header format", slog.String("line", string(trimmed)), slog.Int("submatch_count", len(parts))) + if !bytes.Equal(trimmed, []byte("{Gallery}")) { return nil, parser.NoChildren } - loc, err := time.LoadLocation(string(parts[1])) - if err != nil { - slog.Warn("invalid location specified for a gallery", slog.String("error", err.Error()), slog.String("line", string(trimmed)), slog.String("location", string(parts[1]))) - return nil, parser.NoChildren - } - - return &LightGalleryBlock{Location: loc}, parser.NoChildren + return &LightGalleryBlock{}, parser.NoChildren } func (p *LightGalleryParser) Continue(node ast.Node, reader text.Reader, pc parser.Context) parser.State { line, segment := reader.PeekLine() if len(line) == 0 || segment.Len() == 0 { - return parser.Continue | parser.NoChildren + return parser.Close } trimmed := bytes.TrimSpace(line) - if bytes.Equal(trimmed, []byte("{Gallery}")) || bytes.Equal(trimmed, []byte("{/Gallery}")) { + if bytes.Equal(trimmed, []byte("{Gallery}")) { reader.AdvanceLine() return parser.Close } gallery := node.(*LightGalleryBlock) + lineStr := string(trimmed) - parts := bytes.SplitN(trimmed, []byte{'|'}, 2) - url := bytes.TrimSpace(parts[0]) + parts := strings.SplitN(lineStr, "|", 2) + url := strings.TrimSpace(parts[0]) + caption := "" - caption := make([]byte, 0) if len(parts) > 1 { - caption = bytes.TrimSpace(parts[1]) + caption = strings.TrimSpace(parts[1]) } gallery.Images = append(gallery.Images, LightGalleryImage{ - URL: string(url), + URL: url, Caption: caption, }) @@ -121,7 +121,7 @@ func main() { querySort := c.Query("sort") if querySort == "" { - querySort = "publicationDateDesc" + querySort = "titleAsc" } encodedQuery := c.Request().URI().QueryString() @@ -148,7 +148,7 @@ func main() { tagsMap := make(map[string]int) for _, page := range pages { - slog.Debug("enlist page for catalogue", slog.Any("page", page), slog.String("endpoint", "/"+lang+"/blog")) + slog.Debug("enlist page for catalogue", slog.Any("page", page), slog.String("endpoint", "/")) for _, tag := range page.Metadata.Tags { tagsMap[tag]++ } @@ -265,7 +265,7 @@ func main() { "Link": page.Link, "ArticleLink": "/" + lang + "/blog/" + page.FileName, "Title": page.Metadata.Title, - "PublishedTime": page.Metadata.PublishedTime.Format("2006-01-02 15:04:05 -07:00"), + "PublishedTime": page.Metadata.PublishedTime.Format("2006-01-02 15:04:05 -0700"), "ActionDate": page.Metadata.ActionDate, "ShortDescription": page.Metadata.ShortDescription, "Thumbnail": page.Metadata.Thumbnail, @@ -287,12 +287,12 @@ func main() { case "actionDateDesc": return strings.Compare(b["ActionDate"], a["ActionDate"]) case "publicationDateAsc": - publishedTimeA, _ := time.Parse("2006-01-02 15:04:05 -07:00", a["PublishedTime"]) - publishedTimeB, _ := time.Parse("2006-01-02 15:04:05 -07:00", b["PublishedTime"]) + publishedTimeA, _ := time.Parse(time.RFC3339, a["PublishedTime"]) + publishedTimeB, _ := time.Parse(time.RFC3339, b["PublishedTime"]) return publishedTimeA.Compare(publishedTimeB) case "publicationDateDesc": - publishedTimeA, _ := time.Parse("2006-01-02 15:04:05 -07:00", a["PublishedTime"]) - publishedTimeB, _ := time.Parse("2006-01-02 15:04:05 -07:00", b["PublishedTime"]) + publishedTimeA, _ := time.Parse(time.RFC3339, a["PublishedTime"]) + publishedTimeB, _ := time.Parse(time.RFC3339, b["PublishedTime"]) return publishedTimeB.Compare(publishedTimeA) } return 0 diff --git a/saya-today-web b/saya-today-web Binary files differdeleted file mode 100755 index b6522e8..0000000 --- a/saya-today-web +++ /dev/null diff --git a/static/input.css b/static/input.css index cfc7391..8776b25 100644 --- a/static/input.css +++ b/static/input.css @@ -88,15 +88,6 @@ --squares-and-triangles-background-height-coef: 15; } -a { - color: var(--main-medium-color) !important; - text-decoration: underline; -} - -a:hover { - color: var(--main-light-color) !important; -} - .bg-interlocked-hexagons { background-image: var(--interlocked-hexagons-background); background-repeat: var(--interlocked-hexagons-background-repeat); @@ -163,6 +154,10 @@ a:hover { background-size: calc(var(--squares-and-triangles-background-width-coef) * 1vw) calc(var(--squares-and-triangles-background-height-coef) * 1vw); } +body:has(.lg-container:not([class~="lg-inline"])) .desktop-sidebar-custom { + display: none !important; +} + .mobile-topbar-custom { width: 90vw; height: 8%; @@ -170,7 +165,7 @@ a:hover { margin-left: 5vw; } -body:has(.lg-container:not([class~="lg-inline"])) #sidebar { +body:has(.lg-container:not([class~="lg-inline"])) .mobile-topbar-custom { display: none !important; } @@ -341,11 +336,6 @@ body:has(.lg-container:not([class~="lg-inline"])) #sidebar { } } -body[data-theme~="night"] .leaflet-tile, -body[data-theme~="ram"] .leaflet-tile { - filter: invert(1) hue-rotate(190deg) contrast(1.1) brightness(1.2) !important; -} - @media (min-aspect-ratio: 0.8/1) { .ar-gt-0\.8\:mr-\[1\.2vmin\] { margin-right: 1.2vmin; @@ -426,10 +416,6 @@ body[data-theme~="ram"] .leaflet-tile { height: 10vh; } - .ar-lt-0\.8\:h-\[30vh\] { - height: 30vh; - } - .ar-lt-0\.8\:h-\[90vh\] { height: 90vh; } @@ -438,10 +424,6 @@ body[data-theme~="ram"] .leaflet-tile { width: 8vh; } - .ar-lt-0\.8\:w-\[80\%\] { - width: 80%; - } - .ar-lt-0\.8\:w-\[90vw\] { width: 90vw; } @@ -454,10 +436,6 @@ body[data-theme~="ram"] .leaflet-tile { max-height: 80%; } - .ar-lt-0\.8\:max-h-\[calc\(100\%-25vh\)\] { - max-height: calc(100% - 25vh); - } - .ar-lt-0\.8\:text-\[5vh\] { font-size: 5vh; } diff --git a/views/layouts/general-page.html b/views/layouts/general-page.html index eab7cd1..121a5cd 100644 --- a/views/layouts/general-page.html +++ b/views/layouts/general-page.html @@ -33,7 +33,7 @@ switchTheme(savedTheme); </script> - <div id="sidebar" class="bg-sidebar flex sticky flex-col ar-gt-0.8:items-center shadow-2xl z-20 w-[5vw] ar-lt-0.8:w-[90vw] ar-lt-0.4:w-[100vw] h-screen ar-lt-0.8:h-[8vh] ar-lt-0.8:ml-[5vw] ar-lt-0.4:ml-0"> + <div class="bg-sidebar flex sticky flex-col ar-gt-0.8:items-center shadow-2xl z-20 w-[5vw] ar-lt-0.8:w-[90vw] ar-lt-0.4:w-[100vw] h-screen ar-lt-0.8:h-[8vh] ar-lt-0.8:ml-[5vw] ar-lt-0.4:ml-0"> <div class="logo-custom ar-lt-0.8:hidden text-sidebar font-patua font-extrabold text-center relative z-20"> saya.today </div> diff --git a/views/pages/blog-catalogue.html b/views/pages/blog-catalogue.html index 6c0f629..4cda19d 100644 --- a/views/pages/blog-catalogue.html +++ b/views/pages/blog-catalogue.html @@ -1,5 +1,5 @@ {{ define "body" }} -<div class="flex flex-row ar-lt-0.8:flex-col max-h-[calc(100%-7vmax)] ar-lt-0.8:max-h-[calc(100%-25vh)] flex-1 text-[1vmax]"> +<div class="flex flex-row ar-lt-0.8:flex-col max-h-[calc(100%-6vmax)] ar-lt-0.8:max-h-[80%] flex-1 text-[1vmax]"> <form hx-get="/api/v1/blog-search" hx-vals='{"lang": "{{ .Lang }}"}' hx-target=".blog-cards" hx-swap="innerHTML" class="tags-list ar-gt-0.8:min-w-[10vh] ar-gt-0.8:max-w-[20vh] ar-gt-0.8:w-fit ar-lt-0.8:w-[100%] flex shrink-0 flex-col overflow-y-auto ar-gt-0.8:mr-[1.2vmin]"> <fieldset> @@ -91,7 +91,7 @@ checkboxes.forEach(checkbox => { checkbox.checked = true; }); - const sortRadio = tagsContainer.querySelector('#sortPublicationDateDesc'); + const sortRadio = tagsContainer.querySelector('#sortTitleAsc'); sortRadio.checked = true; }); } diff --git a/views/pages/blog-page.html b/views/pages/blog-page.html index 330a1ac..5f001d6 100644 --- a/views/pages/blog-page.html +++ b/views/pages/blog-page.html @@ -15,11 +15,11 @@ {{ end }} {{ define "body" }} -<div class="bg-background-light flex flex-col grow overflow-y-auto inset-shadow p-[2.4vmin] max-h-[calc(100%-7vmax)] ar-lt-0.8:max-h-[calc(100%-25vh)]"> +<div class="bg-background-light flex flex-col grow overflow-y-auto inset-shadow p-[2.4vmin] ar-lt-0.8:max-h-[80%]"> {{ .ParsedMarkdown }} {{- if .MapLocationX }} <hr class="border-t-4 border-dotted border-main-dark mt-[0.8vmin] mb-[0.8vmin] w-[80%] ml-auto mr-auto"> - <div id="location-map" class="relative p-[0.8vmin] flex-none ml-auto mr-auto w-[60%] ar-lt-0.8:w-[80%] h-[40vh] ar-lt-0.8:h-[30vh]"></div> + <div id="location-map" class="relative p-[0.8vmin] flex-none ml-auto mr-auto w-[60%] h-[40vh]"></div> <hr class="border-t-4 border-dotted border-main-dark mt-[0.8vmin] mb-[0.8vmin] w-[80%] ml-auto mr-auto"> {{- end }} </div> @@ -34,10 +34,10 @@ <script> function initLocationMap(id, x, y, relativeErrorMeters = 0) { - var map = L.map(id).setView([x, y], 8); + var map = L.map(id).setView([x, y], 13); L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { - maxZoom: 18, + maxZoom: 19, attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' }).addTo(map); @@ -50,11 +50,7 @@ }).addTo(map); } - var marker = L.marker([x, y], { - title: '{{ .Title }}', - }).addTo(map) - .bindPopup('<span><b>{{ .Title }}</b><br><a href="https://www.openstreetmap.org/#map=13/{{ .MapLocationX }}/{{ .MapLocationY }}">{{ .MapLocationX }}, {{ .MapLocationY }}</a></span>') - .openPopup(); + var marker = L.marker([x, y]).addTo(map); } document.addEventListener('DOMContentLoaded', () => {initLocationMap( |