Zum Hauptinhalt springen

Hugo & Blowfish

·2309 Wörter·11 min·

Hugo

Bin jetzt von Ghost Blog auf Hugo Static Site Generator umgestiegen und habe mir dazu einen Docker Image Container mit allen nützlichen Tools erstellt.

Installation unter dwOS.

dwapt install dw-hugo

Übersicht des dwhugo Kommandos.

dwhugo

Übersicht des integrierten dwbuild Kommandos.

dwhugo dwbuild

Blowfish

Als Hugo Theme nutze ich Blowfish

Hier die Chroma CSS Klassen für Code Blocks, einzufügen in assets/css/custom.css

/* Chroma */

/* in markup.toml set noClasses to true
 * [highlight]
 *   noClasses = true
*/

.chroma,
.chroma *,
.chroma:is(.dark *),
.chroma:is(.dark *) * {
  color: unset;
  font-weight: unset;
  font-style: unset;
}
html:not(.dark) {
/* Generated using: hugo gen chromastyles --style=github */

/* Background */ .bg { background-color:#f7f7f7; }
/* PreWrapper */ .chroma { background-color:#f7f7f7; }
/* Error */ .chroma .err { color:#f6f8fa;background-color:#82071e }
/* LineLink */ .chroma .lnlinks { outline:none;text-decoration:none;color:inherit }
/* LineTableTD */ .chroma .lntd { vertical-align:top;padding:0;margin:0;border:0; }
/* LineTable */ .chroma .lntable { border-spacing:0;padding:0;margin:0;border:0; }
/* LineHighlight */ .chroma .hl { background-color:#dedede }
/* LineNumbersTable */ .chroma .lnt { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f }
/* LineNumbers */ .chroma .ln { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f }
/* Line */ .chroma .line { display:flex; }
/* Keyword */ .chroma .k { color:#cf222e }
/* KeywordConstant */ .chroma .kc { color:#cf222e }
/* KeywordDeclaration */ .chroma .kd { color:#cf222e }
/* KeywordNamespace */ .chroma .kn { color:#cf222e }
/* KeywordPseudo */ .chroma .kp { color:#cf222e }
/* KeywordReserved */ .chroma .kr { color:#cf222e }
/* KeywordType */ .chroma .kt { color:#cf222e }
/* NameAttribute */ .chroma .na { color:#1f2328 }
/* NameClass */ .chroma .nc { color:#1f2328 }
/* NameConstant */ .chroma .no { color:#0550ae }
/* NameDecorator */ .chroma .nd { color:#0550ae }
/* NameEntity */ .chroma .ni { color:#6639ba }
/* NameLabel */ .chroma .nl { color:#900;font-weight:bold }
/* NameNamespace */ .chroma .nn { color:#24292e }
/* NameOther */ .chroma .nx { color:#1f2328 }
/* NameTag */ .chroma .nt { color:#0550ae }
/* NameBuiltin */ .chroma .nb { color:#6639ba }
/* NameBuiltinPseudo */ .chroma .bp { color:#6a737d }
/* NameVariable */ .chroma .nv { color:#953800 }
/* NameVariableClass */ .chroma .vc { color:#953800 }
/* NameVariableGlobal */ .chroma .vg { color:#953800 }
/* NameVariableInstance */ .chroma .vi { color:#953800 }
/* NameVariableMagic */ .chroma .vm { color:#953800 }
/* NameFunction */ .chroma .nf { color:#6639ba }
/* NameFunctionMagic */ .chroma .fm { color:#6639ba }
/* LiteralString */ .chroma .s { color:#0a3069 }
/* LiteralStringAffix */ .chroma .sa { color:#0a3069 }
/* LiteralStringBacktick */ .chroma .sb { color:#0a3069 }
/* LiteralStringChar */ .chroma .sc { color:#0a3069 }
/* LiteralStringDelimiter */ .chroma .dl { color:#0a3069 }
/* LiteralStringDoc */ .chroma .sd { color:#0a3069 }
/* LiteralStringDouble */ .chroma .s2 { color:#0a3069 }
/* LiteralStringEscape */ .chroma .se { color:#0a3069 }
/* LiteralStringHeredoc */ .chroma .sh { color:#0a3069 }
/* LiteralStringInterpol */ .chroma .si { color:#0a3069 }
/* LiteralStringOther */ .chroma .sx { color:#0a3069 }
/* LiteralStringRegex */ .chroma .sr { color:#0a3069 }
/* LiteralStringSingle */ .chroma .s1 { color:#0a3069 }
/* LiteralStringSymbol */ .chroma .ss { color:#032f62 }
/* LiteralNumber */ .chroma .m { color:#0550ae }
/* LiteralNumberBin */ .chroma .mb { color:#0550ae }
/* LiteralNumberFloat */ .chroma .mf { color:#0550ae }
/* LiteralNumberHex */ .chroma .mh { color:#0550ae }
/* LiteralNumberInteger */ .chroma .mi { color:#0550ae }
/* LiteralNumberIntegerLong */ .chroma .il { color:#0550ae }
/* LiteralNumberOct */ .chroma .mo { color:#0550ae }
/* Operator */ .chroma .o { color:#0550ae }
/* OperatorWord */ .chroma .ow { color:#0550ae }
/* Punctuation */ .chroma .p { color:#1f2328 }
/* Comment */ .chroma .c { color:#57606a }
/* CommentHashbang */ .chroma .ch { color:#57606a }
/* CommentMultiline */ .chroma .cm { color:#57606a }
/* CommentSingle */ .chroma .c1 { color:#57606a }
/* CommentSpecial */ .chroma .cs { color:#57606a }
/* CommentPreproc */ .chroma .cp { color:#57606a }
/* CommentPreprocFile */ .chroma .cpf { color:#57606a }
/* GenericDeleted */ .chroma .gd { color:#82071e;background-color:#ffebe9 }
/* GenericEmph */ .chroma .ge { color:#1f2328 }
/* GenericInserted */ .chroma .gi { color:#116329;background-color:#dafbe1 }
/* GenericOutput */ .chroma .go { color:#1f2328 }
/* GenericUnderline */ .chroma .gl { text-decoration:underline }
/* TextWhitespace */ .chroma .w { color:#fff }
}
html.dark {
/* Generated using: hugo gen chromastyles --style=github-dark */

/* Background */ .bg { color:#e6edf3;background-color:#0d1117; }
/* PreWrapper */ .chroma { color:#e6edf3;background-color:#0d1117; }
/* Error */ .chroma .err { color:#f85149 }
/* LineLink */ .chroma .lnlinks { outline:none;text-decoration:none;color:inherit }
/* LineTableTD */ .chroma .lntd { vertical-align:top;padding:0;margin:0;border:0; }
/* LineTable */ .chroma .lntable { border-spacing:0;padding:0;margin:0;border:0; }
/* LineHighlight */ .chroma .hl { background-color:#6e7681 }
/* LineNumbersTable */ .chroma .lnt { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679 }
/* LineNumbers */ .chroma .ln { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681 }
/* Line */ .chroma .line { display:flex; }
/* Keyword */ .chroma .k { color:#ff7b72 }
/* KeywordConstant */ .chroma .kc { color:#79c0ff }
/* KeywordDeclaration */ .chroma .kd { color:#ff7b72 }
/* KeywordNamespace */ .chroma .kn { color:#ff7b72 }
/* KeywordPseudo */ .chroma .kp { color:#79c0ff }
/* KeywordReserved */ .chroma .kr { color:#ff7b72 }
/* KeywordType */ .chroma .kt { color:#ff7b72 }
/* NameClass */ .chroma .nc { color:#f0883e;font-weight:bold }
/* NameConstant */ .chroma .no { color:#79c0ff;font-weight:bold }
/* NameDecorator */ .chroma .nd { color:#d2a8ff;font-weight:bold }
/* NameEntity */ .chroma .ni { color:#ffa657 }
/* NameException */ .chroma .ne { color:#f0883e;font-weight:bold }
/* NameLabel */ .chroma .nl { color:#79c0ff;font-weight:bold }
/* NameNamespace */ .chroma .nn { color:#ff7b72 }
/* NameProperty */ .chroma .py { color:#79c0ff }
/* NameTag */ .chroma .nt { color:#7ee787 }
/* NameVariable */ .chroma .nv { color:#79c0ff }
/* NameVariableClass */ .chroma .vc { color:#79c0ff }
/* NameVariableGlobal */ .chroma .vg { color:#79c0ff }
/* NameVariableInstance */ .chroma .vi { color:#79c0ff }
/* NameVariableMagic */ .chroma .vm { color:#79c0ff }
/* NameFunction */ .chroma .nf { color:#d2a8ff;font-weight:bold }
/* NameFunctionMagic */ .chroma .fm { color:#d2a8ff;font-weight:bold }
/* Literal */ .chroma .l { color:#a5d6ff }
/* LiteralDate */ .chroma .ld { color:#79c0ff }
/* LiteralString */ .chroma .s { color:#a5d6ff }
/* LiteralStringAffix */ .chroma .sa { color:#79c0ff }
/* LiteralStringBacktick */ .chroma .sb { color:#a5d6ff }
/* LiteralStringChar */ .chroma .sc { color:#a5d6ff }
/* LiteralStringDelimiter */ .chroma .dl { color:#79c0ff }
/* LiteralStringDoc */ .chroma .sd { color:#a5d6ff }
/* LiteralStringDouble */ .chroma .s2 { color:#a5d6ff }
/* LiteralStringEscape */ .chroma .se { color:#79c0ff }
/* LiteralStringHeredoc */ .chroma .sh { color:#79c0ff }
/* LiteralStringInterpol */ .chroma .si { color:#a5d6ff }
/* LiteralStringOther */ .chroma .sx { color:#a5d6ff }
/* LiteralStringRegex */ .chroma .sr { color:#79c0ff }
/* LiteralStringSingle */ .chroma .s1 { color:#a5d6ff }
/* LiteralStringSymbol */ .chroma .ss { color:#a5d6ff }
/* LiteralNumber */ .chroma .m { color:#a5d6ff }
/* LiteralNumberBin */ .chroma .mb { color:#a5d6ff }
/* LiteralNumberFloat */ .chroma .mf { color:#a5d6ff }
/* LiteralNumberHex */ .chroma .mh { color:#a5d6ff }
/* LiteralNumberInteger */ .chroma .mi { color:#a5d6ff }
/* LiteralNumberIntegerLong */ .chroma .il { color:#a5d6ff }
/* LiteralNumberOct */ .chroma .mo { color:#a5d6ff }
/* Operator */ .chroma .o { color:#ff7b72;font-weight:bold }
/* OperatorWord */ .chroma .ow { color:#ff7b72;font-weight:bold }
/* Comment */ .chroma .c { color:#8b949e;font-style:italic }
/* CommentHashbang */ .chroma .ch { color:#8b949e;font-style:italic }
/* CommentMultiline */ .chroma .cm { color:#8b949e;font-style:italic }
/* CommentSingle */ .chroma .c1 { color:#8b949e;font-style:italic }
/* CommentSpecial */ .chroma .cs { color:#8b949e;font-weight:bold;font-style:italic }
/* CommentPreproc */ .chroma .cp { color:#8b949e;font-weight:bold;font-style:italic }
/* CommentPreprocFile */ .chroma .cpf { color:#8b949e;font-weight:bold;font-style:italic }
/* GenericDeleted */ .chroma .gd { color:#ffa198;background-color:#490202 }
/* GenericEmph */ .chroma .ge { font-style:italic }
/* GenericError */ .chroma .gr { color:#ffa198 }
/* GenericHeading */ .chroma .gh { color:#79c0ff;font-weight:bold }
/* GenericInserted */ .chroma .gi { color:#56d364;background-color:#0f5323 }
/* GenericOutput */ .chroma .go { color:#8b949e }
/* GenericPrompt */ .chroma .gp { color:#8b949e }
/* GenericStrong */ .chroma .gs { font-weight:bold }
/* GenericSubheading */ .chroma .gu { color:#79c0ff }
/* GenericTraceback */ .chroma .gt { color:#ff7b72 }
/* GenericUnderline */ .chroma .gl { text-decoration:underline }
/* TextWhitespace */ .chroma .w { color:#6e7681 }
}

/* Chroma */

Blowfish + Isso Comments

Zu Blowfish habe ich die Isso Comments hinzugefügt.

In config/_default/params.toml folgendes anfügen und die URL https://comments.example.com/ an den Server anpassen.

[comments]
  type = "isso" #set commend type to isso, set urls and enable showComments in article section
  
[isso]
  url = "https://comments.example.com/" #set isso url
  src = "https://comments.example.com/js/embed.min.js" #set isso js url

In config/_default/params.toml im Abschnitt article noch showComments auf true setzen.

[article]
  showComments = true

Die Datei layouts/partials/comments.html erzeugen.

{{/* Only load comments if the comment type is isso */}}
{{ if eq .Site.Params.comments.type "isso" }}
  {{ partial "comments/isso.html" . }}
{{ end }}

Die Datei layouts/partials/comments/isso.html erzeugen, eventuell die Parameter anpassen.

{{/* Only load comments if the page allows it */}}
{{ if .Params.showComments | default (.Site.Params.article.showComments) }}
  <section id="isso-thread"></section>
  <script
    data-isso= "{{ .Site.Params.isso.url }}"
    data-isso-css="true"
    data-isso-lang="{{ .Language.Lang }}"
    data-isso-reply-to-self="false"
    data-isso-require-author="false"
    data-isso-require-email="false"
    data-isso-max-comments-top="10"
    data-isso-max-comments-nested="5"
    data-isso-reveal-on-click="5"
    src="{{ .Site.Params.isso.src }}">
  </script>
{{ end }}

Jetzt noch die CSS Definationen für Isso, in assets/css/custom.css anfügen.

/* Isso Comments */

#isso-thread {
  margin-top: 3rem;
}

.isso-textarea-wrapper .isso-textarea {
  background-color: white !important;
  color: black !important;
}

.isso-postbox.isso-preview-mode {
  background-color: white !important;
  color: black !important;
}

#isso-postbox-author {
  background-color: white !important;
  color: black !important;
}

#isso-postbox-email {
  background-color: white !important;
  color: black !important;
}

#isso-postbox-website {
  background-color: white !important;
  color: black !important;
}

html.dark {
  h4.isso-thread-heading {
    color: white !important;
  }

  .isso-post-action > input {
    color: white !important;
    background-color: #0550ae !important;
  }

  .isso-post-action > input:hover {
    background-color: #053d8eff !important;
  }

  .isso-feedlink:hover,
  .isso-comment-footer a:hover {
    color: white !important;
  }
}

/* Isso Comments */

Blowfish + Fancybox

Die Bild Integration in Blowfish hat mich nicht überzeugt, darum habe ich Fancybox hinzugefügt.

Die Datei layouts/partials/extend-head.html erzeugen.

<script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui@6.1/dist/fancybox/fancybox.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui@6.1/dist/fancybox/l10n/de_DE.umd.js"></script>

Die Datei layouts/partials/extend-footer.html erzeugen.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui@6.1/dist/fancybox/fancybox.css"/>

Die Datei layouts/shortcodes/fancybox-gallery.html erzeugen.

{{ $id := delimit (slice "fancybox-gallery" (partial "functions/uid.html" .)) "-" }}
{{/* Get the 'subdir' parameter, default to 'gallery' if not provided */}}
{{ $subdir := .Get "dir" | default "gallery" }}
{{ $pattern := printf "%s/*" $subdir }}
{{ $images := .Page.Resources.Match $pattern }}

<div class="fancybox-gallery" id="{{ $id }}">
  {{ range $images }}
    {{ if eq .ResourceType "image" }}
      {{/* get basename */}}
      {{ $file := path.Base .Name }}
      {{/* remove extension like ".jpg" */}}
      {{ $base := strings.TrimSuffix (path.Ext $file) $file }}
      {{/* remove numeric prefix like "05-" */}}
      {{ $clean := replaceRE `^[0-9]+[-_ ]*` `` $base }}
      {{/* replace "-" */}}
      {{ $title := replaceRE `[-]` ` ` $clean }}
      <a data-fancybox="fancybox-gallery" data-caption="{{ $title}}" href="{{ .RelPermalink }}" title="{{ $title }}">
        <img src="{{ .RelPermalink }}" alt="{{ $title }}" />
      </a>
    {{ else }}
      {{/* Optional: Log a warning if a non-image file is in the folder */}}
      {{ warnf "File %s in %s is not an image." .Name $subdir }}
    {{ end }}
  {{ end }}
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
  Fancybox.bind(document.getElementById("{{ $id }}"), "[data-fancybox=fancybox-gallery]", {
    Carousel: {
      Zoomable: {
        Panzoom : {
          protected: false,
        },
      },
      showClass: "f-fadeIn",
      Thumbs: true,
      Thumbs: {
        showOnStart: false,
      },
      Autoplay: {
        autoStart: false,
        timeout: 2500,
      },
      Toolbar: {
        display: {
          left: ["counter"],
          middle: ["zoomIn", "zoomOut"],
          right: ["autoplay", "thumbs", "close"],
        },
      },
      breakpoints: {
        "(min-width: 768px)": {
          Toolbar: {
            display: {
              left: ["counter"],
              middle: ["zoomIn", "zoomOut", "toggle1to1", "fullscreen"],
              right: ["autoplay", "thumbs", "close"],
           },
          },
        },
      },
    },
  });
});
</script>

Die Datei layouts/shortcodes/fancybox-image.html erzeugen.

{{ $id := delimit (slice "fancybox-image" (partial "functions/uid.html" .)) "-" }}
{{/* Get the 'src' parameter (e.g., "sunset.jpg") */}}
{{ $src := .Get "src" }}
{{/* Get the 'width' parameter (e.g., "50%") */}}
{{ $width := .Get "width" | default "100%" }}
{{ $image := .Page.Resources.GetMatch $src }}

{{ if $image }}
  {{/* get basename */}}
  {{ $file := path.Base $image.Name }}
  {{/* remove extension like ".jpg" */}}
  {{ $base := strings.TrimSuffix (path.Ext $file) $file }}
  {{/* remove numeric prefix like "05-" */}}
  {{ $clean := replaceRE `^[0-9]+[-_ ]*` `` $base }}
  {{/* replace "-" */}}
  {{ $title := replaceRE `[-]` ` ` $clean }}
  <div class="fancybox-image" id="{{ $id }}">
    <a data-fancybox="fancybox-image" data-caption="{{ $title }}" href="{{ $image.RelPermalink }}" title="{{ $title }}">
      <img width="{{ $width }}" src="{{ $image.RelPermalink }}" alt="{{ $title }}" />
    </a>
  </div>
{{ else }}
  {{ warnf "Image %s not found in page resources." $src }}
{{ end }}
<script>
document.addEventListener("DOMContentLoaded", () => {
  Fancybox.bind(document.getElementById("{{ $id }}"), "[data-fancybox=fancybox-image]", {
    Carousel: {
      Zoomable: {
        Panzoom : {
          protected: true,
        },
      },
      showClass: "f-fadeIn",
      Thumbs: true,
      Thumbs: {
        showOnStart: false,
      },
      Autoplay: {
        autoStart: false,
        timeout: 2500,
      },
      Toolbar: {
        display: {
          left: ["counter"],
          middle: ["zoomIn", "zoomOut"],
          right: ["close"],
        },
      },
      breakpoints: {
        "(min-width: 768px)": {
          Toolbar: {
            display: {
              left: ["counter"],
              middle: ["zoomIn", "zoomOut", "toggle1to1", "fullscreen"],
              right: ["close"],
           },
          },
        },
      },
    },
  });
});
</script>

Jetzt noch die CSS Definationen für Fancybox, in assets/css/custom.css anfügen.

/* Fancybox */

.fancybox-image {
  display: flex;
  place-items: center;
}

.fancybox-image img {
  border-radius: 0.25rem;
  transition: transform 0.2s ease;
  display: block;
  margin: .6rem auto;
}

.fancybox-image img:hover {
  transform: scale(1.03);
}

.fancybox-gallery {
  display: grid;
  gap: .6rem;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  @media only screen and (min-width: 768px) {
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  }
  place-items: center;
}

.fancybox-gallery img {
  border-radius: 0.25rem;
  transition: transform 0.2s ease;
  margin: 0 auto;
}

.fancybox-gallery img:hover {
  transform: scale(1.03);
}

.fancybox__container {
  --fancybox-backdrop-bg: rgba(48, 48, 56, .95);
}

/* Fancybox */

Fancybox benutzen

Im Artikel Unterverzeichniss wird ein Ordner gallery angelegt und dort die Bilddateien gespeichert.

Die Bilder kann man mit vorangestelltem xx- sortieren, durch den Shortcode wird der Dateinamen 01-Erstes-Bild.webp zum Bild Titel Erstes Bild umgewandelt.

.
├── assets
│   └── img
│       └── author.jpg
├── config
│   └── _default
├── content
│   ├── _index.md
│   └── blog
│       ├── _index.md
│       └── my-post
│           └── index.md
│           └── gallery
│               ├── 01-Erstes-Bild.webp
│               └── 02-Zweites-Bild.webp
└── themes
    └── blowfish

In der index.md mit folgendem Shortcode für eine Gallery.

{{< fancybox-gallery dir="gallery" >}}

In der index.md mit folgendem Shortcode für ein Einzelnes Bild.

Die Bildbreite kann man mit dem width Parameter beeinflussen, es wird automatisch zentriert.

{{< fancybox-image src="gallery/01-Erstes-Bild.webp" width="100%" >}}
{{< fancybox-image src="gallery/02-Zweites-Bild.webp" width="40%" >}}
Erstes Bild
Zweites Bild
alst.
Autor
alst.
Pensionist [ Autodidakt, Programmierer, Systemadministrator ]