Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/workflows/hugo-theme-dream.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Dream integration
on:
pull_request:
paths: ['packages/hugo-theme-dream/**', 'verification/hugo-theme-dream/**', 'test/hugo-theme-dream.test.js', '.github/workflows/hugo-theme-dream.yml']
permissions:
contents: read
jobs:
live-site:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: firekylin/firekylin
ref: ae71b610ed2dcb6e992366bc06080ec51805042c
path: .verification-core
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- run: pnpm install --frozen-lockfile
working-directory: .verification-core
- run: pnpm install --frozen-lockfile --ignore-scripts
- name: Verify real SQLite site and installed ZIP
env:
FIREKYLIN_CHECKOUT: ${{ github.workspace }}/.verification-core
PLAYWRIGHT_MODULE: ${{ github.workspace }}/node_modules/playwright-core
CHROMIUM_PATH: /usr/bin/google-chrome
run: node verification/hugo-theme-dream/live.cjs
- uses: actions/upload-artifact@v4
if: always()
with:
name: hugo-theme-dream-live-evidence
path: verification/hugo-theme-dream/
21 changes: 21 additions & 0 deletions packages/hugo-theme-dream/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2019 Yue Yang

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
7 changes: 7 additions & 0 deletions packages/hugo-theme-dream/NOTICE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Source and attribution

