Compare commits

..
Author SHA1 Message Date
JimmyandGitHub 25bf63778c feat: use thumbnail in section and links image (#1291)
* feat: use thumbnail for section image

* feat: use thumbnail in links image

* fix typo
2026-03-05 13:57:37 +01:00
JimmyandGitHub a95d0e7c85 refactor: use helper/image in links.html (#1290)
* refactor: use `helper/image` in `links.html`

* fix: check for $image existence before accessing
2026-03-05 13:51:34 +01:00
JimmyandGitHub 12f05571cf refactor: make article-list/compact better for term pages (#1289)
* refactor: rename article-time class to article-footer for consistency

* feat: show the number of subpages in `article-compact` when the page is a term
2026-03-05 13:43:55 +01:00
230a28fa8d feat(widgets): link widget titles to their respective pages (#1277)
- archives.html: Link the "Archives" title to the archives page,
  consistent with how individual year links already work
- taxonomy.html: Change showLink default from false to true so
  tag-cloud and categories widget titles link to their taxonomy
  listing pages by default

Widget titles are the primary affordance users interact with, so
linking them to the full listing page improves navigation. The
showLink param can still be set to false to disable this.

Co-authored-by: delize <4028612+delize@users.noreply.github.com>
2026-03-05 13:18:32 +01:00
JimmyandGitHub a9a14ce815 feat: hide search results container if no keyword is given (#1288)
feat: hide search results container if no keyword is given and add typing
2026-03-05 12:52:06 +01:00
JimmyandGitHub 0ad02c5c0f refactor: add standardized spacing variables and more style cleanup (#1287)
* refactor: add standardized spacing variables and more style cleanup

* refactor: rename `spacing-xxl` to `spacing-2xl` and introduce `spacing-3xl` for consistent spacing variables.

* refactor: Migrate related content styling to use the `subsection-list` component with updated layout adjustments.

* style: adjust padding values for article list tiles.

* refactor: Introduce shadow variables and dynamic spacing to prevent shadow cutoff in list layouts.

* refactor: relocate dark scheme Mermaid variables within the root scope.
2026-03-05 12:29:48 +01:00
JimmyandGitHub 88468ad84e refactor: cleanup stylesheet (#1286)
* refactor: cleanup stylesheet

* Update assets/scss/partials/article.scss
2026-03-05 10:40:13 +01:00
JimmyandGitHub 3dae60ac7a fix: use IsImageResourceWithMeta to check whether image has width / height attributes (#1285) 2026-03-05 09:23:47 +01:00
22edce91ab feat: add responsive image support (#1283)
* feat: create responsive-image helper

* feat: new responsive-image helper

* feat: Implement responsive image rendering in page content using a new partial and configure image processing widths.

* feat: add `sizes` attribute for enhanced image responsiveness

* feat: add responsive image to page header too

* Check for `.Enabled` before processing image

* style: remove wrong article-list--tile width and height

* feat: add `helper/thumbnail-image` for responsive thumbnails

* feat: enable content image processing and update responsive image `sizes` attributes for improved display.

* fix: get src from the attributes so external image can work correctly

* fix: add missing data-title-escaped

* Update layouts/_partials/article/components/header.html

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* feat: remove default `true` for thumbnail resize parameter.

* Update layouts/_markup/render-image.html

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update layouts/_partials/helper/thumbnail-image.html

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add original width and height to thumbnail pictures, for case that resize is not enabled

* Update layouts/_partials/helper/thumbnail-image.html

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-04 23:32:33 +01:00
JimmyandGitHub 78800c7379 fix: keep external image url, and use reflect.IsImageResourceProcessable to check for svg and other non-processable image formats (#1282)
* fix: keep external image url, and use reflect.IsImageResourceProcessable to check for svg

* build: update Hugo version to 0.157.0 in the build script.

* docs: remove `Local` return value and simplify `Resource` description in image helper

* Check for reflect.IsImageResourceProcessable before getting color from image

* chore: update minimum Hugo version to 0.157.0
2026-03-04 13:05:17 +01:00
a0bd0073bb fix: reading time not displaying in article/components/details partial (#1278)
Fix reading time not displaying due to .Site context in dict partial

The details.html partial receives a dict context via:
  partial "article/components/details" (dict "Page" $Page "IsList" $IsList)

When the dot context is a dict, .Site resolves to nil, so
.Site.Params.article.readingTime always returns empty/false,
causing the reading time to never display.

Lines 40 and 67 of the same file already correctly use
$Page.Site.Params — this fixes line 31 to be consistent.

Co-authored-by: delize <4028612+delize@users.noreply.github.com>
2026-03-04 11:58:09 +01:00
Jimmy Cai 3ca74956a6 ci: update Hugo version to 0.156.0 2026-02-19 17:54:42 +01:00
Jimmy Cai 22b8d091f4 doc(demo): disable cookie consent 2026-02-19 17:50:48 +01:00
JimmyandGitHub bd7a34966c refactor(sidebar): drop support for .Pre and remove old codes (#1271)
* refactor(sidebar): drop support for .Pre and remove old codes

* Simplify
2026-02-19 17:49:50 +01:00
JimmyandGitHub 2be5b48324 feat(list): ability to show article tags on homepage (#1270)
* feat(list): ability to show article tags on homepage

closes https://github.com/CaiJimmy/hugo-theme-stack/issues/880

* Disable showTags by default
2026-02-19 17:45:09 +01:00
4ad88a327e feat: add GDPR cookie consent banner (#1216)
* feat: add GDPR cookie consent banner

Add a GDPR-compliant cookie consent banner that gates analytics (Google
Analytics) and functional cookies (comments) until user consent is given.

Features:
- Theme-consistent styling using CSS variables (light/dark mode)
- Settings panel with category toggles (necessary, analytics, functional)
- Consent stored in cookie for 365 days
- Google Analytics only loads after analytics consent
- Comments only load after functional consent
- Translations for all 32 supported languages
- Accessible with proper ARIA attributes and focus management

Configuration in hugo.yaml:
  params:
    cookies:
      enabled: true
      categories:
        analytics: true
        functional: true

When cookies.enabled is false, scripts load normally without consent gating.

Closes #412

* refactor: move consent-gated GA to cookies/analytics.html

Move Google Analytics consent logic to avoid shadowing Hugo's internal
`_internal/google_analytics.html` template.

Changes:
- Rename google_analytics.html → cookies/analytics.html
- Update head.html to conditionally load:
  - cookies.enabled=true: cookies/analytics.html (consent-gated)
  - cookies.enabled=false: Hugo's _internal/google_analytics.html
- Align cookies/analytics.html with Hugo's internal template:
  - Support Privacy.GoogleAnalytics.Disable setting
  - Support Privacy.GoogleAnalytics.RespectDoNotTrack setting
  - Add UA- prefix deprecation warning

Note: Custom JS is required for consent-gated GA because Hugo templates
render at build time, but consent happens at runtime. We cannot call
Hugo's internal template after the user grants consent - we must
dynamically inject the GA script via JavaScript.

* style: indent cookies/analytics.html

* refactor: don't use default, put default configurations in params.toml

* fix: don't use `description` as translation key because it's reserved and will throw error

* fix: remove footer/components/custom-font.html call

* fix: banner showSettings key

* style: indent comments/include

* Add missing article.alert translation back

* style: format i18n toml files

* style: indent head/head.html

* style adjustment

* style: add missing -

* style: format demo/config/params.toml

* style: remove redundant comments from cookies.scss

* style: update box-shadow for cookie banner content

---------

Co-authored-by: delize <4028612+delize@users.noreply.github.com>
Co-authored-by: Jimmy Cai <jimmy@cai.im>
2026-02-19 17:21:25 +01:00
77 changed files with 1863 additions and 290 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
main() {
DART_SASS_VERSION=1.97.1
GO_VERSION=1.25.5
HUGO_VERSION=0.154.5
HUGO_VERSION=0.157.0
NODE_VERSION=24.12.0
export TZ=Europe/Oslo
+1 -1
View File
@@ -19,7 +19,7 @@ a {
.section-title {
text-transform: uppercase;
margin-top: 0;
margin-bottom: 10px;
margin-bottom: var(--spacing-sm);
display: block;
font-size: 1.6rem;
font-weight: bold;
+4 -8
View File
@@ -60,14 +60,10 @@
display: flex;
flex-direction: row;
&.column {
flex-direction: column;
}
&.on-phone--column {
flex-direction: column;
@include respond(md) {
flex-direction: unset;
flex-direction: row;
}
}
@@ -93,11 +89,11 @@ main.main {
.main-container {
min-height: 100vh;
align-items: flex-start;
padding: 0 15px;
padding: 0 var(--container-padding);
gap: var(--section-separation);
padding-top: var(--main-top-padding);
@include respond(md) {
padding: 0 20px;
padding-top: 0;
}
}
+5
View File
@@ -0,0 +1,5 @@
@mixin card-style {
background-color: var(--card-background);
box-shadow: var(--shadow-l1);
border-radius: var(--card-border-radius);
}
+33 -52
View File
@@ -7,9 +7,7 @@
article {
display: flex;
flex-direction: column;
background-color: var(--card-background);
box-shadow: var(--shadow-l1);
border-radius: var(--card-border-radius);
@include card-style;
overflow: hidden;
transition: box-shadow 0.3s ease;
@@ -21,16 +19,8 @@
.article-image {
img {
width: 100%;
height: 150px;
height: var(--article-image-height);
object-fit: cover;
@include respond(md) {
height: 200px;
}
@include respond(xl) {
height: 250px;
}
}
}
}
@@ -41,7 +31,7 @@
flex-direction: column;
justify-content: center;
padding: var(--card-padding);
gap: 15px;
gap: var(--spacing-md);
}
.article-title {
@@ -59,7 +49,7 @@
color: var(--card-text-color-main);
&:hover {
color: var(--card-text-color-main);
opacity: 0.8;
}
}
}
@@ -81,11 +71,12 @@
gap: 8px;
}
.article-time,
.article-translations {
.article-footer,
.article-translations,
.article-header-tags {
display: flex;
color: var(--card-text-color-tertiary);
gap: 15px;
gap: var(--spacing-md);
svg {
vertical-align: middle;
@@ -104,7 +95,7 @@
& > div {
display: inline-flex;
align-items: center;
gap: 15px;
gap: var(--spacing-md);
}
}
@@ -112,7 +103,8 @@
flex-wrap: wrap;
}
.article-translations {
.article-translations,
.article-header-tags {
& > div {
flex-wrap: wrap;
}
@@ -121,7 +113,7 @@
.article-category,
.article-tags {
display: flex;
gap: 10px;
gap: var(--spacing-sm);
flex-wrap: wrap;
a {
@@ -142,21 +134,15 @@
/* Compact style article list */
.article-list--compact {
border-radius: var(--card-border-radius);
box-shadow: var(--shadow-l1);
background-color: var(--card-background);
--image-size: 50px;
@include respond(md) {
--image-size: 60px;
}
@include card-style;
--image-size: var(--article-compact-image-size);
article {
& > a {
display: flex;
align-items: center;
padding: var(--small-card-padding);
gap: 15px;
gap: var(--spacing-md);
}
&:not(:last-of-type) {
@@ -167,7 +153,7 @@
flex-grow: 1;
padding: 0;
min-height: var(--image-size);
gap: 10px;
gap: var(--spacing-sm);
}
.article-title {
@@ -187,7 +173,7 @@
}
}
.article-time {
.article-footer {
font-size: 1.4rem;
}
@@ -202,14 +188,12 @@
/* Tile style article list */
.article-list--tile {
article {
border-radius: var(--card-border-radius);
overflow: hidden;
position: relative;
height: 350px;
width: 250px;
box-shadow: var(--shadow-l1);
width: var(--article-tile-width);
height: var(--article-tile-height);
@include card-style;
overflow: hidden;
transition: box-shadow 0.3s ease;
background-color: var(--card-background);
&:hover {
box-shadow: var(--shadow-l2);
@@ -217,11 +201,11 @@
&.has-image {
.article-details {
background-color: rgba(#000, 0.25);
background-color: var(--article-tile-overlay-bg);
}
.article-title {
color: #fff;
color: var(--article-tile-text-color);
}
}
@@ -326,19 +310,19 @@
button {
padding: 0.5rem 1rem;
background: rgba(255, 255, 255, 0.95);
border: 2px solid #333;
background: var(--mermaid-toolbar-btn-bg);
border: 2px solid var(--mermaid-toolbar-btn-text);
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
font-weight: 600;
color: #333;
color: var(--mermaid-toolbar-btn-text);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
transition: all 0.2s;
&:hover {
background: #333;
color: #fff;
background: var(--mermaid-toolbar-btn-text);
color: var(--card-background);
transform: scale(1.05);
}
}
@@ -352,7 +336,7 @@
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.95);
background: var(--mermaid-modal-bg);
z-index: 9999;
&.active {
@@ -377,14 +361,15 @@
.mermaid-modal-controls button,
.mermaid-modal-close {
padding: 0.5rem 1rem;
background: #fff;
background: var(--mermaid-modal-btn-bg);
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.9rem;
color: var(--card-text-color-main);
&:hover {
background: #ddd;
background: var(--mermaid-modal-btn-hover);
}
}
@@ -402,11 +387,7 @@
bottom: 1rem;
border-radius: 8px;
overflow: hidden;
background: #fff;
[data-scheme="dark"] & {
background: #1e1e1e;
}
background: var(--mermaid-modal-content-bg);
}
.mermaid-panzoom-container {
+197
View File
@@ -0,0 +1,197 @@
.cookie-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 9999;
padding: var(--container-padding);
&[aria-hidden="true"] {
display: none;
}
}
.cookie-banner__content {
max-width: 900px;
margin: 0 auto;
background: var(--card-background);
border-radius: var(--card-border-radius);
box-shadow: var(--shadow-l2);
padding: var(--card-padding);
display: flex;
flex-direction: column;
gap: var(--spacing-md);
@include respond(md) {
flex-direction: row;
flex-wrap: wrap;
align-items: center;
gap: var(--spacing-lg);
}
}
.cookie-banner__text {
flex: 1;
min-width: 200px;
strong {
display: block;
color: var(--card-text-color-main);
font-size: 1.6rem;
margin-bottom: var(--spacing-xs);
}
p {
color: var(--card-text-color-secondary);
font-size: 1.4rem;
line-height: 1.6;
margin: 0;
}
}
.cookie-banner__actions {
display: flex;
gap: var(--spacing-sm);
flex-shrink: 0;
}
.cookie-banner__settings-link {
width: auto;
align-self: flex-start;
background: none;
border: none;
color: var(--accent-color);
font-size: 1.3rem;
cursor: pointer;
padding: 5px;
&:hover {
text-decoration: underline;
}
@include respond(md) {
margin-left: auto;
align-self: center;
}
}
.cookie-btn {
padding: 10px 20px;
border-radius: var(--tag-border-radius);
font-size: 1.4rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
border: none;
&--primary {
background: var(--accent-color);
color: var(--accent-color-text);
&:hover {
background: var(--accent-color-darker);
}
}
&--secondary {
background: transparent;
color: var(--card-text-color-main);
border: 1.5px solid var(--card-separator-color);
&:hover {
background: var(--card-background-selected);
}
}
}
// Cookie settings panel
.cookie-settings {
padding-top: 20px;
border-top: 1.5px solid var(--card-separator-color);
&[aria-hidden="true"] {
display: none;
}
h3 {
color: var(--card-text-color-main);
font-size: 1.6rem;
margin: 0 0 var(--spacing-md) 0;
}
&__actions {
display: flex;
justify-content: flex-end;
gap: var(--spacing-sm);
margin-top: var(--spacing-lg);
}
}
.cookie-category {
padding: var(--spacing-md) 0;
border-bottom: 1px solid var(--card-separator-color);
&:last-of-type {
border-bottom: none;
}
label {
display: flex;
align-items: center;
gap: var(--spacing-sm);
cursor: pointer;
input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: var(--accent-color);
cursor: pointer;
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
strong {
color: var(--card-text-color-main);
font-size: 1.5rem;
}
}
p {
color: var(--card-text-color-secondary);
font-size: 1.3rem;
margin: 8px 0 0 28px;
line-height: 1.5;
}
}
// Footer link for reopening settings
.cookie-settings-link {
display: inline-block;
color: var(--body-text-color);
font-size: 1.2rem;
cursor: pointer;
background: none;
border: none;
padding: 0;
&:hover {
color: var(--accent-color);
text-decoration: underline;
}
}
// Comments placeholder when consent not given
.consent-placeholder {
@include card-style;
padding: var(--card-padding);
text-align: center;
color: var(--card-text-color-secondary);
font-size: 1.4rem;
p {
margin: 0 0 var(--spacing-md) 0;
}
}
+3 -3
View File
@@ -1,5 +1,5 @@
footer.site-footer {
padding: 20px 0 var(--section-separation) 0;
padding: var(--spacing-lg) 0 var(--section-separation) 0;
font-size: 1.4rem;
line-height: 1.75;
@@ -9,13 +9,13 @@ footer.site-footer {
height: 3px;
width: 50px;
background: var(--body-text-color);
margin-bottom: 20px;
margin-bottom: var(--spacing-lg);
}
.copyright {
color: var(--accent-color);
font-weight: bold;
margin-bottom: 5px;
margin-bottom: var(--spacing-xs);
}
.powerby {
+14 -40
View File
@@ -111,14 +111,14 @@
}
li {
margin: 15px 0 15px 20px;
padding: 5px;
margin: var(--spacing-md) 0 var(--spacing-md) var(--spacing-lg);
padding: var(--spacing-xs);
&>ol,
&>ul {
margin-top: 10px;
padding-left: 10px;
margin-bottom: -5px;
margin-top: var(--spacing-sm);
padding-left: var(--spacing-sm);
margin-bottom: calc(var(--spacing-xs) * -1);
&>li:last-child {
margin-bottom: 0;
@@ -182,7 +182,7 @@
.article-toc {
padding: 0 var(--card-padding);
margin-top: 20px;
margin-top: var(--spacing-lg);
@include respond(lg) {
display: none;
@@ -203,7 +203,7 @@
summary {
list-style: none;
padding: 15px 20px;
padding: var(--spacing-md) var(--spacing-lg);
cursor: pointer;
&::-webkit-details-marker {
@@ -213,7 +213,7 @@
.article-toc-title {
display: flex;
align-items: center;
gap: 10px;
gap: var(--spacing-sm);
text-transform: uppercase;
font-size: 1.4rem;
font-weight: bold;
@@ -230,35 +230,6 @@
}
}
.related-content {
overflow-x: auto;
padding-bottom: 15px;
&>.flex {
float: left;
}
article {
margin-right: 15px;
flex-shrink: 0;
overflow: hidden;
width: 250px;
height: 150px;
.article-title {
font-size: 1.8rem;
margin: 0;
}
&.has-image {
.article-details {
padding: 20px;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.25) 0%, rgba(0, 0, 0, 0.75) 100%);
}
}
}
}
.article-content {
font-family: var(--article-font-family);
font-size: var(--article-font-size);
@@ -315,7 +286,7 @@
position: relative;
margin: 1.5em 0;
border-inline-start: var(--blockquote-border-size) solid var(--card-separator-color);
padding: 15px calc(var(--card-padding) - var(--blockquote-border-size));
padding: var(--spacing-md) calc(var(--card-padding) - var(--blockquote-border-size));
background-color: var(--blockquote-background-color);
.cite {
@@ -385,7 +356,7 @@
flex-direction: row;
justify-content: center;
margin: 1.5em 0;
gap: 10px;
gap: var(--spacing-sm);
figure {
margin: 0;
@@ -420,6 +391,10 @@
padding: var(--card-padding);
position: relative;
pre {
background-color: transparent;
}
&:hover {
.copyCodeButton {
opacity: 1;
@@ -432,7 +407,6 @@
}
pre {
margin: initial;
padding: 0;
margin: 0;
width: auto;
+15 -11
View File
@@ -1,11 +1,9 @@
.section-card {
border-radius: var(--card-border-radius);
background-color: var(--card-background);
@include card-style;
padding: var(--small-card-padding);
box-shadow: var(--shadow-l1);
display: flex;
align-items: center;
gap: 20px;
gap: var(--spacing-lg);
--separation: 15px;
@@ -47,18 +45,24 @@
}
.subsection-list {
overflow-x: auto;
margin-left: -24px;
--shadow-overlap-x: calc(var(--shadow-l2-blur) + 4px);
--shadow-overlap-y: calc(var(--shadow-l2-blur) + var(--shadow-l2-y) + 4px);
overflow-x: auto;
/// These values are computed from the shadow of the article-list--tile (which uses shadow-l1 and shadow-l2)
/// so that the shadow is not cut off
margin-left: calc(var(--shadow-overlap-x) * -1);
margin-right: calc(var(--shadow-overlap-x) * -1);
margin-bottom: calc(var(--shadow-overlap-y) * -1);
.article-list--tile {
display: flex;
padding: 8px 0 34px 24px;
padding: var(--spacing-xs) var(--shadow-overlap-x) var(--shadow-overlap-y) var(--shadow-overlap-x);
width: max-content;
gap: var(--spacing-lg);
article {
width: 250px;
height: 150px;
margin-right: 20px;
flex-shrink: 0;
.article-title {
@@ -67,7 +71,7 @@
}
.article-details {
padding: 20px;
padding: var(--spacing-lg);
}
}
}
+12 -7
View File
@@ -7,12 +7,12 @@
label {
font-size: 1.3rem;
top: 10px;
top: var(--spacing-sm);
}
input {
font-size: 1.5rem;
padding: 30px 20px 15px 20px;
padding: var(--spacing-2xl) var(--spacing-lg) var(--spacing-md) var(--spacing-lg);
}
}
@@ -23,21 +23,20 @@
label {
position: absolute;
top: 15px;
inset-inline-start: 20px;
top: var(--spacing-md);
inset-inline-start: var(--spacing-lg);
font-size: 1.4rem;
color: var(--card-text-color-tertiary);
}
input {
padding: 40px 20px 20px;
border-radius: var(--card-border-radius);
background-color: var(--card-background);
box-shadow: var(--shadow-l1);
@include card-style;
color: var(--card-text-color-main);
width: 100%;
border: 0;
-webkit-appearance: none;
appearance: none;
transition: box-shadow 0.3s ease;
@@ -79,4 +78,10 @@
}
}
}
.search-result {
&.hidden {
display: none;
}
}
+5 -15
View File
@@ -7,7 +7,7 @@
*/
.hamburger {
padding-top: 10px;
padding-top: var(--spacing-sm);
display: inline-block;
cursor: pointer;
transition-property: opacity, filter;
@@ -26,16 +26,6 @@
opacity: 0.7;
}
.hamburger.is-active:hover {
opacity: 0.7;
}
.hamburger.is-active .hamburger-inner,
.hamburger.is-active .hamburger-inner::before,
.hamburger.is-active .hamburger-inner::after {
background-color: #000;
}
.hamburger-box {
width: 30px;
height: 24px;
@@ -149,7 +139,7 @@
display: none;
margin: 0 calc(var(--container-padding) * -1);
padding: 30px 30px;
padding: var(--spacing-2xl);
@include respond(xl) {
padding: 15px 0;
@@ -158,10 +148,10 @@
&,
.menu-bottom-section ol {
flex-direction: column;
gap: 30px;
gap: var(--spacing-2xl);
@include respond(xl) {
gap: 25px;
gap: var(--spacing-xl);
}
}
@@ -230,7 +220,7 @@
margin: 0;
display: flex;
flex-direction: row;
gap: 10px;
gap: var(--spacing-sm);
svg {
width: 24px;
+1 -3
View File
@@ -1,8 +1,6 @@
.pagination {
display: flex;
background-color: var(--card-background);
box-shadow: var(--shadow-l1);
border-radius: var(--card-border-radius);
@include card-style;
overflow: hidden;
.page-link {
+4 -4
View File
@@ -35,7 +35,7 @@
position: relative;
--sidebar-avatar-size: 100px;
--sidebar-element-separation: 20px;
--sidebar-element-separation: var(--spacing-lg);
--emoji-size: 40px;
--emoji-font-size: 20px;
@@ -48,7 +48,7 @@
@include respond(2xl) {
--sidebar-avatar-size: 120px;
--sidebar-element-separation: 25px;
--sidebar-element-separation: var(--spacing-xl);
--emoji-size: 40px;
}
@@ -67,7 +67,7 @@
}
.site-meta {
gap: 5px;
gap: var(--spacing-xs);
}
.site-name {
@@ -144,7 +144,7 @@
.site-meta {
display: flex;
flex-direction: column;
gap: 10px;
gap: var(--spacing-sm);
justify-content: center;
}
+5 -8
View File
@@ -4,8 +4,8 @@
.widget-icon {
svg {
width: 32px;
height: 32px;
width: var(--widget-icon-size);
height: var(--widget-icon-size);
stroke-width: 1.6;
color: var(--body-text-color);
}
@@ -17,11 +17,10 @@
.tagCloud-tags {
display: flex;
flex-wrap: wrap;
gap: 10px;
gap: var(--spacing-sm);
a {
background: var(--card-background);
box-shadow: var(--shadow-l1);
@include card-style;
border-radius: var(--tag-border-radius);
padding: 8px 20px;
color: var(--card-text-color-main);
@@ -38,9 +37,7 @@
/* Archives widget */
.widget.archives {
.widget-archive--list {
border-radius: var(--card-border-radius);
box-shadow: var(--shadow-l1);
background-color: var(--card-background);
@include card-style;
}
.archives-year {
+2
View File
@@ -8,6 +8,7 @@
@import "breakpoints.scss";
@import "variables.scss";
@import "mixins.scss";
@import "grid.scss";
@import "external/normalize.scss";
@@ -23,6 +24,7 @@
@import "partials/layout/list.scss";
@import "partials/layout/404.scss";
@import "partials/layout/search.scss";
@import "partials/cookies.scss";
@import "general.scss";
@import "custom.scss";
+69 -9
View File
@@ -120,9 +120,6 @@
--link-background-opacity: 0.5;
--link-background-opacity-hover: 0.7;
--pre-background-color: #272822;
--pre-text-color: #f8f8f2;
--code-background-color: rgba(0, 0, 0, 0.12);
--code-text-color: #808080;
@@ -147,11 +144,13 @@
* Thanks to https://www.figma.com/community/plugin/744987207861965946/Shadow-picker
*/
:root {
--shadow-l1: 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 0px 2px rgba(0, 0, 0, 0.06), 0px 0px 1px rgba(0, 0, 0, 0.04);
--shadow-l2: 0px 10px 20px rgba(0, 0, 0, 0.04), 0px 2px 6px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04);
--shadow-l3: 0px 10px 20px rgba(0, 0, 0, 0.04), 0px 2px 6px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04);
--shadow-l4: 0px 24px 32px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04),
0px 0px 1px rgba(0, 0, 0, 0.04);
--shadow-l1-y: 4px;
--shadow-l1-blur: 8px;
--shadow-l2-y: 10px;
--shadow-l2-blur: 20px;
--shadow-l1: 0px var(--shadow-l1-y) var(--shadow-l1-blur) rgba(0, 0, 0, 0.04), 0px 0px 2px rgba(0, 0, 0, 0.06), 0px 0px 1px rgba(0, 0, 0, 0.04);
--shadow-l2: 0px var(--shadow-l2-y) var(--shadow-l2-blur) rgba(0, 0, 0, 0.04), 0px 2px 6px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04);
}
[data-scheme="light"] {
@@ -169,5 +168,66 @@
:root {
--menu-icon-separation: 40px;
--container-padding: 15px;
@include respond(md) {
--container-padding: 20px;
}
--widget-separation: var(--section-separation);
}
/* Spacing tokens */
--spacing-xs: 5px;
--spacing-sm: 10px;
--spacing-md: 15px;
--spacing-lg: 20px;
--spacing-xl: 25px;
--spacing-2xl: 30px;
--spacing-3xl: 40px;
/* Article image height variables */
--article-image-height: 150px;
@include respond(md) {
--article-image-height: 200px;
}
@include respond(xl) {
--article-image-height: 250px;
}
/* Mermaid component variables */
--mermaid-toolbar-btn-bg: rgba(255, 255, 255, 0.95);
--mermaid-toolbar-btn-text: #333;
--mermaid-modal-bg: rgba(0, 0, 0, 0.95);
--mermaid-modal-content-bg: #fff;
--mermaid-modal-btn-bg: #fff;
--mermaid-modal-btn-hover: #ddd;
&[data-scheme="dark"] {
--mermaid-toolbar-btn-bg: rgba(0, 0, 0, 0.85);
--mermaid-toolbar-btn-text: #eee;
--mermaid-modal-content-bg: #1e1e1e;
--mermaid-modal-btn-bg: #333;
--mermaid-modal-btn-hover: #444;
}
/* Article compact image size */
--article-compact-image-size: 50px;
@include respond(md) {
--article-compact-image-size: 60px;
}
/* Widget icon size */
--widget-icon-size: 32px;
/* Article list tile variables */
--article-tile-width: 250px;
--article-tile-height: 150px;
--article-tile-overlay-bg: rgba(0, 0, 0, 0.25);
--article-tile-text-color: #fff;
&[data-scheme="dark"] {
--article-tile-overlay-bg: rgba(0, 0, 0, 0.45);
}
}
+221
View File
@@ -0,0 +1,221 @@
interface ConsentState {
necessary: boolean;
analytics: boolean;
functional: boolean;
timestamp: number;
}
class CookieConsent {
private static COOKIE_NAME = 'cookie_consent';
private static COOKIE_DAYS = 365;
private state: ConsentState | null = null;
private banner: HTMLElement | null = null;
private settingsPanel: HTMLElement | null = null;
constructor() {
this.banner = document.getElementById('cookie-consent-banner');
this.settingsPanel = document.getElementById('cookie-settings-panel');
this.state = this.loadState();
if (!this.state && this.banner) {
this.showBanner();
}
this.bindEvents();
this.dispatchConsentEvent();
}
private loadState(): ConsentState | null {
const cookie = document.cookie
.split('; ')
.find(row => row.startsWith(CookieConsent.COOKIE_NAME + '='));
if (!cookie) return null;
try {
return JSON.parse(decodeURIComponent(cookie.split('=')[1]));
} catch {
return null;
}
}
private saveState(): void {
if (!this.state) return;
const expires = new Date();
expires.setDate(expires.getDate() + CookieConsent.COOKIE_DAYS);
document.cookie = `${CookieConsent.COOKIE_NAME}=${encodeURIComponent(JSON.stringify(this.state))}; expires=${expires.toUTCString()}; path=/; SameSite=Lax`;
}
private showBanner(): void {
if (this.banner) {
this.banner.removeAttribute('aria-hidden');
}
}
private hideBanner(): void {
if (this.banner) {
// Blur any focused element inside the banner before hiding
const activeElement = document.activeElement as HTMLElement;
if (activeElement && this.banner.contains(activeElement)) {
activeElement.blur();
}
this.banner.setAttribute('aria-hidden', 'true');
}
this.hideSettings();
}
private showSettings(): void {
if (this.settingsPanel) {
this.settingsPanel.removeAttribute('aria-hidden');
// Restore checkbox states from current state or defaults
const checkboxes = this.settingsPanel.querySelectorAll('input[data-cookie-category]');
checkboxes.forEach((cb) => {
const input = cb as HTMLInputElement;
const category = input.dataset.cookieCategory as keyof ConsentState;
if (category && this.state && typeof this.state[category] === 'boolean') {
input.checked = this.state[category] as boolean;
} else {
input.checked = false;
}
});
}
}
private hideSettings(): void {
if (this.settingsPanel) {
// Blur any focused element inside the settings panel before hiding
const activeElement = document.activeElement as HTMLElement;
if (activeElement && this.settingsPanel.contains(activeElement)) {
activeElement.blur();
}
this.settingsPanel.setAttribute('aria-hidden', 'true');
}
}
private bindEvents(): void {
document.addEventListener('click', (e) => {
const target = e.target as HTMLElement;
const action = target.dataset.cookieAction;
if (!action) return;
switch (action) {
case 'accept':
this.acceptAll();
break;
case 'deny':
this.denyAll();
break;
case 'settings':
this.showSettings();
break;
case 'save':
this.saveSettings();
break;
case 'cancel':
this.hideSettings();
break;
case 'reopen':
this.showBanner();
break;
}
});
}
private acceptAll(): void {
this.state = {
necessary: true,
analytics: true,
functional: true,
timestamp: Date.now()
};
this.saveState();
this.hideBanner();
this.dispatchConsentEvent();
}
private denyAll(): void {
this.state = {
necessary: true,
analytics: false,
functional: false,
timestamp: Date.now()
};
this.saveState();
this.hideBanner();
this.dispatchConsentEvent();
}
private saveSettings(): void {
const checkboxes = document.querySelectorAll('input[data-cookie-category]');
this.state = {
necessary: true,
analytics: false,
functional: false,
timestamp: Date.now()
};
checkboxes.forEach((cb) => {
const input = cb as HTMLInputElement;
const category = input.dataset.cookieCategory as keyof ConsentState;
if (category && category in this.state!) {
(this.state as any)[category] = input.checked;
}
});
this.saveState();
this.hideBanner();
this.dispatchConsentEvent();
}
private dispatchConsentEvent(): void {
const event = new CustomEvent('onCookieConsentChange', {
detail: this.state
});
window.dispatchEvent(event);
// Set data attributes on document for CSS-based control
if (this.state) {
document.documentElement.dataset.consentAnalytics = String(this.state.analytics);
document.documentElement.dataset.consentFunctional = String(this.state.functional);
}
}
// Public API
public hasConsent(category: keyof Omit<ConsentState, 'timestamp'>): boolean {
if (!this.state) return false;
return this.state[category] ?? false;
}
public getState(): ConsentState | null {
return this.state;
}
public reopenBanner(): void {
this.showBanner();
}
}
// Export for module usage
export default CookieConsent;
// Initialize when DOM is ready and expose globally
declare global {
interface Window {
cookieConsent: CookieConsent;
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
window.cookieConsent = new CookieConsent();
});
} else {
window.cookieConsent = new CookieConsent();
}
+38 -23
View File
@@ -26,15 +26,15 @@ const tagsToReplace = {
'…': '&hellip;'
};
function replaceTag(tag) {
return tagsToReplace[tag] || tag;
function replaceTag(tag: string) {
return (tagsToReplace as Record<string, string>)[tag] || tag;
}
function replaceHTMLEnt(str) {
function replaceHTMLEnt(str: string) {
return str.replace(/[&<>"]/g, replaceTag);
}
function escapeRegExp(string) {
function escapeRegExp(string: string) {
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
}
@@ -45,13 +45,21 @@ class Search {
private list: HTMLDivElement;
private resultTitle: HTMLHeadElement;
private resultTitleTemplate: string;
private container: HTMLDivElement;
constructor({ form, input, list, resultTitle, resultTitleTemplate }) {
constructor({ form, input, list, resultTitle, resultTitleTemplate }: {
form: HTMLFormElement,
input: HTMLInputElement,
list: HTMLDivElement,
resultTitle: HTMLHeadingElement,
resultTitleTemplate: string
}) {
this.form = form;
this.input = input;
this.list = list;
this.resultTitle = resultTitle;
this.resultTitleTemplate = resultTitleTemplate;
this.container = list.parentElement as HTMLDivElement;
/// Check if there's already value in the search input
if (this.input.value.trim() !== '') {
@@ -203,16 +211,18 @@ class Search {
const endTime = performance.now();
this.resultTitle.innerText = this.generateResultTitle(results.length, ((endTime - startTime) / 1000).toPrecision(1));
this.container?.classList.remove('hidden');
}
private generateResultTitle(resultLen, time) {
return this.resultTitleTemplate.replace("#PAGES_COUNT", resultLen).replace("#TIME_SECONDS", time);
private generateResultTitle(resultLen: number, time: string) {
return this.resultTitleTemplate.replace("#PAGES_COUNT", resultLen.toString()).replace("#TIME_SECONDS", time);
}
public async getData() {
if (!this.data) {
/// Not fetched yet
const jsonURL = this.form.dataset.json;
const jsonURL = this.form.dataset.json as string;
this.data = await fetch(jsonURL).then(res => res.json());
const parser = new DOMParser();
@@ -227,7 +237,7 @@ class Search {
private bindSearchForm() {
let lastSearch = '';
const eventHandler = (e) => {
const eventHandler = (e: Event) => {
e.preventDefault();
const keywords = this.input.value.trim();
@@ -251,6 +261,7 @@ class Search {
private clear() {
this.list.innerHTML = '';
this.resultTitle.innerText = '';
this.container.classList.add('hidden');
}
private bindQueryStringChange() {
@@ -261,7 +272,7 @@ class Search {
private handleQueryString() {
const pageURL = new URL(window.location.toString());
const keywords = pageURL.searchParams.get('keyword');
const keywords = pageURL.searchParams.get('keyword') || '';
this.input.value = keywords;
if (keywords) {
@@ -291,19 +302,21 @@ class Search {
}
public static render(item: pageData) {
return <article>
<a href={item.permalink}>
<div class="article-details">
<h2 class="article-title" dangerouslySetInnerHTML={{ __html: item.title }}></h2>
<section class="article-preview" dangerouslySetInnerHTML={{ __html: item.preview }}></section>
</div>
{item.image &&
<div class="article-image">
<img src={item.image} loading="lazy" />
return (
<article>
<a href={item.permalink}>
<div class="article-details">
<h2 class="article-title" dangerouslySetInnerHTML={{ __html: item.title }}></h2>
<section class="article-preview" dangerouslySetInnerHTML={{ __html: item.preview }}></section>
</div>
}
</a>
</article>;
{item.image &&
<div class="article-image">
<img src={item.image} loading="lazy" />
</div>
}
</a>
</article>
);
}
}
@@ -316,10 +329,12 @@ declare global {
window.addEventListener('load', () => {
setTimeout(function () {
const searchForm = document.querySelector('.search-form') as HTMLFormElement,
searchInput = searchForm.querySelector('input') as HTMLInputElement,
searchInput = searchForm?.querySelector('input') as HTMLInputElement,
searchResultList = document.querySelector('.search-result--list') as HTMLDivElement,
searchResultTitle = document.querySelector('.search-result--title') as HTMLHeadingElement;
if (!searchForm || !searchInput || !searchResultList || !searchResultTitle) return;
new Search({
form: searchForm,
input: searchInput,
+1 -1
View File
@@ -1,3 +1,3 @@
[hugoVersion]
extended = true
min = "0.154.0"
min = "0.157.0"
+20 -4
View File
@@ -24,6 +24,10 @@ SortBy = "default"
toc = true
readingTime = true
[article.list]
# Showing article tags in the list view (e.g. homepage, section pages)
showTags = false
[article.license]
enabled = false
default = "Licensed under CC BY-NC-SA 4.0"
@@ -71,11 +75,23 @@ SortBy = "default"
toggle = true
default = "auto"
[imageProcessing.cover]
enabled = true
[imageProcessing]
[imageProcessing.content]
widths = [800, 1600, 2400]
enabled = true
[imageProcessing.content]
enabled = true
[imageProcessing.thumbnail]
enabled = true
# GDPR Cookie Consent Configuration
# When enabled, analytics and functional cookies require user consent
[cookies]
enabled = false
showSettings = true
[cookies.categories]
analytics = true
functional = true
[comments]
enabled = true
+9
View File
@@ -28,3 +28,12 @@ favicon = "img/avatar.png"
# See complete configuration in config/_default/params.toml
enabled = true
provider = "disqus"
# GDPR Cookie Consent Configuration
# When enabled, analytics and functional cookies require user consent
[cookies]
enabled = false
[cookies.categories]
analytics = true
functional = true
+24
View File
@@ -52,3 +52,27 @@ darkMode = "الوضع الداكن"
[footer]
builtWith = "مبني باستخدام {{ .Generator }}"
designedBy = "قالب {{ .Theme }} مصمم من {{ .DesignedBy }}"
[cookies]
title = "موافقة ملفات تعريف الارتباط"
text = "نستخدم ملفات تعريف الارتباط لتحسين تجربة التصفح وتحليل حركة المرور على الموقع."
acceptAll = "قبول الكل"
deny = "رفض"
managePreferences = "إدارة التفضيلات"
settingsTitle = "تفضيلات ملفات تعريف الارتباط"
savePreferences = "حفظ التفضيلات"
cancel = "إلغاء"
footerLink = "إعدادات ملفات تعريف الارتباط"
commentsDisabled = "التعليقات معطلة حتى تقبل ملفات تعريف الارتباط الوظيفية."
[cookies.necessary]
title = "ضروري"
text = "ملفات تعريف الارتباط هذه مطلوبة لعمل الموقع ولا يمكن تعطيلها."
[cookies.analytics]
title = "التحليلات"
text = "تساعدنا ملفات تعريف الارتباط هذه على فهم كيفية تفاعل الزوار مع موقعنا."
[cookies.functional]
title = "وظيفي"
text = "تتيح ملفات تعريف الارتباط هذه ميزات مثل التعليقات والمحتوى المضمن."
+24
View File
@@ -50,3 +50,27 @@ darkMode = "Цёмны рэжым"
[footer]
builtWith = "Створана пры дапамозе {{ .Generator }}"
designedBy = "Тэма {{ .Theme }}, дызайн {{ .DesignedBy }}"
[cookies]
title = "Згода на файлы cookie"
text = "Мы выкарыстоўваем файлы cookie для паляпшэння вашага досведу і аналізу трафіку сайта."
acceptAll = "Прыняць усё"
deny = "Адхіліць"
managePreferences = "Кіраваць налады"
settingsTitle = "Налады файлаў cookie"
savePreferences = "Захаваць налады"
cancel = "Скасаваць"
footerLink = "Налады файлаў cookie"
commentsDisabled = "Каментары адключаны, пакуль вы не прымеце функцыянальныя файлы cookie."
[cookies.necessary]
title = "Неабходныя"
text = "Гэтыя файлы cookie неабходныя для працы сайта і не могуць быць адключаны."
[cookies.analytics]
title = "Аналітыка"
text = "Гэтыя файлы cookie дапамагаюць нам зразумець, як наведвальнікі ўзаемадзейнічаюць з нашым сайтам."
[cookies.functional]
title = "Функцыянальныя"
text = "Гэтыя файлы cookie ўключаюць такія функцыі, як каментары і ўбудаваны кантэнт."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Тъмен Режим"
[footer]
builtWith = "Създадено с {{ .Generator }}"
designedBy = "Тема {{ .Theme }} създадена от {{ .DesignedBy }}"
[cookies]
title = "Съгласие за бисквитки"
text = "Използваме бисквитки, за да подобрим вашето изживяване и да анализираме трафика на сайта."
acceptAll = "Приемане на всички"
deny = "Откажи"
managePreferences = "Управление на предпочитанията"
settingsTitle = "Предпочитания за бисквитки"
savePreferences = "Запазване на предпочитанията"
cancel = "Отказ"
footerLink = "Настройки на бисквитките"
commentsDisabled = "Коментарите са деактивирани, докато не приемете функционални бисквитки."
[cookies.necessary]
title = "Необходими"
text = "Тези бисквитки са необходими за функционирането на уебсайта и не могат да бъдат деактивирани."
[cookies.analytics]
title = "Анализи"
text = "Тези бисквитки ни помагат да разберем как посетителите взаимодействат с нашия уебсайт."
[cookies.functional]
title = "Функционални"
text = "Тези бисквитки активират функции като коментари и вградено съдържание."
+29 -5
View File
@@ -15,12 +15,12 @@ darkMode = "ডার্ক মোড"
[article]
back = "পেছনে"
tableOfContents = "সূচিপত্র"
relatedContent = "সম্পর্কিত বিষবস্তু"
relatedContent = "সম্পর্কিত বিষয়বস্তু"
lastUpdatedOn = "সর্বশেষ আপডেট করা হয়েছে"
[article.readingTime]
one = "{{ .Count }} মিনিটে পা যাবে"
other = "{{ .Count }} মিনিটে পা যাবে"
one = "{{ .Count }} মিনিটে পড়া যাবে"
other = "{{ .Count }} মিনিটে পড়া যাবে"
[article.alert]
note = "টীকা"
@@ -30,7 +30,7 @@ darkMode = "ডার্ক মোড"
caution = "সাবধানতা"
[notFound]
title = "পাওা যানি"
title = "পাওয়া যায়নি"
subtitle = "এই পাতাটি বিদ্যমান নেই"
[widget]
@@ -50,5 +50,29 @@ darkMode = "ডার্ক মোড"
resultTitle = "#PAGES_COUNT পাতা (#TIME_SECONDS সেকেন্ড)"
[footer]
builtWith = "{{ .Generator }} দিে নির্মিত"
builtWith = "{{ .Generator }} দিয়ে নির্মিত"
designedBy = "থিম {{ .Theme }} ডিজাইন করেছেন {{ .DesignedBy }}"
[cookies]
title = "কুকি সম্মতি"
text = "আমরা আপনার ব্রাউজিং অভিজ্ঞতা উন্নত করতে এবং সাইট ট্রাফিক বিশ্লেষণ করতে কুকি ব্যবহার করি।"
acceptAll = "সবগুলো গ্রহণ করুন"
deny = "প্রত্যাখ্যান"
managePreferences = "পছন্দসমূহ পরিচালনা করুন"
settingsTitle = "কুকি পছন্দসমূহ"
savePreferences = "পছন্দসমূহ সংরক্ষণ করুন"
cancel = "বাতিল"
footerLink = "কুকি সেটিংস"
commentsDisabled = "আপনি কার্যকরী কুকি গ্রহণ না করা পর্যন্ত মন্তব্য অক্ষম করা আছে।"
[cookies.necessary]
title = "প্রয়োজনীয়"
text = "এই কুকিগুলি ওয়েবসাইট কাজ করার জন্য প্রয়োজনীয় এবং নিষ্ক্রিয় করা যায় না।"
[cookies.analytics]
title = "বিশ্লেষণ"
text = "এই কুকিগুলি আমাদের বুঝতে সাহায্য করে যে দর্শকরা কীভাবে আমাদের ওয়েবসাইটের সাথে ইন্টারঅ্যাক্ট করে।"
[cookies.functional]
title = "কার্যকরী"
text = "এই কুকিগুলি মন্তব্য এবং এম্বেড করা বিষয়বস্তুর মতো বৈশিষ্ট্যগুলি সক্ষম করে."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Mode fosc"
[footer]
builtWith = "Creat amb {{ .Generator }}"
designedBy = "Tema {{ .Theme }} dissenyat per {{ .DesignedBy }}"
[cookies]
title = "Consentiment de galetes"
text = "Utilitzem galetes per millorar la vostra experiència de navegació i analitzar el trànsit del lloc."
acceptAll = "Acceptar-ho tot"
deny = "Denegar"
managePreferences = "Gestionar preferències"
settingsTitle = "Preferències de galetes"
savePreferences = "Desar preferències"
cancel = "Cancel·lar"
footerLink = "Configuració de galetes"
commentsDisabled = "Els comentaris estan desactivats fins que accepteu les galetes funcionals."
[cookies.necessary]
title = "Necessàries"
text = "Aquestes galetes són necessàries perquè el lloc web funcioni i no es poden desactivar."
[cookies.analytics]
title = "Analítiques"
text = "Aquestes galetes ens ajuden a entendre com els visitants interactuen amb el nostre lloc web."
[cookies.functional]
title = "Funcionals"
text = "Aquestes galetes habiliten funcionalitats com comentaris i contingut incrustat."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Tmavý režim"
[footer]
builtWith = "Vytvořeno pomocí {{ .Generator }}"
designedBy = "Šablona {{ .Theme }} od {{ .DesignedBy }}"
[cookies]
title = "Souhlas s cookies"
text = "Používáme cookies ke zlepšení vašeho prohlížení a analýze návštěvnosti webu."
acceptAll = "Přijmout vše"
deny = "Odmítnout"
managePreferences = "Spravovat předvolby"
settingsTitle = "Předvolby cookies"
savePreferences = "Uložit předvolby"
cancel = "Zrušit"
footerLink = "Nastavení cookies"
commentsDisabled = "Komentáře jsou zakázány, dokud nepřijmete funkční cookies."
[cookies.necessary]
title = "Nutné"
text = "Tyto cookies jsou nutné pro fungování webu a nelze je zakázat."
[cookies.analytics]
title = "Analytické"
text = "Tyto cookies nám pomáhají pochopit, jak návštěvníci interagují s naším webem."
[cookies.functional]
title = "Funkční"
text = "Tyto cookies umožňují funkce jako komentáře a vložený obsah."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Dunkler Modus"
[footer]
builtWith = "Erstellt mit {{ .Generator }}"
designedBy = "Theme {{ .Theme }} gestaltet von {{ .DesignedBy }}"
[cookies]
title = "Cookie-Einwilligung"
text = "Wir verwenden Cookies, um Ihr Surferlebnis zu verbessern und den Website-Traffic zu analysieren."
acceptAll = "Alle akzeptieren"
deny = "Ablehnen"
managePreferences = "Einstellungen verwalten"
settingsTitle = "Cookie-Einstellungen"
savePreferences = "Einstellungen speichern"
cancel = "Abbrechen"
footerLink = "Cookie-Einstellungen"
commentsDisabled = "Kommentare sind deaktiviert, bis Sie funktionale Cookies akzeptieren."
[cookies.necessary]
title = "Notwendig"
text = "Diese Cookies sind für die Funktion der Website erforderlich und können nicht deaktiviert werden."
[cookies.analytics]
title = "Analyse"
text = "Diese Cookies helfen uns zu verstehen, wie Besucher mit unserer Website interagieren."
[cookies.functional]
title = "Funktional"
text = "Diese Cookies ermöglichen Funktionen wie Kommentare und eingebettete Inhalte."
+24
View File
@@ -49,3 +49,27 @@ darkMode = "Σκοτεινό θέμα"
[footer]
builtWith = "Δημιουργήθηκε με τη χρήση {{ .Generator }}"
designedBy = "Το θέμα {{ .Theme }} σχεδιάστηκε από το {{ .DesignedBy }}"
[cookies]
title = "Συγκατάθεση για cookies"
text = "Χρησιμοποιούμε cookies για να βελτιώσουμε την εμπειρία περιήγησής σας και να αναλύσουμε την επισκεψιμότητα του ιστότοπου."
acceptAll = "Αποδοχή όλων"
deny = "Άρνηση"
managePreferences = "Διαχείριση προτιμήσεων"
settingsTitle = "Προτιμήσεις cookies"
savePreferences = "Αποθήκευση προτιμήσεων"
cancel = "Ακύρωση"
footerLink = "Ρυθμίσεις cookies"
commentsDisabled = "Τα σχόλια είναι απενεργοποιημένα μέχρι να αποδεχτείτε τα λειτουργικά cookies."
[cookies.necessary]
title = "Απαραίτητα"
text = "Αυτά τα cookies είναι απαραίτητα για τη λειτουργία του ιστότοπου και δεν μπορούν να απενεργοποιηθούν."
[cookies.analytics]
title = "Αναλυτικά"
text = "Αυτά τα cookies μας βοηθούν να κατανοήσουμε πώς οι επισκέπτες αλληλεπιδρούν με τον ιστότοπό μας."
[cookies.functional]
title = "Λειτουργικά"
text = "Αυτά τα cookies ενεργοποιούν λειτουργίες όπως σχόλια και ενσωματωμένο περιεχόμενο."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Dark Mode"
[footer]
builtWith = "Built with {{ .Generator }}"
designedBy = "Theme {{ .Theme }} designed by {{ .DesignedBy }}"
[cookies]
title = "Cookie Consent"
text = "We use cookies to enhance your browsing experience and analyze site traffic."
acceptAll = "Accept All"
deny = "Deny"
managePreferences = "Manage preferences"
settingsTitle = "Cookie Preferences"
savePreferences = "Save preferences"
cancel = "Cancel"
footerLink = "Cookie settings"
commentsDisabled = "Comments are disabled until you accept functional cookies."
[cookies.necessary]
title = "Necessary"
text = "These cookies are required for the website to function and cannot be disabled."
[cookies.analytics]
title = "Analytics"
text = "These cookies help us understand how visitors interact with our website."
[cookies.functional]
title = "Functional"
text = "These cookies enable features like comments and embedded content."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Modo oscuro"
[footer]
builtWith = "Creado con {{ .Generator }}"
designedBy = "Tema {{ .Theme }} diseñado por {{ .DesignedBy }}"
[cookies]
title = "Consentimiento de cookies"
text = "Utilizamos cookies para mejorar su experiencia de navegación y analizar el tráfico del sitio."
acceptAll = "Aceptar todo"
deny = "Rechazar"
managePreferences = "Gestionar preferencias"
settingsTitle = "Preferencias de cookies"
savePreferences = "Guardar preferencias"
cancel = "Cancelar"
footerLink = "Configuración de cookies"
commentsDisabled = "Los comentarios están desactivados hasta que acepte las cookies funcionales."
[cookies.necessary]
title = "Necesarias"
text = "Estas cookies son necesarias para el funcionamiento del sitio web y no se pueden desactivar."
[cookies.analytics]
title = "Analíticas"
text = "Estas cookies nos ayudan a entender cómo los visitantes interactúan con nuestro sitio web."
[cookies.functional]
title = "Funcionales"
text = "Estas cookies permiten funciones como comentarios y contenido incrustado."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "حالت شب"
[footer]
builtWith = "قدرت گرفته از {{ .Generator }}"
designedBy = "قالب {{ .Theme }} ساخته شده توسط {{ .DesignedBy }}"
[cookies]
title = "رضایت کوکی"
text = "ما از کوکی‌ها برای بهبود تجربه مرورگری شما و تجزیه و تحلیل ترافیک سایت استفاده می‌کنیم."
acceptAll = "پذیرش همه"
deny = "رد کردن"
managePreferences = "مدیریت تنظیمات"
settingsTitle = "تنظیمات کوکی"
savePreferences = "ذخیره تنظیمات"
cancel = "لغو"
footerLink = "تنظیمات کوکی"
commentsDisabled = "نظرات تا زمانی که کوکی‌های کاربردی را بپذیرید غیرفعال هستند."
[cookies.necessary]
title = "ضروری"
text = "این کوکی‌ها برای عملکرد وب‌سایت الزامی هستند و نمی‌توان آنها را غیرفعال کرد."
[cookies.analytics]
title = "تجزیه و تحلیل"
text = "این کوکی‌ها به ما کمک می‌کنند تا بفهمیم بازدیدکنندگان چگونه با وب‌سایت ما تعامل دارند."
[cookies.functional]
title = "کاربردی"
text = "این کوکی‌ها ویژگی‌هایی مانند نظرات و محتوای جاسازی‌شده را فعال می‌کنند."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Mode sombre"
[footer]
builtWith = "Généré avec {{ .Generator }}"
designedBy = "Thème {{ .Theme }} conçu par {{ .DesignedBy }}"
[cookies]
title = "Consentement aux cookies"
text = "Nous utilisons des cookies pour améliorer votre expérience de navigation et analyser le trafic du site."
acceptAll = "Tout accepter"
deny = "Refuser"
managePreferences = "Gérer les préférences"
settingsTitle = "Préférences de cookies"
savePreferences = "Enregistrer les préférences"
cancel = "Annuler"
footerLink = "Paramètres des cookies"
commentsDisabled = "Les commentaires sont désactivés jusqu'à ce que vous acceptiez les cookies fonctionnels."
[cookies.necessary]
title = "Nécessaires"
text = "Ces cookies sont nécessaires au fonctionnement du site et ne peuvent pas être désactivés."
[cookies.analytics]
title = "Analytiques"
text = "Ces cookies nous aident à comprendre comment les visiteurs interagissent avec notre site."
[cookies.functional]
title = "Fonctionnels"
text = "Ces cookies permettent des fonctionnalités comme les commentaires et le contenu intégré."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "डार्क मोड"
[footer]
builtWith = "निर्मित {{ .Generator }} के साथ"
designedBy = "थीम {{ .Theme }} द्वारा डिज़ाइन किया गया {{ .DesignedBy }}"
[cookies]
title = "कुकी सहमति"
text = "हम आपके ब्राउज़िंग अनुभव को बेहतर बनाने और साइट ट्रैफ़िक का विश्लेषण करने के लिए कुकीज़ का उपयोग करते हैं।"
acceptAll = "सभी स्वीकार करें"
deny = "अस्वीकार करें"
managePreferences = "वरीयताएँ प्रबंधित करें"
settingsTitle = "कुकी वरीयताएँ"
savePreferences = "वरीयताएँ सहेजें"
cancel = "रद्द करें"
footerLink = "कुकी सेटिंग्स"
commentsDisabled = "जब तक आप कार्यात्मक कुकीज़ स्वीकार नहीं करते हैं, टिप्पणियाँ अक्षम हैं।"
[cookies.necessary]
title = "आवश्यक"
text = "ये कुकीज़ वेबसाइट के कार्य के लिए आवश्यक हैं और इन्हें अक्षम नहीं किया जा सकता।"
[cookies.analytics]
title = "विश्लेषण"
text = "ये कुकीज़ हमें यह समझने में मदद करती हैं कि विज़िटर हमारी वेबसाइट के साथ कैसे इंटरैक्ट करते हैं।"
[cookies.functional]
title = "कार्यात्मक"
text = "ये कुकीज़ टिप्पणियों और एम्बेडेड सामग्री जैसी सुविधाओं को सक्षम करती हैं."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Sötét Mód"
[footer]
builtWith = "{{ .Generator }} használatával készült"
designedBy = "A {{ .Theme }} dizájnt {{ .DesignedBy }} tervezte"
[cookies]
title = "Süti hozzájárulás"
text = "Sütiket használunk a böngészési élmény javítása és a webhely forgalmának elemzése érdekében."
acceptAll = "Összes elfogadása"
deny = "Elutasítás"
managePreferences = "Beállítások kezelése"
settingsTitle = "Süti beállítások"
savePreferences = "Beállítások mentése"
cancel = "Mégse"
footerLink = "Süti beállítások"
commentsDisabled = "A hozzászólások le vannak tiltva, amíg nem fogadja el a funkcionális sütiket."
[cookies.necessary]
title = "Szükséges"
text = "Ezek a sütik szükségesek a webhely működéséhez és nem lehet őket letiltani."
[cookies.analytics]
title = "Analitikai"
text = "Ezek a sütik segítenek megérteni, hogyan lépnek kapcsolatba a látogatók a weboldalunkkal."
[cookies.functional]
title = "Funkcionális"
text = "Ezek a sütik olyan funkciókat tesznek lehetővé, mint a hozzászólások és a beágyazott tartalom."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Mode Gelap"
[footer]
builtWith = "Dibangun dengan {{ .Generator }}"
designedBy = "Tema {{ .Theme }} dirancang oleh {{ .DesignedBy }}"
[cookies]
title = "Persetujuan Cookie"
text = "Kami menggunakan cookie untuk meningkatkan pengalaman browsing Anda dan menganalisis lalu lintas situs."
acceptAll = "Terima Semua"
deny = "Tolak"
managePreferences = "Kelola preferensi"
settingsTitle = "Preferensi Cookie"
savePreferences = "Simpan preferensi"
cancel = "Batal"
footerLink = "Pengaturan cookie"
commentsDisabled = "Komentar dinonaktifkan sampai Anda menerima cookie fungsional."
[cookies.necessary]
title = "Diperlukan"
text = "Cookie ini diperlukan agar situs web berfungsi dan tidak dapat dinonaktifkan."
[cookies.analytics]
title = "Analitik"
text = "Cookie ini membantu kami memahami bagaimana pengunjung berinteraksi dengan situs web kami."
[cookies.functional]
title = "Fungsional"
text = "Cookie ini mengaktifkan fitur seperti komentar dan konten yang disematkan."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Modalità scura"
[footer]
builtWith = "Realizzato con {{ .Generator }}"
designedBy = "Tema {{ .Theme }} realizzato da {{ .DesignedBy }}"
[cookies]
title = "Consenso ai cookie"
text = "Utilizziamo i cookie per migliorare la tua esperienza di navigazione e analizzare il traffico del sito."
acceptAll = "Accetta tutti"
deny = "Rifiuta"
managePreferences = "Gestisci preferenze"
settingsTitle = "Preferenze cookie"
savePreferences = "Salva preferenze"
cancel = "Annulla"
footerLink = "Impostazioni cookie"
commentsDisabled = "I commenti sono disabilitati finché non accetti i cookie funzionali."
[cookies.necessary]
title = "Necessari"
text = "Questi cookie sono necessari per il funzionamento del sito web e non possono essere disattivati."
[cookies.analytics]
title = "Analitici"
text = "Questi cookie ci aiutano a capire come i visitatori interagiscono con il nostro sito web."
[cookies.functional]
title = "Funzionali"
text = "Questi cookie abilitano funzionalità come commenti e contenuti incorporati."
+24
View File
@@ -43,3 +43,27 @@ darkMode = "ダークモード"
[footer]
builtWith = "{{ .Generator }} で構築されています。"
designedBy = "テーマ {{ .Theme }} は {{ .DesignedBy }} によって設計されています。"
[cookies]
title = "Cookieの同意"
text = "ブラウジング体験を向上させ、サイトトラフィックを分析するためにCookieを使用しています。"
acceptAll = "すべて受け入れる"
deny = "拒否"
managePreferences = "設定を管理"
settingsTitle = "Cookie設定"
savePreferences = "設定を保存"
cancel = "キャンセル"
footerLink = "Cookie設定"
commentsDisabled = "機能的なCookieを受け入れるまで、コメントは無効になっています。"
[cookies.necessary]
title = "必須"
text = "これらのCookieはウェブサイトの機能に必要であり、無効にすることはできません。"
[cookies.analytics]
title = "分析"
text = "これらのCookieは、訪問者が当サイトとどのように対話するかを理解するのに役立ちます。"
[cookies.functional]
title = "機能的"
text = "これらのCookieは、コメントや埋め込みコンテンツなどの機能を有効にします."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "다크 모드"
[footer]
builtWith = "{{ .Generator }}로 만듦"
designedBy = "{{ .DesignedBy }}의 {{ .Theme }} 테마 사용 중"
[cookies]
title = "쿠키 동의"
text = "브라우징 경험을 향상시키고 사이트 트래픽을 분석하기 위해 쿠키를 사용합니다."
acceptAll = "모두 허용"
deny = "거부"
managePreferences = "설정 관리"
settingsTitle = "쿠키 설정"
savePreferences = "설정 저장"
cancel = "취소"
footerLink = "쿠키 설정"
commentsDisabled = "기능적 쿠키를 수락할 때까지 댓글이 비활성화됩니다."
[cookies.necessary]
title = "필수"
text = "이 쿠키는 웹사이트 기능에 필요하며 비활성화할 수 없습니다."
[cookies.analytics]
title = "분석"
text = "이 쿠키는 방문자가 웹사이트와 상호작용하는 방식을 이해하는 데 도움이 됩니다."
[cookies.functional]
title = "기능적"
text = "이 쿠키는 댓글 및 임베디드 콘텐츠와 같은 기능을 활성화합니다."
+24
View File
@@ -47,3 +47,27 @@ darkMode = "Donkere modus"
[footer]
builtWith = "Gemaakt met {{ .Generator }}"
designedBy = "Theme {{ .Theme }} ontworpen door {{ .DesignedBy }}"
[cookies]
title = "Cookie-toestemming"
text = "We gebruiken cookies om uw browse-ervaring te verbeteren en siteverkeer te analyseren."
acceptAll = "Accepteer alles"
deny = "Weigeren"
managePreferences = "Voorkeuren beheren"
settingsTitle = "Cookie-voorkeuren"
savePreferences = "Voorkeuren opslaan"
cancel = "Annuleren"
footerLink = "Cookie-instellingen"
commentsDisabled = "Reacties zijn uitgeschakeld totdat u functionele cookies accepteert."
[cookies.necessary]
title = "Noodzakelijk"
text = "Deze cookies zijn vereist voor de werking van de website en kunnen niet worden uitgeschakeld."
[cookies.analytics]
title = "Analyse"
text = "Deze cookies helpen ons begrijpen hoe bezoekers omgaan met onze website."
[cookies.functional]
title = "Functioneel"
text = "Deze cookies maken functies mogelijk zoals reacties en ingesloten inhoud."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Mòde fosc"
[footer]
builtWith = "Creat amb {{ .Generator }}"
designedBy = "Tàma {{ .Theme }} concebut per {{ .DesignedBy }}"
[cookies]
title = "Consentiment de cookies"
text = "Utilizem cookies per melhorar vòstra experiéncia de navegacion e analisar lo trafic del site."
acceptAll = "Acceptar tot"
deny = "Refusar"
managePreferences = "Gerir las preferéncias"
settingsTitle = "Preferéncias de cookies"
savePreferences = "Enregistrar las preferéncias"
cancel = "Anullar"
footerLink = "Paramètres de cookies"
commentsDisabled = "Los comentaris son desactivats fins qu'acceptatz las cookies foncionalas."
[cookies.necessary]
title = "Necessaris"
text = "Aquestes cookies son necessàrias pel foncionament del site web e se pòdon pas desactivar."
[cookies.analytics]
title = "Analiticas"
text = "Aquestes cookies nos ajudan a comprendre cossí los visitaires interagisson amb nòstre site web."
[cookies.functional]
title = "Foncionalas"
text = "Aquestes cookies activan de foncionalitats coma los comentaris e lo contengut incorporat."
+24
View File
@@ -63,3 +63,27 @@ darkMode = "Tryb ciemny"
[footer]
builtWith = "Zbudowano z {{ .Generator }}"
designedBy = "Motyw {{ .Theme }} zaprojektowany przez {{ .DesignedBy }}"
[cookies]
title = "Zgoda na pliki cookie"
text = "Używamy plików cookie, aby poprawić Twoje wrażenia z przeglądania i analizować ruch w witrynie."
acceptAll = "Akceptuj wszystkie"
deny = "Odrzuć"
managePreferences = "Zarządzaj preferencjami"
settingsTitle = "Preferencje plików cookie"
savePreferences = "Zapisz preferencje"
cancel = "Anuluj"
footerLink = "Ustawienia plików cookie"
commentsDisabled = "Komentarze są wyłączone, dopóki nie zaakceptujesz funkcjonalnych plików cookie."
[cookies.necessary]
title = "Niezbędne"
text = "Te pliki cookie są wymagane do działania witryny i nie można ich wyłączyć."
[cookies.analytics]
title = "Analityczne"
text = "Te pliki cookie pomagają nam zrozumieć, jak odwiedzający wchodzą w interakcję z naszą witryną."
[cookies.functional]
title = "Funkcjonalne"
text = "Te pliki cookie włączają funkcje takie jak komentarze i osadzone treści."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Modo Escuro"
[footer]
builtWith = "Criado com {{ .Generator }}"
designedBy = "Tema {{ .Theme }} desenvolvido por {{ .DesignedBy }}"
[cookies]
title = "Consentimento de cookies"
text = "Usamos cookies para melhorar sua experiência de navegação e analisar o tráfego do site."
acceptAll = "Aceitar todos"
deny = "Recusar"
managePreferences = "Gerenciar preferências"
settingsTitle = "Preferências de cookies"
savePreferences = "Salvar preferências"
cancel = "Cancelar"
footerLink = "Configurações de cookies"
commentsDisabled = "Os comentários estão desativados até que você aceite os cookies funcionais."
[cookies.necessary]
title = "Necessários"
text = "Estes cookies são necessários para o funcionamento do site e não podem ser desativados."
[cookies.analytics]
title = "Analíticos"
text = "Estes cookies nos ajudam a entender como os visitantes interagem com nosso site."
[cookies.functional]
title = "Funcionais"
text = "Estes cookies habilitam recursos como comentários e conteúdo incorporado."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Modo Escuro"
[footer]
builtWith = "Criado com {{ .Generator }}"
designedBy = "Tema {{ .Theme }} desenvolvido por {{ .DesignedBy }}"
[cookies]
title = "Consentimento de cookies"
text = "Utilizamos cookies para melhorar a sua experiência de navegação e analisar o tráfego do site."
acceptAll = "Aceitar todos"
deny = "Recusar"
managePreferences = "Gerir preferências"
settingsTitle = "Preferências de cookies"
savePreferences = "Guardar preferências"
cancel = "Cancelar"
footerLink = "Definições de cookies"
commentsDisabled = "Os comentários estão desativados até que aceite os cookies funcionais."
[cookies.necessary]
title = "Necessários"
text = "Estes cookies são necessários para o funcionamento do website e não podem ser desativados."
[cookies.analytics]
title = "Analíticos"
text = "Estes cookies ajudam-nos a compreender como os visitantes interagem com o nosso website."
[cookies.functional]
title = "Funcionais"
text = "Estes cookies permitem funcionalidades como comentários e conteúdo incorporado."
+24
View File
@@ -53,3 +53,27 @@ darkMode = "Тёмный режим"
[footer]
builtWith = "Создано при помощи {{ .Generator }}"
designedBy = "Тема {{ .Theme }}, дизайн {{ .DesignedBy }}"
[cookies]
title = "Согласие на использование файлов cookie"
text = "Мы используем файлы cookie для улучшения вашего опыта просмотра и анализа трафика сайта."
acceptAll = "Принять все"
deny = "Отклонить"
managePreferences = "Управление настройками"
settingsTitle = "Настройки файлов cookie"
savePreferences = "Сохранить настройки"
cancel = "Отмена"
footerLink = "Настройки файлов cookie"
commentsDisabled = "Комментарии отключены, пока вы не примете функциональные файлы cookie."
[cookies.necessary]
title = "Необходимые"
text = "Эти файлы cookie необходимы для работы сайта и не могут быть отключены."
[cookies.analytics]
title = "Аналитика"
text = "Эти файлы cookie помогают нам понять, как посетители взаимодействуют с нашим сайтом."
[cookies.functional]
title = "Функциональные"
text = "Эти файлы cookie включают такие функции, как комментарии и встроенный контент."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Tmavý režim"
[footer]
builtWith = "Vytvorené pomocou {{ .Generator }}"
designedBy = "Šablóna {{ .Theme }} od {{ .DesignedBy }}"
[cookies]
title = "Súhlas s cookies"
text = "Používame cookies na zlepšenie vášho prehliadania a analýzu návštevnosti webu."
acceptAll = "Prijať všetko"
deny = "Odmietnuť"
managePreferences = "Spravovať predvoľby"
settingsTitle = "Predvoľby cookies"
savePreferences = "Uložiť predvoľby"
cancel = "Zrušiť"
footerLink = "Nastavenia cookies"
commentsDisabled = "Komentáre sú zakázané, kým neprijmete funkčné cookies."
[cookies.necessary]
title = "Nutné"
text = "Tieto cookies sú nutné pre fungovanie webu a nemožno ich zakázať."
[cookies.analytics]
title = "Analytické"
text = "Tieto cookies nám pomáhajú pochopiť, ako návštevníci interagujú s naším webom."
[cookies.functional]
title = "Funkčné"
text = "Tieto cookies umožňujú funkcie ako komentáre a vložený obsah."
+24
View File
@@ -49,3 +49,27 @@ darkMode = "ธีมมืด"
[footer]
builtWith = "ถูกสร้างด้วย {{ .Generator }}"
designedBy = "ธีม {{ .Theme }} ออกแบบโดย {{ .DesignedBy }}"
[cookies]
title = "ความยินยอมคุกกี้"
text = "เราใช้คุกกี้เพื่อปรับปรุงประสบการณ์การเรียกดูของคุณและวิเคราะห์การเข้าชมเว็บไซต์"
acceptAll = "ยอมรับทั้งหมด"
deny = "ปฏิเสธ"
managePreferences = "จัดการการตั้งค่า"
settingsTitle = "การตั้งค่าคุกกี้"
savePreferences = "บันทึกการตั้งค่า"
cancel = "ยกเลิก"
footerLink = "การตั้งค่าคุกกี้"
commentsDisabled = "ความคิดเห็นถูกปิดการใช้งานจนกว่าคุณจะยอมรับคุกกี้เชิงหน้าที่"
[cookies.necessary]
title = "จำเป็น"
text = "คุกกี้เหล่านี้จำเป็นสำหรับการทำงานของเว็บไซต์และไม่สามารถปิดการใช้งานได้"
[cookies.analytics]
title = "การวิเคราะห์"
text = "คุกกี้เหล่านี้ช่วยให้เราเข้าใจว่าผู้เยี่ยมชมโต้ตอบกับเว็บไซต์ของเราอย่างไร"
[cookies.functional]
title = "เชิงหน้าที่"
text = "คุกกี้เหล่านี้เปิดใช้งานคุณสมบัติต่างๆ เช่น ความคิดเห็นและเนื้อหาที่ฝังไว้"
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Koyu Mod"
[footer]
builtWith = "{{ .Generator }} ile oluşturuldu."
designedBy = "{{ .Theme }} teması {{ .DesignedBy }} tarafından tasarlandı"
[cookies]
title = "Çerez Onayı"
text = "Tarayıcı deneyiminizi iyileştirmek ve site trafiğini analiz etmek için çerezler kullanıyoruz."
acceptAll = "Tümünü Kabul Et"
deny = "Reddet"
managePreferences = "Tercihleri yönet"
settingsTitle = "Çerez Tercihleri"
savePreferences = "Tercihleri kaydet"
cancel = "İptal"
footerLink = "Çerez ayarları"
commentsDisabled = "İşlevsel çerezleri kabul edene kadar yorumlar devre dışıdır."
[cookies.necessary]
title = "Gerekli"
text = "Bu çerezler web sitesinin çalışması için gereklidir ve devre dışı bırakılamaz."
[cookies.analytics]
title = "Analitik"
text = "Bu çerezler, ziyaretçilerin web sitemizle nasıl etkileşimde bulunduğunu anlamamıza yardımcı olur."
[cookies.functional]
title = "İşlevsel"
text = "Bu çerezler, yorumlar ve gömülü içerik gibi özellikleri etkinleştirir."
+24
View File
@@ -53,3 +53,27 @@ darkMode = "Темна тема"
[footer]
builtWith = "Створено з {{ .Generator }}"
designedBy = "Тема {{ .Theme }}, дизайн {{ .DesignedBy }}"
[cookies]
title = "Згода на використання файлів cookie"
text = "Ми використовуємо файли cookie для покращення вашого досвіду перегляду та аналізу трафіку сайту."
acceptAll = "Прийняти все"
deny = "Відхилити"
managePreferences = "Керувати налаштуваннями"
settingsTitle = "Налаштування файлів cookie"
savePreferences = "Зберегти налаштування"
cancel = "Скасувати"
footerLink = "Налаштування файлів cookie"
commentsDisabled = "Коментарі вимкнено, доки ви не приймете функціональні файли cookie."
[cookies.necessary]
title = "Необхідні"
text = "Ці файли cookie необхідні для роботи сайту і не можуть бути вимкнені."
[cookies.analytics]
title = "Аналітика"
text = "Ці файли cookie допомагають нам зрозуміти, як відвідувачі взаємодіють з нашим сайтом."
[cookies.functional]
title = "Функціональні"
text = "Ці файли cookie вмикають такі функції, як коментарі та вбудований контент."
+24
View File
@@ -52,3 +52,27 @@ darkMode = "Chế độ nền tối"
[footer]
builtWith = "Built with {{ .Generator }}"
designedBy = "Theme {{ .Theme }} thiết kế bởi {{ .DesignedBy }}"
[cookies]
title = "Đồng ý cookie"
text = "Chúng tôi sử dụng cookie để nâng cao trải nghiệm duyệt web của bạn và phân tích lưu lượng truy cập trang web."
acceptAll = "Chấp nhận tất cả"
deny = "Từ chối"
managePreferences = "Quản lý tùy chọn"
settingsTitle = "Tùy chọn cookie"
savePreferences = "Lưu tùy chọn"
cancel = "Hủy"
footerLink = "Cài đặt cookie"
commentsDisabled = "Bình luận bị vô hiệu hóa cho đến khi bạn chấp nhận cookie chức năng."
[cookies.necessary]
title = "Cần thiết"
text = "Những cookie này là cần thiết để trang web hoạt động và không thể vô hiệu hóa."
[cookies.analytics]
title = "Phân tích"
text = "Những cookie này giúp chúng tôi hiểu cách khách truy cập tương tác với trang web của chúng tôi."
[cookies.functional]
title = "Chức năng"
text = "Những cookie này cho phép các tính năng như bình luận và nội dung nhúng."
+24
View File
@@ -43,3 +43,27 @@ darkMode = "深色模式"
[footer]
builtWith = "使用 {{ .Generator }} 建立"
designedBy = "主題 {{ .Theme }} 由 {{ .DesignedBy }} 設計"
[cookies]
title = "Cookie 同意"
text = "我們使用 Cookie 來增強您的瀏覽體驗並分析網站流量。"
acceptAll = "接受全部"
deny = "拒絕"
managePreferences = "管理偏好設定"
settingsTitle = "Cookie 偏好設定"
savePreferences = "儲存偏好設定"
cancel = "取消"
footerLink = "Cookie 設定"
commentsDisabled = "評論已停用,直到您接受功能性 Cookie。"
[cookies.necessary]
title = "必要"
text = "這些 Cookie 是網站運作所必需的,無法停用。"
[cookies.analytics]
title = "分析"
text = "這些 Cookie 幫助我們了解訪客如何與我們的網站互動。"
[cookies.functional]
title = "功能性"
text = "這些 Cookie 啟用評論和嵌入內容等功能."
+24
View File
@@ -43,3 +43,27 @@ darkMode = "夜晚模式"
[footer]
builtWith = "使用 {{ .Generator }} 建立"
designedBy = "主題 {{ .Theme }} 由 {{ .DesignedBy }} 設計"
[cookies]
title = "Cookie 同意"
text = "我們使用 Cookie 來增強您的瀏覽體驗並分析網站流量。"
acceptAll = "接受全部"
deny = "拒絕"
managePreferences = "管理偏好設定"
settingsTitle = "Cookie 偏好設定"
savePreferences = "儲存偏好設定"
cancel = "取消"
footerLink = "Cookie 設定"
commentsDisabled = "評論已停用,直到您接受功能性 Cookie。"
[cookies.necessary]
title = "必要"
text = "這些 Cookie 是網站運作所必需的,無法停用。"
[cookies.analytics]
title = "分析"
text = "這些 Cookie 幫助我們了解訪客如何與我們的網站互動。"
[cookies.functional]
title = "功能性"
text = "這些 Cookie 啟用評論和嵌入內容等功能."
+24
View File
@@ -43,3 +43,27 @@ darkMode = "暗色模式"
[footer]
builtWith = "使用 {{ .Generator }} 构建"
designedBy = "主题 {{ .Theme }} 由 {{ .DesignedBy }} 设计"
[cookies]
title = "Cookie 同意"
text = "我们使用 Cookie 来增强您的浏览体验并分析网站流量。"
acceptAll = "接受所有"
deny = "拒绝"
managePreferences = "管理偏好设置"
settingsTitle = "Cookie 偏好设置"
savePreferences = "保存偏好设置"
cancel = "取消"
footerLink = "Cookie 设置"
commentsDisabled = "评论已禁用,直到您接受功能性 Cookie。"
[cookies.necessary]
title = "必要"
text = "这些 Cookie 是网站运行所必需的,无法禁用。"
[cookies.analytics]
title = "分析"
text = "这些 Cookie 帮助我们了解访问者如何与我们的网站互动。"
[cookies.functional]
title = "功能性"
text = "这些 Cookie 启用评论和嵌入内容等功能."
+24 -18
View File
@@ -2,23 +2,29 @@
{{- $alt := .PlainText | safeHTML -}}
{{- $title := .Title | markdownify -}}
{{/* SVG and external images won't work with gallery layout, because their width and height attributes are unknown */}}
{{/* There are some types of images that don't have width and height attributes, such as SVG */}}
{{- $galleryImage := and $image.Height $image.Width -}}
<img src="{{ $image.Permalink }}"
{{ with $image.Width }}width="{{ . }}"{{ end }}
{{ with $image.Height }}height="{{ . }}"{{ end }}
loading="lazy"
{{ with $alt }}
alt="{{ . }}"
{{ end }}
{{ with $title }}
title="{{ . }}"
data-title-escaped="{{ . | htmlEscape }}"
{{ end }}
{{ if $galleryImage }}
class="gallery-image"
data-flex-grow="{{ div (mul $image.Width 100) $image.Height }}"
data-flex-basis="{{ div (mul $image.Width 240) $image.Height }}px"
{{ end }}
>
{{- $attributes := dict -}}
{{- if $galleryImage -}}
{{- $attributes = (dict
"data-flex-grow" (div (mul $image.Width 100) $image.Height)
"data-flex-basis" (printf "%dpx" (div (mul $image.Width 240) $image.Height))
) -}}
{{- end -}}
{{ partial "helper/responsive-image" (dict
"Resource" $image.Resource
"Widths" (cond .Page.Site.Params.ImageProcessing.Content.Enabled .Page.Site.Params.ImageProcessing.Content.Widths nil)
"Attributes" (merge $attributes (dict
"src" $image.Permalink
"width" $image.Width
"height" $image.Height
"alt" $alt
"title" $title
"data-title-escaped" ($title | htmlEscape)
"class" (cond $galleryImage "gallery-image" "")
"loading" "lazy"
"sizes" "(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px"
))
) }}
+30 -7
View File
@@ -4,18 +4,41 @@
<h2 class="article-title">
{{- .Title -}}
</h2>
<footer class="article-time">
<time datetime='{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}'>
{{- .Date | time.Format (or .Site.Params.dateFormat.published "Jan 02, 2006") -}}
</time>
</footer>
{{- $description := .Params.description -}}
{{- $IsTerm := in .Kind "term" -}}
{{- if $IsTerm -}}
{{- $description = $description | default (T "list.page" (len .Pages)) -}}
{{- end -}}
{{- if or $description (not .Date.IsZero) -}}
<footer class="article-footer">
{{- if not .Date.IsZero -}}
<time datetime="{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}">
{{- .Date | time.Format .Site.Params.dateFormat.published -}}
</time>
{{- else if $description -}}
<span class="article-description">{{- $description -}}</span>
{{ end }}
</footer>
{{- end -}}
</div>
{{- $image := partial "helper/image" (dict "Image" .Params.image "Resources" .Resources) -}}
{{ if $image }}
<div class="article-image">
<img src="{{ $image.Permalink }}" width="{{ $image.Width }}" height="{{ $image.Height }}" alt="{{ .Title }}"
loading="lazy">
{{ partial "helper/thumbnail-image" (dict
"Resource" $image.Resource
"Width" 60
"Height" 60
"Resize" .Site.Params.ImageProcessing.Thumbnail.Enabled
"Attributes" (dict
"src" $image.Permalink
"alt" .Title
"loading" "lazy"
"width" $image.Width
"height" $image.Height
)
) }}
</div>
{{ end }}
</a>
+1 -1
View File
@@ -1,4 +1,4 @@
{{ $image := partial "helper/image" (dict "Image" .Params.image "Resources" .Resources) }}
<article class="{{ if $image }}has-image{{ end }}">
{{ partial "article/components/header" . }}
{{ partial "article/components/header" (dict "Page" . "IsList" true) }}
</article>
+14 -3
View File
@@ -3,13 +3,24 @@
<a href="{{ .context.RelPermalink }}">
{{ if $image }}
<div class="article-image">
<img src="{{ $image.Permalink }}" width="{{ $image.Width }}" height="{{ $image.Height }}" loading="lazy"
alt="Featured image of post {{ .context.Title }}">
{{ partial "helper/thumbnail-image" (dict
"Resource" $image.Resource
"Width" 250
"Height" 150
"Resize" .context.Site.Params.ImageProcessing.Thumbnail.Enabled
"Attributes" (dict
"src" $image.Permalink
"alt" (printf "Featured image of post %s" .context.Title)
"loading" "lazy"
"width" $image.Width
"height" $image.Height
)
) }}
</div>
{{ end }}
<div class="article-details"
{{- if and $image $image.Resource -}}
{{- if and $image (reflect.IsImageResourceProcessable $image.Resource) -}}
{{- $colors := $image.Resource.Colors -}}
{{- if $colors -}}
{{- $vibrant := index $colors 0 -}}
+1 -1
View File
@@ -1,5 +1,5 @@
<article class="{{ if .Params.image }}has-image {{ end }}main-article">
{{ partial "article/components/header" . }}
{{ partial "article/components/header" (dict "Page" . "IsList" false) }}
{{ if .Scratch.Get "TOCEnabled" }}
<aside class="article-toc">
@@ -1,7 +1,9 @@
{{- $IsList := .IsList -}}
{{- $Page := .Page -}}
<div class="article-details">
{{ if .Params.categories }}
{{ if $Page.Params.categories }}
<header class="article-category">
{{ range (.GetTerms "categories") }}
{{ range ($Page.GetTerms "categories") }}
{{ $color := partial "helper/color-from-str" .LinkTitle }}
{{ $BackgroundColor := default $color.BackgroundColor .Params.style.background }}
{{ $TextColor := default $color.TextColor .Params.style.color }}
@@ -14,28 +16,28 @@
<div class="article-title-wrapper">
<h2 class="article-title">
<a href="{{ .RelPermalink }}">
{{- .Title -}}
<a href="{{ $Page.RelPermalink }}">
{{- $Page.Title -}}
</a>
</h2>
{{ with .Params.description }}
{{ with $Page.Params.description }}
<h3 class="article-subtitle">
{{ . }}
</h3>
{{ end }}
</div>
{{ $showReadingTime := .Params.readingTime | default (.Site.Params.article.readingTime) }}
{{ $showDate := not .Date.IsZero }}
{{ $showReadingTime := $Page.Params.readingTime | default ($Page.Site.Params.article.readingTime) }}
{{ $showDate := not $Page.Date.IsZero }}
{{ $showFooter := or $showDate $showReadingTime }}
{{ if $showFooter }}
<footer class="article-time">
<footer class="article-footer">
{{ if $showDate }}
<div>
{{ partial "helper/icon" "date" }}
<time class="article-time--published" datetime='{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}'>
{{- .Date | time.Format .Site.Params.dateFormat.published -}}
<time class="article-time--published" datetime='{{ $Page.Date.Format "2006-01-02T15:04:05Z07:00" }}'>
{{- $Page.Date | time.Format $Page.Site.Params.dateFormat.published -}}
</time>
</div>
{{ end }}
@@ -44,21 +46,32 @@
<div>
{{ partial "helper/icon" "clock" }}
<time class="article-time--reading">
{{ T "article.readingTime" .ReadingTime }}
{{ T "article.readingTime" $Page.ReadingTime }}
</time>
</div>
{{ end }}
</footer>
{{ end }}
{{ if .IsTranslated }}
{{ if $Page.IsTranslated }}
<footer class="article-translations">
{{ partial "helper/icon" "language" }}
<div>
{{ range .Translations }}
{{ range $Page.Translations }}
<a href="{{ .RelPermalink }}" class="link">{{ .Language.LanguageName }}</a>
{{ end }}
</div>
</footer>
{{ end }}
{{ if and $Page.Site.Params.Article.List.ShowTags ($Page.GetTerms "tags") $IsList }}
<footer class="article-header-tags">
{{ partial "helper/icon" "tag" }}
<div>
{{ range ($Page.GetTerms "tags") }}
<a href="{{ .RelPermalink }}" class="link">{{ .LinkTitle }}</a>
{{ end }}
</div>
</footer>
{{ end }}
</div>
@@ -1,13 +1,25 @@
{{- $IsList := .IsList -}}
{{- $Page := .Page -}}
<header class="article-header">
{{- $image := partial "helper/image" (dict "Image" .Params.image "Resources" .Resources) -}}
{{- $image := partial "helper/image" (dict "Image" $Page.Params.image "Resources" $Page.Resources) -}}
{{ if $image }}
<div class="article-image">
<a href="{{ .RelPermalink }}">
<img src="{{ $image.Permalink }}" width="{{ $image.Width }}" height="{{ $image.Height }}" loading="lazy"
alt="Featured image of post {{ .Title }}" />
<a href="{{ $Page.RelPermalink }}">
{{ partial "helper/responsive-image" (dict
"Resource" $image.Resource
"Widths" (cond $Page.Site.Params.ImageProcessing.Content.Enabled $Page.Site.Params.ImageProcessing.Content.Widths nil)
"Attributes" (dict
"src" $image.Permalink
"width" $image.Width
"height" $image.Height
"alt" (printf "Featured image of post %s" $Page.Title)
"loading" "lazy"
"sizes" "(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px"
)
) }}
</a>
</div>
{{ end }}
{{ partialCached "article/components/details" . .RelPermalink }}
{{ partial "article/components/details" (dict "Page" $Page "IsList" $IsList) }}
</header>
@@ -1,12 +1,12 @@
<div class="article-list--compact links">
{{ range $i, $link := .Params.links }}
{{ range $link := .Params.links }}
<article>
<a href="{{ $link.website }}" target="_blank" rel="noopener">
<div class="article-details">
<h2 class="article-title">
{{- $link.title -}}
</h2>
<footer class="article-time">
<footer class="article-footer">
{{ with $link.description }}
{{ . }}
{{ else }}
@@ -14,14 +14,23 @@
{{ end }}
</footer>
</div>
{{ with $link.image }}
{{ $permalink := . }}
{{ with ($.Resources.GetMatch (printf "%s" (. | safeURL))) }}
{{ $permalink = .RelPermalink }}
{{ end }}
{{- $image := partial "helper/image" (dict "Image" $link.image "Resources" $.Resources) -}}
{{- with $image -}}
<div class="article-image">
<img src="{{ $permalink }}" loading="lazy"{{ with $link.alt }} alt="{{ . }}"{{ end }}>
{{ partial "helper/thumbnail-image" (dict
"Resource" .Resource
"Width" 60
"Height" 60
"Resize" $.Site.Params.ImageProcessing.Thumbnail.Enabled
"Attributes" (dict
"src" $image.Permalink
"alt" $link.title
"loading" "lazy"
"width" .Width
"height" .Height
)
) }}
</div>
{{ end }}
</a>
@@ -1,9 +1,9 @@
{{ $related := (.Site.RegularPages.Related .) | first 5 }}
{{ with $related }}
<aside class="related-content--wrapper">
<aside>
<h2 class="section-title">{{ T "article.relatedContent" }}</h2>
<div class="related-content">
<div class="flex article-list--tile">
<div class="subsection-list">
<div class="article-list--tile">
{{ range . }}
{{ partial "article-list/tile" (dict "context" . "size" "250x150" "Type" "articleList") }}
{{ end }}
+45 -1
View File
@@ -1,3 +1,47 @@
{{ if .Site.Params.comments.enabled }}
{{ partial (printf "comments/provider/%s" .Site.Params.comments.provider) . }}
{{- $needsConsent := and .Site.Params.cookies.enabled .Site.Params.cookies.categories.functional -}}
{{- if $needsConsent -}}
{{/* Consent-gated comments - show placeholder until functional consent */}}
<div id="comments-consent-placeholder" class="consent-placeholder">
<p>{{ T "cookies.commentsDisabled" }}</p>
<button class="cookie-btn cookie-btn--primary" data-cookie-action="reopen">
{{ T "cookies.managePreferences" }}
</button>
</div>
<div id="comments-container" style="display: none;">
{{ partial (printf "comments/provider/%s" .Site.Params.comments.provider) . }}
</div>
<script>
(function() {
var placeholder = document.getElementById('comments-consent-placeholder');
var container = document.getElementById('comments-container');
function showComments() {
if (placeholder) placeholder.style.display = 'none';
if (container) container.style.display = 'block';
}
function hideComments() {
if (placeholder) placeholder.style.display = 'block';
if (container) container.style.display = 'none';
}
window.addEventListener('onCookieConsentChange', function(e) {
if (e.detail && e.detail.functional) {
showComments();
} else {
hideComments();
}
});
// Check if already consented
if (window.cookieConsent && window.cookieConsent.hasConsent('functional')) {
showComments();
}
})();
</script>
{{- else -}}
{{/* No consent required - load comments normally */}}
{{ partial (printf "comments/provider/%s" .Site.Params.comments.provider) . }}
{{- end -}}
{{ end }}
+50
View File
@@ -0,0 +1,50 @@
{{- if not site.Config.Privacy.GoogleAnalytics.Disable -}}
{{- with site.Config.Services.GoogleAnalytics.ID -}}
{{- if strings.HasPrefix (lower .) "ua-" -}}
{{- warnf "Google Analytics 4 (GA4) replaced Google Universal Analytics (UA) effective 1 July 2023. See https://support.google.com/analytics/answer/11583528. Create a GA4 property and data stream, then replace the Google Analytics ID in your site configuration with the new value." -}}
{{- else -}}
{{/* Consent-gated Google Analytics - only loads after analytics consent */}}
<script>
(function() {
var gaId = {{ . }};
var loaded = false;
var respectDNT = {{ site.Config.Privacy.GoogleAnalytics.RespectDoNotTrack }};
function loadGA() {
if (loaded) return;
// Respect Do Not Track browser setting if configured
if (respectDNT) {
var dnt = (navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack);
if (dnt == "1" || dnt == "yes") {
return;
}
}
loaded = true;
var script = document.createElement('script');
script.async = true;
script.src = 'https://www.googletagmanager.com/gtag/js?id=' + gaId;
document.head.appendChild(script);
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', gaId);
}
window.addEventListener('onCookieConsentChange', function(e) {
if (e.detail && e.detail.analytics) {
loadGA();
}
});
if (window.cookieConsent && window.cookieConsent.hasConsent('analytics')) {
loadGA();
}
})();
</script>
{{- end -}}
{{- end -}}
{{- end -}}
+68
View File
@@ -0,0 +1,68 @@
{{- $cfg := .Site.Params.cookies -}}
{{- $categories := $cfg.categories -}}
<div id="cookie-consent-banner" class="cookie-banner" aria-hidden="true" role="dialog" aria-labelledby="cookie-banner-title">
<div class="cookie-banner__content">
<div class="cookie-banner__text">
<strong id="cookie-banner-title">{{ T "cookies.title" }}</strong>
<p>{{ T "cookies.text" }}</p>
</div>
<div class="cookie-banner__actions">
<button class="cookie-btn cookie-btn--secondary" data-cookie-action="deny">
{{ T "cookies.deny" }}
</button>
<button class="cookie-btn cookie-btn--primary" data-cookie-action="accept">
{{ T "cookies.acceptAll" }}
</button>
</div>
{{- if $cfg.showSettings }}
<button class="cookie-banner__settings-link" data-cookie-action="settings">
{{ T "cookies.managePreferences" }}
</button>
{{- end }}
{{/* Settings panel */}}
<div id="cookie-settings-panel" class="cookie-settings" aria-hidden="true">
<h3>{{ T "cookies.settingsTitle" }}</h3>
<div class="cookie-category">
<label>
<input type="checkbox" name="necessary" checked disabled />
<strong>{{ T "cookies.necessary.title" }}</strong>
</label>
<p>{{ T "cookies.necessary.text" }}</p>
</div>
{{- if $categories.analytics -}}
<div class="cookie-category">
<label>
<input type="checkbox" name="analytics" data-cookie-category="analytics" />
<strong>{{ T "cookies.analytics.title" }}</strong>
</label>
<p>{{ T "cookies.analytics.text" }}</p>
</div>
{{- end -}}
{{- if $categories.functional -}}
<div class="cookie-category">
<label>
<input type="checkbox" name="functional" data-cookie-category="functional" />
<strong>{{ T "cookies.functional.title" }}</strong>
</label>
<p>{{ T "cookies.functional.text" }}</p>
</div>
{{- end -}}
<div class="cookie-settings__actions">
<button class="cookie-btn cookie-btn--secondary" data-cookie-action="cancel">
{{ T "cookies.cancel" }}
</button>
<button class="cookie-btn cookie-btn--primary" data-cookie-action="save">
{{ T "cookies.savePreferences" }}
</button>
</div>
</div>
</div>
</div>
+9
View File
@@ -0,0 +1,9 @@
{{- if .Site.Params.cookies.enabled -}}
{{/* Render the banner HTML */}}
{{ partial "cookies/banner.html" . }}
{{/* Load the consent manager script */}}
{{- $opts := dict "minify" hugo.IsProduction -}}
{{- $script := resources.Get "ts/cookies.ts" | js.Build $opts | fingerprint -}}
<script type="text/javascript" src="{{ $script.RelPermalink }}" defer></script>
{{- end -}}
+2 -1
View File
@@ -1,2 +1,3 @@
{{ partial "cookies/include.html" . }}
{{ partialCached "footer/components/script.html" . }}
{{ partial "footer/custom.html" . }}
{{ partial "footer/custom.html" . }}
+6 -1
View File
@@ -28,5 +28,10 @@
<link rel="shortcut icon" href="{{ .Permalink }}" />
{{ end }}
{{- partial "google_analytics.html" . -}}
{{- if .Site.Params.cookies.enabled -}}
{{- partial "cookies/analytics.html" . -}}
{{- else -}}
{{- template "_internal/google_analytics.html" . -}}
{{- end -}}
{{- partial "head/custom.html" . -}}
+17 -16
View File
@@ -3,8 +3,7 @@
/// Image: Image URL
/// Returns:
/// Local: true if the image is local, false if it is remote
/// Resource: Hugo image resource object, not nil if the image is local
/// Resource: Hugo image resource object
/// Permalink: Image URL
/// Height: Image height
/// Width: Image width
@@ -13,10 +12,15 @@
{{ if .Image }}
{{ $url := urls.Parse .Image }}
{{ $permalink := .Image }}
{{ $resource := "" }}
{{ if not (in (slice "https" "http") $url.Scheme) }}
{{/* Local image */}}
{{ $resource = .Resources.Get (printf "%s" (.Image | safeURL)) }}
{{ $permalink = $resource.RelPermalink }}
{{ else }}
{{/* Remote image */}}
{{ with try (resources.GetRemote $url) }}
{{ with .Err }}
{{ errorf "%s" . }}
@@ -28,25 +32,22 @@
{{ end }}
{{ end }}
{{ if $resource }}
{{- $isSVG := eq $resource.MediaType.SubType "svg" -}}
{{- if $isSVG -}}
{{ with $resource }}
{{ if reflect.IsImageResourceWithMeta . }}
{{ $result = dict
"Local" true
"Resource" $resource
"Permalink" $resource.RelPermalink
"Height" nil
"Width" nil
}}
{{- else -}}
{{- $result = dict
"Local" true
"Resource" $resource
"Permalink" $resource.RelPermalink
"Permalink" $permalink
"Height" $resource.Height
"Width" $resource.Width
}}
{{- end -}}
{{ else }}
{{ $result = dict
"Resource" $resource
"Permalink" $permalink
"Height" nil
"Width" nil
}}
{{ end }}
{{ end }}
{{ end }}
@@ -0,0 +1,33 @@
{{- /*
Params:
Resource: Hugo image resource object (Optional, for processing)
Widths: Slice of widths to generate for srcset
Attributes: Map of HTML attributes for the <img> tag
*/ -}}
{{- $resource := .Resource -}}
{{- $widths := .Widths -}}
{{- $attributes := .Attributes | default dict -}}
{{/* Generate srcset if Resource and Widths are provided */}}
{{- if and $widths $resource (reflect.IsImageResourceProcessable $resource) -}}
{{- $srcset := slice -}}
{{- range $widths -}}
{{- if lt . $resource.Width -}}
{{- $resized := $resource.Resize (printf "%dx" .) -}}
{{- $srcset = $srcset | append (printf "%s %dw" $resized.RelPermalink .) -}}
{{- end -}}
{{- end -}}
{{- if gt (len $srcset) 0 -}}
{{- $permalink := default $resource.RelPermalink (index $attributes "src") -}}
{{- $srcset = $srcset | append (printf "%s %dw" $permalink $resource.Width) -}}
{{- $attributes = merge $attributes (dict "srcset" (delimit $srcset ", ")) -}}
{{- end -}}
{{- end -}}
<img {{- range $k, $v := $attributes -}}
{{- if $v -}}
{{- printf " %s=%q" (lower $k) (printf "%v" $v) | safeHTMLAttr -}}
{{- end -}}
{{- end -}}>
@@ -0,0 +1,41 @@
{{- /*
Params:
Resource: Hugo image resource object (Optional, for processing)
Width: Image width (Required)
Height: Image height (Required)
Resize: Whether to perform resize (Optional, default: false)
Attributes: Map of HTML attributes for the <img> tag
*/ -}}
{{- $resource := .Resource -}}
{{- $width := .Width -}}
{{- $height := .Height -}}
{{- $resize := .Resize -}}
{{- $attributes := .Attributes | default dict -}}
{{- if and $resize $resource (reflect.IsImageResourceProcessable $resource) $width $height -}}
{{/* Create thumbnail with 1x/2x descriptors */}}
{{- $srcset := slice -}}
{{- range (slice 1 2) -}}
{{- $w := mul $width . -}}
{{- $h := mul $height . -}}
{{- if and (le $w $resource.Width) (le $h $resource.Height) -}}
{{- $resized := $resource.Fill (printf "%dx%d" $w $h) -}}
{{- $srcset = $srcset | append (printf "%s %dx" $resized.RelPermalink .) -}}
{{- if eq . 1 -}}
{{- $attributes = merge $attributes (dict "src" $resized.RelPermalink) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if gt (len $srcset) 0 -}}
{{- $attributes = merge $attributes (dict "width" $width "height" $height "srcset" (delimit $srcset ", ")) -}}
{{- end -}}
{{- end -}}
<img {{- range $k, $v := $attributes -}}
{{- if $v -}}
{{- printf " %s=%q" (lower $k) (printf "%v" $v) | safeHTMLAttr -}}
{{- end -}}
{{- end -}}>
+4 -8
View File
@@ -35,7 +35,7 @@
<li>
<a
href='{{ .URL }}'
{{ if eq (default true .Params.newTab) true }}target="_blank"{{ end }}
{{ if .Params.newTab }}target="_blank"{{ end }}
{{ with .Name }}title="{{ . }}"{{ end }}
rel="me"
>
@@ -53,13 +53,9 @@
{{ $currentPage := . }}
{{ range .Site.Menus.main }}
{{ $active := or (eq $currentPage.RelPermalink .URL) (or ($currentPage.HasMenuCurrent "main" .) ($currentPage.IsMenuCurrent "main" .)) }}
<li {{ if $active }} class='current' {{ end }}>
<a href='{{ .URL }}' {{ if eq .Params.newTab true }}target="_blank"{{ end }}>
{{ $icon := default .Pre .Params.Icon }}
{{ if .Pre }}
{{ warnf "Menu item [%s] is using [pre] field to set icon, please use [params.icon] instead.\nMore information: https://stack.jimmycai.com/config/menu" .URL }}
{{ end }}
{{ with $icon }}
<li {{ if $active }} class='current'{{ end }}>
<a href='{{ .URL }}' {{ if .Params.newTab }}target="_blank"{{ end }}>
{{ with .Params.Icon }}
{{ partial "helper/icon" . }}
{{ end }}
<span>{{- .Name -}}</span>
+5 -1
View File
@@ -7,7 +7,11 @@
<div class="widget-icon">
{{ partial "helper/icon" "infinity" }}
</div>
<h2 class="widget-title section-title">{{ T "widget.archives.title" }}</h2>
<h2 class="widget-title section-title">
<a href="{{ $archivesPage.RelPermalink }}">
{{ T "widget.archives.title" }}
</a>
</h2>
{{ $pages := partial "helper/pages.html" $context.Site.Home }}
{{ $archives := $pages.GroupByDate "2006" }}
+1 -1
View File
@@ -13,7 +13,7 @@
{{- $widgetTitle := .Params.title -}}
{{- $limit := default 10 .Params.limit -}}
{{- $icon := default .Params.taxonomy .Params.icon -}}
{{- $showLink := default false .Params.showLink -}}
{{- $showLink := default true .Params.showLink -}}
<section class="widget tagCloud">
<div class="widget-icon">
+13 -4
View File
@@ -20,10 +20,19 @@
{{- $image := partial "helper/image" (dict "Image" .Params.image "Resources" .Resources) -}}
{{ if $image }}
<div class="section-image">
<img src="{{ $image.Permalink }}"
width="{{ $image.Width }}"
height="{{ $image.Height }}"
loading="lazy">
{{ partial "helper/thumbnail-image" (dict
"Resource" $image.Resource
"Width" 60
"Height" 60
"Resize" .Site.Params.ImageProcessing.Thumbnail.Enabled
"Attributes" (dict
"src" $image.Permalink
"alt" .Title
"loading" "lazy"
"width" $image.Width
"height" $image.Height
)
) }}
</div>
{{ end }}
</div>
+1 -1
View File
@@ -16,7 +16,7 @@
</button>
</form>
<div class="search-result">
<div class="search-result hidden">
<h3 class="search-result--title section-title"></h3>
<div class="search-result--list article-list--compact"></div>
</div>
+1 -1
View File
@@ -20,7 +20,7 @@ features = [
"search",
]
min_version = "0.154.0"
min_version = "0.157.0"
[author]
name = "Jimmy Cai"