Skip to main content
  1. Blog/

Deployment and Expansion of My Website

·3097 words·15 mins·
Tech Software Introduction
yuuniji
Author
yuuniji
Table of Contents

Complete Deployment Guide for Hugo Blog on GitHub Pages
#

★Publishing to GitHub
#

1. Create GitHub Repository
#

  • mywebsite (for Hugo source code)
  • yuuniji.github.io (for generated static files)

2. Push mywebsite Repository
#

git remote add origin https://github.com/yuuniji/mywebsite.git
git branch -M main
git add .
git commit -m "Initial commit"
git push -u origin main

3. Generate Static Files
#

hugo -D

Static files are located in the public/ directory.

4. Push to yuuniji.github.io
#

cd public
git init
git remote add origin https://github.com/yuuniji/yuuniji.github.io.git
git checkout -b main
git add .
git commit -m "Deploy Hugo site"
git push -f origin main

★Configure GitHub Pages
#

  • Enter yuuniji.github.io repository Settings → Pages.
  • Select main branch, save, and wait for deployment to complete.
  • Visit https://yuuniji.github.io to view the blog.

★Automated Deployment (Optional)
#

1. Add deploy.sh to mywebsite repository
#

nano deploy.sh

Paste the following content:

#!/bin/bash
hugo -D
cd public
git add .
git commit -m "Deploy: $(date)"
git push origin main
cd ..

Save and exit (press Ctrl + X, then Y, and press Enter).

2. Assign Execution Permissions
#

chmod +x deploy.sh

3. Run Deployment Script
#

./deploy.sh

★All Done!
#

After updating the blog, just:

  1. Write articles in mywebsite/ directory hugo new posts/xxx.md
  2. Run ./deploy.sh
  3. Visit https://yuuniji.github.io to view updates

💡 Refer back to this guide if you have any questions! 🚀

★ My Website Structure
#

  • Categories(Fixed large categories)
  • Tags(Keywords)
  • Topics(Series/Themes)
Categories:
  ├── Language Acquisition
  ├── IT / Technology
  ├── Reading Notes
  ├── Life Records
  └── Reflections & Essays

Tags:
  ├── Japanese, English, Grammar
  ├── Front-end, JavaScript, Cloud Computing
  ├── Philosophy, Economics, Psychology, History
  ├── Travel, 318 Sichuan-Tibet Highway, Photography
  ├── Time Management, Productivity Improvement...

Topics:
  ├── How to Master Japanese
  ├── Top 10 Books
  ├── 2025 Reading Log...

★Hugo Function Extension Guide
#

◇Quotes
#

Create quoteszh.html file and place it in the layouts/shortcodes/ directory of your Hugo site. Then, in content/../_index.md, reference {{\< quoteszh >}} (remove \).

<!-- layouts/shortcodes/quoteszh.html -->
<blockquote>
    <p>{{ .Inner }}</p>
    <footer>{{ .Get "source" }}</footer>
</blockquote>

<div id="quote" class="quote-box"></div>
<script>
    const quotes = [
        ""
        ""
        ""
    ]
    // 随机选择一个引语
    const randomQuote = quotes[Math.floor(Math.random() * quotes.length)];

    // 显示引语
    document.getElementById('quote').innerHTML = randomQuote;
</script>

