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/zola-zolarwind.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Zolarwind integration
on:
pull_request:
paths: ['packages/zola-zolarwind/**', 'verification/zola-zolarwind/**', 'test/zola-zolarwind.test.js', '.github/workflows/zola-zolarwind.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/zola-zolarwind/live.cjs
- uses: actions/upload-artifact@v4
if: always()
with:
name: zola-zolarwind-live-evidence
path: verification/zola-zolarwind/
21 changes: 21 additions & 0 deletions packages/zola-zolarwind/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright 2023 Thomas Weitzel

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.
9 changes: 9 additions & 0 deletions packages/zola-zolarwind/NOTICE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Source and attribution

Adapted from [Zolarwind](https://github.com/thomasweitzel/zolarwind/tree/b793bc2bff6d31da193fcc8f337729db7e399a76) by Thomas Weitzel, pinned at `b793bc2bff6d31da193fcc8f337729db7e399a76`. 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 Tailwind 4.3.3 generated CSS and its source CSS are retained. Full Tailwind MIT notice is included. Upstream font stack uses system fonts. No default banner, sample photography, icons, math/diagram engines, analytics or comment-service code is bundled.

Four unused embedded callout icon data URLs are removed from source and generated CSS; native prose does not use those Zola shortcode icons.
7 changes: 7 additions & 0 deletions packages/zola-zolarwind/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Zolarwind for Firekylin

Thomas Weitzel’s MIT Zolarwind blog layout is adapted using its original Tailwind 4.3.3 generated CSS, warm-neutral light/dark palette, blue links, sticky desktop header, 3/2/1-column image cards, 192px article banners and centered footer. Use a post’s featuredImage for covers; missing or unsafe image URLs omit the image instead of bundling demo photography. Current public-result IDs bound tag lookups. Images and title links remain separate from tag controls.

Native posts/pages, archive/taxonomy/search, pagination, protected content, comments and attachments are supported. A persistent light/dark toggle and safe DOM-generated collapsible article TOC are included. Navigation wraps and stays visible without JavaScript; no-JS uses light mode. Footer text/author are configurable.

Native blog routes replace Zola sections. Series navigation, localized resources, reading-time counts, syntax engines, math/diagram shortcodes, Artalk integration and feeds are not implemented. No remote fonts, default banner, icons or service scripts are bundled. No live upstream pixel comparison or remote comment submission is claimed.
3 changes: 3 additions & 0 deletions packages/zola-zolarwind/VISUAL-COMPARISON.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Visual adaptation

Pinned list/list-element/blog-post/header templates and generated Tailwind CSS are the reference: 3/2/1-column cards with 192px covers, warm-neutral palette, blue links, metadata under summaries, sticky desktop header and full-width article banner. Native links replace SVG icons; mobile navigation stays expanded and no default photo is bundled. Installed screenshots and reproducible tests are in verification/zola-zolarwind; no live upstream pixel comparison was performed.
6 changes: 6 additions & 0 deletions packages/zola-zolarwind/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><% }) %>
1 change: 1 addition & 0 deletions packages/zola-zolarwind/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',()=>{ %><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><% }) %>
6 changes: 6 additions & 0 deletions packages/zola-zolarwind/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><% }) %>
1 change: 1 addition & 0 deletions packages/zola-zolarwind/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/zola-zolarwind/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/zola-zolarwind/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="post-grid"><% while(archive.next()){count++;const meta=firekylin.metaByPost?.get(archive.row.id)||archive.row;const image=String(archive.row.options?.featuredImage||''); %><article class="entry"><% if(/^(https?:\/\/|\/(?!\/))/i.test(image)){ %><a href="<%= archive.permalink %>" aria-label="<%= archive.title() %>"><img class="entry-cover" src="<%= image %>" alt="" loading="lazy"></a><% } %><div class="entry-body"><div class="entry-tags"><% (meta.tag||[]).forEach(t=>{ %><a href="/tag/<%= encodeURIComponent(t.pathname||t.name) %>">#<%= t.name %></a><% }); %></div><h2><a href="<%= archive.permalink %>"><%= archive.title() %></a></h2><p class="entry-excerpt"><%= archive.excerpt(260) %></p><p class="entry-date"><time><%= archive.date('YYYY-MM-DD') %></time></p></div></article><% } %></div><% if(!count){ %><p>暂无文章。</p><% } %>
1 change: 1 addition & 0 deletions packages/zola-zolarwind/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/zola-zolarwind/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'});const metaByPost=new Map();const ids=archive.stack.map(p=>p.id).filter(Boolean);if(ids.length&&typeof archive.model==='function'){const related=await archive.model('post').where({id:['IN',ids]}).field('id').setRelation('cate,tag').select();related.forEach(p=>metaByPost.set(p.id,p));} %><% block('title',()=>{ %><%= archive.archiveTitle||site.title() %><% }) %><% block('content',()=>{ %><h1 class="listing-heading"><%= archive.archiveTitle||site.title() %></h1><%~ include('./inc/list',{archive,metaByPost}) %><%~ include('./inc/pagination',{archive}) %><% }) %>
1 change: 1 addition & 0 deletions packages/zola-zolarwind/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"><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="native-header"><div class="header-inner"><a class="site-name" href="/"><%= site.title() %></a><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><button id="dark-mode-toggle" class="theme-toggle" hidden>浅色 / 深色</button></div></header><main id="main" class="content-inner"><%~ block('content') %></main><footer class="site-footer"><p><%= cfg.footerText||site.description() %></p><p class="footer-author"><%= cfg.authorName||site.title() %></p><p>Firekylin · <a href="https://github.com/thomasweitzel/zolarwind">Zolarwind</a> · Thomas Weitzel</p></footer></body></html>
36 changes: 36 additions & 0 deletions packages/zola-zolarwind/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "zola-zolarwind",
"title": "Zolarwind",
"version": "0.1.0",
"private": true,
"description": "Zolarwind’s original Tailwind 4 palettes, responsive image-card grid, sticky navigation and full-width article banners.",
"license": "MIT",
"author": "Thomas Weitzel; Firekylin contributors",
"homepage": "https://github.com/thomasweitzel/zolarwind",
"repository": {
"type": "git",
"url": "https://github.com/firekylin/themes.git",
"directory": "packages/zola-zolarwind"
},
"firekylin": {
"type": "theme",
"id": "zola-zolarwind"
},
"engines": {
"firekylin": ">=2.17.1"
},
"configElements": [
{
"type": "text",
"name": "authorName",
"label": "页脚作者",
"help": "留空使用站点标题。"
},
{
"type": "text",
"name": "footerText",
"label": "页脚介绍",
"help": "留空使用站点描述。"
}
]
}
1 change: 1 addition & 0 deletions packages/zola-zolarwind/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',()=>{ %><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><% }) %>
1 change: 1 addition & 0 deletions packages/zola-zolarwind/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',()=>{ %><article class="post post-detail"><% if(found){ %><% if(/^(https?:\/\/|\/(?!\/))/i.test(cover)){ %><img class="article-banner" src="<%= cover %>" alt=""><% } %><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><details id="TableOfContents" class="article-toc" hidden><summary>目录</summary></details><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><% }) %>
Loading
Loading