feat: page content
This commit is contained in:
+1
-4
@@ -1,11 +1,8 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules
|
node_modules
|
||||||
/tmp
|
/tmp
|
||||||
|
/tmp/*
|
||||||
/build
|
/build
|
||||||
/.svelte-kit
|
|
||||||
/package
|
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
!.env.example
|
!.env.example
|
||||||
vite.config.js.timestamp-*
|
|
||||||
vite.config.ts.timestamp-*
|
|
||||||
|
|||||||
-46
@@ -1,46 +0,0 @@
|
|||||||
# Stage 1: Build the application
|
|
||||||
FROM node:24-slim AS builder
|
|
||||||
|
|
||||||
# Enable corepack to use pnpm
|
|
||||||
RUN corepack enable
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy configuration files
|
|
||||||
COPY package.json pnpm-lock.yaml .npmrc ./
|
|
||||||
|
|
||||||
# Install all dependencies (including devDependencies)
|
|
||||||
RUN pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
# Copy the rest of the application code
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Build the SvelteKit application
|
|
||||||
RUN pnpm run build
|
|
||||||
|
|
||||||
# Stage 2: Run the application
|
|
||||||
FROM node:24-slim AS runner
|
|
||||||
|
|
||||||
# Enable corepack to use pnpm
|
|
||||||
RUN corepack enable
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy the build output and necessary files for production
|
|
||||||
COPY --from=builder /app/build ./build
|
|
||||||
COPY --from=builder /app/package.json ./package.json
|
|
||||||
COPY --from=builder /app/pnpm-lock.yaml ./pnpm-lock.yaml
|
|
||||||
COPY --from=builder /app/.npmrc ./.npmrc
|
|
||||||
|
|
||||||
# Install only production dependencies
|
|
||||||
RUN pnpm install --prod --frozen-lockfile
|
|
||||||
|
|
||||||
# Expose the port the app runs on (SvelteKit defaults to 3000)
|
|
||||||
EXPOSE 3000
|
|
||||||
|
|
||||||
# Set environment variables
|
|
||||||
ENV NODE_ENV=production
|
|
||||||
ENV PORT=3000
|
|
||||||
|
|
||||||
# Run the application
|
|
||||||
CMD ["node", "build"]
|
|
||||||
@@ -6,13 +6,15 @@ tool github.com/a-h/templ/cmd/templ
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/a-h/templ v0.3.977
|
github.com/a-h/templ v0.3.977
|
||||||
|
github.com/alicebob/miniredis/v2 v2.36.1
|
||||||
|
github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab
|
||||||
github.com/studio-b12/gowebdav v0.12.0
|
github.com/studio-b12/gowebdav v0.12.0
|
||||||
github.com/valkey-io/valkey-go v1.0.72
|
github.com/valkey-io/valkey-go v1.0.72
|
||||||
|
golang.org/x/image v0.36.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect
|
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect
|
||||||
github.com/alicebob/miniredis/v2 v2.36.1 // indirect
|
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/cli/browser v1.3.0 // indirect
|
github.com/cli/browser v1.3.0 // indirect
|
||||||
@@ -22,7 +24,6 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/natefinch/atomic v1.0.1 // indirect
|
github.com/natefinch/atomic v1.0.1 // indirect
|
||||||
github.com/yuin/gopher-lua v1.1.1 // indirect
|
github.com/yuin/gopher-lua v1.1.1 // indirect
|
||||||
golang.org/x/image v0.36.0 // indirect
|
|
||||||
golang.org/x/mod v0.26.0 // indirect
|
golang.org/x/mod v0.26.0 // indirect
|
||||||
golang.org/x/net v0.48.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
golang.org/x/sync v0.16.0 // indirect
|
golang.org/x/sync v0.16.0 // indirect
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
|||||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
|
github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab h1:VYNivV7P8IRHUam2swVUNkhIdp0LRRFKe4hXNnoZKTc=
|
||||||
|
github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
@@ -51,9 +53,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
|
||||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
|
||||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||||
|
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||||
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ var ErrNotAnImage = errors.New("not an image")
|
|||||||
func (s *Service) getMime(path string) (mimeType string, err error) {
|
func (s *Service) getMime(path string) (mimeType string, err error) {
|
||||||
info, err := s.fs.Stat(path)
|
info, err := s.fs.Stat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("image file info could not be fetched")
|
err = fmt.Errorf("image file info could not be fetched: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if mimer, ok := info.(interface{ ContentType() string }); ok {
|
if mimer, ok := info.(interface{ ContentType() string }); ok {
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"compress/flate"
|
"compress/flate"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UIDFromPath(path string) (uid string, err error) {
|
func UIDFromPath(path string) (uid string, err error) {
|
||||||
@@ -24,6 +26,12 @@ func UIDFromPath(path string) (uid string, err error) {
|
|||||||
return base64.RawURLEncoding.EncodeToString(b.Bytes()), nil
|
return base64.RawURLEncoding.EncodeToString(b.Bytes()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func URLFromPath(path string) (url string, err error) {
|
||||||
|
uid, err := UIDFromPath(path)
|
||||||
|
url = "/images/" + uid
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func PathFromUID(uid string) (string, error) {
|
func PathFromUID(uid string) (string, error) {
|
||||||
// 1. Decode the Base64 string back to compressed bytes
|
// 1. Decode the Base64 string back to compressed bytes
|
||||||
compressedBytes, err := base64.RawURLEncoding.DecodeString(uid)
|
compressedBytes, err := base64.RawURLEncoding.DecodeString(uid)
|
||||||
@@ -45,3 +53,16 @@ func PathFromUID(uid string) (string, error) {
|
|||||||
|
|
||||||
return out.String(), nil
|
return out.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SourceSet(src string) string {
|
||||||
|
sb := strings.Builder{}
|
||||||
|
|
||||||
|
for i := range 19 {
|
||||||
|
sb.WriteString(fmt.Sprintf("%s?w=%d %dw, ", src, 100*(i+1), 100*(i+1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 20
|
||||||
|
sb.WriteString(fmt.Sprintf("%s?w=%d %dw", src, 100*(i+1), 100*(i+1)))
|
||||||
|
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ type PageHeader struct {
|
|||||||
UID string
|
UID string
|
||||||
Title string
|
Title string
|
||||||
CoverImageUID string
|
CoverImageUID string
|
||||||
|
md []byte // markdown of the page //markdown of the page
|
||||||
}
|
}
|
||||||
|
|
||||||
type Page struct {
|
type Page struct {
|
||||||
@@ -41,51 +42,18 @@ func (s *Service) GetPages(ctx context.Context) (pages []PageHeader, err error)
|
|||||||
|
|
||||||
func (s *Service) GetPage(ctx context.Context, uid string) (page Page, err error) {
|
func (s *Service) GetPage(ctx context.Context, uid string) (page Page, err error) {
|
||||||
p, err := s.getPageHeaders(ctx, uid)
|
p, err := s.getPageHeaders(ctx, uid)
|
||||||
slog.Debug("got page", "page", p, "err", err)
|
if err != nil {
|
||||||
content := `
|
return
|
||||||
<h1>LOL</h1>
|
}
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
|
||||||
<img fetchpriority="low" loading="lazy" src="https://picsum.photos/500/1000">
|
|
||||||
<h2>LOL</h2>
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
|
||||||
|
|
||||||
<img fetchpriority="low" loading="lazy" src="https://picsum.photos/600/500">
|
html, err := s.getHTML(ctx, p)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
|
||||||
|
|
||||||
<h3>LOL</h3>
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
|
||||||
<h2>LOL</h2>
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
|
||||||
|
|
||||||
<img fetchpriority="low" loading="lazy" src="https://picsum.photos/601/500">
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
|
||||||
|
|
||||||
<h2>LOL</h2>
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
|
||||||
|
|
||||||
<img fetchpriority="low" loading="lazy" src="https://picsum.photos/500/500">
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
|
||||||
|
|
||||||
|
|
||||||
`
|
|
||||||
page = Page{
|
page = Page{
|
||||||
PageHeader: PageHeader{
|
PageHeader: p,
|
||||||
UID: uid,
|
Content: string(html),
|
||||||
Title: "This is my page Title",
|
|
||||||
CoverImageUID: "uid",
|
|
||||||
},
|
|
||||||
Content: content,
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package page
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.schreifuchs.ch/schreifuchs/schreifuchs.ch/internal/components/images"
|
||||||
|
"github.com/gomarkdown/markdown"
|
||||||
|
"github.com/gomarkdown/markdown/ast"
|
||||||
|
"github.com/gomarkdown/markdown/html"
|
||||||
|
"github.com/gomarkdown/markdown/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Service) getHTML(ctx context.Context, page PageHeader) (out []byte, err error) {
|
||||||
|
extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock
|
||||||
|
p := parser.NewWithExtensions(extensions)
|
||||||
|
|
||||||
|
doc := p.Parse(page.md)
|
||||||
|
|
||||||
|
// create HTML renderer with extensions
|
||||||
|
htmlFlags := html.CommonFlags | html.HrefTargetBlank
|
||||||
|
opts := html.RendererOptions{
|
||||||
|
Flags: htmlFlags,
|
||||||
|
RenderNodeHook: imageMiddleware(page),
|
||||||
|
}
|
||||||
|
renderer := html.NewRenderer(opts)
|
||||||
|
|
||||||
|
out = markdown.Render(doc, renderer)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func imageMiddleware(page PageHeader) func(w io.Writer, node ast.Node, entering bool) (ast.WalkStatus, bool) {
|
||||||
|
return func(w io.Writer, node ast.Node, entering bool) (ast.WalkStatus, bool) {
|
||||||
|
if !entering {
|
||||||
|
return ast.GoToNext, false
|
||||||
|
}
|
||||||
|
img, ok := node.(*ast.Image)
|
||||||
|
if !ok {
|
||||||
|
return ast.GoToNext, false
|
||||||
|
}
|
||||||
|
src := string(img.Destination)
|
||||||
|
if strings.HasPrefix(src, ".attachments") {
|
||||||
|
imgPath, err := url.PathUnescape(src)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("could not unescape path", "err", err)
|
||||||
|
return ast.GoToNext, false
|
||||||
|
}
|
||||||
|
src, err = images.URLFromPath(page.UID + "/" + imgPath)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("could not get image url", "err", err)
|
||||||
|
return ast.GoToNext, false
|
||||||
|
}
|
||||||
|
|
||||||
|
img.Attribute = &ast.Attribute{Attrs: map[string][]byte{
|
||||||
|
"srcset": []byte(images.SourceSet(src)),
|
||||||
|
"src": []byte(src),
|
||||||
|
}}
|
||||||
|
// // img.Attrs["srcset"] =
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
img.Destination = []byte(src)
|
||||||
|
|
||||||
|
return ast.GoToNext, false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
package page
|
package page
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"compress/flate"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
@@ -62,12 +59,12 @@ func (s *Service) getPageHeaders(ctx context.Context, uid string) (page PageHead
|
|||||||
|
|
||||||
slog.Debug("loaded page infos", "cover", coverImage, "content", contentMD)
|
slog.Debug("loaded page infos", "cover", coverImage, "content", contentMD)
|
||||||
|
|
||||||
content, err := s.fs.Read(contentMD.Path())
|
page.md, err = s.fs.Read(contentMD.Path())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
title := titleRGX.FindStringSubmatch(string(content))
|
title := titleRGX.FindStringSubmatch(string(page.md))
|
||||||
if len(title) < 2 {
|
if len(title) < 2 {
|
||||||
err = fmt.Errorf("%w: no matches for title", ErrMalFormedInput)
|
err = fmt.Errorf("%w: no matches for title", ErrMalFormedInput)
|
||||||
return
|
return
|
||||||
@@ -81,14 +78,3 @@ func (s *Service) getPageHeaders(ctx context.Context, uid string) (page PageHead
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getImageUID(path string) string {
|
|
||||||
var b bytes.Buffer
|
|
||||||
// NewWriter with NoDict (nil) creates a raw DEFLATE compressor
|
|
||||||
zw, _ := flate.NewWriter(&b, flate.BestCompression)
|
|
||||||
|
|
||||||
zw.Write([]byte(path))
|
|
||||||
zw.Close() // Essential to flush the final bits
|
|
||||||
|
|
||||||
return base64.RawURLEncoding.EncodeToString(b.Bytes())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package translate
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -55,6 +56,7 @@ func (s *Service) Middleware(next http.Handler) http.Handler {
|
|||||||
targetURL += "?" + r.URL.RawQuery
|
targetURL += "?" + r.URL.RawQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slog.Debug("redirecting for translated url", "old", path, "new", targetURL)
|
||||||
http.Redirect(w, r, targetURL, http.StatusFound)
|
http.Redirect(w, r, targetURL, http.StatusFound)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
package components
|
package components
|
||||||
|
|
||||||
import "strings"
|
import "git.schreifuchs.ch/schreifuchs/schreifuchs.ch/internal/components/images"
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
templ ImageTile(src string) {
|
templ ImageTile(src string) {
|
||||||
<div class="grid grid-cols-1 grid-rows-1">
|
<div class="grid grid-cols-1 grid-rows-1">
|
||||||
<img
|
<img
|
||||||
src={ src }
|
src={ src }
|
||||||
srcset={ makeSrcset(src) }
|
srcset={ images.SourceSet(src) }
|
||||||
fetchpriority="high"
|
fetchpriority="high"
|
||||||
class="row-start-1 col-start-1 pointer-events-none w-full h-96 object-cover"
|
class="row-start-1 col-start-1 pointer-events-none w-full h-96 object-cover"
|
||||||
/>
|
/>
|
||||||
@@ -16,16 +15,3 @@ templ ImageTile(src string) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeSrcset(src string) string {
|
|
||||||
sb := strings.Builder{}
|
|
||||||
|
|
||||||
for i := range 19 {
|
|
||||||
sb.WriteString(fmt.Sprintf("%s?w=%d %dw, ", src, 100*(i+1), 50*(i+1)))
|
|
||||||
}
|
|
||||||
|
|
||||||
i := 20
|
|
||||||
sb.WriteString(fmt.Sprintf("%s?w=%d %dw", src, 100*(i+1), 50*(i+1)))
|
|
||||||
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ package components
|
|||||||
import "github.com/a-h/templ"
|
import "github.com/a-h/templ"
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
import "strings"
|
import "git.schreifuchs.ch/schreifuchs/schreifuchs.ch/internal/components/images"
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func ImageTile(src string) templ.Component {
|
func ImageTile(src string) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
@@ -39,7 +38,7 @@ func ImageTile(src string) templ.Component {
|
|||||||
var templ_7745c5c3_Var2 string
|
var templ_7745c5c3_Var2 string
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(src)
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(src)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/imagetile.templ`, Line: 9, Col: 12}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/imagetile.templ`, Line: 8, Col: 12}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -50,9 +49,9 @@ func ImageTile(src string) templ.Component {
|
|||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var3 string
|
var templ_7745c5c3_Var3 string
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(makeSrcset(src))
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(images.SourceSet(src))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/imagetile.templ`, Line: 10, Col: 27}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/imagetile.templ`, Line: 9, Col: 33}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -74,17 +73,4 @@ func ImageTile(src string) templ.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeSrcset(src string) string {
|
|
||||||
sb := strings.Builder{}
|
|
||||||
|
|
||||||
for i := range 19 {
|
|
||||||
sb.WriteString(fmt.Sprintf("%s?w=%d %dw, ", src, 100*(i+1), 50*(i+1)))
|
|
||||||
}
|
|
||||||
|
|
||||||
i := 20
|
|
||||||
sb.WriteString(fmt.Sprintf("%s?w=%d %dw", src, 100*(i+1), 50*(i+1)))
|
|
||||||
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
var _ = templruntime.GeneratedTemplate
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import "git.schreifuchs.ch/schreifuchs/schreifuchs.ch/internal/components/page"
|
|||||||
import "git.schreifuchs.ch/schreifuchs/schreifuchs.ch/web/components"
|
import "git.schreifuchs.ch/schreifuchs/schreifuchs.ch/web/components"
|
||||||
|
|
||||||
templ ContentPage(content page.Page) {
|
templ ContentPage(content page.Page) {
|
||||||
@components.ImageTile("https://picsum.photos/1000/500") {
|
@components.ImageTile("/images/" + content.PageHeader.CoverImageUID) {
|
||||||
<h2 class="flex items-center justify-center h-full text-3xl font-bold text-shadow-lg">
|
<h2 class="flex items-center justify-center h-full text-3xl font-bold text-shadow-lg">
|
||||||
{ content.Title }
|
{ content.Title }
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func ContentPage(content page.Page) templ.Component {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
templ_7745c5c3_Err = components.ImageTile("https://picsum.photos/1000/500").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
templ_7745c5c3_Err = components.ImageTile("/images/"+content.PageHeader.CoverImageUID).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user