Adapted from [Dream](https://github.com/g1eny0ung/hugo-theme-dream/tree/1bf86a1b1f7a5cf2be8538446bd6804292687686) by Yue Yang, pinned at `1bf86a1b1f7a5cf2be8538446bd6804292687686`. The complete original license is in LICENSE.

Native Firekylin Eta integration by Firekylin contributors. No demo content, remote fonts, analytics or third-party service credentials included.

Original generated CSS and source input are retained. Bundled generated styles include Tailwind 4.3.2, typography 0.5.20 and DaisyUI 5.6.17; complete MIT notices are in res/licenses. Native JavaScript replaces Alpine/masonry/TOC dependencies. No default photos, avatar, icon library, remote fonts, math/diagram engines, analytics or third-party services are bundled.
7 changes: 7 additions & 0 deletions packages/hugo-theme-dream/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Dream for Firekylin

Yue Yang’s MIT Dream v3 design is adapted with its original Tailwind/DaisyUI CSS, emerald/forest palettes, rounded shadow cards, optional avatar and flippable About panel. Native CSS columns provide the 4/3/2/1-column masonry-like feed; reading order is down each column, unlike upstream JavaScript masonry. Missing/unsafe covers are omitted rather than using random demo photos. Title and cover links avoid an overlay on the entire card.

The About button flips between articles and escaped configurable About text/navigation cards. Hidden faces are inert, focus follows the visible face, the control supports keyboard activation, and reduced-motion disables rotation. Without JavaScript the article face remains readable and controls are hidden. A persistent light/dark toggle and safe DOM article TOC are included.

Native posts/pages, archive/taxonomy/search, pagination, protected content, comments and attachments are supported. Footer text/author and avatar URL are configurable. The port does not implement arbitrary Hugo About bundles, Zen mode, image processing/zoom, scroll-spy, reading-time estimates, service comments, math/diagram rendering, ads, or feed generation. Default images, icons and service scripts are omitted. No live upstream pixel comparison or remote comment submission is claimed.
3 changes: 3 additions & 0 deletions packages/hugo-theme-dream/VISUAL-COMPARISON.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Visual adaptation

Pinned summary/nav/back/single templates plus original generated CSS define the reference: rounded image cards, emerald/forest schemes, compact avatar/motto header, flipped About cards and narrow article with right TOC. CSS columns replace JS masonry and native controls replace icons/Alpine; reduced-motion and inactive-face focus handling are explicit. Installed screenshots/tests are included; no live upstream pixel comparison was performed.
6 changes: 6 additions & 0 deletions packages/hugo-theme-dream/archive.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<% layout('./layout'); const site = await firekylin.widget('Widget_Options'); const archive = await firekylin.widget('Widget_Archive', {type:'archive'}); %>
<% block('title', () => { %>归档 - <%= site.title() %><% }) %>
<% block('content', () => { %><section class="directory"><h1 class="archive-header">归档</h1>
<% const groups = Object.entries(archive.grouped); if (!groups.length) { %><p>暂无文章。</p><% } %>
<% for (const [month, posts] of groups) { %><h2><%= month %></h2><ul><% for (const post of posts) { %><li><a href="<%= archive.permalinkFor(post) %>"><%= post.title %></a></li><% } %></ul><% } %>
</section></div><% }) %>
1 change: 1 addition & 0 deletions packages/hugo-theme-dream/attachment.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<% layout('./layout');const site=await firekylin.widget('Widget_Options');const archive=await firekylin.widget('Widget_Archive',{type:'attachment'});const found=archive.next();const cover=String(archive.row?.options?.featuredImage||''); %><% block('title',()=>{ %><%= found?archive.title()+' · ':'' %><%= site.title() %><% }) %><% block('content',()=>{ %><div class="native-wrap"><article class="post post-detail"><% if(found){ %><header class="article-meta"><h1 class="post-title"><%= archive.title() %></h1><p class="post-date"><%= archive.date('YYYY/MM/DD') %> · <%= archive.author('display_name') %></p></header><div class="prose post-content" id="post-content"><% const media=archive.row?.options?.attachment||{};const mediaUrl=String(media.url||'');const mediaValid=/^(https?:\/\/|\/(?!\/))/.test(mediaUrl);const mime=String(media.mime||''); %><% if(mediaValid){ %><section class="attachment-media"><% if(mime.startsWith('image/')){ %><a href="<%= mediaUrl %>"><img src="<%= mediaUrl %>" alt="<%= archive.title() %>"></a><% }else if(mime.startsWith('audio/')){ %><audio controls preload="metadata" src="<%= mediaUrl %>"></audio><% }else if(mime.startsWith('video/')){ %><video controls preload="metadata" src="<%= mediaUrl %>"></video><% } %><p><a href="<%= mediaUrl %>" download><%= media.name||archive.title() %></a></p></section><% } %><%~ archive.content() %></div><footer class="post-footer"><div class="tags"><% (archive.row.cate||[]).forEach(c=>{ %><a href="/cate/<%= c.pathname %>"><%= c.name %></a> <% }); %><% (archive.row.tag||[]).forEach(t=>{ %><a href="/tag/<%= t.pathname %>">#<%= t.name %></a> <% }); %></div><div class="post-actions"><a href="/archives">所有文章</a><a href="#main">↑ 返回顶部</a></div></footer><%~ firekylin.comment({title:'评论'}) %><% }else{ %><p>暂无内容。</p><% } %></article></div><% }) %>
6 changes: 6 additions & 0 deletions packages/hugo-theme-dream/cate.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<% layout('./layout'); const site = await firekylin.widget('Widget_Options'); const metas = await firekylin.widget('Widget_Metas_Category_List'); %>
<% block('title', () => { %>分类 - <%= site.title() %><% }) %>
<% block('content', () => { %><section class="directory"><h1 class="archive-header">分类</h1><ul>
<% while (metas.next()) { %><li><a href="<%= metas.permalink %>"><%= metas.row.name %></a> (<%= metas.row.count %>)
<% if (metas.row.children && metas.row.children.length) { %><ul><% for (const child of metas.row.children) { %><li><a href="/cate/<%= child.pathname %>"><%= child.name %></a> (<%= child.count %>)</li><% } %></ul><% } %>
</li><% } %></ul><% if (!metas.have()) { %><p>暂无分类。</p><% } %></section></div><% }) %>
1 change: 1 addition & 0 deletions packages/hugo-theme-dream/error/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!doctype html><meta charset="utf-8"><title>404</title><h1>404</h1><a href="/">Home</a>
1 change: 1 addition & 0 deletions packages/hugo-theme-dream/error/500.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!doctype html><meta charset="utf-8"><title>500</title><h1>500</h1><a href="/">Home</a>
1 change: 1 addition & 0 deletions packages/hugo-theme-dream/inc/list.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<% const archive=firekylin.archive;let count=0; %><div class="dream-grid-native"><% while(archive.next()){count++;const cover=String(archive.row.options?.featuredImage||''); %><article class="entry dream-column-native"><div class="dream-card"><% if(/^(https?:\/\/|\/(?!\/))/i.test(cover)){ %><figure><a href="<%= archive.permalink %>" aria-label="<%= archive.title() %>"><img src="<%= cover %>" alt="" loading="lazy"></a></figure><% } %><div class="card-body"><h2 class="card-title"><a href="<%= archive.permalink %>"><%= archive.title() %></a></h2><p class="card-date"><%= archive.date('YYYY-MM-DD') %></p><p><%= archive.excerpt(300) %></p><p class="card-author"><%= archive.author('display_name') %></p></div></div></article><% } %></div><% if(!count){ %><p>暂无文章。</p><% } %>
1 change: 1 addition & 0 deletions packages/hugo-theme-dream/inc/pagination.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<% const archive=firekylin.archive; const p=archive.pagination; %><% if(p && p.totalPages>1){ %><nav class="pagination" aria-label="分页"><% if(p.currentPage>1){ %><a rel="prev" href="<%= archive.pageUrl(p.currentPage-1) %>">上一页</a><% } %><span><%= p.currentPage %> / <%= p.totalPages %></span><% if(p.currentPage<p.totalPages){ %><a rel="next" href="<%= archive.pageUrl(p.currentPage+1) %>">下一页</a><% } %></nav><% } %>
1 change: 1 addition & 0 deletions packages/hugo-theme-dream/index.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<% layout('./layout');const site=await firekylin.widget('Widget_Options');const archive=await firekylin.widget('Widget_Archive',{type:'index'}); %><% block('title',()=>{ %><%= archive.archiveTitle||site.title() %><% }) %><% block('content',()=>{ %><% if(archive.archiveTitle){ %><h1 class="listing-heading"><%= archive.archiveTitle %></h1><% } %><%~ include('./inc/list',{archive}) %><%~ include('./inc/pagination',{archive}) %><% }) %>
1 change: 1 addition & 0 deletions packages/hugo-theme-dream/layout.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<% const site=await firekylin.widget('Widget_Options');const cfg=site.themeConfig; %><!doctype html><html lang="zh-CN" data-theme="emerald"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="light dark"><title><%~ block('title') %></title><meta name="description" content="<%= site.description() %>"><link rel="stylesheet" href="<%= site.themeUrl('res/css/theme.css') %>"><script src="<%= site.themeUrl('res/js/init.js') %>"></script><script defer src="<%= site.themeUrl('res/js/theme.js') %>"></script></head><body><a class="skip" href="#main">跳转到正文</a><header class="dream-header"><div class="dream-container header-inner"><div class="brand"><% const avatar=String(cfg.avatarURL||'');if(/^(https?:\/\/|\/(?!\/))/i.test(avatar)){ %><img class="brand-avatar" src="<%= avatar %>" alt=""><% } %><div><a class="site-name" href="/"><%= site.title() %></a><p class="motto"><%= site.description() %></p></div></div><nav class="native-nav nav-items" aria-label="主导航"><a href="/archives">归档</a><a href="/categories">分类</a><a href="/tags">标签</a><a href="/search.html">搜索</a><% site.navigation.forEach(n=>{ %><a href="<%= n.url %>"><%= n.label %></a><% }); %></nav><div class="native-controls"><button id="dream-flip" aria-controls="dream-about" aria-expanded="false" hidden>关于 / 翻面</button><button id="dark-mode-toggle" hidden>浅色 / 深色</button></div></div></header><main id="main" class="dream-container native-stage"><div id="dream-front" class="native-face" tabindex="-1"><%~ block('content') %></div><section id="dream-about" class="native-face" tabindex="-1" hidden inert aria-label="关于本站"><div class="about-grid"><article class="dream-card"><div class="card-body"><h2 class="card-title"><%= cfg.aboutTitle||site.title() %></h2><p class="about-text"><%= cfg.aboutText||site.description() %></p></div></article><article class="dream-card"><div class="card-body"><h2 class="card-title">站点导航</h2><div class="about-links"><a href="/archives">归档</a><a href="/categories">分类</a><a href="/tags">标签</a><% site.navigation.forEach(n=>{ %><a href="<%= n.url %>"><%= n.label %></a><% }); %></div></div></article></div></section></main><footer class="native-footer"><p><%= cfg.footerText||site.description() %></p><p class="footer-author"><%= cfg.authorName||site.title() %></p><p>Firekylin · <a href="https://github.com/g1eny0ung/hugo-theme-dream">Dream</a> · Yue Yang</p></footer></body></html>
53 changes: 53 additions & 0 deletions packages/hugo-theme-dream/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "hugo-theme-dream",
"title": "Dream",
"version": "0.1.0",
"private": true,
"description": "Dream’s original DaisyUI cards, emerald/forest palettes, masonry-style columns and flippable About panel.",
"license": "MIT",
"author": "Yue Yang; Firekylin contributors",
"homepage": "https://github.com/g1eny0ung/hugo-theme-dream",
"repository": {
"type": "git",
"url": "https://github.com/firekylin/themes.git",
"directory": "packages/hugo-theme-dream"
},
"firekylin": {
"type": "theme",
"id": "hugo-theme-dream"
},
"engines": {
"firekylin": ">=2.17.1"
},
"configElements": [
{
"type": "text",
"name": "authorName",
"label": "页脚作者",
"help": "留空使用站点标题。"
},
{
"type": "text",
"name": "footerText",
"label": "页脚介绍",
"help": "留空使用站点描述。"
},
{
"type": "text",
"name": "avatarURL",
"label": "头像 URL",
"help": "可选,使用站内路径或 HTTP(S) URL。"
},
{
"type": "text",
"name": "aboutTitle",
"label": "关于标题"
},
{
"type": "textarea",
"name": "aboutText",
"label": "关于正文",
"help": "纯文本,保留换行。"
}
]
}
1 change: 1 addition & 0 deletions packages/hugo-theme-dream/page.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<% layout('./layout');const site=await firekylin.widget('Widget_Options');const archive=await firekylin.widget('Widget_Archive',{type:'page'});const found=archive.next();const cover=String(archive.row?.options?.featuredImage||''); %><% block('title',()=>{ %><%= found?archive.title()+' · ':'' %><%= site.title() %><% }) %><% block('content',()=>{ %><div class="native-wrap"><article class="post post-detail"><% if(found){ %><header class="article-meta"><h1 class="post-title"><%= archive.title() %></h1></header><div class="prose post-content" id="post-content"><%~ archive.content() %></div><footer class="post-footer"><div class="tags"><% (archive.row.cate||[]).forEach(c=>{ %><a href="/cate/<%= c.pathname %>"><%= c.name %></a> <% }); %><% (archive.row.tag||[]).forEach(t=>{ %><a href="/tag/<%= t.pathname %>">#<%= t.name %></a> <% }); %></div><div class="post-actions"><a href="/archives">所有文章</a><a href="#main">↑ 返回顶部</a></div></footer><%~ firekylin.comment({title:'评论'}) %><% }else{ %><p>暂无内容。</p><% } %></article></div><% }) %>
1 change: 1 addition & 0 deletions packages/hugo-theme-dream/post.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<% layout('./layout');const site=await firekylin.widget('Widget_Options');const archive=await firekylin.widget('Widget_Archive',{type:'post'});const found=archive.next();const cover=String(archive.row?.options?.featuredImage||''); %><% block('title',()=>{ %><%= found?archive.title()+' · ':'' %><%= site.title() %><% }) %><% block('content',()=>{ %><div class="dream-single"><article class="post-detail"><% if(found){ %><header class="article-meta"><h1 class="post-title"><%= archive.title() %></h1><p class="post-date"><%= archive.date('YYYY/MM/DD') %> · <%= archive.author('display_name') %></p></header><div class="prose post-content" id="post-content"><%~ archive.content() %></div><footer class="post-footer"><div class="tags"><% (archive.row.cate||[]).forEach(c=>{ %><a href="/cate/<%= c.pathname %>"><%= c.name %></a> <% }); %><% (archive.row.tag||[]).forEach(t=>{ %><a href="/tag/<%= t.pathname %>">#<%= t.name %></a> <% }); %></div><div class="post-actions"><a href="/archives">所有文章</a><a href="#main">↑ 返回顶部</a></div></footer><%~ firekylin.comment({title:'评论'}) %><% }else{ %><p>暂无内容。</p><% } %></article><details id="TableOfContents" class="article-toc" hidden open><summary>目录</summary></details></div><% }) %>
172 changes: 172 additions & 0 deletions packages/hugo-theme-dream/res/css/source.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
@import 'tailwindcss' source(none);

@source "input.css";
@source "../layouts";

@plugin '@tailwindcss/typography';
@plugin "daisyui" {
themes: emerald --default, forest --prefersdark;
}

@custom-variant dark (&:where(.dark, .dark *));

@config '../tailwind.config.js';

html {
scroll-behavior: smooth;
}

#disqus_thread iframe[src*='ads-iframe'] {
display: none;
}

#dream-global-bg {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-position: center;
background-size: cover;
z-index: -1;
}

.flip-container {
width: 100vw;
/* The height of navbar is 112px (80px + 2rem). */
height: calc(100vh - 80px - 2rem);
max-width: 100%;
perspective: 1000px;

&.flip-it {
.flipper {
transform: rotateY(180deg);
}

.front {
overflow: hidden;
}

.back {
transform: translateX(0) rotateY(180deg);
}
}

.flipper {
position: relative;
width: 100%;
height: 100%;
transform-style: preserve-3d;
transition: transform 0.75s;
}

.front,
.back {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
backface-visibility: hidden;
transition: transform 0.75s;
}

.front {
transform: rotateY(0deg);
}

.back {
/* Hack: set translateX to -25% to make the back page not overlap the TOC. */
transform: translateX(-25%) rotateY(180deg);
}
}

#TableOfContents {
@apply sticky top-24;

/* top-24 + 1rem */
max-height: calc(100vh - 7rem);
overflow-y: auto;

/* Prevent long headings (e.g. CJK titles) from making the TOC wider than
its grid column and overlapping the article (#411 / #412) */
max-width: 100%;
}

@supports (-moz-appearance: none) {
#TableOfContents {
position: initial;
}
}

#TableOfContents ul {
@apply menu menu-sm;
}

.mermaid-panzoom {
@apply relative border border-transparent rounded-box;
}

.mermaid-panzoom:hover,
.mermaid-panzoom:focus-within {
@apply border-base-300;
}

.mermaid-panzoom > .mermaid {
@apply overflow-hidden;
}

.mermaid-panzoom-reset {
@apply absolute z-1 right-2 bottom-2 opacity-0 pointer-events-none transition-opacity duration-150 ease-in-out;
}

.mermaid-panzoom:hover .mermaid-panzoom-reset,
.mermaid-panzoom:focus-within .mermaid-panzoom-reset {
@apply opacity-100 pointer-events-auto;
}

.dream-alert {
> .heading {
@apply flex items-center gap-1 text-sm font-medium;
}

&.note {
@apply border-s-blue-600 dark:border-s-blue-500;

.heading {
@apply text-blue-600 dark:text-blue-500;
}
}

&.tip {
@apply border-s-green-600 dark:border-s-green-500;

.heading {
@apply text-green-600 dark:text-green-500;
}
}

&.important {
@apply border-s-purple-600 dark:border-s-purple-500;

.heading {
@apply text-purple-600 dark:text-purple-500;
}
}

&.warning {
@apply border-s-yellow-600 dark:border-s-yellow-500;

.heading {
@apply text-yellow-600 dark:text-yellow-500;
}
}

&.caution {
@apply border-s-red-600 dark:border-s-red-500;

.heading {
@apply text-red-600 dark:text-red-500;
}
}
}
Loading
Loading