<style>
    .quote-box {
        max-width: 600px;
        font-size: 0.8rem;
        text-align: left;

    .quote-box {
        animation: fadeIn 1.5s ease-in-out;
    }

    @keyframes fadeIn {
        0% {
            opacity: 0;
        }

        100% {
            opacity: 1;
        }
    }
</style>

◇Add Website Runtime
#

Add the following code to the newly created /layouts/partials/extend-footer.html:

  <span id="runtime_span"></span> <!-- 用于显示网站运行时间的容器 -->
  <script type="text/javascript">
  function show_runtime() {
      // 每隔 1 秒执行一次 show_runtime,实现实时更新
      setTimeout(show_runtime, 1000);

      // 设置网站起始运行时间(2025年2月22日 00:00:00)
      const startDate = new Date("2025/02/22 00:00:00");

      // 获取当前时间
      const now = new Date();

      // 计算时间差(毫秒)
      const diff = now.getTime() - startDate.getTime();

      // 计算运行的天、小时、分钟、秒
      const days = Math.floor(diff / (24 * 60 * 60 * 1000));                     // 天数
      const hours = Math.floor((diff % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)); // 小时
      const minutes = Math.floor((diff % (60 * 60 * 1000)) / (60 * 1000));        // 分钟
      const seconds = Math.floor((diff % (60 * 1000)) / 1000);                    // 秒

      // 获取当前 HTML 页面设置的语言(由 Hugo 生成,如 <html lang="ja">)
      const lang = document.documentElement.lang || "en"; // 默认英文

      // 各语言的显示文本模板
      const translations = {
          zh: `网站已运行 ${days}${hours} 小时 ${minutes}${seconds} 秒`,
          ja: `サイトは稼働してから ${days}${hours}時間 ${minutes}${seconds}秒`,
          en: `Site has been running for ${days}d ${hours}h ${minutes}m ${seconds}s`
      };

      // 根据当前语言选择显示内容,若找不到则使用英文
      const output = translations[lang] || translations["en"];

      // 将显示内容写入页面中
      document.getElementById("runtime_span").innerHTML = output;
  }

  // 初始化运行
  show_runtime();
  </script>

◇Giscus Comments
#

Check params.toml
#

article.showComments = true

Prepare GitHub Repository
#

  • Login to your GitHub account.
  • Open your blog repository (e.g., yourname/yourwebsite).
  • Ensure that Discussions is enabled:
    • Go to SettingsFeatures → Check Discussions.
    • Go to the Discussions page and create a Discussion Category (e.g., General).

Generate Giscus Embed Code
#

Open Giscus configuration page: 👉 https://giscus.app

Configure as follows:

  • Repository: Your blog repository (e.g. yourname/yourblog)
  • Repository ID / Category / Category ID: Automatically generated based on the information in your Discussions
  • Discussion Mapping: It is recommended to select pathname
  • Reaction: Enable (👍, etc.)
  • Input Position: bottom (comment box at the bottom)
  • Theme: preferred_color_scheme (automatically adapt to light/dark colors)

Copy the generated <script> code.

Insert Giscus code into Hugo template
#

You can put the Giscus code into a separate file, for example: layouts/partials/giscus.html

<div id="giscus_container"></div>
<script src="https://giscus.app/client.js"
        data-repo="yourname/yourblog"
        data-repo-id="Your repo ID"
        data-category="General"
        data-category-id="Your category ID"
        data-mapping="pathname"
        data-strict="0"
        data-reactions-enabled="1"
        data-emit-metadata="0"
        data-input-position="bottom"
        data-theme="preferred_color_scheme"
        data-lang="zh-CN"
        crossorigin="anonymous"
        async>
</script>

Then in layouts/partials/comments.html article template:

{{ partial "giscus.html" . }}

◇Music Player
#

  • A mini player fixed to the bottom-right corner of the screen
  • Expandable/collapsible playlist
  • Control buttons for play/pause, previous, and next
  • Preserve playback state when switching between articles (using localStorage)
  • Frosted glass background design for the music player

Create file
#

<!-- layouts/partials/musicplayer.html -->

<style scoped>
  .music-player-container {
    position: fixed;
    bottom: 12px;
    left: 12px;
    z-index: 9999;
    font-family: system-ui, sans-serif;
    font-size: 13px;
  }

  .music-player-container .music-panel {
    display: none;
    margin-bottom: 6px;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 12px;
    padding: 12px 14px;
    width: 200px;
    max-height: 180px;
    overflow-y: auto;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
    backdrop-filter: blur(16px) saturate(180%);
    -webkit-backdrop-filter: blur(16px) saturate(180%);
  }

  .music-player-container .music-panel.show {
    display: block;
  }

  .music-player-container .music-title {
    font-size: 13px;
    color: var(--color-primary-300);
    font-weight: 500;
    margin-bottom: 6px;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  }

  .music-player-container .playlist {
    list-style: none;
    padding: 0;
    margin: 0;
  }

  .music-player-container .playlist li {
    padding: 4px 6px;
    border-radius: 6px;
    cursor: pointer;
    color: var(--content);
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
    transition: all 0.2s ease;
  }

  .music-player-container .playlist li:hover {
    background: rgba(255, 255, 255, 0.15);
    transform: translateY(-1px);
  }

  .music-player-container .playlist li.active {
    background: rgba(255, 255, 255, 0.2);
    color: var(--accent);
    font-weight: 600;
    border: 1px solid rgba(255, 255, 255, 0.3);
  }

  .music-player-container .mini-player {
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 100px;
    display: flex;
    gap: 8px;
    padding: 6px 10px;
    align-items: center;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
    backdrop-filter: blur(20px) saturate(180%);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    transition: all 0.3s ease;
  }

  .music-player-container .mini-player:hover {
    background: rgba(255, 255, 255, 0.15);
    transform: translateY(-2px);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
  }

  .music-player-container .mini-player button {
    background: none;
    border: none;
    font-size: 16px;
    color: var(--secondary);
    cursor: pointer;
    padding: 4px;
    border-radius: 50%;
    transition: all 0.2s ease;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  }

  .music-player-container .mini-player button.hidden {
    display: none;
  }

  .music-player-container.hidden {
    transform: translateX(-100%);
    opacity: 0;
    pointer-events: none;
  }

  .show-player-btn {
    position: fixed;
    bottom: 12px;
    left: 12px;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 50%;
    width: 40px;
    height: 40px;
    display: none;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font-size: 16px;
    color: var(--secondary);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
    backdrop-filter: blur(20px) saturate(180%);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    transition: all 0.3s ease;
    z-index: 9999;
  }

  .music-player-container.hidden + .show-player-btn {
    display: flex;
  }

  .show-player-btn:hover {
    background: rgba(255, 255, 255, 0.15);
    transform: scale(1.1);
  }

  .music-player-container .mini-player button:hover {
    color: var(--accent);
    background: rgba(255, 255, 255, 0.1);
    transform: scale(1.1);
  }

  .music-player-container .mini-player button:active {
    color: var(--accent-active);
    transform: scale(0.95);
  }

  .music-player-container audio {
    display: none;
  }

  /* 滚动条美化 */
  .music-player-container .music-panel::-webkit-scrollbar {
    width: 4px;
  }

  .music-player-container .music-panel::-webkit-scrollbar-track {
    background: rgba(255, 255, 255, 0.1);
    border-radius: 2px;
  }

  .music-player-container .music-panel::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.3);
    border-radius: 2px;
  }

  .music-player-container .music-panel::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.5);
  }
