Skip to main content
  1. Blog/

Deployment and Expansion of My Website

·2111 words·10 mins·
Technology Blog GitHub Hugo
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);
  }
}