已歸檔的文章
這代表著我認為這篇文章的資訊已經過時;或者是對我來說已經不那麼重要。
如果你還是能在這些文字上面發現任何有價值的資訊或想法,歡迎跟我分享,也許我又會有新的想法了!
我想解決的問題
我前幾天透過 ChatGPT 製作了一版麵包屑導航的功能,就是在目前畫面上方的 Library 等連結。
不過,當我測試後,我發現這個版本有點不符合我的需求。因為我習慣會透過 path
這個參數來自定頁面的路徑,例如,我的 Heptabase 頁面的路徑是 library/toolbox/featured/heptabase,但我希望設定成 Library/heptabase。
我想這樣設定的原因是,假設未來我移動了這個頁面的層級,我希望他的 URL 仍是維持固定不變,這樣即使先前有人連結到這個頁面,連結都不會跑掉。
但我發現,這個版本的麵包屑程式碼會跟我自訂 path 的行為衝突,因為當我自訂 path 以後,麵包屑只抓取 path 的路徑,就會讓導航列變成 Library > Heptabase ,而漏了中間的另外兩個層級。
我想像的解決方案
我前兩天嘗試了透過 ChatGPT 修改都失敗,後來重看了 Zola 的官方文件,裡面提到一個有趣的變數叫做 relative_path: String;
,這個變數可以讓我取得當前頁面的相對路徑,因此,即使 path 被我自訂了,我還是能夠抓取原始的路徑。這下子我覺得有機會建構導航了,就開始跟 ChatGPT 合作。
我與 ChatGPT 的對話
我提供的資訊
我把我掌握的資訊、以及我可以提供的範例都一起丟給 ChatGPT ,完整的 Prompt 如下:
我正在使用 Zola 的 Tera 模板,現在我想要為我的 pages 設置麵包屑連結,
我的頁面結構如下:
以 /library/toolbox/featured/keyboard_maestro.md 來說,我有為他設置特定的 path : library/keyboard_maestro ,因此他的 path 會是 library/keyboard_maestro
但我期待的是得到如下的麵包屑架構:
Library > Toolbox > Featured > Keyboard Maestro
我有以下參數可以使用:
當我使用 {{ page.relative_path }} ,我會得到 library/toolbox/featured/keyboard_maestro.md ,我想這個可以透過 get_page 拿來設置 Keyboard Maestro 本身的超連結。
當我使用 {{ page.ancestors }} ,我會得到 [_index.md, library/_index.md, library/toolbox/_index.md, library/toolbox/featured/_index.md] ,我想這些可以讓我透過 get_section 來設置 Library > Toolbox > Featured 等幾個超連結
請為我設置一段 macro ,你可以參考下列 macro:
{% macro breadcrumbs(current_section) %}
<div class="pt" style="padding-bottom: 1rem;">
{% for path in current_section.ancestors %}
{% if path != "_index.md" %}
{{ macro::breadcrumb_link(path=path) }}
<span>></span>
{% endif %}
{% endfor %}
{{ macro::breadcrumb_link(path=current_section.path) }}
</div>
{% endmacro breadcrumbs %}
ChatGPT 的回覆:
ChatGPT 回覆如下:
在 Zola 的 Tera 模板中,我們可以使用 get_section 或 get_page 來根據路徑獲取頁面或者部分的資訊。以下是一個簡單的 macro 範例,它可以解析 page.ancestors 和 page.relative_path 來產生你需要的麵包屑:
{% macro breadcrumb_link(path) %}
{% set section = get_section(path) %}
<a href="{{ section.permalink }}">{{ section.title }}</a>
{% endmacro breadcrumb_link %}
{% macro breadcrumbs(page) %}
<div class="pt" style="padding-bottom: 1rem;">
{% for path in page.ancestors %}
{% if path != "_index.md" %}
{{ breadcrumb_link(path=path) }}
<span>></span>
{% endif %}
{% endfor %}
{% set page_title = get_page(page.relative_path).title %}
<a href="{{ page.permalink }}">{{ page_title }}</a>
</div>
{% endmacro breadcrumbs %}
這裡的 breadcrumb_link macro 會將路徑傳遞給 get_section 來獲取該部分的資訊,然後生成一個麵包屑連結。麵包屑連結的名稱由該部分的 title 決定,而連結的目標則由 permalink 決定。
在 breadcrumbs macro 中,我們首先對 page.ancestors 進行迭代。對於每個祖先路徑,我們檢查它是否為 _index.md。如果不是,則我們使用 breadcrumb_link macro 生成一個麵包屑連結。最後,我們使用 get_page 來獲取當前頁面的 title,並生成一個指向當前頁面的麵包屑連結。
請注意,你需要確保每個部分的 _index.md 檔案都設置了 title 屬性,否則這些麵包屑連結可能不會正確顯示。同時,確保你的路徑都是以 _index.md 結尾的。
我測試了這段 macro 後,接著就是將 Zola 呈現的錯誤訊息再貼回給 ChatGPT ,並且持續調整,大概經過 5 次的調整後,一切都順利完成了。
現在即使我設定頁面的 path
連結為特定的內容,麵包屑的導航還是會準確抓到這個頁面自身、上層的所有 section ,並且幫他們建立連結。
最終的程式碼
在這邊也分享一下最後的成果。
一共有兩段 macro:
{% macro page_breadcrumb_link(path) %}
{% set section = get_section(path=path) %}
<a href="{{ section.permalink }}">{{ section.extra.bread_name }}</a>
{% endmacro page_breadcrumb_link %}
{% macro page_breadcrumbs(page) %}
<div class="pt" style="padding-bottom: 1rem;">
{% for path in page.ancestors %}
{% if path != "_index.md" %}
{{ macro::page_breadcrumb_link(path=path) }}
<span>></span>
{% endif %}
{% endfor %}
{% set current_page = get_page(path=page.relative_path) %}
<a href="{{ page.permalink }}">{{ current_page.title }}</a>
</div>
{% endmacro page_breadcrumbs %}
然後我在 library_page.html 這個 template 模板裡面,透過這段程式碼來呼叫這兩段 macro:
{% block breadcrumbs -%}
{{ macro::page_breadcrumbs(page=page) }}
{% endblock breadcrumbs %}
一切都很順暢的運作,且比原本的 macro 語法還要簡潔很多。
我學到的事
我覺得這次很順利完成目標的原因是,我在第一次的 prompt 就提供給 ChatGPT 還算完整的資訊。(我使用的是 2023 年 5 月的 GPT 4 模型)例如,我提供了我的頁面結構、我期待的內容、我跟他說明當我使用某個參數時我會得到怎樣的內容、最後我也給他「macro」的結構大概是怎樣的,剩下就請他發揮。
但假設我單純請他為我建構一段 Zola 的 macro ,(我有測試了一下單純用「我正在使用 Zola 的 Tera 模板,現在我想要為我的 pages 設置麵包屑連結,請你為我建構對應的 macro 以及 html 程式碼:」來請他協助,但效果差非常很多。
我猜想,一方面因為 Zola 是很冷門的架構,另一方面是這個需求稍微有點複雜,所以若能給他較完整的資訊,表現就會好很多。