</style>

<div class="music-player-container" id="musicPlayerContainer">
  <div class="music-panel" id="musicPanel">
    <div class="music-title" id="musicTitle">Loading...</div>
    <ul class="playlist" id="musicPlaylist"></ul>
  </div>
  <div class="mini-player" id="miniPlayer">
    <button id="musicTogglePanel" title="Toggle Playlist">📂</button>
    <button id="musicPrevBtn" title="Previous">⏮️</button>
    <button id="musicPlayBtn" title="Play">▶️</button>
    <button id="musicNextBtn" title="Next">⏭️</button>
    <button id="musicHideBtn" title="Hide Player">👁️</button>
  </div>
  <audio id="musicAudio"></audio>
</div>
<div class="show-player-btn" id="showPlayerBtn" title="Show Music Player">🎧</div>

<script>
  (function() {
    'use strict';
    
    // 命名空间前缀,避免全局冲突
    const MUSIC_PLAYER_NS = 'HugoMusicPlayer_';
    
    const base = "https://yuuniji.github.io/music/lofi_beats/";
    const jsonURL = base + "songs.json";
    const STORAGE_KEY = MUSIC_PLAYER_NS + "state";
    const INTERACT_KEY = MUSIC_PLAYER_NS + "user_interacted";
    const HIDDEN_KEY = MUSIC_PLAYER_NS + "hidden";

    async function initMusicPanel() {
      try {
        const res = await fetch(jsonURL);
        const songs = await res.json();
        if (!songs.length) return;

        let currentIndex = 0;
        let isPlaying = false;
        
        // 使用带命名空间的ID选择器
        const audio = document.getElementById("musicAudio");
        const playBtn = document.getElementById("musicPlayBtn");
        const prevBtn = document.getElementById("musicPrevBtn");
        const nextBtn = document.getElementById("musicNextBtn");
        const toggleBtn = document.getElementById("musicTogglePanel");
        const hideBtn = document.getElementById("musicHideBtn");
        const showBtn = document.getElementById("showPlayerBtn");
        const panel = document.getElementById("musicPanel");
        const title = document.getElementById("musicTitle");
        const playlist = document.getElementById("musicPlaylist");

        // 检查元素是否存在
        if (!audio || !playBtn || !prevBtn || !nextBtn || !toggleBtn || !hideBtn || !showBtn || !panel || !title || !playlist) {
          console.warn('Music player elements not found');
          return;
        }

        // 恢复保存的状态
        const savedStateStr = localStorage.getItem(STORAGE_KEY);
        if (savedStateStr) {
          try {
            const saved = JSON.parse(savedStateStr);
            if (saved.index >= 0 && saved.index < songs.length) {
              currentIndex = saved.index;
              audio.currentTime = saved.time || 0;
              // 恢复播放状态,如果之前是播放状态则继续播放
              isPlaying = saved.isPlaying || false;
            }
          } catch (e) {
            console.warn('Failed to parse saved music player state:', e);
          }
        }

        // 恢复隐藏状态
        const isHidden = localStorage.getItem(HIDDEN_KEY) === 'true';
        if (isHidden) {
          document.getElementById("musicPlayerContainer").classList.add('hidden');
        }

        // 构建播放列表
        songs.forEach((song, index) => {
          const li = document.createElement("li");
          li.textContent = song.title;
          li.onclick = function() {
            currentIndex = index;
            loadAndPlay(currentIndex);
            localStorage.setItem(INTERACT_KEY, "true");
          };
          playlist.appendChild(li);
        });

        function highlight(index) {
          const listItems = playlist.querySelectorAll("li");
          listItems.forEach((li, idx) => {
            li.classList.toggle("active", idx === index);
          });
        }

        function loadAndPlay(index) {
          audio.src = base + songs[index].file;
          audio.currentTime = 0;
          title.textContent = songs[index].title;
          highlight(index); // 立即高亮
          audio.play().then(() => {
            isPlaying = true;
            playBtn.textContent = "⏸️";
            saveState();
          }).catch((error) => {
            console.warn('Failed to play audio:', error);
            isPlaying = false;
            playBtn.textContent = "▶️";
          });
        }

        function saveState() {
          try {
            localStorage.setItem(STORAGE_KEY, JSON.stringify({
              index: currentIndex,
              time: audio.currentTime,
              isPlaying: !audio.paused
            }));
          } catch (e) {
            console.warn('Failed to save music player state:', e);
          }
        }

        // 初始化显示
        audio.src = base + songs[currentIndex].file;
        title.textContent = songs[currentIndex].title;
        highlight(currentIndex);
        
        // 根据恢复的状态决定是否播放
        if (isPlaying) {
          audio.play().then(() => {
            updatePlayButton();
            saveState();
          }).catch((error) => {
            console.warn('Failed to auto-play audio:', error);
            isPlaying = false;
            updatePlayButton();
          });
        } else {
          audio.pause();
          isPlaying = false;
          updatePlayButton(); // 确保按钮状态正确
        }

        // 更新播放按钮状态的函数
        function updatePlayButton() {
          if (audio.paused) {
            playBtn.textContent = "▶️";
            isPlaying = false;
            // 暂停状态时隐藏其他按钮
            prevBtn.classList.add('hidden');
            nextBtn.classList.add('hidden');
            toggleBtn.classList.add('hidden');
            hideBtn.classList.add('hidden');
          } else {
            playBtn.textContent = "⏸️";
            isPlaying = true;
            // 播放状态时显示所有按钮
            prevBtn.classList.remove('hidden');
            nextBtn.classList.remove('hidden');
            toggleBtn.classList.remove('hidden');
            hideBtn.classList.remove('hidden');
          }
        }

        // 事件监听器
        playBtn.onclick = function() {
          if (audio.paused) {
            audio.play().then(() => {
              updatePlayButton();
              saveState();
            }).catch((error) => {
              console.warn('Failed to play audio:', error);
              updatePlayButton();
            });
          } else {
            audio.pause();
            updatePlayButton();
            saveState();
          }
        };

        prevBtn.onclick = function() {
          currentIndex = (currentIndex - 1 + songs.length) % songs.length;
          loadAndPlay(currentIndex);
        };

        nextBtn.onclick = function() {
          currentIndex = (currentIndex + 1) % songs.length;
          loadAndPlay(currentIndex);
        };

        toggleBtn.onclick = function() {
          panel.classList.toggle("show");
        };

        hideBtn.onclick = function() {
          document.getElementById("musicPlayerContainer").classList.add('hidden');
          localStorage.setItem(HIDDEN_KEY, 'true');
        };

        showBtn.onclick = function() {
          document.getElementById("musicPlayerContainer").classList.remove('hidden');
          localStorage.setItem(HIDDEN_KEY, 'false');
        };

        // 音频事件监听
        audio.ontimeupdate = saveState;
        
        audio.onplay = function() {
          updatePlayButton();
          saveState();
        };
        
        audio.onpause = function() {
          updatePlayButton();
          saveState();
        };
        
        audio.onended = function() {
          currentIndex = (currentIndex + 1) % songs.length;
          loadAndPlay(currentIndex);
        };

        // 移除自动播放功能,用户需要手动点击播放
        // 确保初始状态为暂停
        updatePlayButton();
        
        // 页面卸载时保存状态
        window.addEventListener('beforeunload', function() {
          saveState();
        });
        
        // 页面隐藏时也保存状态(移动设备切换应用时)
        document.addEventListener('visibilitychange', function() {
          if (document.hidden) {
            saveState();
          }
        });

      } catch (error) {
        console.error('Failed to initialize music player:', error);
      }
    }

    // 确保DOM加载完成后再初始化
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', initMusicPanel);
    } else {
      initMusicPanel();
    }
  })();
