| -rw-r--r-- | .github/workflows/build-and-deploy-prod.yml | 37 | ||||
| -rw-r--r-- | .github/workflows/build-and-deploy-stage.yml | 43 | ||||
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Dockerfile | 1 | ||||
| -rw-r--r-- | internal/router/router.go | 57 | ||||
| -rw-r--r-- | static/favicon.ico | bin | 7633 -> 0 bytes | |||
| -rw-r--r-- | static/input.css | 117 | ||||
| -rw-r--r-- | views/layouts/general-page.html | 133 | ||||
| -rw-r--r-- | views/pages/blog-catalogue.html | 8 | ||||
| -rw-r--r-- | views/pages/blog-page.html | 11 | ||||
| -rw-r--r-- | views/pages/global-map.html | 9 | ||||
| -rw-r--r-- | views/pages/home-page.html | 6 | ||||
| -rw-r--r-- | views/pages/unsubscribe-page.html | 5 | ||||
| -rw-r--r-- | views/partials/general-page-header.html | 4 |
14 files changed, 237 insertions, 196 deletions
diff --git a/.github/workflows/build-and-deploy-prod.yml b/.github/workflows/build-and-deploy-prod.yml index 060ae32..97afdf7 100644 --- a/.github/workflows/build-and-deploy-prod.yml +++ b/.github/workflows/build-and-deploy-prod.yml @@ -40,8 +40,6 @@ jobs: context: . file: ./Dockerfile push: true - cache-from: type=gha - cache-to: type=gha,mode=max tags: | ghcr.io/sayaandy/saya-today-web:latest ghcr.io/sayaandy/saya-today-web:stable @@ -52,16 +50,10 @@ jobs: runs-on: ubuntu-latest environment: Production needs: [build-and-push] - container: - image: ghcr.io/ansible/community-ansible-dev-tools:v26.4.6 - options: --user root steps: - name: Checkout uses: actions/checkout@v6 - - name: Install community.docker collection - run: ansible-galaxy collection install community.docker - - name: Set up SSH connection for uz.saya.casa run: | mkdir -p ~/.ssh @@ -75,28 +67,17 @@ jobs: - name: Test SSH connection run: | - ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 svc_github@uz.saya.casa "echo 'SSH connection successful'" + ssh -o ConnectTimeout=10 -i ~/.ssh/id_ed25519 svc_github@uz.saya.casa "echo 'SSH connection successful'" + + - name: Install Ansible + shell: bash + run: | + sudo apt update + sudo apt install -y ansible - name: Run Ansible playbook env: - ANSIBLE_HOST_KEY_CHECKING: "False" + ANSIBLE_HOST_KEY_CHECKING: False working-directory: ./deploy run: | - ansible-playbook \ - playbook.yml \ - -i inventory.yml \ - -l prod \ - --private-key ~/.ssh/id_ed25519 \ - -u svc_github \ - -e saya_today_web_auth_salt="${{ secrets.AUTH_SALT }}" \ - -e saya_today_web_s3_access_key_id="${{ secrets.S3_ACCESS_KEY_ID }}" \ - -e saya_today_web_s3_secret_access_key="${{ secrets.S3_SECRET_ACCESS_KEY }}" \ - -e saya_today_web_environment=prod \ - -e saya_today_web_tag=${{ github.ref_name }} \ - -e saya_today_web_mail_salt="${{ secrets.MAIL_SALT }}" \ - -e saya_today_web_mail_host="${{ secrets.MAIL_HOST }}" \ - -e saya_today_web_mail_address="${{ secrets.MAIL_ADDRESS }}" \ - -e saya_today_web_mail_username="${{ secrets.MAIL_USERNAME }}" \ - -e saya_today_web_mail_password="${{ secrets.MAIL_PASSWORD }}" \ - -e saya_today_google_site_verification="${{ secrets.GOOGLE_SITE_VERIFICATION }}" \ - -e saya_today_yandex_verification="${{ secrets.YANDEX_VERIFICATION }}" + ansible-playbook -i inventory.yml -l prod playbook.yml --private-key ~/.ssh/id_ed25519 -u svc_github -e saya_today_web_auth_salt="${{ secrets.AUTH_SALT }}" -e saya_today_web_s3_access_key_id="${{ secrets.S3_ACCESS_KEY_ID }}" -e saya_today_web_s3_secret_access_key="${{ secrets.S3_SECRET_ACCESS_KEY }}" -e saya_today_web_environment=prod -e saya_today_web_tag=${{ github.ref_name }} -e saya_today_web_mail_salt="${{ secrets.MAIL_SALT }}" -e saya_today_web_mail_host="${{ secrets.MAIL_HOST }}" -e saya_today_web_mail_address="${{ secrets.MAIL_ADDRESS }}" -e saya_today_web_mail_username="${{ secrets.MAIL_USERNAME }}" -e saya_today_web_mail_password="${{ secrets.MAIL_PASSWORD }}" -e saya_today_google_site_verification="${{ secrets.GOOGLE_SITE_VERIFICATION }}" -e saya_today_yandex_verification="${{ secrets.YANDEX_VERIFICATION }}" diff --git a/.github/workflows/build-and-deploy-stage.yml b/.github/workflows/build-and-deploy-stage.yml index ce4a1fb..ffee091 100644 --- a/.github/workflows/build-and-deploy-stage.yml +++ b/.github/workflows/build-and-deploy-stage.yml @@ -9,8 +9,6 @@ jobs: environment: Stage permissions: packages: write - outputs: - sha_short: ${{ steps.ghss_vars.outputs.sha_short }} steps: - name: Checkout uses: actions/checkout@v6 @@ -37,7 +35,7 @@ jobs: - name: Set github short sha id: ghss_vars - run: echo "sha_short=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT + run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - name: Build and push uses: docker/build-push-action@v7 @@ -45,8 +43,6 @@ jobs: context: . file: ./Dockerfile push: true - cache-from: type=gha - cache-to: type=gha,mode=max tags: | ghcr.io/sayaandy/saya-today-web:latest ghcr.io/sayaandy/saya-today-web:commit-${{ steps.ghss_vars.outputs.sha_short }} @@ -56,16 +52,10 @@ jobs: runs-on: ubuntu-latest environment: Stage needs: [build-and-push] - container: - image: ghcr.io/ansible/community-ansible-dev-tools:v26.4.6 - options: --user root steps: - name: Checkout uses: actions/checkout@v6 - - name: Install community.docker collection - run: ansible-galaxy collection install community.docker - - name: Set up SSH connection for uz.saya.casa run: | mkdir -p ~/.ssh @@ -79,26 +69,21 @@ jobs: - name: Test SSH connection run: | - ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 svc_github@uz.saya.casa "echo 'SSH connection successful'" + ssh -o ConnectTimeout=10 -i ~/.ssh/id_ed25519 svc_github@uz.saya.casa "echo 'SSH connection successful'" + + - name: Install Ansible + shell: bash + run: | + sudo apt update + sudo apt install -y ansible + + - name: Set github short sha + id: ghss_vars + run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - name: Run Ansible playbook env: - ANSIBLE_HOST_KEY_CHECKING: "False" + ANSIBLE_HOST_KEY_CHECKING: False working-directory: ./deploy run: | - ansible-playbook \ - playbook.yml \ - -i inventory.yml \ - -l stage \ - --private-key ~/.ssh/id_ed25519 \ - -u svc_github \ - -e saya_today_web_auth_salt="${{ secrets.AUTH_SALT }}" \ - -e saya_today_web_s3_access_key_id="${{ secrets.S3_ACCESS_KEY_ID }}" \ - -e saya_today_web_s3_secret_access_key="${{ secrets.S3_SECRET_ACCESS_KEY }}" \ - -e saya_today_web_environment=stage \ - -e saya_today_web_tag=commit-${{ needs.build-and-push.outputs.sha_short }} \ - -e saya_today_web_mail_salt="${{ secrets.MAIL_SALT }}" \ - -e saya_today_web_mail_host="${{ secrets.MAIL_HOST }}" \ - -e saya_today_web_mail_address="${{ secrets.MAIL_ADDRESS }}" \ - -e saya_today_web_mail_username="${{ secrets.MAIL_USERNAME }}" \ - -e saya_today_web_mail_password="${{ secrets.MAIL_PASSWORD }}" + ansible-playbook -i inventory.yml -l stage playbook.yml --private-key ~/.ssh/id_ed25519 -u svc_github -e saya_today_web_auth_salt="${{ secrets.AUTH_SALT }}" -e saya_today_web_s3_access_key_id="${{ secrets.S3_ACCESS_KEY_ID }}" -e saya_today_web_s3_secret_access_key="${{ secrets.S3_SECRET_ACCESS_KEY }}" -e saya_today_web_environment=stage -e saya_today_web_tag=commit-${{ steps.ghss_vars.outputs.sha_short }} -e saya_today_web_mail_salt="${{ secrets.MAIL_SALT }}" -e saya_today_web_mail_host="${{ secrets.MAIL_HOST }}" -e saya_today_web_mail_address="${{ secrets.MAIL_ADDRESS }}" -e saya_today_web_mail_username="${{ secrets.MAIL_USERNAME }}" -e saya_today_web_mail_password="${{ secrets.MAIL_PASSWORD }}" @@ -42,5 +42,3 @@ config*.json .envrc /.ansible - -.DS_Store @@ -4,7 +4,6 @@ RUN apk add --no-cache sqlite-dev musl-dev gcc WORKDIR /builddir COPY . . -RUN go mod download RUN CGO_ENABLED=1 go build -o sayana-web . FROM alpine:3.23 AS runtime-stage diff --git a/internal/router/router.go b/internal/router/router.go index 7f3511a..65e35a4 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -394,11 +394,6 @@ func (r *Router) Listen() error { case "unix": unixConfig := r.endpoint.Config.(*config.UnixConfig) endpoint, _ := strings.CutPrefix(unixConfig.Path, "unix://") - - if err := os.Remove(endpoint); err != nil && !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("error while cleaning up existing unix socket: %w", err) - } - ln, err := net.Listen("unix", endpoint) if err != nil { return fmt.Errorf("error while initializing unix listener: %w", err) @@ -451,8 +446,8 @@ func (r *Router) generalPage(c *fiber.Ctx, route Route, lang string) error { path := c.Path() method := c.Method() trimmedPath := strings.Trim(path, "/") - queryString := c.Request().URI().QueryString() cacheKey := "" + queryString := c.Request().URI().QueryString() switch route.ToCache() { case ByUrlOnly: @@ -469,7 +464,6 @@ func (r *Router) generalPage(c *fiber.Ctx, route Route, lang string) error { valueMap := fiber.Map{ "L": r.supplements.Localization[lang], "Lang": lang, - "Path": trimmedPath, "QueryString": queryString, "CanonicalEndpoint": r.canonicalEndpoint, "StaticStorageBaseUrl": r.supplements.StaticStorage.BaseUrl, @@ -490,52 +484,10 @@ func (r *Router) generalPage(c *fiber.Ctx, route Route, lang string) error { valueMap["LinkedData"] = template.JS(ldBytes) } - syntheticReferer := path - if len(queryString) > 0 { - syntheticReferer += "?" + string(queryString) - } - c.Request().Header.Set("Referer", syntheticReferer) - - parts := []struct { - name string - key string - render func(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (int, error) - }{ - {"top-embeds", "RenderedTopEmbeds", route.RenderTopEmbeds}, - {"header", "RenderedHeader", route.RenderHeader}, - {"body", "RenderedBody", route.RenderBody}, - {"footer", "RenderedFooter", route.RenderFooter}, - {"bottom-embeds", "RenderedBottomEmbeds", route.RenderBottomEmbeds}, - } - - for _, p := range parts { - statusCode, err := p.render(c, r.supplements, lang, valueMap) - if err != nil { - slog.Error("failed to render segment for full page", - slog.String("path", path), - slog.String("segment", p.name), - slog.String("error", err.Error()), - ) - c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) - return c.Status(statusCode).SendString(err.Error()) - } - segContent, err := r.supplements.TemplateManager.Render("general-page-"+p.name, valueMap, route.TemplatesToInject()...) - if err != nil { - slog.Error("failed to render segment template for full page", - slog.String("path", path), - slog.String("segment", p.name), - slog.String("error", err.Error()), - ) - c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) - return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate segment") - } - valueMap[p.key] = template.HTML(segContent) - } - content, err := r.supplements.TemplateManager.Render("general-page", valueMap) if err != nil { - slog.Warn("failed to generate full page", slog.String("path", path), slog.String("error", err.Error())) - return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate full page") + slog.Warn("failed to generate div", slog.String("path", path), slog.String("error", err.Error())) + return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate div") } go r.supplements.PageCache.SetWithTTL(cacheKey, content, int64(len(content)), route.CacheDuration()) @@ -690,7 +642,8 @@ func (r *Router) getAndValidateLang(c *fiber.Ctx, langSetting LangSetting, defau return lang, nil } } - return "", fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("lang value is invalid: '%s' is not considered an available language", lang)) + c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) + return "", c.Status(fiber.ErrBadRequest.Code).SendString(fmt.Sprintf("lang value is invalid: '%s' is not considered an available language", lang)) } func GetPathFromReferer(c *fiber.Ctx) (path string, pathParts []string, queryString string, err error) { diff --git a/static/favicon.ico b/static/favicon.ico Binary files differdeleted file mode 100644 index 99d5fbf..0000000 --- a/static/favicon.ico +++ /dev/null diff --git a/static/input.css b/static/input.css index e1ba37d..9114514 100644 --- a/static/input.css +++ b/static/input.css @@ -723,6 +723,123 @@ 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-6 { + margin-right: 1.5rem; + } + + .ar-gt-0\.8\:w-fit { + width: fit-content; + } + + .ar-gt-0\.8\:w-\[100dvh\] { + width: 100dvh; + flex-basis: 100dvh; + } + + .ar-gt-0\.8\:min-w-fit { + min-width: fit-content; + } + + .ar-gt-0\.8\:min-w-1\/10 { + min-width: 10%; + } + + .ar-gt-0\.8\:max-w-1\/4 { + max-width: 25%; + } + + .ar-gt-0\.8\:items-center { + align-items: center; + } +} + +@media (max-aspect-ratio: 0.8/1) { + .bg-pattern-body { + background-size: 10dvh; + } + + .ar-lt-0\.8\:grid { + display: grid; + } + + .ar-lt-0\.8\:flex-col { + flex-direction: column; + } + + .ar-lt-0\.8\:flex-row { + flex-direction: row; + } + + .ar-lt-0\.8\:h-\[8dvh\] { + height: 8dvh; + } + + .ar-lt-0\.8\:h-\[10dvh\] { + height: 10dvh; + } + + .ar-lt-0\.8\:h-\[30dvh\] { + height: 30dvh; + } + + .ar-lt-0\.8\:h-\[90dvh\] { + height: 90dvh; + } + + .ar-lt-0\.8\:w-\[8dvh\] { + width: 8dvh; + } + + .ar-lt-0\.8\:w-\[80\%\] { + width: 80%; + } + + .ar-lt-0\.8\:w-\[90dvw\] { + width: 90dvw; + } + + .ar-lt-0\.8\:w-\[100dvw\] { + width: 100dvw; + } + + .ar-lt-0\.8\:max-h-\[30\%\] { + max-height: 30%; + } + + .ar-lt-0\.8\:max-h-\[80\%\] { + max-height: 80%; + } + + .ar-lt-0\.8\:max-h-\[92dvh\] { + max-height: 92dvh; + } + + .ar-lt-0\.8\:text-\[5dvh\] { + font-size: 5dvh; + } + + .ar-lt-0\.8\:hidden { + display: none; + } +} + +@media (min-aspect-ratio: 0.6/1) { + .ar-gt-0\.6\:flex-3\/5 { + flex: 60%; + } + + .ar-gt-0\.6\:flex-2\/5 { + flex: 40%; + } +} + +@media (max-aspect-ratio: 0.6/1) { + .ar-lt-0\.6\:flex-col { + flex-direction: column; + } +} + .home-page-heart { position: absolute; color: var(--color-pink-700); diff --git a/views/layouts/general-page.html b/views/layouts/general-page.html index d787c16..fb018e3 100644 --- a/views/layouts/general-page.html +++ b/views/layouts/general-page.html @@ -43,11 +43,6 @@ type="image/svg+xml" media="(prefers-color-scheme: dark)" /> - <link - rel="icon" - href="/favicon.ico" - type="image/x-icon" - /> {{- with .LinkedData }} <script type="application/ld+json"> {{ . }} @@ -311,50 +306,14 @@ switchTheme(savedTheme); </script> - <script src="{{ .StaticStorageBaseUrl }}/libs/htmx/2.0.6/htmx.min.js"></script> - <script src="{{ .StaticStorageBaseUrl }}/libs/leaflet/1.9.4/leaflet.js"></script> - <script src="{{ .StaticStorageBaseUrl }}/libs/imagesloaded/5.0.0/imagesloaded.pkgd.min.js"></script> - <script src="{{ .StaticStorageBaseUrl }}/libs/packery/3.0.0/dist/packery.pkgd.min.js"></script> - - <script> - // Enable fitWidth-like attribute for Packery. Source: https://codepen.io/desandro/pen/kVmLYz - var __resetLayout = Packery.prototype._resetLayout; - Packery.prototype._resetLayout = function () { - __resetLayout.call(this); - var parentSize = getSize(this.element.parentNode); - var colW = this.columnWidth + this.gutter; - this.fitWidth = - Math.floor((parentSize.innerWidth + this.gutter) / colW) * - colW; - this.packer.width = this.fitWidth; - this.packer.height = Number.POSITIVE_INFINITY; - this.packer.reset(); - }; - - Packery.prototype._getContainerSize = function () { - var emptyWidth = 0; - for (var i = 0, len = this.packer.spaces.length; i < len; i++) { - var space = this.packer.spaces[i]; - if ( - space.y === 0 && - space.height === Number.POSITIVE_INFINITY - ) { - emptyWidth += space.width; - } - } - - return { - width: this.fitWidth - this.gutter - emptyWidth, - height: this.maxY - this.gutter, - }; - }; - - Packery.prototype.needsResizeLayout = function () { - return true; - }; - </script> - - <div id="general-page-top-embeds" class="flex">{{ .RenderedTopEmbeds }}</div> + <div + id="general-page-top-embeds" + class="flex" + hx-get="/api/v1/general-page/top-embeds{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="load" + hx-target="this" + hx-swap="innerHTML" + ></div> <div id="general-page-top-embeds-trigger" hx-get="/api/v1/general-page/top-embeds{{ if .QueryString }}?{{ .QueryString }}{{ end }}" @@ -516,19 +475,23 @@ </nav> <div - class="flex flex-1 min-h-0 z-1 bg-paper bg-background shadow-elevation-3 @container/body" + class="flex flex-1 z-1 bg-paper bg-background shadow-elevation-3 @container/body" > <div class="bg-paper bg-background-dark border-r-2 border-dashed border-main-hard" ></div> <div - class="max-h-full max-w-full flex flex-col grow bg-paper bg-background-dark relative md:pt-4 md:pr-4" + class="max-h-full max-w-full flex flex-col grow bg-paper bg-background-dark relative pt-4 pr-0 md:pr-4" > <header id="general-page-header" class="max-xs:hidden [@media(max-height:32rem)]:hidden flex flex-col ml-8 basis-8" - >{{ .RenderedHeader }}</header> + hx-get="/api/v1/general-page/header{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="load" + hx-target="this" + hx-swap="innerHTML" + ></header> <div id="general-page-header-trigger" hx-get="/api/v1/general-page/header{{ if .QueryString }}?{{ .QueryString }}{{ end }}" @@ -543,8 +506,12 @@ <main id="general-page-body" - class="relative flex-1 overflow-y-auto" - >{{ .RenderedBody }}</main> + class="relative flex flex-col grow overflow-y-auto" + hx-get="/api/v1/general-page/body{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="load" + hx-target="this" + hx-swap="innerHTML" + ></main> <div id="general-page-body-trigger" hx-get="/api/v1/general-page/body{{ if .QueryString }}?{{ .QueryString }}{{ end }}" @@ -556,7 +523,11 @@ <footer id="general-page-footer" class="flex flex-row basis-4 my-1" - >{{ .RenderedFooter }}</footer> + hx-get="/api/v1/general-page/footer{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="load" + hx-target="this" + hx-swap="innerHTML" + ></footer> <div id="general-page-footer-trigger" hx-get="/api/v1/general-page/footer{{ if .QueryString }}?{{ .QueryString }}{{ end }}" @@ -578,7 +549,57 @@ <div class="grow bg-split-right"></div> - <div id="general-page-bottom-embeds" class="flex">{{ .RenderedBottomEmbeds }}</div> + <script src="{{ .StaticStorageBaseUrl }}/libs/htmx/2.0.6/htmx.min.js"></script> + <script src="{{ .StaticStorageBaseUrl }}/libs/leaflet/1.9.4/leaflet.js"></script> + <script src="{{ .StaticStorageBaseUrl }}/libs/imagesloaded/5.0.0/imagesloaded.pkgd.min.js"></script> + <script src="{{ .StaticStorageBaseUrl }}/libs/packery/3.0.0/dist/packery.pkgd.min.js"></script> + + <script> + // Enable fitWidth-like attribute for Packery. Source: https://codepen.io/desandro/pen/kVmLYz + var __resetLayout = Packery.prototype._resetLayout; + Packery.prototype._resetLayout = function () { + __resetLayout.call(this); + var parentSize = getSize(this.element.parentNode); + var colW = this.columnWidth + this.gutter; + this.fitWidth = + Math.floor((parentSize.innerWidth + this.gutter) / colW) * + colW; + this.packer.width = this.fitWidth; + this.packer.height = Number.POSITIVE_INFINITY; + this.packer.reset(); + }; + + Packery.prototype._getContainerSize = function () { + var emptyWidth = 0; + for (var i = 0, len = this.packer.spaces.length; i < len; i++) { + var space = this.packer.spaces[i]; + if ( + space.y === 0 && + space.height === Number.POSITIVE_INFINITY + ) { + emptyWidth += space.width; + } + } + + return { + width: this.fitWidth - this.gutter - emptyWidth, + height: this.maxY - this.gutter, + }; + }; + + Packery.prototype.needsResizeLayout = function () { + return true; + }; + </script> + + <div + id="general-page-bottom-embeds" + class="flex" + hx-get="/api/v1/general-page/bottom-embeds{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="load" + hx-target="this" + hx-swap="innerHTML" + ></div> <div id="general-page-bottom-embeds-trigger" hx-get="/api/v1/general-page/bottom-embeds{{ if .QueryString }}?{{ .QueryString }}{{ end }}" diff --git a/views/pages/blog-catalogue.html b/views/pages/blog-catalogue.html index 9c0f7fd..8e1cf72 100644 --- a/views/pages/blog-catalogue.html +++ b/views/pages/blog-catalogue.html @@ -55,15 +55,15 @@ <path fill="currentColor" d="M23.2453 20.5533c-0.04 -0.2181 -0.1001 -0.4321 -0.1797 -0.6391 -0.1361 -0.3547 -0.3173 -0.6903 -0.5393 -0.9986 -0.3034 -0.4388 -0.637 -0.8559 -0.9986 -1.2482 -0.5284 -0.5741 -1.0956 -1.1113 -1.6976 -1.6078 -0.8288 -0.679 -1.6976 -1.2981 -2.5763 -1.9971 0.2631 -0.5055 0.4803 -1.0335 0.649 -1.5778 0.248 -0.8172 0.4349 -1.6516 0.5592 -2.4965 0.1569 -0.81258 0.2305 -1.63902 0.2197 -2.46652 -0.0078 -0.57747 -0.1055 -1.1502 -0.2896 -1.6976 -0.1575 -0.4947 -0.3476 -0.97841 -0.5692 -1.44796 -0.1562 -0.33202 -0.347 -0.64665 -0.5691 -0.93868 -0.3781 -0.48797 -0.8217 -0.92146 -1.3182 -1.28818 -0.7357 -0.52062 -1.53 -0.95298 -2.3666 -1.288183 -0.5827 -0.254108 -1.1835 -0.464396 -1.7975 -0.629112 -0.6518 -0.1693231 -1.324 -0.246631 -1.9972 -0.22967613C8.94606 0.0473495 8.12871 0.212165 7.34773 0.491599c-0.76731 0.278516 -1.50629 0.629611 -2.20689 1.048521 -0.60025 0.33964 -1.16553 0.73768 -1.68762 1.18832C1.21637 4.69567 0.757023 8.98961 0.727065 9.45895 0.620124 10.7287 0.804559 12.0061 1.2663 13.1937c0.12146 0.3013 0.26153 0.5948 0.41941 0.8787 0.22968 0.3995 0.48931 0.7889 0.75893 1.1784 0.5038 0.7726 1.12352 1.463 1.83741 2.0471 0.49826 0.396 1.06292 0.7003 1.66765 0.8987 0.69046 0.2254 1.39862 0.3924 2.11701 0.4993 0.49142 0.0823 0.99015 0.1124 1.4879 0.0899 0.77439 -0.05 1.53939 -0.1977 2.27679 -0.4394 0.7156 -0.2507 1.4101 -0.5579 2.0771 -0.9187 0.1351 0.1597 0.2617 0.3264 0.3794 0.4993 0.4294 0.6591 0.769 1.3681 1.1684 1.9972 0.2809 0.4341 0.5877 0.851 0.9187 1.2482 0.3126 0.3904 0.646 0.7638 0.9986 1.1184 0.9711 0.9019 2.1935 1.4871 3.505 1.6777 0.262 0.0471 0.5307 0.0413 0.7904 -0.0169 0.2597 -0.0582 0.5052 -0.1678 0.7219 -0.3222 0.2168 -0.1544 0.4005 -0.3506 0.5404 -0.5771 0.1398 -0.2264 0.233 -0.4785 0.2741 -0.7415 0.0404 -0.1745 0.0704 -0.3512 0.0898 -0.5292 0.0097 -0.1763 0.0097 -0.353 0 -0.5293 0.0036 -0.234 -0.0131 -0.4679 -0.0499 -0.699Zm-1.1584 1.4679c-0.0509 0.2532 -0.1979 0.4768 -0.4102 0.6239 -0.2122 0.1471 -0.4732 0.2062 -0.7282 0.165 -1.0467 -0.1308 -2.0325 -0.5647 -2.836 -1.2482 -0.3522 -0.313 -0.6856 -0.6464 -0.9986 -0.9986 -0.3312 -0.341 -0.6381 -0.7047 -0.9187 -1.0885 -0.4393 -0.6191 -0.8388 -1.3081 -1.2981 -1.9372 -0.1219 -0.1597 -0.2519 -0.313 -0.3895 -0.4594l0.1298 -0.0899c0.2213 -0.1586 0.4315 -0.3321 0.6292 -0.5192 0.0617 -0.0665 0.096 -0.1539 0.096 -0.2447 0 -0.0908 -0.0343 -0.1782 -0.096 -0.2447 -0.0637 -0.0634 -0.1499 -0.099 -0.2397 -0.099 -0.0898 0 -0.176 0.0356 -0.2397 0.099 -0.1708 0.1582 -0.3545 0.3018 -0.5492 0.4294 -0.1917 0.1358 -0.3918 0.2592 -0.5991 0.3695 -0.6447 0.3275 -1.3121 0.6078 -1.9972 0.8388 -0.6937 0.2123 -1.4121 0.3332 -2.13702 0.3595 -0.55091 0.0138 -1.10132 -0.0432 -1.63769 -0.1697 -0.5569 -0.1063 -1.10451 -0.2566 -1.63769 -0.4494 -0.62749 -0.2261 -1.19946 -0.5835 -1.67763 -1.0485 -0.49431 -0.4831 -0.93003 -1.0228 -1.29817 -1.6077 -0.22967 -0.3595 -0.46934 -0.719 -0.67904 -1.1185 -0.13067 -0.2462 -0.24735 -0.4996 -0.34951 -0.7589 -0.38354 -1.05 -0.52354 -2.1734 -0.40942 -3.28536 0.45935 -5.29254 3.14556 -6.55076 3.91448 -6.99014 0.63297 -0.38101 1.30193 -0.69877 1.99718 -0.94867 0.6757 -0.25505 1.38606 -0.40655 2.10703 -0.44936 0.56148 -0.01552 1.12238 0.04495 1.66768 0.17975 0.5643 0.13703 1.1183 0.31389 1.6576 0.52925 0.6401 0.2469 1.2581 0.54755 1.8474 0.89873 0.5688 0.32061 1.076 0.7399 1.4979 1.23825 0.1894 0.23504 0.3534 0.48943 0.4893 0.75893 0.2209 0.42872 0.4111 0.87254 0.5692 1.32813 0.1632 0.46345 0.2607 0.94748 0.2896 1.43797 0.0241 0.77857 -0.026 1.55767 -0.1498 2.32672 -0.0854 0.6722 -0.2088 1.3389 -0.3695 1.9972 -0.1609 0.6599 -0.3954 1.2996 -0.699 1.9073 -0.1608 0.3485 -0.3513 0.6826 -0.5692 0.9986 -0.2143 0.3034 -0.4549 0.5874 -0.719 0.8488 -0.0288 0.0275 -0.052 0.0605 -0.0681 0.097 -0.0161 0.0364 -0.0249 0.0757 -0.0258 0.1156 -0.0019 0.0805 0.0283 0.1585 0.084 0.2168 0.0556 0.0582 0.1321 0.092 0.2126 0.0939 0.0805 0.0019 0.1585 -0.0283 0.2168 -0.0839 0.2935 -0.2844 0.5639 -0.5916 0.8088 -0.9187 0.1099 -0.1498 0.1997 -0.3096 0.2996 -0.4694 0.8088 0.6791 1.6177 1.3182 2.3866 1.9972 0.5434 0.4957 1.0506 1.0297 1.5179 1.5978 0.3133 0.3747 0.6034 0.7682 0.8688 1.1783 0.1584 0.2534 0.2858 0.525 0.3794 0.8088 0.0505 0.1638 0.0905 0.3305 0.1198 0.4993 0.0099 0.1597 0.0099 0.3197 0 0.4794l-0.0599 0.8088Z" stroke-width="1"></path> </symbol> </svg> -<div class="flex md:flex-row flex-col text-base h-full"> - <div class="relative flex shrink-0 w-full max-h-[30%] md:min-w-1/10 md:max-w-1/4 md:max-h-full md:h-full md:w-fit"> +<div class="flex flex-row ar-lt-0.8:flex-col text-base h-full"> + <div class="relative flex shrink-0 ar-gt-0.8:min-w-1/10 ar-gt-0.8:max-w-1/4 ar-gt-0.8:w-fit ar-lt-0.8:w-full ar-lt-0.8:max-h-[30%]"> <div class="absolute w-full h-full inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div> <form hx-get="/api/v1/blog-search" hx-vals='{"lang": "{{ .Lang }}"}' hx-target=".blog-cards" hx-swap="innerHTML" class="tags-list relative flex flex-col w-full bg-paper bg-background-light"> <div class="flex flex-col overflow-y-auto"> <fieldset> <legend class="font-bold w-full text-center">{{ .L.BlogSearch.OrderByHeader }}</legend> - <div class="md:flex flex-col grid grid-cols-3 grid-rows-2 grid-flow-col"> + <div class="flex flex-col ar-lt-0.8:grid grid-cols-3 grid-rows-2 grid-flow-col"> <div> <label class="flex justify-start gap-1 items-center"> <input type="radio" id="sortTitleAsc" name="sort" value="titleAsc" class="bg-accent-light border-transparent text-accent-deep focus:border-transparent focus:bg-accent-light focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" {{ if eq .QuerySort "titleAsc" }}checked{{ end }}> @@ -110,7 +110,7 @@ </fieldset> <fieldset class="mt-1"> <legend class="font-bold w-full text-center">{{ .L.BlogSearch.TagsHeader }}</legend> - <div class="md:flex flex-col grid grid-cols-3 xs:grid-cols-4 sm:grid-cols-5 grid-flow-row-dense"> + <div class="flex flex-col ar-lt-0.8:grid grid-cols-3 xs:grid-cols-4 sm:grid-cols-5 grid-flow-row-dense"> <div class="m-1"> <label class="font-bold"><input type="checkbox" id="tagsAllCheckbox" onclick="selectAll();" class="bg-accent-light rounded border-transparent text-accent-deep focus:border-transparent focus:bg-background-dark focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep"> {{ .L.BlogSearch.ChooseAllTags }}</label> </div> diff --git a/views/pages/blog-page.html b/views/pages/blog-page.html index d8cfbff..8978eb8 100644 --- a/views/pages/blog-page.html +++ b/views/pages/blog-page.html @@ -1,10 +1,7 @@ {{ define "body" }} <div class="absolute w-full h-full inset-0 inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div> <article class="relative bg-paper bg-background-light flex flex-col px-8 py-4 overflow-y-auto"> - <h1 class="max-xs:flex flex-row content-center [@media(max-height:32rem)]:block hidden font-gentium text-2xl font-bold italic text-main-hard tracking-[.0125rem] mb-1"> - <div hx-get="/api/v1/like" hx-target="this" hx-swap="outerHTML" hx-trigger="load"></div> - {{ .Title }} - </h1> + <h1 class="max-xs:block [@media(max-height:32rem)]:block hidden font-gentium text-2xl font-bold italic text-main-hard tracking-[.0125rem] mb-1">{{ .Title }}</h1> <p class="block grow text-lg font-gentium text-secondary"> <b>{{ .L.Metadata.Published }}</b>: <time datetime="{{ .PublishedDate }}" hx-get="/api/v1/tz" hx-vals='js:{timestamp: "{{ .PublishedDate }}", tz: clientTimeZone}' hx-target="this" hx-swap="innerHTML" hx-trigger="load"> @@ -17,16 +14,16 @@ {{ .ParsedMarkdown }} {{- if .MapLocationX }} <hr class="border-t-3 border-dotted border-main-hard mt-1 mb-2 w-[80%] ml-auto mr-auto"> - <div id="map-container" class="relative p-1 flex-none ml-auto mr-auto w-[80%] lg:w-[60%] h-[30dvh] md:h-[40dvh]"></div> + <div id="map-container" class="relative p-1 flex-none ml-auto mr-auto w-[60%] ar-lt-0.8:w-[80%] h-[40dvh] ar-lt-0.8:h-[30dvh]"></div> <hr class="border-t-3 border-dotted border-main-hard mt-1 mb-2 w-[80%] ml-auto mr-auto"> <script> function initLocationMap(id, x, y, relativeErrorMeters = 0) { map = L.map(id).setView([x, y], 8); - L.tileLayer('https://tiles.stadiamaps.com/tiles/outdoors/{z}/{x}/{y}{r}.png', { + L.tileLayer('https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png', { maxZoom: 20, - attribution: '© <a href="https://stadiamaps.com/" target="_blank">Stadia Maps</a>, © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>', + attribution: '<a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> | Map data: © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(map); if (relativeErrorMeters != 0) { diff --git a/views/pages/global-map.html b/views/pages/global-map.html index 1501111..248d0b9 100644 --- a/views/pages/global-map.html +++ b/views/pages/global-map.html @@ -35,11 +35,6 @@ media="(prefers-color-scheme: dark)" /> <link - rel="icon" - href="/favicon.ico" - type="image/x-icon" - /> - <link rel="stylesheet" href="{{ .StaticStorageBaseUrl }}/libs/leaflet/1.9.4/leaflet.css" /> @@ -394,9 +389,9 @@ function initLocationMap() { map = L.map('map-container').setView([{{ .MapLocationLat }}, {{ .MapLocationLong }}], 4); - L.tileLayer('https://tiles.stadiamaps.com/tiles/outdoors/{z}/{x}/{y}{r}.png', { + L.tileLayer('https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png', { maxZoom: 20, - attribution: '© <a href="https://stadiamaps.com/" target="_blank">Stadia Maps</a>, © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>', + attribution: '<a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> | Map data: © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', zoomControl: false }).addTo(map); diff --git a/views/pages/home-page.html b/views/pages/home-page.html index 6e026aa..7de9420 100644 --- a/views/pages/home-page.html +++ b/views/pages/home-page.html @@ -18,8 +18,8 @@ </g></symbol> </svg> <div class="absolute w-full h-full inset-0 inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div> -<div class="relative bg-paper bg-background-light font-gentium flex flex-col sm:flex-row w-full h-full overflow-y-auto text-base/loose tracking-wide z-10"> - <div class="sm:flex-3/5 justify-center content-start p-8"> +<div class="relative bg-paper bg-background-light font-gentium flex flex-row ar-lt-0.6:flex-col w-full h-full overflow-y-auto text-base/loose tracking-wide z-10"> + <div class="ar-gt-0.6:flex-3/5 justify-center content-start p-8"> <div class="flex flex-col justify-start items-start bg-paper bg-background-dark mb-8 shadow-[0.4rem_0.4rem_0.4rem_black]"> <p class="text-3xl/loose sm:text-4xl/loose lg:text-5xl/loose tracking-widest uppercase w-full font-m-plus font-thin italic text-left border-l-4 border-solid border-background-dark @@ -43,7 +43,7 @@ </div> </div> - <div class="sm:flex-2/5 justify-center content-start p-8"> + <div class="ar-gt-0.6:flex-2/5 justify-center content-start p-8"> <div class="flex flex-col justify-center items-start mb-4"> <p class="text-xl sm:text-2xl lg:text-3xl p-1 w-full font-m-plus font-thin italic text-left border-l-1.5 border-solid border-background-dark diff --git a/views/pages/unsubscribe-page.html b/views/pages/unsubscribe-page.html index 9a78488..a917746 100644 --- a/views/pages/unsubscribe-page.html +++ b/views/pages/unsubscribe-page.html @@ -16,11 +16,6 @@ type="image/svg+xml" media="(prefers-color-scheme: dark)" /> - <link - rel="icon" - href="/favicon.ico" - type="image/x-icon" - /> <title>{{ .L.UnsubscribePage.Header }} // SAYA.UZ</title> <style> .bg-paper { diff --git a/views/partials/general-page-header.html b/views/partials/general-page-header.html index 3b8a6c8..995ebf3 100644 --- a/views/partials/general-page-header.html +++ b/views/partials/general-page-header.html @@ -1,9 +1,9 @@ <title>{{ .Title }} // SAYA.UZ</title> <div class="flex flex-row mb-2"> {{ block "header" . }}{{ end }} - <h1 + <p class="text-4xl font-m-plus font-bold italic text-shadow-[0_2px_2px_var(--color-main-soft)] text-left my-auto" > {{ .Title }} - </h1> + </p> </div> |