Repository navigation
feat(ux): reduced-motion quick toggle + favorites hub + image lazy-loading + nav links #327
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -92,6 +92,22 @@ | |
| }); | ||
| } | ||
|
|
||
| // Reduced-motion quick toggle (header ✦ button) | ||
| var mqBtn = document.getElementById('neo-motion-quick'); | ||
| var mqState = localStorage.getItem('neo-motion-state'); | ||
| function mqApply(on) { | ||
| document.documentElement.setAttribute('data-reduce-motion', on ? 'false' : 'true'); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: printf '%s\n' '--- repository conventions ---'
find /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971 -maxdepth 2 -type f -name '*.md' -print
printf '%s\n' '--- changed hunk ---'
git diff -- assets/js/custom.js
printf '%s\n' '--- custom.js context ---'
sed -n '70,120p' assets/js/custom.js
printf '%s\n' '--- reduced-motion CSS rules ---'
rg -n -C 8 'prefers-reduced-motion|data-reduce-motion|animation:\s*none|transition:\s*none|scroll-behavior:\s*auto' assets/css/neo.cssRepository: dominicusin/dominicusin.github.io Length of output: 6644 Document OS preference precedence for the motion toggle. When 🤖 Prompt for AI Agents |
||
| if (mqBtn) mqBtn.style.opacity = on ? '' : '.5'; | ||
| localStorage.setItem('neo-motion-state', on ? 'on' : 'off'); | ||
| } | ||
| if (mqBtn) { | ||
| if (mqState === 'off') mqApply(false); | ||
| mqBtn.addEventListener('click', function () { | ||
| var cur = document.documentElement.getAttribute('data-reduce-motion') === 'true'; | ||
| mqApply(!cur); | ||
| }); | ||
| } | ||
|
|
||
| // Quick light/dark toggle | ||
| var tog = document.getElementById('neo-theme-toggle'); | ||
| if (tog){ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| --- | ||
| title: "Избранное" | ||
| description: "Сохранённые карточки (лайки). Хранится локально в браузере." | ||
| layout: "favorites-hub" | ||
| --- | ||
|
|
||
| Здесь собраны репозитории, гисты и awesome-списки, которые вы отметили лайком (♥) на сайте. Вся информация хранится локально в вашем браузере и не отправляется на сервер. | ||
|
|
||
| - Нажмите ♥ на любой карточке, чтобы добавить её сюда. | ||
| - Нажмите ♥ ещё раз, чтобы убрать. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| {{/* Image render hook: lazy-load + CLS guard. | ||
| Local Hugo image resources get intrinsic width/height (no layout shift). | ||
| Remote images get loading="lazy" decoding="async". */}} | ||
| {{- $src := .Destination -}} | ||
| {{- $alt := .Text -}} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: printf '%s\n' '--- repository guidance ---'
find /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971 -maxdepth 2 -type f -name '*.md' -print
printf '%s\n' '--- render hook ---'
cat -n layouts/_default/_markup/render-image.html
printf '%s\n' '--- Hugo configuration and content trust settings ---'
rg -n -i 'goldmark|unsafe|markup|hugo|version|safeHTMLAttr|PlainText|render-image|allowDangerous' \
config* hugo* .github layouts content 2>/dev/null | head -240Repository: dominicusin/dominicusin.github.io Length of output: 36735 🏁 Script executed: printf '%s\n' '--- content convention ---'
cat /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971/conventions/content-blog.md
printf '%s\n' '--- configuration convention ---'
cat /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971/conventions/config-default.md
printf '%s\n' '--- relevant Hugo configuration ---'
cat -n config/_default/hugo.toml | sed -n '1,28p'
printf '%s\n' '--- image syntax with titles or markup ---'
rg -n '!\[[^]]*(<|["'\'']|[*_])[^]]*\]\([^)]*( "[^"]*"| '\''[^'\'']*'\''| \([^)]*\))?\)' content --glob '*.md' | head -120Repository: dominicusin/dominicusin.github.io Length of output: 8533 🌐 Web query:
💡 Result: In Hugo, the image render hook provides a context containing several properties to access image details. The properties.Text,.PlainText, and the availability of attributes in the render hook are defined as follows:.Text Returns the image description as type template.HTML. Because it is of this type, it is considered safe for direct output in templates, although caution should be exercised regarding the source of the Markdown..PlainText Returns the image description as a plain string, stripping any HTML or Markdown formatting. This is useful when you need to output the description within an HTML attribute (e.g., inside an alt tag) to avoid issues with nested HTML or escaping. Attributes Support for Markdown attributes (e.g., {.class}) on images is limited. In standard Markdown, images are inline elements, whereas Markdown attributes are primarily supported for block-level elements. If an image is rendered as a block element (which can occur if it is not wrapped in a paragraph element), the.Attributes property (a map) may be available in the render hook context if the site is configured to support them. safeHTMLAttr While not a property of the image render hook itself, safeHTMLAttr is a Hugo function used to declare a key-value pair as a safe HTML attribute. If you are constructing custom attributes within your render hook, you can use safeHTMLAttr (or the alias safe.HTMLAttr) to prevent Hugo from escaping the attribute value, which is particularly useful for attributes that may contain special characters or that you have verified as safe from code injection. For more information, consult the official Hugo documentation on render hooks [1][2]. Citations: 🏁 Script executed: printf '%s\n' '--- repository trust and publishing inputs ---'
rg -n -i 'untrusted|trusted|contributor|pull request|pull-request|content|markdown|raw html|unsafe|security' \
README.md CONTRIBUTING.md SECURITY.md docs .github scripts config content 2>/dev/null | head -220
printf '%s\n' '--- workflows that build or publish content ---'
cat -n .github/workflows/hugo.yml | sed -n '1,165p'
printf '%s\n' '--- image descriptions containing raw HTML or quote characters ---'
rg -n '!\[[^]]*(<[^>]+>|"|'|["'\''])[^]]*\]\(' content --glob '*.md' | head -100Repository: dominicusin/dominicusin.github.io Length of output: 28702 XSS (CWE-79): Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') Reachability: Internal · Exploitability: Difficult Escape image alt text as plain text. The site enables raw HTML in Markdown. 🤖 Prompt for AI Agents |
||
| {{- $isRemote := or (strings.HasPrefix $src "http://") (strings.HasPrefix $src "https://") -}} | ||
| {{- if and (not $isRemote) (not (strings.HasPrefix $src "/")) -}} | ||
| {{- $img := $.Page.Resources.Get $src -}} | ||
|
Comment on lines
+7
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: printf '%s\n' '--- repository conventions ---'
find /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971 -path '*/conventions/*.md' -type f -maxdepth 4 -print
for f in /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971/*/*.md; do
printf '\n--- %s ---\n' "$f"
head -80 "$f"
done
printf '%s\n' '--- render hook ---'
cat -n layouts/_default/_markup/render-image.html
printf '%s\n' '--- related Hugo configuration and hooks ---'
find . -maxdepth 3 -type f \( -iname '*config*' -o -name '*.toml' -o -name '*.yaml' -o -name '*.yml' \) -print
rg -n --glob '!public/**' --glob '!resources/**' 'render-image|PageInner|safeHTMLAttr|resources\.Get|Resources\.Get|urls\.Parse|markup\.goldmark|unsafe' .Repository: dominicusin/dominicusin.github.io Length of output: 14398 🏁 Script executed: printf '%s\n' '--- image/link hooks and Hugo context ---'
cat -n layouts/_default/_markup/render-link.html
printf '%s\n' '--- Hugo configuration ---'
cat -n config/_default/hugo.toml
printf '%s\n' '--- Hugo version declarations ---'
rg -n --glob '!node_modules/**' --glob '!public/**' --glob '!resources/**' 'hugo(version)?|HUGO_VERSION|hugo_extended|PageInner|render-image' .github config layouts README.md AGENTS.md package.json 2>/dev/null
printf '%s\n' '--- relevant architecture and learning notes ---'
for f in /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971/architecture/*.md /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971/learnings/*.md; do
[ -f "$f" ] || continue
printf '\n--- %s ---\n' "$f"
cat "$f"
doneRepository: dominicusin/dominicusin.github.io Length of output: 14345 🌐 Web query:
💡 Result: In Hugo v0.164, the embedded image render hook is designed to resolve Markdown image destinations by searching for matching page resources (via PageInner) and then falling back to global resources [1][2]. The implementation logic specifically handles URL components such as query strings and fragments by parsing the destination using urls.Parse and then reconstructing the source URL to preserve these elements [2]. Technical details regarding your query: 1. PageInner: The embedded render hook utilizes.PageInner.Resources.Get to resolve image paths [1][2]. PageInner provides the necessary context for nested pages—such as those rendered via RenderShortcodes—to ensure correct resolution of resources relative to the inclusion point [1][3]. 2. URL Parsing and Reconstruction: The embedded hook parses the destination string ($u:= urls.Parse.Destination). If the path is relative, it attempts to resolve the resource. If resolved, it constructs the new URL by appending the original.RawQuery and.Fragment back onto the resolved.RelPermalink [2]. This ensures that any query parameters or fragments present in the original Markdown image link are preserved in the final HTML output [2][4]. 3. Root-relative paths: Hugo's resource resolution (resources.Get) typically treats paths as relative to the content directory or page bundle [1][5]. If you require behavior specific to site-root-relative paths (e.g., starting with /), note that standard resource resolution functions generally expect paths relative to the current page or the global assets/static structure [1]. Configuration: You can control the use of this embedded hook via the configuration setting markup.goldmark.renderHooks.image.useEmbedded, which accepts values such as auto, always, fallback, or never [1][6]. Setting this to auto is the default, allowing Hugo to determine usage based on project settings (like multilingual configurations) [1][6]. If you define a custom render hook, it will take precedence unless the embedded hook is explicitly forced [1][7]. If you find that the default embedded hook does not meet your specific requirements (such as needing to search additional paths or add specific HTML attributes), you can create a custom render hook in your layouts/_default/_markup/ folder, often by basing your code on the embedded hook's source [2][5][8]. Citations:
🏁 Script executed: printf '%s\n' '--- all image render hooks and theme files ---'
find . -path './node_modules' -prune -o -type f -iname '*render*image*' -print
git ls-files | rg '(^|/)(render-image\.html|go\.mod|go\.sum|package-lock\.json|themes/)' | head -100
printf '%s\n' '--- build workflow version context ---'
sed -n '20,35p' .github/workflows/hugo.yml
sed -n '78,90p' .github/workflows/hugo.yml
printf '%s\n' '--- relevant content image destinations ---'
rg -n --glob 'content/**' '!\[[^]]*\]\([^)]*(\?|#|^\./|^/)' content | head -80Repository: dominicusin/dominicusin.github.io Length of output: 17887 🏁 Script executed: curl -fsSL https://raw.githubusercontent.com/gohugoio/hugo/v0.164.0/tpl/tplimpl/embedded/templates/_markup/render-image.html | nl -baRepository: dominicusin/dominicusin.github.io Length of output: 237 🏁 Script executed: curl -fsSL https://raw.githubusercontent.com/gohugoio/hugo/v0.164.0/tpl/tplimpl/embedded/templates/_markup/render-image.html | awk '{printf "%4d %s\n", NR, $0}'Repository: dominicusin/dominicusin.github.io Length of output: 990 Use Hugo’s complete internal-resource resolver. This branch skips lookup for root-relative destinations and passes 🤖 Prompt for AI Agents |
||
| {{- if $img -}} | ||
| <img src="{{ $img.RelPermalink }}" alt="{{ $alt | safeHTMLAttr }}" width="{{ $img.Width }}" height="{{ $img.Height }}" loading="lazy" decoding="async" /> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- applicable repository knowledge files ---'
find /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971 -maxdepth 2 -type f -name '*.md' -print | sort
printf '%s\n' '--- target file ---'
cat -n layouts/_default/_markup/render-image.html
printf '%s\n' '--- Hugo/content references ---'
rg -n --glob '*.md' --glob '*.html' --glob '*.yaml' --glob '*.toml' --glob '*.json' 'render-image|![^]]*\]\([^)]*"[^)]*"\)|safeHTMLAttr|\.Title|image' layouts content config* hugo* 2>/dev/null | head -200Repository: dominicusin/dominicusin.github.io Length of output: 31798 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- content convention ---'
cat /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971/conventions/content-blog.md
printf '%s\n' '--- Hugo configuration and version declarations ---'
find . -maxdepth 3 -type f \( -name 'hugo.toml' -o -name 'hugo.yaml' -o -name 'hugo.json' -o -name 'config.toml' -o -name 'config.yaml' -o -name 'config.yml' -o -name 'config.json' -o -name 'go.mod' -o -name '.github' \) -print
rg -n --glob '*.md' '!\[[^]]*\]\([^)]+"' content | head -40
printf '%s\n' '--- render hook and image-related conventions ---'
rg -n 'render hook|image|title|alt|Hugo' /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971/conventions /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971/architecture /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971/learnings 2>/dev/null | head -120Repository: dominicusin/dominicusin.github.io Length of output: 2032 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- Hugo configuration ---'
cat -n config/_default/hugo.toml
printf '%s\n' '--- image syntax with titles (all tracked content) ---'
rg -n --pcre2 --glob '*.md' '!\[[^\]]*\]\([^)\n]+ "[^"\n]*"\)' content || true
printf '%s\n' '--- image render-hook references in repository ---'
rg -n --glob '!layouts/_default/_markup/render-image.html' 'render-image|render hook|\.Destination|\.Text|\.Title' . /tmp/coderabbit-repo-knowledge/dominicusin-dominicusin-github-io-fa795971 2>/dev/null | head -160Repository: dominicusin/dominicusin.github.io Length of output: 13030 🌐 Web query:
💡 Result: In Hugo, the.Title property within an image render hook represents the title attribute provided in the Markdown image syntax (e.g., Citations:
Preserve Markdown image titles. When 🤖 Prompt for AI Agents |
||
| {{- else -}} | ||
| <img src="{{ $src | safeURL }}" alt="{{ $alt | safeHTMLAttr }}" loading="lazy" decoding="async" /> | ||
| {{- end -}} | ||
| {{- else -}} | ||
| <img src="{{ $src | safeURL }}" alt="{{ $alt | safeHTMLAttr }}" loading="lazy" decoding="async" /> | ||
| {{- end -}} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,7 +27,7 @@ <h2>{{ .name }}</h2> | |
| {{ range .repos }} | ||
| {{ $repo := . }} | ||
| {{ $p := $.Site.GetPage (printf "/awesome/%s/%s" $repo.group $repo.name) }} | ||
| <article class="awesome-card" data-name="{{ $repo.name }}" data-stars="{{ $repo.stars | default 0 }}" data-group="{{ $repo.group }}"> | ||
| <article class="awesome-card" data-name="{{ $repo.name }}" data-title="{{ $repo.name }}" data-type="awesome" data-stars="{{ $repo.stars | default 0 }}" data-group="{{ $repo.group }}"> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will 10BC8 be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Persist an item-specific URL for Awesome favorites. The shared handler in Expose a canonical preview or repository URL through the card metadata contract, and make the handler read it before falling back to 🤖 Prompt for AI Agents |
||
| <span class="card-halo"></span> | ||
| <span class="shimmer"></span> | ||
| <span class="card-gloss"></span> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| {{/* Favorites hub — Neo design system (standalone). Client-rendered from localStorage. | ||
| Title/type/href come from our own like-meta store; textContent is used to avoid HTML injection. */}} | ||
| <!DOCTYPE html> | ||
| <html lang="{{ site.Language.LanguageCode | default "ru" }}" data-theme="dark"> | ||
| {{ partial "neo-head.html" . }} | ||
| <body> | ||
| {{ partial "neo-header.html" . }} | ||
| <main class="neo" id="main"> | ||
| <div class="neo-wrap"> | ||
| <div class="neo-sec-head" style="margin-top:20px"><h2>{{ .Title }}</h2></div> | ||
| {{ .Content }} | ||
| <div id="neo-fav-results" class="repo-grid"></div> | ||
| <p id="neo-fav-empty" class="neo-search-empty">Пока пусто. Нажмите ♥ на любой карточке, чтобы добавить её в избранное.</p> | ||
| </div> | ||
| </main> | ||
| {{ partial "neo-footer.html" . }} | ||
| <script> | ||
| (function () { | ||
| "use strict"; | ||
| var STORE = "neo-likes-v1", META = "neo-fav-meta"; | ||
| var KIND = { post: "📝 Блог", repo: "📦 Репо", gist: "📄 Гист", awesome: "⭐ Awesome" }; | ||
| try { var likes = JSON.parse(localStorage.getItem(STORE) || "{}"); | ||
| var meta = JSON.parse(localStorage.getItem(META) || "{}"); } | ||
| catch (e) { var likes = {}, meta = {}; } | ||
| var res = document.getElementById("neo-fav-results"); | ||
| var empty = document.getElementById("neo-fav-empty"); | ||
| var keys = Object.keys(likes).filter(function (k) { return likes[k]; }); | ||
| if (!keys.length) { empty.style.display = "block"; return; } | ||
| empty.style.display = "none"; | ||
| keys.forEach(function (k) { | ||
| var m = meta[k] || {}; | ||
| var a = document.createElement("a"); | ||
| a.className = "repo-card"; | ||
| a.href = m.href || "#"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Do not render metadata-less legacy likes as
🧰 Tools🤖 Prompt for AI Agents |
||
| a.dataset.type = m.type || "post"; | ||
| var halo = document.createElement("span"); halo.className = "card-halo"; | ||
| var gloss = document.createElement("span"); gloss.className = "card-gloss"; | ||
| var spark = document.createElement("span"); spark.className = "card-sparkle"; spark.textContent = "✨"; | ||
| var top = document.createElement("div"); top.className = "repo-card-top"; | ||
| var name = document.createElement("span"); name.className = "repo-card-name"; name.textContent = m.title || k; | ||
| var badge = document.createElement("span"); badge.className = "repo-badge"; badge.textContent = KIND[m.type] || "•"; | ||
| top.appendChild(name); top.appendChild(badge); | ||
| a.appendChild(halo); a.appendChild(gloss); a.appendChild(spark); a.appendChild(top); | ||
| res.appendChild(a); | ||
| }); | ||
| })(); | ||
| </script> | ||
| </body> | ||
| </html> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: dominicusin/dominicusin.github.io
Length of output: 8382
Use the guarded storage helpers.
localStorage.getItem()can throw while initializingmqState, which can stop the IIFE before the quick-toggle handler is registered.localStorage.setItem()inmqApply()can also throw and prevent persistence. Replace both calls withls()andss().🤖 Prompt for AI Agents