</script>

In layout template
#

Add the following code to the newly created /layouts/partials/extend-footer.html:

{{ partial "musicplayer.html" . }}

◇Icon
#

https://favicon.io/favicon-generator/

website/
  ├── static/
  │   ├── android-chrome-192x192.png
  │   ├── android-chrome-512x512.png
  │   ├── apple-touch-icon.png
  │   ├── favicon.ico
  │   ├── favicon-16x16.png
  │   ├── favicon-32x32.png
  │   └── site.webmanifest

当然,以下是更精简且专业的英文笔记:


◇Sidenote Shortcode
#

1. Usage
#

  • Markdown:

    {{\< sidenote label="☍" >}}Your note here{{\< /sidenote >}} (remove `\`).
    
  • e.g.: The term “sidenote" A small note that provides extra context without interrupting the flow. is often used in academic writing.

  • File: layouts/shortcodes/sidenote.html

2. Shortcode (HTML)
#

{{- $id := .Get "id" | default (printf "sn-%d" (.Page.Scratch.Get "sidenoteID" | default 1)) -}}
{{- $label := .Get "label" | default "☍" -}}

<span class="sidenote-block">
  <label for="{{ $id }}-toggle" class="sidenote-label">{{ $label }}</label>
  <input type="checkbox" id="{{ $id }}-toggle" class="sidenote-toggle" hidden>
  <span class="sidenote">{{ .Inner }}</span>
</span>

{{- .Page.Scratch.Set "sidenoteID" ((.Page.Scratch.Get "sidenoteID" | default 1) | add 1) -}}

3. CSS
#

/* assets/css/custom.css */
.sidenote-block {
  position: relative;
  display: inline-block;
}

.sidenote-label {
  font-size: 0.8em;
  vertical-align: super;
  color: #888;
  cursor: pointer;
  user-select: none;
}

.sidenote {
  display: none;
  position: absolute;
  left: 100%;
  top: 0;
  margin-left: 1rem;
  width: 15rem;
  font-size: 0.85em;
  background: var(--sidenote-bg, #f9f9f9);
  padding: 0.5rem;
  border-left: 3px solid var(--sidenote-border, #ccc);
  color: var(--sidenote-text, #333);
  z-index: 10;
  box-shadow: 0 0 5px rgba(0,0,0,0.1);
}

.sidenote-toggle:checked ~ .sidenote {
  display: inline-block;
}


/* 深色模式适配(适用于 prefers-color-scheme 或 Blowfish 的 .dark class) */
@media (prefers-color-scheme: dark) {
  .sidenote {
    --sidenote-bg: #1e1e1e;
    --sidenote-text: #ddd;
    --sidenote-border: #444;
  }
  .sidenote-label {
    --sidenote-label-color: #aaa;
  }
}

.dark .sidenote {
  --sidenote-bg: #1e1e1e;
  --sidenote-text: #ddd;
  --sidenote-border: #444;
}
.dark .sidenote-label {
  --sidenote-label-color: #aaa;
}

/* 手机端适配 */
@media (max-width: 768px) {
  .sidenote {
    position: relative;
    left: auto;
    top: auto;
    margin-left: 0;
    margin-top: 0.3rem;
    width: 100%;
    border-left: none;
    border-top: 2px solid var(--sidenote-border, #ccc);
    background: var(--sidenote-bg, #f2f2f2);
  }
}

◇Chart
#

Hugo + Chart.js Shortcode example:

{{\< chart >}}
type: 'bar',
data: {
  labels: ['Tomato', 'Blueberry', 'Banana', 'Lime', 'Orange'],
  datasets: [{
    label: '# of votes',
    data: [12, 19, 3, 5, 3],
  }]
}
{{\< /chart >}}
(remove `\`).

◇Mermaid
#

Hugo + Mermaid Shortcode example:

{{\< mermaid >}}
graph LR;
A[Lemons]-->B[Lemonade];
B-->C[Profit]
{{\< /mermaid >}}
(remove `\`).
graph LR;
A[Lemons]-->B[Lemonade];
B-->C[Profit]

The examples below are a small selection taken from the official Mermaid docs. You can also view the page source on GitHub to see the markup.

Flowchart
#

<div style="background-color:white; padding: 20px">
{{\< mermaid >}}
graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
B --> G[/Another/]
C ==>|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]
subgraph Section
C
D
E
F
G
end
(remove `\`).
{{\< /mermaid >}}
</div>
graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
B --> G[/Another/]
C ==>|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]
subgraph Section
C
D
E
F
G
end

Sequence diagram
#

<div style="background-color:white; padding: 20px">
{{\< mermaid >}}
sequenceDiagram
autonumber
par Action 1
Alice->>John: Hello John, how are you?
and Action 2
Alice->>Bob: Hello Bob, how are you?
end
Alice->>+John: Hello John, how are you?
Alice->>+John: John, can you hear me?
John-->>-Alice: Hi Alice, I can hear you!
Note right of John: John is perceptive
John-->>-Alice: I feel great!
loop Every minute
John-->Alice: Great!
end
(remove `\`).
{{\< /mermaid >}}
</div>
sequenceDiagram
autonumber
par Action 1
Alice->>John: Hello John, how are you?
and Action 2
Alice->>Bob: Hello Bob, how are you?
end
Alice->>+John: Hello John, how are you?
Alice->>+John: John, can you hear me?
John-->>-Alice: Hi Alice, I can hear you!
Note right of John: John is perceptive
John-->>-Alice: I feel great!
loop Every minute
John-->Alice: Great!
end

Class diagram
#

<div style="background-color:white; padding: 20px">
{{\< mermaid >}}
classDiagram
Animal "1" <|-- Duck
Animal <|-- Fish
Animal <--o Zebra
Animal : +int age
Animal : +String gender
Animal: +isMammal()
Animal: +mate()
class Duck{
+String beakColor
+swim()
+quack()
}
class Fish{
-int sizeInFeet
-canEat()
}
class Zebra{
+bool is_wild
+run()
}
(remove `\`).
{{\< /mermaid >}}
</div>
classDiagram
Animal "1" <|-- Duck
Animal <|-- Fish
Animal <--o Zebra
Animal : +int age
Animal : +String gender
Animal: +isMammal()
Animal: +mate()
class Duck{
+String beakColor
+swim()
+quack()
}
class Fish{
-int sizeInFeet
-canEat()
}
class Zebra{
+bool is_wild
+run()
}
```

Entity relationship diagram
#

<div style="background-color:white; padding: 20px">
{{\< mermaid >}}
erDiagram
CUSTOMER }|..|{ DELIVERY-ADDRESS : has
CUSTOMER ||--o{ ORDER : places
CUSTOMER ||--o{ INVOICE : "liable for"
DELIVERY-ADDRESS ||--o{ ORDER : receives
INVOICE ||--|{ ORDER : covers
ORDER ||--|{ ORDER-ITEM : includes
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
PRODUCT ||--o{ ORDER-ITEM : "ordered in"
(remove `\`).
{{\< /mermaid >}}
</div>
erDiagram
CUSTOMER }|..|{ DELIVERY-ADDRESS : has
CUSTOMER ||--o{ ORDER : places
CUSTOMER ||--o{ INVOICE : "liable for"
DELIVERY-ADDRESS ||--o{ ORDER : receives
INVOICE ||--|{ ORDER : covers
ORDER ||--|{ ORDER-ITEM : includes
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
PRODUCT ||--o{ ORDER-ITEM : "ordered in"

◇Article Heatmap
#

Create the Shortcode File
#

Create a new file named heatmap.html in the layouts/shortcodes/ directory of your Hugo site.

<div id="heatmap" style="max-width: 600px;height: 180px;padding: 2px;text-align: center;"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.3.0/dist/echarts.min.js"></script>
<script>
(function() {
  // Hugo 数据
  var dataArr = [
    {{- $posts := where .Site.RegularPages "Section" "posts" -}}
    {{- range $posts -}}
      ["{{ .Date.Format "2006-01-02" }}", {{ printf "%.1f" (div .WordCount 1000.0) }}, "{{ .RelPermalink }}", "{{ .Title }}"],
    {{- end -}}
  ];

  var dataMap = new Map();
  dataArr.forEach(function(item) {
    var key = item[0];
    var wordCount = item[1];
    var link = item[2];
    var title = item[3];
    var value = dataMap.get(key);
    if (!value || wordCount > value.wordCount) {
      dataMap.set(key, {wordCount, link, title});
    }
  });

  var data = [];
  for (const [key, value] of dataMap.entries()) {
    data.push([key, value.wordCount]);
  }

  var chartDom = document.getElementById('heatmap');
  var myChart = echarts.init(chartDom);

  function heatmap_width(months){
    var startDate = new Date();
    var mill = startDate.setMonth(startDate.getMonth() - months);
    var endDate = +new Date();
    startDate = +new Date(mill);
    endDate = echarts.format.formatTime('yyyy-MM-dd', endDate);
    startDate = echarts.format.formatTime('yyyy-MM-dd', startDate);
    return [[startDate, endDate]];
  }

  function getRangeArr() {
    const windowWidth = window.innerWidth;
    if (windowWidth >= 600) return heatmap_width(12);
    else if (windowWidth >= 400) return heatmap_width(9);
    else return heatmap_width(6);
  }

  // 配色方案
  function getThemeOptions(isDark) {
  return {
    title: {
  top: 15,                // 下移一点
  left: 'center',
  text: 'Article Activity Heatmap',
  textStyle: { 
    color: isDark ? '#ccc' : '#666', // 颜色淡化,不太突出
    fontSize: 14               // 字体小一点
  }
},

    tooltip: {
      backgroundColor: isDark ? '#222' : '#fff',
      borderColor: isDark ? '#444' : '#ccc',
      textStyle: { color: isDark ? '#F6F5EB' : '#222' },
      formatter: function(p) {
        const post = dataMap.get(p.data[0]);
        if (!post) return '';
        return (post.title || 'No title') + ' | ' + post.wordCount + 'k words';
      }
    },
    visualMap: {
      min: 0,
      max: 10,
      type: 'piecewise',
      orient: 'horizontal', // 也可以改成 'vertical'
      left: 'center',
      bottom: 10, // 移到底部
      inRange: {
        color: isDark
          ? ['#3A3D5C', '#9AA7E0']
          : ['#9aa7e04c', '#3A3D5C']
      },
      splitNumber: 4,
      text: ['k words', ''],
      showLabel: true,
      itemGap: 20,
      textStyle: { color: isDark ? '#F6F5EB' : '#222' }
    },
    calendar: {
      top: 60, // 标题下移后,让日历靠下
      left: 20,
      right: 20,
      cellSize: ['auto', 12],
      range: getRangeArr(),
      itemStyle: {
        color: isDark ? '#23272e' : '#F1F1F1',
        borderWidth: 2.5,
        borderColor: isDark ? '#444' : '#fff'
      },
      yearLabel: { show: false, color: isDark ? '#F6F5EB' : '#222' },
      dayLabel: { color: isDark ? '#F6F5EB' : '#222' },
      monthLabel: { color: isDark ? '#F6F5EB' : '#222' },
      splitLine: { lineStyle: { color: 'rgba(0,0,0,0)' } }
    },
    series: {
      type: 'heatmap',
      coordinateSystem: 'calendar',
      data: data
    }
  };
}


  // 判断深色模式
  function isDarkMode() {
    return document.documentElement.classList.contains('dark');
  }

  function setChartTheme() {
    myChart.setOption(getThemeOptions(isDarkMode()));
  }

  // 初始化
  setChartTheme();

  // 监听主题切换
  var observer = new MutationObserver(setChartTheme);
  observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });

  // 响应窗口变化
  window.onresize = function() { myChart.resize(); };

  // 点击跳转文章
  myChart.on('click', function(params) {
    if (params.componentType === 'series') {
      const post = dataMap.get(params.data[0]);
      if (post) {
        window.open(window.location.origin + post.link, '_blank').focus();
      }
    }
  });
})();
</script>

Use the Shortcode in Pages
#

{{\< heatmap >}}
(remove `\`).