Compare commits
266 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35263fd99d | ||
|
|
f7ea52be7f | ||
|
|
8beb6d388a | ||
|
|
9f02e2620b | ||
|
|
6722f61728 | ||
|
|
2e78ea21dc | ||
|
|
f2916398ce | ||
|
|
1ff10bc070 | ||
|
|
82cd2aee91 | ||
|
|
15d5ea1e7d | ||
|
|
f733212c4b | ||
|
|
841223f4f3 | ||
|
|
72df0007a9 | ||
|
|
1efcf279d5 | ||
|
|
ee38206dbb | ||
|
|
b3ad18e700 | ||
|
|
94fbc885b4 | ||
|
|
caeccdb4ce | ||
|
|
5016b545de | ||
|
|
fb5cedb23e | ||
|
|
a2daa78448 | ||
|
|
a2e40e84d4 | ||
|
|
0e2ba5db7a | ||
|
|
148a6aaf78 | ||
|
|
30e09c6aab | ||
|
|
a23cfe26be | ||
|
|
1bb985b92e | ||
|
|
39ae055491 | ||
|
|
ffd230c109 | ||
|
|
bd7ec11c32 | ||
|
|
0841323a0d | ||
|
|
78ce8e048b | ||
|
|
6affa0c5e1 | ||
|
|
fdfbf2a42e | ||
|
|
600898f139 | ||
|
|
4d9fe5ac8f | ||
|
|
e93fd1b946 | ||
|
|
ad55a2b0c0 | ||
|
|
00574fa648 | ||
|
|
4341f82b5d | ||
|
|
f24c0743e9 | ||
|
|
49a4a46eb0 | ||
|
|
9eaeb4ad9f | ||
|
|
8e6813fd7c | ||
|
|
4b25932edc | ||
|
|
7c3232fcb1 | ||
|
|
245d9af1ca | ||
|
|
1ed8b84565 | ||
|
|
1636f68819 | ||
|
|
f8fcd03fdb | ||
|
|
b3c7e297c4 | ||
|
|
61d2309b2a | ||
|
|
a7e4e685c0 | ||
|
|
88e434cb26 | ||
|
|
be545404f2 | ||
|
|
e0c5923dcb | ||
|
|
0cc2674a4d | ||
|
|
52a9d3b67c | ||
|
|
8131149b55 | ||
|
|
039e785ef5 | ||
|
|
03ee911f40 | ||
|
|
d7b5949340 | ||
|
|
aaf6d1670f | ||
|
|
98b8114b32 | ||
|
|
d2aff31483 | ||
|
|
4959661d0b | ||
|
|
d7f01816ba | ||
|
|
91dacffe12 | ||
|
|
bd75753a73 | ||
|
|
4132a239a7 | ||
|
|
79d8dfb086 | ||
|
|
3f384abfbc | ||
|
|
95e6fb3e25 | ||
|
|
db0ccd2b5d | ||
|
|
ae95ed6a82 | ||
|
|
a32a663320 | ||
|
|
0a1d0a8b94 | ||
|
|
eeba5e0259 | ||
|
|
2399aa660f | ||
|
|
abaa9ea06e | ||
|
|
ee4ab509a2 | ||
|
|
d741d46eec | ||
|
|
887aaf4831 | ||
|
|
fbd85539e5 | ||
|
|
2866af55b4 | ||
|
|
668435a637 | ||
|
|
d6f9b0804a | ||
|
|
9def4814f9 | ||
|
|
ddad57bc46 | ||
|
|
11ce33e617 | ||
|
|
38c90b9ab6 | ||
|
|
a2183cf4be | ||
|
|
a548882b31 | ||
|
|
a15d7932d9 | ||
|
|
fdce24ca68 | ||
|
|
f252d0f16e | ||
|
|
f184fd3b7a | ||
|
|
e5af352964 | ||
|
|
6e8e04c634 | ||
|
|
5cebb6fd72 | ||
|
|
aa86583194 | ||
|
|
b9a47920db | ||
|
|
725b96aa7e | ||
|
|
c90f747ca6 | ||
|
|
9234c74350 | ||
|
|
eb406080f0 | ||
|
|
150e6f130f | ||
|
|
cfcd46f2a7 | ||
|
|
c65115c298 | ||
|
|
4b5d4d8674 | ||
|
|
698d8c1c4c | ||
|
|
a96474eb5f | ||
|
|
222729c853 | ||
|
|
3fe6856fd6 | ||
|
|
8ec5cd7660 | ||
|
|
2520e643cd | ||
|
|
0e26eb37cc | ||
|
|
e940a34d36 | ||
|
|
ddb92e7ac1 | ||
|
|
ecb2d25dee | ||
|
|
fd81262713 | ||
|
|
e37edc2af5 | ||
|
|
adb49e6f3f | ||
|
|
bf24312ca6 | ||
|
|
6f034169b1 | ||
|
|
6b97f811da | ||
|
|
f74498fc30 | ||
|
|
f62030696e | ||
|
|
fad9e5f5e3 | ||
|
|
2e6b2fbbf6 | ||
|
|
3315462448 | ||
|
|
14a57bd25e | ||
|
|
4b22bc41ac | ||
|
|
994097e150 | ||
|
|
caac9c9dfe | ||
|
|
4330e64d7d | ||
|
|
20db67dcd9 | ||
|
|
7b66777d00 | ||
|
|
807cfb7e10 | ||
|
|
8336d89834 | ||
|
|
8bd18d07d4 | ||
|
|
c48f2fa38f | ||
|
|
60ee77d1ff | ||
|
|
f5701d7be3 | ||
|
|
e2d5b0f05d | ||
|
|
7a13c48a9d | ||
|
|
519af2d118 | ||
|
|
fb631270bf | ||
|
|
79cdce1ff8 | ||
|
|
85fa76d845 | ||
|
|
619e88eef0 | ||
|
|
1a34d7db5b | ||
|
|
ce6e2f5275 | ||
|
|
74d2dd4e71 | ||
|
|
0c09abc327 | ||
|
|
3a9a014adc | ||
|
|
729d83b999 | ||
|
|
0896ba2e8a | ||
|
|
7825683636 | ||
|
|
47a33a939f | ||
|
|
0d65cfba26 | ||
|
|
d411c8ff36 | ||
|
|
02e8d193e7 | ||
|
|
24e0bc876d | ||
|
|
19e900fa7f | ||
|
|
051d230678 | ||
|
|
5cde4f52e1 | ||
|
|
a69ac9f9c1 | ||
|
|
3d893d110e | ||
|
|
2ed0a6cafc | ||
|
|
14e31c8b3e | ||
|
|
10fef99327 | ||
|
|
f82996fa5a | ||
|
|
d881e4fdb3 | ||
|
|
b911db95f8 | ||
|
|
6ea211db05 | ||
|
|
5e2e881d04 | ||
|
|
a797b9a396 | ||
|
|
f1a94cada3 | ||
|
|
3587c22378 | ||
|
|
13a08e4dd8 | ||
|
|
7ed365fc72 | ||
|
|
58e516bce3 | ||
|
|
e63a781858 | ||
|
|
2ccff657e1 | ||
|
|
dfff3bc199 | ||
|
|
22ac983335 | ||
|
|
1321046bb1 | ||
|
|
fd2a49ebbe | ||
|
|
2a93089d72 | ||
|
|
d2f84b539e | ||
|
|
54925a8868 | ||
|
|
bccc2e6a8f | ||
|
|
eccc091c09 | ||
|
|
0c4ea07c56 | ||
|
|
00c5ea0fbf | ||
|
|
8722e4f800 | ||
|
|
9af7e00b2b | ||
|
|
f3e40d3761 | ||
|
|
4d177fb34b | ||
|
|
d2a53f6cd4 | ||
|
|
3e95b51cc7 | ||
|
|
bf28ad41c5 | ||
|
|
55211a6eda | ||
|
|
712cea94f1 | ||
|
|
f3ec0f9c28 | ||
|
|
2f245e7987 | ||
|
|
2cbd584a44 | ||
|
|
6e15cc8437 | ||
|
|
ea1ae75251 | ||
|
|
356a3b5b57 | ||
|
|
3234fb2a63 | ||
|
|
ab1a59677b | ||
|
|
814caef4fc | ||
|
|
033dcf0e08 | ||
|
|
305862dfeb | ||
|
|
b6539d148c | ||
|
|
f1584f9e9e | ||
|
|
ebc61593a0 | ||
|
|
c9ad72dbe0 | ||
|
|
595c1c8879 | ||
|
|
656c97bc03 | ||
|
|
547d4137de | ||
|
|
826850ccd8 | ||
|
|
c786987543 | ||
|
|
2ee940313f | ||
|
|
2754066085 | ||
|
|
dbbc15e095 | ||
|
|
d970382ebb | ||
|
|
6280f10b12 | ||
|
|
3c6d1d3932 | ||
|
|
199121d3f1 | ||
|
|
93e0487703 | ||
|
|
95fdccf3b6 | ||
|
|
c7571e20d0 | ||
|
|
f50524bdb5 | ||
|
|
5333cd189c | ||
|
|
9b5a8a8d62 | ||
|
|
506d918264 | ||
|
|
bce294921c | ||
|
|
224c59fc20 | ||
|
|
c2cc3beb43 | ||
|
|
e67297b6fd | ||
|
|
6e7ca11645 | ||
|
|
772f4960f5 | ||
|
|
764f495ca6 | ||
|
|
a38123450a | ||
|
|
b4ef9defdc | ||
|
|
ccffc5a5d2 | ||
|
|
0632986060 | ||
|
|
9306186f85 | ||
|
|
919a924141 | ||
|
|
dd3455f423 | ||
|
|
098e765011 | ||
|
|
fe917e7316 | ||
|
|
9df8c8560d | ||
|
|
417a02d4cc | ||
|
|
c5b846a16e | ||
|
|
131c0aa8e8 | ||
|
|
1372d195fc | ||
|
|
2915079ab3 | ||
|
|
a447a824d8 | ||
|
|
ac0478cf06 | ||
|
|
c4561b6a74 | ||
|
|
369543e155 | ||
|
|
7f1d03ce59 |
43
.github/workflows/static.yml
vendored
Normal file
43
.github/workflows/static.yml
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
# Simple workflow for deploying static content to GitHub Pages
|
||||
name: Deploy static content to Pages
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches: ["main"]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# Single deploy job since we're just deploying
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
# Upload entire repository
|
||||
path: '.'
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
116
CONIFG.md
116
CONIFG.md
@ -1,116 +0,0 @@
|
||||
# 配置文件说明
|
||||
|
||||
### `config.json` 文件
|
||||
|
||||
里面的`key`值请不要修改,如没有其中的`value`值请填写`""`
|
||||
|
||||
```javascript
|
||||
{
|
||||
"title":"Meekdai",
|
||||
"displayTitle":"eekdai",
|
||||
"subTitle":"童话是一种生活态度,仅此而已。",
|
||||
"homeUrl":"http://blog.meekdai.com",
|
||||
"avatarUrl":"http://meekdai.com/avatar.jpg",
|
||||
"faviconUrl":"http://meekdai.com/favicon.ico",
|
||||
"email":"meekdai@163.com",
|
||||
"startSite":"02/16/2015",
|
||||
"filingNum":"浙ICP备20023628号",
|
||||
"onePageListNum":15,
|
||||
"singlePage":["link","about"],
|
||||
"commentLabelColor":"#006b75",
|
||||
"yearColorList":["#bc4c00", "#0969da", "#1f883d", "#A333D0"],
|
||||
"i18n":"CN",
|
||||
"GMEEK_VERSION":"v2.3"
|
||||
}
|
||||
```
|
||||
|
||||
### `.github/workflows/Gmeek.yml` 文件
|
||||
|
||||
此文件保存到指定目录即可,无需修改。
|
||||
|
||||
```yml
|
||||
name: build Gmeek
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Generate blog
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.event.repository.owner.id == github.event.sender.id
|
||||
permissions: write-all
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Pages
|
||||
id: pages
|
||||
uses: actions/configure-pages@v3
|
||||
|
||||
- name: Get config.json
|
||||
run: |
|
||||
echo "====== check config.josn file ======"
|
||||
cat config.json
|
||||
echo "====== check config.josn end ======"
|
||||
sudo apt-get install jq
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
- name: Clone source code
|
||||
run: |
|
||||
git clone -b $(jq -r ".GMEEK_VERSION" config.json) https://github.com/Meekdai/Gmeek.git /opt/Gmeek
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install -r /opt/Gmeek/requirements.txt
|
||||
|
||||
- name: Generate new html
|
||||
run: |
|
||||
cp -r ./* /opt/Gmeek/
|
||||
cd /opt/Gmeek/
|
||||
python Gmeek.py ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} --issue_number '${{ github.event.issue.number }}'
|
||||
cp -a /opt/Gmeek/docs ${{ github.workspace }}
|
||||
cp -a /opt/Gmeek/backup ${{ github.workspace }}
|
||||
cp /opt/Gmeek/blogBase.json ${{ github.workspace }}
|
||||
|
||||
- name: update html
|
||||
run: |
|
||||
git config --local user.email "$(jq -r ".email" config.json)"
|
||||
git config --local user.name "${{ github.repository_owner }}"
|
||||
git add .
|
||||
git commit -a -m '🎉auto update by Gmeek action' || echo "nothing to commit"
|
||||
git push || echo "nothing to push"
|
||||
sleep 3
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v2
|
||||
with:
|
||||
path: 'docs/.'
|
||||
|
||||
deploy:
|
||||
name: Deploy blog
|
||||
runs-on: ubuntu-20.04
|
||||
needs: build
|
||||
permissions:
|
||||
contents: write
|
||||
pages: write
|
||||
id-token: write
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v2
|
||||
|
||||
```
|
||||
334
Gmeek.py
334
Gmeek.py
@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import time
|
||||
import datetime
|
||||
@ -7,14 +8,18 @@ import shutil
|
||||
import urllib
|
||||
import requests
|
||||
import argparse
|
||||
import html
|
||||
from github import Github
|
||||
from xpinyin import Pinyin
|
||||
from feedgen.feed import FeedGenerator
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from transliterate import translit
|
||||
from collections import OrderedDict
|
||||
######################################################################################
|
||||
i18n={"Search":"Search","switchTheme":"switch theme","link":"link","home":"home","comments":"comments","run":"run ","days":" days","Previous":"Previous","Next":"Next"}
|
||||
i18nCN={"Search":"搜索","switchTheme":"切换主题","link":"友情链接","home":"首页","comments":"评论","run":"网站运行","days":"天","Previous":"上一页","Next":"下一页"}
|
||||
IconList={
|
||||
i18n={"Search":"Search","switchTheme":"switch theme","home":"home","comments":"comments","run":"run ","days":" days","Previous":"Previous","Next":"Next"}
|
||||
i18nCN={"Search":"搜索","switchTheme":"切换主题","home":"首页","comments":"评论","run":"网站运行","days":"天","Previous":"上一页","Next":"下一页"}
|
||||
i18nRU={"Search":"Поиск","switchTheme": "Сменить тему","home":"Главная","comments":"Комментарии","run":"работает ","days":" дней","Previous":"Предыдущая","Next":"Следующая"}
|
||||
IconBase={
|
||||
"post":"M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0 1 14.25 14H1.75A1.75 1.75 0 0 1 0 12.25Zm1.75-.25a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25ZM3.5 6.25a.75.75 0 0 1 .75-.75h7a.75.75 0 0 1 0 1.5h-7a.75.75 0 0 1-.75-.75Zm.75 2.25h4a.75.75 0 0 1 0 1.5h-4a.75.75 0 0 1 0-1.5Z",
|
||||
"link":"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z",
|
||||
"about":"M10.561 8.073a6.005 6.005 0 0 1 3.432 5.142.75.75 0 1 1-1.498.07 4.5 4.5 0 0 0-8.99 0 .75.75 0 0 1-1.498-.07 6.004 6.004 0 0 1 3.431-5.142 3.999 3.999 0 1 1 5.123 0ZM10.5 5a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0Z",
|
||||
@ -24,7 +29,10 @@ IconList={
|
||||
"rss":"M2.002 2.725a.75.75 0 0 1 .797-.699C8.79 2.42 13.58 7.21 13.974 13.201a.75.75 0 0 1-1.497.098 10.502 10.502 0 0 0-9.776-9.776.747.747 0 0 1-.7-.798ZM2.84 7.05h-.002a7.002 7.002 0 0 1 6.113 6.111.75.75 0 0 1-1.49.178 5.503 5.503 0 0 0-4.8-4.8.75.75 0 0 1 .179-1.489ZM2 13a1 1 0 1 1 2 0 1 1 0 0 1-2 0Z",
|
||||
"upload":"M2.75 14A1.75 1.75 0 0 1 1 12.25v-2.5a.75.75 0 0 1 1.5 0v2.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 1.5 0v2.5A1.75 1.75 0 0 1 13.25 14Z M11.78 4.72a.749.749 0 1 1-1.06 1.06L8.75 3.811V9.5a.75.75 0 0 1-1.5 0V3.811L5.28 5.78a.749.749 0 1 1-1.06-1.06l3.25-3.25a.749.749 0 0 1 1.06 0l3.25 3.25Z",
|
||||
"github":"M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z",
|
||||
"home":"M6.906.664a1.749 1.749 0 0 1 2.187 0l5.25 4.2c.415.332.657.835.657 1.367v7.019A1.75 1.75 0 0 1 13.25 15h-3.5a.75.75 0 0 1-.75-.75V9H7v5.25a.75.75 0 0 1-.75.75h-3.5A1.75 1.75 0 0 1 1 13.25V6.23c0-.531.242-1.034.657-1.366l5.25-4.2Zm1.25 1.171a.25.25 0 0 0-.312 0l-5.25 4.2a.25.25 0 0 0-.094.196v7.019c0 .138.112.25.25.25H5.5V8.25a.75.75 0 0 1 .75-.75h3.5a.75.75 0 0 1 .75.75v5.25h2.75a.25.25 0 0 0 .25-.25V6.23a.25.25 0 0 0-.094-.195Z"
|
||||
"home":"M6.906.664a1.749 1.749 0 0 1 2.187 0l5.25 4.2c.415.332.657.835.657 1.367v7.019A1.75 1.75 0 0 1 13.25 15h-3.5a.75.75 0 0 1-.75-.75V9H7v5.25a.75.75 0 0 1-.75.75h-3.5A1.75 1.75 0 0 1 1 13.25V6.23c0-.531.242-1.034.657-1.366l5.25-4.2Zm1.25 1.171a.25.25 0 0 0-.312 0l-5.25 4.2a.25.25 0 0 0-.094.196v7.019c0 .138.112.25.25.25H5.5V8.25a.75.75 0 0 1 .75-.75h3.5a.75.75 0 0 1 .75.75v5.25h2.75a.25.25 0 0 0 .25-.25V6.23a.25.25 0 0 0-.094-.195Z",
|
||||
"sync":"M1.705 8.005a.75.75 0 0 1 .834.656 5.5 5.5 0 0 0 9.592 2.97l-1.204-1.204a.25.25 0 0 1 .177-.427h3.646a.25.25 0 0 1 .25.25v3.646a.25.25 0 0 1-.427.177l-1.38-1.38A7.002 7.002 0 0 1 1.05 8.84a.75.75 0 0 1 .656-.834ZM8 2.5a5.487 5.487 0 0 0-4.131 1.869l1.204 1.204A.25.25 0 0 1 4.896 6H1.25A.25.25 0 0 1 1 5.75V2.104a.25.25 0 0 1 .427-.177l1.38 1.38A7.002 7.002 0 0 1 14.95 7.16a.75.75 0 0 1-1.49.178A5.5 5.5 0 0 0 8 2.5Z",
|
||||
"copy":"M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z",
|
||||
"check":"M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"
|
||||
}
|
||||
######################################################################################
|
||||
class GMEEK():
|
||||
@ -32,6 +40,7 @@ class GMEEK():
|
||||
self.options=options
|
||||
|
||||
self.root_dir='docs/'
|
||||
self.static_dir='static/'
|
||||
self.post_folder='post/'
|
||||
self.backup_dir='backup/'
|
||||
self.post_dir=self.root_dir+self.post_folder
|
||||
@ -39,24 +48,22 @@ class GMEEK():
|
||||
user = Github(self.options.github_token)
|
||||
self.repo = self.get_repo(user, options.repo_name)
|
||||
self.feed = FeedGenerator()
|
||||
self.oldFeedString=''
|
||||
|
||||
self.labelColorDict=json.loads('{}')
|
||||
for label in self.repo.get_labels():
|
||||
self.labelColorDict[label.name]='#'+label.color
|
||||
|
||||
print(self.labelColorDict)
|
||||
self.defaultConfig()
|
||||
|
||||
config=json.loads(open('config.json', 'r', encoding='utf-8').read())
|
||||
self.blogBase=config.copy()
|
||||
self.blogBase["postListJson"]=json.loads('{}')
|
||||
self.blogBase["singeListJson"]=json.loads('{}')
|
||||
|
||||
if self.blogBase["i18n"]=="CN":
|
||||
self.i18n=i18nCN
|
||||
else:
|
||||
self.i18n=i18n
|
||||
|
||||
def cleanFile(self):
|
||||
workspace_path = os.environ.get('GITHUB_WORKSPACE')
|
||||
if os.path.exists(workspace_path+"/"+self.backup_dir):
|
||||
shutil.rmtree(workspace_path+"/"+self.backup_dir)
|
||||
|
||||
if os.path.exists(workspace_path+"/"+self.root_dir):
|
||||
shutil.rmtree(workspace_path+"/"+self.root_dir)
|
||||
|
||||
if os.path.exists(self.backup_dir):
|
||||
shutil.rmtree(self.backup_dir)
|
||||
|
||||
@ -67,46 +74,148 @@ class GMEEK():
|
||||
os.mkdir(self.root_dir)
|
||||
os.mkdir(self.post_dir)
|
||||
|
||||
if os.path.exists(self.static_dir):
|
||||
for item in os.listdir(self.static_dir):
|
||||
src = os.path.join(self.static_dir, item)
|
||||
dst = os.path.join(self.root_dir, item)
|
||||
if os.path.isfile(src):
|
||||
shutil.copy(src, dst)
|
||||
print(f"Copied {item} to docs")
|
||||
elif os.path.isdir(src):
|
||||
shutil.copytree(src, dst)
|
||||
print(f"Copied directory {item} to docs")
|
||||
else:
|
||||
print("static does not exist")
|
||||
|
||||
def defaultConfig(self):
|
||||
dconfig={"singlePage":[],"startSite":"","filingNum":"","onePageListNum":15,"commentLabelColor":"#006b75","yearColorList":["#bc4c00", "#0969da", "#1f883d", "#A333D0"],"i18n":"CN","themeMode":"manual","dayTheme":"light","nightTheme":"dark","urlMode":"pinyin","script":"","style":"","head":"","indexScript":"","indexStyle":"","bottomText":"","showPostSource":1,"iconList":{},"UTC":+8,"rssSplit":"sentence","exlink":{},"needComment":1,"allHead":""}
|
||||
config=json.loads(open('config.json', 'r', encoding='utf-8').read())
|
||||
self.blogBase={**dconfig,**config}.copy()
|
||||
self.blogBase["postListJson"]=json.loads('{}')
|
||||
self.blogBase["singeListJson"]=json.loads('{}')
|
||||
self.blogBase["labelColorDict"]=self.labelColorDict
|
||||
if "displayTitle" not in self.blogBase:
|
||||
self.blogBase["displayTitle"]=self.blogBase["title"]
|
||||
|
||||
if "faviconUrl" not in self.blogBase:
|
||||
self.blogBase["faviconUrl"]=self.blogBase["avatarUrl"]
|
||||
|
||||
if "ogImage" not in self.blogBase:
|
||||
self.blogBase["ogImage"]=self.blogBase["avatarUrl"]
|
||||
|
||||
if "primerCSS" not in self.blogBase:
|
||||
self.blogBase["primerCSS"]="<link href='https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/Primer/21.0.7/primer.css' rel='stylesheet' />"
|
||||
|
||||
if "homeUrl" not in self.blogBase:
|
||||
if str(self.repo.name).lower() == (str(self.repo.owner.login) + ".github.io").lower():
|
||||
self.blogBase["homeUrl"] = f"https://{self.repo.name}"
|
||||
else:
|
||||
self.blogBase["homeUrl"] = f"https://{self.repo.owner.login}.github.io/{self.repo.name}"
|
||||
print("GitHub Pages URL: ", self.blogBase["homeUrl"])
|
||||
|
||||
if self.blogBase["i18n"]=="CN":
|
||||
self.i18n=i18nCN
|
||||
elif self.blogBase["i18n"]=="RU":
|
||||
self.i18n=i18nRU
|
||||
else:
|
||||
self.i18n=i18n
|
||||
|
||||
self.TZ=datetime.timezone(datetime.timedelta(hours=self.blogBase["UTC"]))
|
||||
|
||||
def get_repo(self,user:Github, repo:str):
|
||||
return user.get_repo(repo)
|
||||
|
||||
def markdown2html(self,mdstr):
|
||||
payload = {"text": mdstr, "mode": "markdown"}
|
||||
ret=requests.post("https://api.github.com/markdown", json=payload,headers={"Authorzation":"token {}".format(self.options.github_token)})
|
||||
if ret.status_code==200:
|
||||
return ret.text
|
||||
else:
|
||||
raise Exception("markdown2html error status_code=%d"%(ret.status_code))
|
||||
def markdown2html(self, mdstr):
|
||||
payload = {"text": mdstr, "mode": "gfm"}
|
||||
headers = {"Authorization": "token {}".format(self.options.github_token)}
|
||||
try:
|
||||
response = requests.post("https://api.github.com/markdown", json=payload, headers=headers)
|
||||
response.raise_for_status() # Raises an exception if status code is not 200
|
||||
return response.text
|
||||
except requests.RequestException as e:
|
||||
raise Exception("markdown2html error: {}".format(e))
|
||||
|
||||
def renderHtml(self,template,blogBase,postListJson,htmlDir):
|
||||
def renderHtml(self,template,blogBase,postListJson,htmlDir,icon):
|
||||
file_loader = FileSystemLoader('templates')
|
||||
env = Environment(loader=file_loader)
|
||||
template = env.get_template(template)
|
||||
output = template.render(blogBase=blogBase,postListJson=postListJson,i18n=self.i18n,IconList=IconList)
|
||||
output = template.render(blogBase=blogBase,postListJson=postListJson,i18n=self.i18n,IconList=icon)
|
||||
f = open(htmlDir, 'w', encoding='UTF-8')
|
||||
f.write(output)
|
||||
f.close()
|
||||
|
||||
def createPostHtml(self,issue):
|
||||
f = open("backup/"+issue["postTitle"]+".md", 'r', encoding='UTF-8')
|
||||
mdFileName=re.sub(r'[<>:/\\|?*\"]|[\0-\31]', '-', issue["postTitle"])
|
||||
f = open(self.backup_dir+mdFileName+".md", 'r', encoding='UTF-8')
|
||||
post_body=self.markdown2html(f.read())
|
||||
f.close()
|
||||
|
||||
postBase=self.blogBase.copy()
|
||||
|
||||
if '<math-renderer' in post_body:
|
||||
post_body=re.sub(r'<math-renderer.*?>','',post_body)
|
||||
post_body=re.sub(r'</math-renderer>','',post_body)
|
||||
issue["script"]=issue["script"]+'<script>MathJax = {tex: {inlineMath: [["$", "$"]]}};</script><script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>'
|
||||
|
||||
if '<p class="markdown-alert-title">' in post_body:
|
||||
issue["style"]=issue["style"]+'<style>.markdown-alert{padding:0.5rem 1rem;margin-bottom:1rem;border-left:.25em solid var(--borderColor-default,var(--color-border-default));}.markdown-alert .markdown-alert-title {display:flex;font-weight:var(--base-text-weight-medium,500);align-items:center;line-height:1;}.markdown-alert>:first-child {margin-top:0;}.markdown-alert>:last-child {margin-bottom:0;}</style>'
|
||||
alerts = {
|
||||
'note': 'accent',
|
||||
'tip': 'success',
|
||||
'important': 'done',
|
||||
'warning': 'attention',
|
||||
'caution': 'danger'
|
||||
}
|
||||
|
||||
for alert, style in alerts.items():
|
||||
if f'markdown-alert-{alert}' in post_body:
|
||||
issue["style"] += (
|
||||
f'<style>.markdown-alert.markdown-alert-{alert} {{'
|
||||
f'border-left-color:var(--borderColor-{style}-emphasis, var(--color-{style}-emphasis));'
|
||||
f'background-color:var(--color-{style}-subtle);}}'
|
||||
f'.markdown-alert.markdown-alert-{alert} .markdown-alert-title {{'
|
||||
f'color: var(--fgColor-{style},var(--color-{style}-fg));}}</style>'
|
||||
)
|
||||
|
||||
if '<code class="notranslate">Gmeek-html' in post_body:
|
||||
post_body = re.sub(r'<code class="notranslate">Gmeek-html(.*?)</code>', lambda match: html.unescape(match.group(1)), post_body, flags=re.DOTALL)
|
||||
|
||||
postBase["postTitle"]=issue["postTitle"]
|
||||
postBase["postUrl"]=self.blogBase["homeUrl"]+"/"+issue["postUrl"]
|
||||
postBase["description"]=issue["description"]
|
||||
postBase["ogImage"]=issue["ogImage"]
|
||||
postBase["postBody"]=post_body
|
||||
postBase["commentNum"]=issue["commentNum"]
|
||||
postBase["style"]=issue["style"]
|
||||
postBase["script"]=issue["script"]
|
||||
postBase["head"]=issue["head"]
|
||||
postBase["top"]=issue["top"]
|
||||
postBase["postSourceUrl"]=issue["postSourceUrl"]
|
||||
postBase["repoName"]=options.repo_name
|
||||
|
||||
if issue["labels"][0] in self.blogBase["singlePage"]:
|
||||
postBase["bottomText"]=''
|
||||
|
||||
self.renderHtml('post.html',postBase,{},issue["htmlDir"])
|
||||
if '<pre class="notranslate">' in post_body:
|
||||
keys=['sun','moon','sync','home','github','copy','check']
|
||||
if '<div class="highlight' in post_body:
|
||||
postBase["highlight"]=1
|
||||
else:
|
||||
postBase["highlight"]=2
|
||||
else:
|
||||
keys=['sun','moon','sync','home','github']
|
||||
postBase["highlight"]=0
|
||||
|
||||
postIcon=dict(zip(keys, map(IconBase.get, keys)))
|
||||
self.renderHtml('post.html',postBase,{},issue["htmlDir"],postIcon)
|
||||
print("create postPage title=%s file=%s " % (issue["postTitle"],issue["htmlDir"]))
|
||||
|
||||
def createPlistHtml(self):
|
||||
self.blogBase["postListJson"]=dict(sorted(self.blogBase["postListJson"].items(),key=lambda x:(x[1]["top"],x[1]["createdAt"]),reverse=True))#使列表由时间排序
|
||||
keys=list(OrderedDict.fromkeys(['sun', 'moon','sync', 'search', 'rss', 'upload', 'post'] + self.blogBase["singlePage"]))
|
||||
plistIcon={**dict(zip(keys, map(IconBase.get, keys))),**self.blogBase["iconList"]}
|
||||
keys=['sun','moon','sync','home','search','post']
|
||||
tagIcon=dict(zip(keys, map(IconBase.get, keys)))
|
||||
|
||||
postNum=len(self.blogBase["postListJson"])
|
||||
pageFlag=0
|
||||
@ -128,7 +237,7 @@ class GMEEK():
|
||||
self.blogBase["prevUrl"]="/page%d.html" % pageFlag
|
||||
self.blogBase["nextUrl"]="disabled"
|
||||
|
||||
self.renderHtml('plist.html',self.blogBase,onePageList,htmlDir)
|
||||
self.renderHtml('plist.html',self.blogBase,onePageList,htmlDir,plistIcon)
|
||||
print("create "+htmlDir)
|
||||
break
|
||||
else:
|
||||
@ -146,11 +255,14 @@ class GMEEK():
|
||||
self.blogBase["prevUrl"]="/page%d.html" % pageFlag
|
||||
self.blogBase["nextUrl"]="/page%d.html" % (pageFlag+2)
|
||||
|
||||
self.renderHtml('plist.html',self.blogBase,onePageList,htmlDir)
|
||||
self.renderHtml('plist.html',self.blogBase,onePageList,htmlDir,plistIcon)
|
||||
print("create "+htmlDir)
|
||||
|
||||
pageFlag=pageFlag+1
|
||||
|
||||
self.renderHtml('tag.html',self.blogBase,onePageList,self.root_dir+"tag.html",tagIcon)
|
||||
print("create tag.html")
|
||||
|
||||
def createFeedXml(self):
|
||||
self.blogBase["postListJson"]=dict(sorted(self.blogBase["postListJson"].items(),key=lambda x:x[1]["createdAt"],reverse=False))#使列表由时间排序
|
||||
feed = FeedGenerator()
|
||||
@ -158,7 +270,6 @@ class GMEEK():
|
||||
feed.description(self.blogBase["subTitle"])
|
||||
feed.link(href=self.blogBase["homeUrl"])
|
||||
feed.image(url=self.blogBase["avatarUrl"],title="avatar", link=self.blogBase["homeUrl"])
|
||||
feed.pubDate(time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()))
|
||||
feed.copyright(self.blogBase["title"])
|
||||
feed.managingEditor(self.blogBase["title"])
|
||||
feed.webMaster(self.blogBase["title"])
|
||||
@ -180,39 +291,67 @@ class GMEEK():
|
||||
item.link(href=self.blogBase["homeUrl"]+"/"+self.blogBase["postListJson"][num]["postUrl"])
|
||||
item.pubDate(time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(self.blogBase["postListJson"][num]["createdAt"])))
|
||||
|
||||
if self.oldFeedString!='':
|
||||
feed.rss_file(self.root_dir+'new.xml')
|
||||
newFeed=open(self.root_dir+'new.xml','r',encoding='utf-8')
|
||||
new=newFeed.read()
|
||||
newFeed.close()
|
||||
|
||||
new=re.sub(r'<lastBuildDate>.*?</lastBuildDate>','',new)
|
||||
old=re.sub(r'<lastBuildDate>.*?</lastBuildDate>','',self.oldFeedString)
|
||||
os.remove(self.root_dir+'new.xml')
|
||||
|
||||
if new==old:
|
||||
print("====== rss xml no update ======")
|
||||
feedFile=open(self.root_dir+'rss.xml',"w")
|
||||
feedFile.write(self.oldFeedString)
|
||||
feedFile.close()
|
||||
return
|
||||
|
||||
print("====== create rss xml ======")
|
||||
feed.rss_file(self.root_dir+'rss.xml')
|
||||
|
||||
def addOnePostJson(self,issue):
|
||||
if len(issue.labels)==1:
|
||||
if len(issue.labels)>=1:
|
||||
if issue.labels[0].name in self.blogBase["singlePage"]:
|
||||
listJsonName='singeListJson'
|
||||
gen_Html = 'docs/{}.html'.format(issue.labels[0].name)
|
||||
htmlFile='{}.html'.format(self.createFileName(issue,useLabel=True))
|
||||
gen_Html = self.root_dir+htmlFile
|
||||
else:
|
||||
listJsonName='postListJson'
|
||||
gen_Html = self.post_dir+'{}.html'.format(Pinyin().get_pinyin(issue.title))
|
||||
htmlFile='{}.html'.format(self.createFileName(issue))
|
||||
gen_Html = self.post_dir+htmlFile
|
||||
|
||||
postNum="P"+str(issue.number)
|
||||
self.blogBase[listJsonName][postNum]=json.loads('{}')
|
||||
self.blogBase[listJsonName][postNum]["htmlDir"]=gen_Html
|
||||
self.blogBase[listJsonName][postNum]["label"]=issue.labels[0].name
|
||||
self.blogBase[listJsonName][postNum]["labelColor"]=self.labelColorDict[issue.labels[0].name]
|
||||
self.blogBase[listJsonName][postNum]["labels"]=[label.name for label in issue.labels]
|
||||
self.blogBase[listJsonName][postNum]["postTitle"]=issue.title
|
||||
self.blogBase[listJsonName][postNum]["postUrl"]=urllib.parse.quote(self.post_folder+'{}.html'.format(Pinyin().get_pinyin(issue.title)))
|
||||
self.blogBase[listJsonName][postNum]["postUrl"]=urllib.parse.quote(gen_Html[len(self.root_dir):])
|
||||
|
||||
self.blogBase[listJsonName][postNum]["postSourceUrl"]="https://github.com/"+options.repo_name+"/issues/"+str(issue.number)
|
||||
self.blogBase[listJsonName][postNum]["commentNum"]=issue.get_comments().totalCount
|
||||
if self.blogBase["i18n"]=="CN":
|
||||
period="。"
|
||||
else:
|
||||
period="."
|
||||
self.blogBase[listJsonName][postNum]["description"]=issue.body.split(period)[0]+period
|
||||
|
||||
if issue.body==None:
|
||||
self.blogBase[listJsonName][postNum]["description"]=''
|
||||
self.blogBase[listJsonName][postNum]["wordCount"]=0
|
||||
else:
|
||||
self.blogBase[listJsonName][postNum]["wordCount"]=len(issue.body)
|
||||
if self.blogBase["rssSplit"]=="sentence":
|
||||
if self.blogBase["i18n"]=="CN":
|
||||
period="。"
|
||||
else:
|
||||
period="."
|
||||
else:
|
||||
period=self.blogBase["rssSplit"]
|
||||
self.blogBase[listJsonName][postNum]["description"]=issue.body.split(period)[0].replace("\"", "\'")+period
|
||||
|
||||
self.blogBase[listJsonName][postNum]["top"]=0
|
||||
for event in issue.get_events():
|
||||
if event.event=="pinned":
|
||||
self.blogBase[listJsonName][postNum]["top"]=1
|
||||
break
|
||||
elif event.event=="unpinned":
|
||||
break
|
||||
self.blogBase[listJsonName][postNum]["top"]=0
|
||||
|
||||
try:
|
||||
postConfig=json.loads(issue.body.split("\r\n")[-1:][0].split("##")[1])
|
||||
@ -225,23 +364,42 @@ class GMEEK():
|
||||
self.blogBase[listJsonName][postNum]["createdAt"]=postConfig["timestamp"]
|
||||
else:
|
||||
self.blogBase[listJsonName][postNum]["createdAt"]=int(time.mktime(issue.created_at.timetuple()))
|
||||
|
||||
if "style" in postConfig:
|
||||
self.blogBase[listJsonName][postNum]["style"]=str(postConfig["style"])
|
||||
self.blogBase[listJsonName][postNum]["style"]=self.blogBase["style"]+str(postConfig["style"])
|
||||
else:
|
||||
self.blogBase[listJsonName][postNum]["style"]=""
|
||||
self.blogBase[listJsonName][postNum]["style"]=self.blogBase["style"]
|
||||
|
||||
if "script" in postConfig:
|
||||
self.blogBase[listJsonName][postNum]["script"]=str(postConfig["script"])
|
||||
self.blogBase[listJsonName][postNum]["script"]=self.blogBase["script"]+str(postConfig["script"])
|
||||
else:
|
||||
self.blogBase[listJsonName][postNum]["script"]=""
|
||||
self.blogBase[listJsonName][postNum]["script"]=self.blogBase["script"]
|
||||
|
||||
if "head" in postConfig:
|
||||
self.blogBase[listJsonName][postNum]["head"]=self.blogBase["head"]+str(postConfig["head"])
|
||||
else:
|
||||
self.blogBase[listJsonName][postNum]["head"]=self.blogBase["head"]
|
||||
|
||||
if "ogImage" in postConfig:
|
||||
self.blogBase[listJsonName][postNum]["ogImage"]=postConfig["ogImage"]
|
||||
else:
|
||||
self.blogBase[listJsonName][postNum]["ogImage"]=self.blogBase["ogImage"]
|
||||
|
||||
thisTime=datetime.datetime.fromtimestamp(self.blogBase[listJsonName][postNum]["createdAt"])
|
||||
thisTime=thisTime.astimezone(self.TZ)
|
||||
thisYear=thisTime.year
|
||||
self.blogBase[listJsonName][postNum]["createdDate"]=thisTime.strftime("%Y-%m-%d")
|
||||
self.blogBase[listJsonName][postNum]["dateLabelColor"]=self.blogBase["yearColorList"][int(thisYear)%len(self.blogBase["yearColorList"])]
|
||||
|
||||
f = open("backup/"+issue.title+".md", 'w', encoding='UTF-8')
|
||||
f.write(issue.body)
|
||||
mdFileName=re.sub(r'[<>:/\\|?*\"]|[\0-\31]', '-', issue.title)
|
||||
f = open(self.backup_dir+mdFileName+".md", 'w', encoding='UTF-8')
|
||||
|
||||
if issue.body==None:
|
||||
f.write('')
|
||||
else:
|
||||
f.write(issue.body)
|
||||
f.close()
|
||||
return listJsonName
|
||||
|
||||
def runAll(self):
|
||||
print("====== start create static html ======")
|
||||
@ -264,11 +422,28 @@ class GMEEK():
|
||||
def runOne(self,number_str):
|
||||
print("====== start create static html ======")
|
||||
issue=self.repo.get_issue(int(number_str))
|
||||
self.addOnePostJson(issue)
|
||||
self.createPostHtml(self.blogBase["postListJson"]["P"+number_str])
|
||||
self.createPlistHtml()
|
||||
self.createFeedXml()
|
||||
print("====== create static html end ======")
|
||||
if issue.state == "open":
|
||||
listJsonName=self.addOnePostJson(issue)
|
||||
self.createPostHtml(self.blogBase[listJsonName]["P"+number_str])
|
||||
self.createPlistHtml()
|
||||
self.createFeedXml()
|
||||
print("====== create static html end ======")
|
||||
else:
|
||||
print("====== issue is closed ======")
|
||||
|
||||
def createFileName(self,issue,useLabel=False):
|
||||
if useLabel==True:
|
||||
fileName=issue.labels[0].name
|
||||
else:
|
||||
if self.blogBase["urlMode"]=="issue":
|
||||
fileName=str(issue.number)
|
||||
elif self.blogBase["urlMode"]=="ru_translit":
|
||||
fileName=str(translit(issue.title, language_code='ru', reversed=True)).replace(' ', '-')
|
||||
else:
|
||||
fileName=Pinyin().get_pinyin(issue.title)
|
||||
|
||||
fileName=re.sub(r'[<>:/\\|?*\"]|[\0-\31]', '-', fileName)
|
||||
return fileName
|
||||
|
||||
######################################################################################
|
||||
parser = argparse.ArgumentParser()
|
||||
@ -283,17 +458,68 @@ if not os.path.exists("blogBase.json"):
|
||||
print("blogBase is not exists, runAll")
|
||||
blog.runAll()
|
||||
else:
|
||||
if os.path.exists(blog.root_dir+'rss.xml'):
|
||||
oldFeedFile=open(blog.root_dir+'rss.xml','r',encoding='utf-8')
|
||||
blog.oldFeedString=oldFeedFile.read()
|
||||
oldFeedFile.close()
|
||||
if options.issue_number=="0" or options.issue_number=="":
|
||||
print("issue_number=='0', runAll")
|
||||
blog.runAll()
|
||||
else:
|
||||
f=open("blogBase.json","r")
|
||||
print("blogBase is exists and issue_number!=0, runOne")
|
||||
blog.blogBase=json.loads(f.read())
|
||||
oldBlogBase=json.loads(f.read())
|
||||
for key, value in oldBlogBase.items():
|
||||
blog.blogBase[key] = value
|
||||
f.close()
|
||||
blog.blogBase["labelColorDict"]=blog.labelColorDict
|
||||
blog.runOne(options.issue_number)
|
||||
|
||||
listFile=open("blogBase.json","w")
|
||||
listFile.write(json.dumps(blog.blogBase))
|
||||
listFile.close()
|
||||
|
||||
commentNumSum=0
|
||||
wordCount=0
|
||||
print("====== create postList.json file ======")
|
||||
blog.blogBase["postListJson"]=dict(sorted(blog.blogBase["postListJson"].items(),key=lambda x:x[1]["createdAt"],reverse=True))#使列表由时间排序
|
||||
for i in blog.blogBase["postListJson"]:
|
||||
del blog.blogBase["postListJson"][i]["description"]
|
||||
del blog.blogBase["postListJson"][i]["postSourceUrl"]
|
||||
del blog.blogBase["postListJson"][i]["htmlDir"]
|
||||
del blog.blogBase["postListJson"][i]["createdAt"]
|
||||
del blog.blogBase["postListJson"][i]["script"]
|
||||
del blog.blogBase["postListJson"][i]["style"]
|
||||
del blog.blogBase["postListJson"][i]["top"]
|
||||
del blog.blogBase["postListJson"][i]["ogImage"]
|
||||
|
||||
if 'head' in blog.blogBase["postListJson"][i]:
|
||||
del blog.blogBase["postListJson"][i]["head"]
|
||||
|
||||
if 'commentNum' in blog.blogBase["postListJson"][i]:
|
||||
commentNumSum=commentNumSum+blog.blogBase["postListJson"][i]["commentNum"]
|
||||
del blog.blogBase["postListJson"][i]["commentNum"]
|
||||
|
||||
if 'wordCount' in blog.blogBase["postListJson"][i]:
|
||||
wordCount=wordCount+blog.blogBase["postListJson"][i]["wordCount"]
|
||||
del blog.blogBase["postListJson"][i]["wordCount"]
|
||||
|
||||
blog.blogBase["postListJson"]["labelColorDict"]=blog.labelColorDict
|
||||
|
||||
docListFile=open(blog.root_dir+"postList.json","w")
|
||||
docListFile.write(json.dumps(blog.blogBase["postListJson"]))
|
||||
docListFile.close()
|
||||
|
||||
if os.environ.get('GITHUB_EVENT_NAME')!='schedule':
|
||||
print("====== update readme file ======")
|
||||
workspace_path = os.environ.get('GITHUB_WORKSPACE')
|
||||
readme="# %s :link: %s \r\n" % (blog.blogBase["title"],blog.blogBase["homeUrl"])
|
||||
readme=readme+"### :page_facing_up: [%d](%s/tag.html) \r\n" % (len(blog.blogBase["postListJson"])-1,blog.blogBase["homeUrl"])
|
||||
readme=readme+"### :speech_balloon: %d \r\n" % commentNumSum
|
||||
readme=readme+"### :hibiscus: %d \r\n" % wordCount
|
||||
readme=readme+"### :alarm_clock: %s \r\n" % datetime.datetime.now(blog.TZ).strftime('%Y-%m-%d %H:%M:%S')
|
||||
readme=readme+"### Powered by :heart: [Gmeek](https://github.com/Meekdai/Gmeek)\r\n"
|
||||
readmeFile=open(workspace_path+"/README.md","w")
|
||||
readmeFile.write(readme)
|
||||
readmeFile.close()
|
||||
######################################################################################
|
||||
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Meekdai
|
||||
|
||||
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.
|
||||
16
README-en.md
16
README-en.md
@ -1,18 +1,20 @@
|
||||
**[简体中文](README.md)** | **English**
|
||||
**[简体中文](README.md)** | **English** | **[Русский](README-ru.md)**
|
||||
# Gmeek
|
||||
|
||||
Gmeek is a Blog Generator based on `Github Pages` and `Github Issues` and `Github Actions`. No local deployment is required, and it only takes a few minutes from building to writing.
|
||||
Gmeek is a Blog Generator based on `Github Pages` and `Github Issues` and `Github Actions`. No local deployment is required, and it only takes a few minutes to deploy.
|
||||
|
||||
- [Demo](http://meekdai.github.io/)
|
||||
- [Update Log](https://meekdai.github.io/post/Gmeek-geng-xin-ri-zhi.html)
|
||||
- [Gmeek Get Started](https://blog.meekdai.com/post/Gmeek-kuai-su-shang-shou.html)
|
||||
|
||||

|
||||
|
||||
### Installation
|
||||
1. Create your own `XXX.github.io` repository. In the repository `Settings`, select `Github Actions` under `Pages->Build and deployment->Source`.
|
||||
2. Create files `config.json` and `.github/workflows/Gmeek.yml` in the repository, copy the code in [link](CONIFG.md) and save them separately.
|
||||
3. Delete redundant tags in Issues and create your own tags, such as `link`, `about`, `daily`, etc.
|
||||
4. Open an issue and start writing. After saving the issue, the blog content will be automatically created. After a while, it can be accessed through https://XXX.github.io
|
||||
1. Click [Create a repository from template](https://github.com/new?template_name=Gmeek-template&template_owner=Meekdai), the recommended repository name is `XXX.github.io`,where `XXX` is your github username.
|
||||
|
||||
2. In the repository `Settings`, select `Github Actions` in `Pages->Build and deployment->Source`.
|
||||
|
||||
3. Open an issue, start writing, and add a label. After saving the issue, blog content will be automatically created. After a while, it can be accessed through https://XXX.github.io
|
||||
|
||||
The above installation is just a guide, and some configuration details will be written later. If you have any questions, please submit [Issues](https://github.com/Meekdai/Gmeek/issues) in this repository
|
||||
|
||||
@ -23,7 +25,7 @@ The above installation is just a guide, and some configuration details will be w
|
||||
- The comment system used [utteranc.es](https://utteranc.es/)
|
||||
|
||||
### Thanks
|
||||
|
||||
- [jinja2](https://jinja.palletsprojects.com/)
|
||||
- [utteranc.es](https://utteranc.es/)
|
||||
- [primer.style](https://primer.style/css)
|
||||
- [gitblog](https://github.com/yihong0618/gitblog)
|
||||
|
||||
31
README-ru.md
Normal file
31
README-ru.md
Normal file
@ -0,0 +1,31 @@
|
||||
**[简体中文](README.md)** | **[English](README-en.md)** | **Русский**
|
||||
# Gmeek
|
||||
|
||||
Gmeek это блог генератор, использующий `Github Pages` и `Github Issues` и `Github Actions`. Никакого локального развертывания не требуется, и развертывание занимает всего несколько минут.
|
||||
|
||||
- [Пример](http://meekdai.github.io/)
|
||||
- [Журнал обновлений](https://meekdai.github.io/post/Gmeek-geng-xin-ri-zhi.html)
|
||||
- [Начать работу с Gmeek](https://blog.meekdai.com/post/Gmeek-kuai-su-shang-shou.html)
|
||||
|
||||

|
||||
|
||||
### Установка
|
||||
1. Нажмите [Create a repository from template](https://github.com/new?template_name=Gmeek-template&template_owner=Meekdai), рекомендуемое имя репозитория - `XXX.github.io`, где `XXX` это имя вашего профиля github.
|
||||
|
||||
2. В репозитории выберите `Settings`, выберите `Github Actions` в следующем месте `Pages->Build and deployment->Source`.
|
||||
|
||||
3. Откройте issue и начните писать, далее добавьте label. После сохранения issue, содержание блога будет создано автоматически. Через некоторое время к нему можно будет получить доступ через https://XXX.github.io
|
||||
|
||||
Приведенная выше установка является лишь кратким руководством, некоторые детали конфигурации будут написаны позже. Если у вас есть вопросы, пожалуйста, отправьте [Issues](https://github.com/Meekdai/Gmeek/issues) в этот репозиторий.
|
||||
|
||||
### Особенности
|
||||
|
||||
- Интерфейс UI имеет то же происхождение, что и Github, только внедрен собственный CSS Github:[primer.style](https://primer.style/css)
|
||||
- После завершения написания блога в Issues автоматически запускаются Actions для выполнения задач развертывания.
|
||||
- Система комментариев, используется [utteranc.es](https://utteranc.es/)
|
||||
|
||||
### Благодарность
|
||||
- [jinja2](https://jinja.palletsprojects.com/)
|
||||
- [utteranc.es](https://utteranc.es/)
|
||||
- [primer.style](https://primer.style/css)
|
||||
- [gitblog](https://github.com/yihong0618/gitblog)
|
||||
56
README.md
56
README.md
@ -1,21 +1,31 @@
|
||||
**简体中文** | **[English](README-en.md)**
|
||||
**简体中文** | **[English](README-en.md)** | **[Русский](README-ru.md)**
|
||||
# Gmeek
|
||||
|
||||
一个博客框架,超轻量级个人博客模板。完全基于`Github Pages` 、 `Github Issues` 和 `Github Actions`。不需要本地部署,从搭建到写作,只需要几分钟的时间,3步搭建好博客,第4步就是写作。
|
||||
一个博客框架,超轻量级个人博客模板。完全基于`Github Pages` 、 `Github Issues` 和 `Github Actions`。不需要本地部署,从搭建到写作,只需要18秒,2步搭建好博客,第3步就是写作。
|
||||
|
||||
- [Demo页面](http://meekdai.github.io/)
|
||||
- [更新日志](https://meekdai.github.io/post/Gmeek-geng-xin-ri-zhi.html)
|
||||
- [Gmeek更新日志](https://meekdai.github.io/post/Gmeek-geng-xin-ri-zhi.html)
|
||||
- [Gmeek快速上手](https://blog.meekdai.com/post/Gmeek-kuai-su-shang-shou.html)
|
||||
|
||||

|
||||
|
||||
### 安装
|
||||
|
||||
1. 创建自己的`XXX.github.io`的仓库,在仓库的设置中`Pages->Build and deployment->Source`下面选择`Github Actions`。
|
||||
2. 在仓库中创建文件`config.json`和`.github/workflows/Gmeek.yml`复制[链接](CONIFG.md)中的代码分别保存。
|
||||
3. 在Issues中删除多余标签,创建自己的标签,如`link`、`about`、`日常`等。
|
||||
4. 打开一篇issue,开始写作,并且添加一个标签,保存issue后会自动创建博客内容,片刻后可通过https://XXX.github.io 访问
|
||||
1. 【创建仓库】点击[通过模板创建仓库](https://github.com/new?template_name=Gmeek-template&template_owner=Meekdai),建议仓库名称为`XXX.github.io`,其中`XXX`为你的github用户名。
|
||||
|
||||
如果有问题可在本仓库提交[Issues](https://github.com/Meekdai/Gmeek/issues) 或者添加 QQ:`294977308`
|
||||
2. 【启用Pages】在仓库的`Settings`中`Pages->Build and deployment->Source`下面选择`Github Actions`。
|
||||
|
||||
3. 【开始写作】打开一篇issue,开始写作,并且**必须**添加一个`标签Label`(至少添加一个),再保存issue后会自动创建博客内容,片刻后可通过https://XXX.github.io 访问(可进入Actions页面查看构建进度)。
|
||||
|
||||
4. 【手动全局生成】这个步骤只有在修改`config.json`文件或者出现奇怪问题的时候,需要执行。
|
||||
```
|
||||
通过Actions->build Gmeek->Run workflow->里面的按钮全局重新生成一次
|
||||
```
|
||||
|
||||
### 提交问题
|
||||
|
||||
1. 如果有问题可参考[Gmeek快速上手](https://blog.meekdai.com/post/Gmeek-kuai-su-shang-shou.html)
|
||||
2. 在本仓库提交[Issues](https://github.com/Meekdai/Gmeek/issues)之前,请手动全局生成一次。如果还有错误,提交`Issues`后,我会帮忙查看构建流程,定位问题出处。
|
||||
|
||||
### 特性
|
||||
|
||||
@ -24,34 +34,11 @@
|
||||
- 评论系统引入[utteranc.es](https://utteranc.es/)
|
||||
- 使用`jinja2`对html进行渲染,可通过模板自定义UI主题
|
||||
|
||||
### 说明
|
||||
1. 请确保每一篇文章有且仅有一个`Label`,为了防止他人提交的Issue也被抓取生成文章。
|
||||
|
||||
2. 如果要导入以前的文章,如何设置发布时间呢?
|
||||
如需上传旧博客的文章需要修改发布时间,可以在文章最后一行添加如下代码。里面的时间是采用时间戳的形式,可以用如下[网站](https://tool.lu/timestamp)转换。
|
||||
```html
|
||||
<!-- ##{"timestamp":1490764800}## -->
|
||||
```
|
||||
|
||||
3. 自定义单篇文章页面的`style`和`script`,同样是在文章最后一行添加如下代码,为JSON格式。
|
||||
```html
|
||||
<!-- ##{"style":"<style>#postBody{font-size:20px}</style>"}## -->
|
||||
```
|
||||
```html
|
||||
<!-- ##{"script":"<script async src='//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js'></script>"}## -->
|
||||
```
|
||||
4. 可同时一起添加多种自定义参数:
|
||||
```html
|
||||
<!-- ##{"script":"<script async src='//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js'></script>","style":"<style>#postBody{font-size:20px}</style>","timestamp":1490764800}## -->
|
||||
```
|
||||
|
||||
5. 如果修改过config.json里面的参数后,发现生成文章失败,或其他奇奇怪怪的问题。
|
||||
建议通过Actions->build Gmeek->Run workflow->里面的按钮全局重新生成一次就行。
|
||||
|
||||
6. 置顶博客文章,只需要`Pin issue`即可。
|
||||
|
||||
### 赞赏
|
||||
|
||||
如果本项目对你有帮助,可以用微信赞赏一下作者,让项目有继续更新维护下去的动力,谢谢!
|
||||
|
||||

|
||||
|
||||
### 鸣谢
|
||||
- [jinja2](https://jinja.palletsprojects.com/)
|
||||
@ -62,3 +49,4 @@
|
||||
### License
|
||||
|
||||
请保留页面底部和console界面版权信息,谢谢!
|
||||
|
||||
|
||||
BIN
img/赞赏码.jpg
Normal file
BIN
img/赞赏码.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
16
plugins/GmeekBSZ.js
Normal file
16
plugins/GmeekBSZ.js
Normal file
@ -0,0 +1,16 @@
|
||||
function createBSZ() {
|
||||
var postBody = document.getElementById('postBody');
|
||||
if (postBody){
|
||||
postBody.insertAdjacentHTML('afterend','<div id="busuanzi_container_page_pv" style="display:none;float:left;margin-top:8px;font-size:small;">本文浏览量<span id="busuanzi_value_page_pv"></span>次</div>');
|
||||
}
|
||||
var runday = document.getElementById('runday');
|
||||
runday.insertAdjacentHTML('afterend', '<div id="busuanzi_container_site_pv" style="display:none;">总浏览量<span id="busuanzi_value_site_pv"></span>次 • </div>');
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
createBSZ();
|
||||
var element = document.createElement('script');
|
||||
element.src = '//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js';
|
||||
document.head.appendChild(element);
|
||||
console.log("\n %c GmeekBSZ Plugins https://github.com/Meekdai/Gmeek \n","padding:5px 0;background:#bc4c00;color:#fff");
|
||||
});
|
||||
100
plugins/GmeekTOC.js
Normal file
100
plugins/GmeekTOC.js
Normal file
@ -0,0 +1,100 @@
|
||||
function createTOC() {
|
||||
var tocElement = document.createElement('div');
|
||||
tocElement.className = 'toc';
|
||||
var contentContainer = document.getElementById('content');
|
||||
|
||||
const headings = contentContainer.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
||||
|
||||
if (headings.length === 0) {
|
||||
return; // 如果没有标题元素,则不创建TOC
|
||||
}
|
||||
|
||||
tocElement.insertAdjacentHTML('afterbegin', '<div class="toc-title">文章目录</div>');
|
||||
|
||||
headings.forEach(heading => {
|
||||
if (!heading.id) {
|
||||
heading.id = heading.textContent.trim().replace(/\s+/g, '-').toLowerCase();
|
||||
}
|
||||
const link = document.createElement('a');
|
||||
link.href = '#' + heading.id;
|
||||
link.textContent = heading.textContent;
|
||||
link.className = 'toc-link';
|
||||
link.style.paddingLeft = `${(parseInt(heading.tagName.charAt(1)) - 1) * 10}px`;
|
||||
tocElement.appendChild(link);
|
||||
});
|
||||
|
||||
tocElement.insertAdjacentHTML('beforeend', '<a class="toc-end" onclick="window.scrollTo({top:0,behavior: \'smooth\'});">Top</a>');
|
||||
contentContainer.prepend(tocElement);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
createTOC();
|
||||
var css = `
|
||||
.toc {
|
||||
position:fixed;
|
||||
top:130px;
|
||||
left:50%;
|
||||
transform: translateX(50%) translateX(320px);
|
||||
width:200px;
|
||||
border: 1px solid #e1e4e8;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
max-height: 70vh;
|
||||
}
|
||||
.toc-title{
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.toc-end{
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
visibility: hidden;
|
||||
}
|
||||
.toc a {
|
||||
display: block;
|
||||
color: var(--color-diff-blob-addition-num-text);
|
||||
text-decoration: none;
|
||||
padding: 5px 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
border-bottom: 1px solid #e1e4e8;
|
||||
}
|
||||
.toc a:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.toc a:hover {
|
||||
background-color:var(--color-select-menu-tap-focus-bg);
|
||||
}
|
||||
|
||||
@media (max-width: 1249px)
|
||||
{
|
||||
.toc{
|
||||
position:static;
|
||||
top:auto;
|
||||
left:auto;
|
||||
transform:none;
|
||||
padding:10px;
|
||||
margin-bottom:20px;
|
||||
}
|
||||
}`;
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = css;
|
||||
document.head.appendChild(style);
|
||||
|
||||
window.onscroll = function() {
|
||||
const backToTopButton = document.querySelector('.toc-end');
|
||||
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
|
||||
backToTopButton.style="visibility: visible;"
|
||||
} else {
|
||||
backToTopButton.style="visibility: hidden;"
|
||||
}
|
||||
};
|
||||
|
||||
console.log("\n %c GmeekTOC Plugins https://github.com/Meekdai/Gmeek \n","padding:5px 0;background:#C333D0;color:#fff");
|
||||
});
|
||||
59
plugins/GmeekTocBot.js
Normal file
59
plugins/GmeekTocBot.js
Normal file
@ -0,0 +1,59 @@
|
||||
function loadResource(type, attributes, callback) {
|
||||
var element;
|
||||
if (type === 'script') {
|
||||
element = document.createElement('script');
|
||||
element.src = attributes.src;
|
||||
element.onload = callback;
|
||||
} else if (type === 'link') {
|
||||
element = document.createElement('link');
|
||||
element.rel = attributes.rel;
|
||||
element.href = attributes.href;
|
||||
} else if (type === 'style') {
|
||||
element = document.createElement('style');
|
||||
element.rel = 'stylesheet';
|
||||
element.appendChild(document.createTextNode(attributes.css));
|
||||
}
|
||||
document.head.appendChild(element);
|
||||
}
|
||||
|
||||
function createTOC() {
|
||||
var tocElement = document.createElement('div');
|
||||
tocElement.className = 'toc';
|
||||
var contentContainer = document.getElementById('content');
|
||||
if (contentContainer.firstChild) {
|
||||
contentContainer.insertBefore(tocElement, contentContainer.firstChild);
|
||||
} else {
|
||||
contentContainer.appendChild(tocElement);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
createTOC();
|
||||
var css = '.toc {position:fixed;top:130px;left:50%;transform: translateX(50%) translateX(300px);width:200px;padding-left:30px;}@media (max-width: 1249px) {.toc{position:static;top:auto;left:auto;transform:none;padding:10px;margin-bottom:20px;background-color:var(--color-open-muted);}}';
|
||||
loadResource('style', {css: css});
|
||||
|
||||
loadResource('script', { src: 'https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.27.4/tocbot.min.js' }, function() {
|
||||
tocbot.init({
|
||||
tocSelector: '.toc',
|
||||
contentSelector: '.markdown-body',
|
||||
headingSelector: 'h1, h2, h3, h4, h5, h6',
|
||||
scrollSmooth: true,
|
||||
scrollSmoothOffset: -10,
|
||||
headingsOffset: 10,
|
||||
});
|
||||
});
|
||||
|
||||
loadResource('link', { rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.27.4/tocbot.css' });
|
||||
|
||||
const headings = document.querySelectorAll('.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6');
|
||||
headings.forEach((heading) => {
|
||||
if (!heading.id) {
|
||||
heading.id = heading.textContent.trim().replace(/\s+/g, '-');
|
||||
}
|
||||
});
|
||||
|
||||
var footerPlaceholder = document.createElement('div');
|
||||
footerPlaceholder.style.height = window.innerHeight + 'px';
|
||||
document.body.appendChild(footerPlaceholder);
|
||||
console.log("\n %c GmeekTocBot Plugins https://github.com/Meekdai/Gmeek \n","padding:5px 0;background:#C333D0;color:#fff");
|
||||
});
|
||||
16
plugins/GmeekVercount.js
Normal file
16
plugins/GmeekVercount.js
Normal file
@ -0,0 +1,16 @@
|
||||
function createVercount() {
|
||||
var postBody = document.getElementById('postBody');
|
||||
if (postBody){
|
||||
postBody.insertAdjacentHTML('afterend','<div id="busuanzi_container_page_pv" style="display:none;float:left;margin-top:8px;font-size:small;">本文浏览量<span id="busuanzi_value_page_pv"></span>次</div>');
|
||||
}
|
||||
var runday = document.getElementById('runday');
|
||||
runday.insertAdjacentHTML('afterend', '<span id="busuanzi_container_site_pv" style="display:none">总浏览量<span id="busuanzi_value_site_pv"></span>次 • </span>');
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
createVercount();
|
||||
var element = document.createElement('script');
|
||||
element.src = 'https://vercount.one/js';
|
||||
document.head.appendChild(element);
|
||||
console.log("\n %c GmeekVercount Plugins https://github.com/Meekdai/Gmeek \n","padding:5px 0;background:#bc4c00;color:#fff");
|
||||
});
|
||||
158
plugins/articletoc.js
Normal file
158
plugins/articletoc.js
Normal file
@ -0,0 +1,158 @@
|
||||
function loadResource(type, attributes) {
|
||||
if (type === 'style') {
|
||||
const style = document.createElement('style');
|
||||
style.textContent = attributes.css;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
}
|
||||
|
||||
function createTOC() {
|
||||
const tocElement = document.createElement('div');
|
||||
tocElement.className = 'toc';
|
||||
const contentContainer = document.querySelector('.markdown-body');
|
||||
contentContainer.appendChild(tocElement);
|
||||
|
||||
const headings = contentContainer.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
||||
headings.forEach(heading => {
|
||||
if (!heading.id) {
|
||||
heading.id = heading.textContent.trim().replace(/\s+/g, '-').toLowerCase();
|
||||
}
|
||||
const link = document.createElement('a');
|
||||
link.href = '#' + heading.id;
|
||||
link.textContent = heading.textContent;
|
||||
link.className = 'toc-link';
|
||||
link.style.paddingLeft = `${(parseInt(heading.tagName.charAt(1)) - 1) * 10}px`;
|
||||
tocElement.appendChild(link);
|
||||
});
|
||||
}
|
||||
|
||||
function toggleTOC() {
|
||||
const tocElement = document.querySelector('.toc');
|
||||
const tocIcon = document.querySelector('.toc-icon');
|
||||
if (tocElement) {
|
||||
tocElement.classList.toggle('show');
|
||||
tocIcon.classList.toggle('active');
|
||||
tocIcon.textContent = tocElement.classList.contains('show') ? '✖' : '☰';
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
createTOC();
|
||||
const css = `
|
||||
:root {
|
||||
--toc-bg: #fff;
|
||||
--toc-border: #e1e4e8;
|
||||
--toc-text: #24292e;
|
||||
--toc-hover: #f6f8fa;
|
||||
--toc-icon-bg: #fff;
|
||||
--toc-icon-color: #ad6598;
|
||||
--toc-icon-active-bg: #813c85;
|
||||
--toc-icon-active-color: #fff;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--toc-bg: #2d333b;
|
||||
--toc-border: #444c56;
|
||||
--toc-text: #adbac7;
|
||||
--toc-hover: #373e47;
|
||||
--toc-icon-bg: #22272e;
|
||||
--toc-icon-color: #ad6598;
|
||||
--toc-icon-active-bg: #813c85;
|
||||
--toc-icon-active-color: #adbac7;
|
||||
}
|
||||
}
|
||||
|
||||
.toc {
|
||||
position: fixed;
|
||||
bottom: 60px;
|
||||
right: 20px;
|
||||
width: 250px;
|
||||
max-height: 70vh;
|
||||
background-color: var(--toc-bg);
|
||||
border: 1px solid var(--toc-border);
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
overflow-y: auto;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(20px) scale(0.9);
|
||||
transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s;
|
||||
}
|
||||
.toc.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
.toc a {
|
||||
display: block;
|
||||
color: var(--toc-text);
|
||||
text-decoration: none;
|
||||
padding: 5px 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
border-bottom: 1px solid var(--toc-border);
|
||||
transition: background-color 0.2s ease, padding-left 0.2s ease;
|
||||
}
|
||||
.toc a:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.toc a:hover {
|
||||
background-color: var(--toc-hover);
|
||||
padding-left: 5px;
|
||||
}
|
||||
.toc-icon {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
cursor: pointer;
|
||||
font-size: 24px;
|
||||
background-color: var(--toc-icon-bg);
|
||||
color: var(--toc-icon-color);
|
||||
border: 2px solid var(--toc-icon-color);
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
|
||||
z-index: 1001;
|
||||
transition: all 0.3s ease;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
outline: none;
|
||||
}
|
||||
.toc-icon:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.toc-icon:active {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
.toc-icon.active {
|
||||
background-color: var(--toc-icon-active-bg);
|
||||
color: var(--toc-icon-active-color);
|
||||
border-color: var(--toc-icon-active-bg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
`;
|
||||
loadResource('style', {css: css});
|
||||
|
||||
const tocIcon = document.createElement('div');
|
||||
tocIcon.className = 'toc-icon';
|
||||
tocIcon.textContent = '☰';
|
||||
tocIcon.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
toggleTOC();
|
||||
};
|
||||
document.body.appendChild(tocIcon);
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
const tocElement = document.querySelector('.toc');
|
||||
if (tocElement && tocElement.classList.contains('show') && !tocElement.contains(e.target) && !e.target.classList.contains('toc-icon')) {
|
||||
toggleTOC();
|
||||
}
|
||||
});
|
||||
});
|
||||
357
plugins/lightbox.js
Normal file
357
plugins/lightbox.js
Normal file
@ -0,0 +1,357 @@
|
||||
(function() {
|
||||
class Lightbox {
|
||||
constructor(options = {}) {
|
||||
this.options = Object.assign({
|
||||
animationDuration: 300,
|
||||
closeOnOverlayClick: true,
|
||||
onOpen: null,
|
||||
onClose: null,
|
||||
onNavigate: null
|
||||
}, options);
|
||||
|
||||
this.images = [];
|
||||
this.currentIndex = 0;
|
||||
this.isOpen = false;
|
||||
this.zoomLevel = 1;
|
||||
this.touchStartX = 0;
|
||||
this.touchEndX = 0;
|
||||
this.wheelTimer = null;
|
||||
this.preloadedImages = {};
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.createStyles();
|
||||
this.createLightbox();
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
createStyles() {
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.lb-lightbox-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: transparent;
|
||||
backdrop-filter: blur(5px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity ${this.options.animationDuration}ms ease;
|
||||
pointer-events: none;
|
||||
z-index: 10000;
|
||||
}
|
||||
.lb-lightbox-overlay.active {
|
||||
pointer-events: auto;
|
||||
opacity: 1;
|
||||
}
|
||||
.lb-lightbox-content-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.lb-lightbox-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
transition: transform ${this.options.animationDuration}ms cubic-bezier(0.25, 0.1, 0.25, 1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.lb-lightbox-image-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
.lb-lightbox-image {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||||
transition: transform ${this.options.animationDuration}ms cubic-bezier(0.25, 0.1, 0.25, 1), opacity ${this.options.animationDuration}ms ease;
|
||||
opacity: 0;
|
||||
}
|
||||
.lb-lightbox-nav, .lb-lightbox-close {
|
||||
position: absolute;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
color: #333;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
font-size: 30px;
|
||||
z-index: 2;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
.lb-lightbox-nav:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.lb-lightbox-prev {
|
||||
left: 20px;
|
||||
top: calc(50% - 25px);
|
||||
}
|
||||
.lb-lightbox-next {
|
||||
right: 20px;
|
||||
top: calc(50% - 25px);
|
||||
}
|
||||
.lb-lightbox-close {
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.lb-lightbox-nav, .lb-lightbox-close {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
createLightbox() {
|
||||
this.overlay = document.createElement('div');
|
||||
this.overlay.className = 'lb-lightbox-overlay';
|
||||
|
||||
this.contentWrapper = document.createElement('div');
|
||||
this.contentWrapper.className = 'lb-lightbox-content-wrapper';
|
||||
|
||||
this.container = document.createElement('div');
|
||||
this.container.className = 'lb-lightbox-container';
|
||||
|
||||
this.imageWrapper = document.createElement('div');
|
||||
this.imageWrapper.className = 'lb-lightbox-image-wrapper';
|
||||
|
||||
this.image = document.createElement('img');
|
||||
this.image.className = 'lb-lightbox-image';
|
||||
|
||||
this.prevButton = document.createElement('button');
|
||||
this.prevButton.className = 'lb-lightbox-nav lb-lightbox-prev';
|
||||
this.prevButton.innerHTML = '❮';
|
||||
|
||||
this.nextButton = document.createElement('button');
|
||||
this.nextButton.className = 'lb-lightbox-nav lb-lightbox-next';
|
||||
this.nextButton.innerHTML = '❯';
|
||||
|
||||
this.closeButton = document.createElement('button');
|
||||
this.closeButton.className = 'lb-lightbox-close';
|
||||
this.closeButton.innerHTML = '×';
|
||||
|
||||
this.imageWrapper.appendChild(this.image);
|
||||
this.container.appendChild(this.imageWrapper);
|
||||
this.contentWrapper.appendChild(this.container);
|
||||
this.contentWrapper.appendChild(this.prevButton);
|
||||
this.contentWrapper.appendChild(this.nextButton);
|
||||
this.contentWrapper.appendChild(this.closeButton);
|
||||
|
||||
this.overlay.appendChild(this.contentWrapper);
|
||||
document.body.appendChild(this.overlay);
|
||||
|
||||
this.closeButton.addEventListener('click', this.close.bind(this));
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
document.addEventListener('click', this.handleImageClick.bind(this), true);
|
||||
this.overlay.addEventListener('click', this.handleOverlayClick.bind(this));
|
||||
this.prevButton.addEventListener('click', this.showPreviousImage.bind(this));
|
||||
this.nextButton.addEventListener('click', this.showNextImage.bind(this));
|
||||
this.closeButton.addEventListener('click', this.close.bind(this));
|
||||
document.addEventListener('keydown', this.handleKeyDown.bind(this));
|
||||
this.overlay.addEventListener('wheel', this.handleWheel.bind(this));
|
||||
this.overlay.addEventListener('touchstart', this.handleTouchStart.bind(this));
|
||||
this.overlay.addEventListener('touchmove', this.handleTouchMove.bind(this));
|
||||
this.overlay.addEventListener('touchend', this.handleTouchEnd.bind(this));
|
||||
}
|
||||
|
||||
handleImageClick(event) {
|
||||
const clickedImage = event.target.closest('img');
|
||||
if (clickedImage && !this.isOpen) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.images = Array.from(document.querySelectorAll('.markdown-body img, table img'));
|
||||
this.currentIndex = this.images.indexOf(clickedImage);
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
|
||||
handleOverlayClick(event) {
|
||||
if (event.target === this.overlay && this.options.closeOnOverlayClick) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyDown(event) {
|
||||
if (!this.isOpen) return;
|
||||
switch (event.key) {
|
||||
case 'ArrowLeft':
|
||||
this.showPreviousImage();
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
this.showNextImage();
|
||||
break;
|
||||
case 'Escape':
|
||||
this.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleWheel(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.ctrlKey) {
|
||||
this.zoomLevel += event.deltaY > 0 ? -0.1 : 0.1;
|
||||
this.zoomLevel = Math.max(1, this.zoomLevel);
|
||||
this.image.style.transform = `scale(${this.zoomLevel})`;
|
||||
} else {
|
||||
clearTimeout(this.wheelTimer);
|
||||
this.wheelTimer = setTimeout(() => {
|
||||
const delta = Math.sign(event.deltaY);
|
||||
if (delta > 0) {
|
||||
this.showNextImage();
|
||||
} else {
|
||||
this.showPreviousImage();
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
handleTouchStart(event) {
|
||||
this.touchStartX = event.touches[0].clientX;
|
||||
}
|
||||
|
||||
handleTouchMove(event) {
|
||||
this.touchEndX = event.touches[0].clientX;
|
||||
}
|
||||
|
||||
handleTouchEnd() {
|
||||
const difference = this.touchStartX - this.touchEndX;
|
||||
if (Math.abs(difference) > 50) {
|
||||
difference > 0 ? this.showNextImage() : this.showPreviousImage();
|
||||
}
|
||||
}
|
||||
|
||||
open() {
|
||||
this.isOpen = true;
|
||||
this.overlay.classList.add('active');
|
||||
this.showImage(this.images[this.currentIndex].src);
|
||||
document.body.style.overflow = 'hidden';
|
||||
if (typeof this.options.onOpen === 'function') {
|
||||
this.options.onOpen();
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
document.body.style.overflow = '';
|
||||
this.overlay.classList.remove('active');
|
||||
this.isOpen = false;
|
||||
this.clearPreloadedImages();
|
||||
if (typeof this.options.onClose === 'function') {
|
||||
this.options.onClose();
|
||||
}
|
||||
this.unbindEvents();
|
||||
}
|
||||
|
||||
showPreviousImage() {
|
||||
if (this.currentIndex > 0) {
|
||||
this.currentIndex--;
|
||||
this.showImage(this.images[this.currentIndex].src);
|
||||
this.resetButtonScale(this.prevButton);
|
||||
}
|
||||
}
|
||||
|
||||
showNextImage() {
|
||||
if (this.currentIndex < this.images.length - 1) {
|
||||
this.currentIndex++;
|
||||
this.showImage(this.images[this.currentIndex].src);
|
||||
this.resetButtonScale(this.nextButton);
|
||||
}
|
||||
}
|
||||
|
||||
resetButtonScale(button) {
|
||||
button.style.transform = 'scale(1.1)';
|
||||
setTimeout(() => {
|
||||
button.style.transform = 'scale(1)';
|
||||
}, 200);
|
||||
}
|
||||
|
||||
showImage(imgSrc) {
|
||||
const newImage = new Image();
|
||||
newImage.src = imgSrc;
|
||||
|
||||
newImage.onload = () => {
|
||||
this.image.style.transition = `opacity ${this.options.animationDuration}ms ease`;
|
||||
this.image.style.transform = 'scale(1)';
|
||||
this.image.src = imgSrc;
|
||||
this.image.style.opacity = '1';
|
||||
|
||||
this.preloadImages();
|
||||
this.prevButton.style.display = this.currentIndex === 0 ? 'none' : 'block';
|
||||
this.nextButton.style.display = this.currentIndex === this.images.length - 1 ? 'none' : 'block';
|
||||
};
|
||||
|
||||
newImage.onerror = () => {
|
||||
console.error('Failed to load image:', imgSrc);
|
||||
};
|
||||
}
|
||||
|
||||
preloadImages() {
|
||||
const preloadNext = this.currentIndex + 1;
|
||||
const preloadPrev = this.currentIndex - 1;
|
||||
|
||||
if (preloadNext < this.images.length) {
|
||||
this.preloadedImages[preloadNext] = new Image();
|
||||
this.preloadedImages[preloadNext].src = this.images[preloadNext].src;
|
||||
}
|
||||
|
||||
if (preloadPrev >= 0) {
|
||||
this.preloadedImages[preloadPrev] = new Image();
|
||||
this.preloadedImages[preloadPrev].src = this.images[preloadPrev].src;
|
||||
}
|
||||
}
|
||||
|
||||
clearPreloadedImages() {
|
||||
Object.keys(this.preloadedImages).forEach(key => {
|
||||
this.preloadedImages[key].src = '';
|
||||
});
|
||||
this.preloadedImages = {};
|
||||
}
|
||||
|
||||
unbindEvents() {
|
||||
document.removeEventListener('click', this.handleImageClick.bind(this), true);
|
||||
this.overlay.removeEventListener('click', this.handleOverlayClick.bind(this));
|
||||
this.prevButton.removeEventListener('click', this.showPreviousImage.bind(this));
|
||||
this.nextButton.removeEventListener('click', this.showNextImage.bind(this));
|
||||
this.closeButton.removeEventListener('click', this.close.bind(this));
|
||||
document.removeEventListener('keydown', this.handleKeyDown.bind(this));
|
||||
this.overlay.removeEventListener('wheel', this.handleWheel.bind(this));
|
||||
this.overlay.removeEventListener('touchstart', this.handleTouchStart.bind(this));
|
||||
this.overlay.removeEventListener('touchmove', this.handleTouchMove.bind(this));
|
||||
this.overlay.removeEventListener('touchend', this.handleTouchEnd.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
window.Lightbox = Lightbox;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new Lightbox();
|
||||
});
|
||||
})();
|
||||
@ -3,3 +3,4 @@ requests
|
||||
xpinyin
|
||||
feedgen
|
||||
Jinja2
|
||||
transliterate
|
||||
@ -1,32 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-color-mode="light" data-dark-theme="dark" data-light-theme="light">
|
||||
<html data-color-mode="light" data-dark-theme="{{ blogBase['nightTheme'] }}" data-light-theme="{{ blogBase['dayTheme'] }}" lang={% if blogBase['i18n']=='CN' %}"zh-CN"{% elif blogBase['i18n']=='RU' %}"ru"{% else -%}"en"{%- endif -%}>
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link href="//cdn.staticfile.org/Primer/21.0.7/primer.css" rel="stylesheet" />
|
||||
{{ blogBase['primerCSS'] }}
|
||||
{{ blogBase['allHead'] }}
|
||||
<link rel="icon" href="{{ blogBase['faviconUrl'] }}">
|
||||
{%- if blogBase['themeMode']=='manual' -%}
|
||||
<script>
|
||||
if(localStorage.getItem("meek_theme")==null){}
|
||||
else if(localStorage.getItem("meek_theme")=="dark"){document.getElementsByTagName("html")[0].attributes.getNamedItem("data-color-mode").value="dark";}
|
||||
else if(localStorage.getItem("meek_theme")=="light"){document.getElementsByTagName("html")[0].attributes.getNamedItem("data-color-mode").value="light";}
|
||||
let theme = localStorage.getItem("meek_theme") || "light";
|
||||
document.documentElement.setAttribute("data-color-mode", theme);
|
||||
</script>
|
||||
{%- endif -%}
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<style>
|
||||
body{
|
||||
box-sizing: border-box;
|
||||
min-width: 200px;
|
||||
max-width: 900px;
|
||||
margin: 20px auto;
|
||||
padding: 45px;
|
||||
font-size: 16px;
|
||||
font-family: sans-serif;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.avatar {transition: 0.8s;}
|
||||
.avatar:hover {transform: scale(1.15) rotate(360deg);}
|
||||
body{box-sizing: border-box;min-width: 200px;max-width: 900px;margin: 20px auto;padding: 45px;font-size: 16px;font-family: sans-serif;line-height: 1.25;}
|
||||
#header{display:flex;padding-bottom:8px;border-bottom: 1px solid var(--borderColor-muted, var(--color-border-muted));margin-bottom: 16px;}
|
||||
#footer {margin-top:64px; text-align: center;font-size: small;}
|
||||
|
||||
@ -41,33 +31,34 @@ body{
|
||||
<script>
|
||||
var IconList={{ IconList }};
|
||||
var utterancesLoad=0;
|
||||
if(localStorage.getItem("meek_theme")==null){localStorage.setItem("meek_theme","light");changeLight();}
|
||||
else if(localStorage.getItem("meek_theme")=="dark"){changeDark();}
|
||||
else if(localStorage.getItem("meek_theme")=="light"){changeLight();}
|
||||
|
||||
function changeDark(){
|
||||
document.getElementsByTagName("html")[0].attributes.getNamedItem("data-color-mode").value="dark";
|
||||
document.getElementById("themeSwitch").setAttribute("d",value=IconList["moon"]);
|
||||
document.getElementById("themeSwitch").parentNode.style.color="#00f0ff";
|
||||
if(utterancesLoad==1){utterancesTheme("dark-blue");}
|
||||
}
|
||||
function changeLight(){
|
||||
document.getElementsByTagName("html")[0].attributes.getNamedItem("data-color-mode").value="light";
|
||||
document.getElementById("themeSwitch").setAttribute("d",value=IconList["sun"]);
|
||||
document.getElementById("themeSwitch").parentNode.style.color="#ff5000";
|
||||
if(utterancesLoad==1){utterancesTheme("github-light");}
|
||||
{% if blogBase['themeMode']=='manual' %}
|
||||
let themeSettings={
|
||||
"dark": ["dark","moon","#00f0ff","dark-blue"],
|
||||
"light": ["light","sun","#ff5000","github-light"],
|
||||
"auto": ["auto","sync","","preferred-color-scheme"]
|
||||
};
|
||||
function changeTheme(mode, icon, color, utheme){
|
||||
document.documentElement.setAttribute("data-color-mode",mode);
|
||||
document.getElementById("themeSwitch").setAttribute("d",value=IconList[icon]);
|
||||
document.getElementById("themeSwitch").parentNode.style.color=color;
|
||||
if(utterancesLoad==1){utterancesTheme(utheme);}
|
||||
}
|
||||
function modeSwitch(){
|
||||
if(document.getElementsByTagName("html")[0].attributes[0].value=="light"){changeDark();localStorage.setItem("meek_theme","dark");}
|
||||
else{changeLight();localStorage.setItem("meek_theme","light");}
|
||||
let currentMode=document.documentElement.getAttribute('data-color-mode');
|
||||
let newMode = currentMode === "light" ? "dark" : currentMode === "dark" ? "auto" : "light";
|
||||
localStorage.setItem("meek_theme", newMode);
|
||||
if(themeSettings[newMode]){
|
||||
changeTheme(...themeSettings[newMode]);
|
||||
}
|
||||
}
|
||||
function utterancesTheme(theme){
|
||||
const message = {type: 'set-theme',theme: theme};
|
||||
const iframe = document.getElementsByClassName('utterances-frame')[0];
|
||||
iframe.contentWindow.postMessage(message, 'https://utteranc.es');
|
||||
const message={type:'set-theme',theme: theme};
|
||||
const iframe=document.getElementsByClassName('utterances-frame')[0];
|
||||
iframe.contentWindow.postMessage(message,'https://utteranc.es');
|
||||
}
|
||||
|
||||
console.log("\n %c Gmeek {{ blogBase['GMEEK_VERSION'] }} %c https://github.com/Meekdai/Gmeek \n\n", "color: #fff; background-image: linear-gradient(90deg, rgb(47, 172, 178) 0%, rgb(45, 190, 96) 100%); padding:5px 1px;", "background-image: linear-gradient(90deg, rgb(45, 190, 96) 0%, rgb(255, 255, 255) 100%); padding:5px 0;");
|
||||
if(themeSettings[theme]){changeTheme(...themeSettings[theme]);}
|
||||
{%- endif %}
|
||||
console.log("\n %c Gmeek {{ blogBase['GMEEK_VERSION'] }} https://github.com/Meekdai/Gmeek \n","padding:5px 0;background:#02d81d;color:#fff");
|
||||
</script>
|
||||
{% block script %}{% endblock %}
|
||||
</html>
|
||||
|
||||
@ -1,14 +1,19 @@
|
||||
Copyright © <span id="year"></span> <a href="{{ blogBase['homeUrl'] }}"> {{ blogBase['title'] }} </a><p> {{ blogBase['filingNum'] }} <span id="runday"></span>Powered by <a href="https://github.com/Meekdai/Gmeek" target="_blank">Gmeek</a></p>
|
||||
<div id="footer1">Copyright © <span id="copyrightYear"></span> <a href="{{ blogBase['homeUrl'] }}">{{ blogBase['title'] }}</a></div>
|
||||
<div id="footer2">
|
||||
{%- if blogBase['filingNum']!='' -%}
|
||||
<span id="filingNum"><a href="https://beian.miit.gov.cn/" target="_blank">{{ blogBase['filingNum'] }}</a> • </span>
|
||||
{%- endif %}
|
||||
<span id="runday"></span><span>Powered by <a href="https://meekdai.com/Gmeek.html" target="_blank">Gmeek</a></span>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var now=new Date();
|
||||
document.getElementById("copyrightYear").innerHTML=now.getFullYear();
|
||||
|
||||
if("{{ blogBase['startSite'] }}"!=""){
|
||||
var now=new Date();
|
||||
var startSite=new Date("{{ blogBase['startSite'] }}");
|
||||
var diff=now.getTime()-startSite.getTime();
|
||||
var diffDay=Math.floor(diff/(1000*60*60*24));
|
||||
document.getElementById("year").innerHTML=now.getFullYear();
|
||||
if("{{ blogBase['filingNum'] }}"!=""){document.getElementById("runday").innerHTML=" • "+"{{ i18n['run'] }}"+diffDay+"{{ i18n['days'] }}"+" • ";}
|
||||
else{document.getElementById("runday").innerHTML="{{ i18n['run'] }}"+diffDay+"{{ i18n['days'] }}"+" • ";}
|
||||
document.getElementById("runday").innerHTML="{{ i18n['run'] }}"+diffDay+"{{ i18n['days'] }}"+" • ";
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -1,67 +1,81 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block head %}
|
||||
<meta name="description" content="{{ blogBase['subTitle'] }}">
|
||||
<meta property="og:title" content="{{ blogBase['title'] }}">
|
||||
<meta property="og:description" content="{{ blogBase['subTitle'] }}">
|
||||
<meta property="og:type" content="blog">
|
||||
<meta property="og:url" content="{{ blogBase['homeUrl'] }}">
|
||||
<meta property="og:image" content="{{ blogBase['ogImage'] }}">
|
||||
<title>{{ blogBase['title'] }}</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
<style>
|
||||
h1 a:not([href]){color:inherit;text-decoration:none;vertical-align: bottom;font-size:40px;font-family:Monaco;}
|
||||
.avatar {transition: 0.8s;width:64px;height:64px;object-fit: cover;}
|
||||
.avatar:hover{transform: scale(1.15) rotate(360deg);}
|
||||
.title-left a{color:inherit;text-decoration:none;vertical-align: bottom;font-size:40px;font-weight: bold;font-family:Monaco;margin-left:8px;}
|
||||
.title-right{display:flex;margin:auto 0 0 auto;}
|
||||
.title-right button{margin-right:8px;padding:16px;}
|
||||
|
||||
.subnav-search{margin-top:8px;margin-right:8px;}
|
||||
.subnav-search-input{width:160px;border-top-right-radius:0px;border-bottom-right-radius:0px;}
|
||||
.subnav-search button{padding:5px 8px;border-top-left-radius:0px;border-bottom-left-radius:0px;}
|
||||
|
||||
.title-right .circle{padding: 14px 16px;}
|
||||
|
||||
.SideNav{min-width: 360px;}
|
||||
.SideNav-icon{margin-right: 16px}
|
||||
.SideNav-item .Label{color: #fff;margin-left:8px;}
|
||||
@media (max-width: 767px) {
|
||||
body { padding: 8px;}
|
||||
h1 a:not([href]){font-size:24px;}
|
||||
.subnav-search form{display:none;}
|
||||
.subnav-search svg{display:none;}
|
||||
.SideNav-item .Label{color: #fff;margin-left:4px;}
|
||||
.d-flex{min-width:0;}
|
||||
.listTitle{overflow:hidden;white-space:nowrap;text-overflow: ellipsis;max-width: 100%;}
|
||||
.listLabels{white-space:nowrap;display:flex;}
|
||||
.listLabels object{max-height:16px;max-width:24px;}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
body {padding: 8px;}
|
||||
.avatar {width:40px;height:40px;}
|
||||
.blogTitle{display:none;}
|
||||
#buttonRSS{display:none;}
|
||||
.SideNav-item .listLabels{display:none;}
|
||||
.LabelTime{display:none;}
|
||||
}
|
||||
</style>
|
||||
{{ blogBase['indexStyle'] }}
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<h1>
|
||||
<img src="{{ blogBase['avatarUrl'] }}" size="64" height="64" width="64" class="avatar circle" id="avatarImg">
|
||||
<a>{{ blogBase['displayTitle'] }}</a>
|
||||
</h1>
|
||||
<div class="title-left">
|
||||
<img src="{{ blogBase['avatarUrl'] }}" class="avatar circle" id="avatarImg" alt="avatar">
|
||||
{%- if blogBase['displayTitle']=='Meekdai' -%}
|
||||
<a class="blogTitle" href="https://meekdai.com"><span style="font-size:0;">M</span>eekdai</a>
|
||||
{% else -%}
|
||||
<a class="blogTitle">{{ blogBase['displayTitle'] }}</a>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
<div class="title-right">
|
||||
<div class="subnav-search">
|
||||
<form action="https://www.google.com/search" method="get" target="_blank">
|
||||
<input type="hidden" name="q" value="site:{{ blogBase['homeUrl'] }}">
|
||||
<input type="search" name="q" class="form-control subnav-search-input float-left" aria-label="Search site" value="">
|
||||
<button class="btn float-left" type="submit">{{ i18n['Search'] }}</button>
|
||||
</form>
|
||||
<svg class="subnav-search-icon octicon octicon-search" width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
|
||||
<path id="searchSVG" fill-rule="evenodd" d=""></path>
|
||||
<a href="{{ blogBase['homeUrl'] }}/tag.html" id="buttonSearch" class="btn btn-invisible circle" title="{{ i18n['Search'] }}">
|
||||
<svg class="octicon" width="16" height="16" >
|
||||
<path id="pathSearch" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</a>
|
||||
{% for key, value in blogBase['exlink'].items() -%}
|
||||
<a href="{{ value }}" class="btn btn-invisible circle" title="{{ key }}" target="_blank">
|
||||
<svg class="octicon" width="16" height="16" >
|
||||
<path id="{{ key }}" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</a>
|
||||
{%- endfor %}
|
||||
{% for num in blogBase['singeListJson'] -%}
|
||||
<a href="/{{ blogBase['singeListJson'][num]['label'] }}.html"><button class="btn btn-invisible circle" title="{{ blogBase['singeListJson'][num]['postTitle'] }}">
|
||||
<a href="{{ blogBase['homeUrl'] }}/{{ blogBase['singeListJson'][num]['labels'][0] }}.html" class="btn btn-invisible circle" title="{{ blogBase['singeListJson'][num]['postTitle'] }}">
|
||||
<svg class="octicon" width="16" height="16" >
|
||||
<path id="{{ blogBase['singeListJson'][num]['postTitle'] }}" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button></a>
|
||||
</a>
|
||||
{%- endfor %}
|
||||
<a href="/rss.xml" target="_blank"><button id="buttonRSS" class="btn btn-invisible circle" title="RSS">
|
||||
<a href="{{ blogBase['homeUrl'] }}/rss.xml" target="_blank" id="buttonRSS" class="btn btn-invisible circle" title="RSS">
|
||||
<svg class="octicon" width="16" height="16" >
|
||||
<path id="pathRSS" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button></a>
|
||||
<button class="btn btn-invisible circle" onclick="modeSwitch()" title="{{ i18n['switchTheme'] }}">
|
||||
</a>
|
||||
<a class="btn btn-invisible circle" onclick="modeSwitch()" title="{{ i18n['switchTheme'] }}" {%- if blogBase['themeMode']=='fix' -%}style="display:none;"{%- endif -%}>
|
||||
<svg class="octicon" width="16" height="16" >
|
||||
<path id="themeSwitch" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -73,12 +87,14 @@ h1 a:not([href]){color:inherit;text-decoration:none;vertical-align: bottom;font-
|
||||
<div class="d-flex flex-items-center">
|
||||
<svg class="SideNav-icon octicon" style="witdh:16px;height:16px"><path class="svgTop{{ postListJson[num]['top'] }}" d=""></path>
|
||||
</svg>
|
||||
<span>{{ postListJson[num]['postTitle']|e }}</span>
|
||||
<span class="listTitle">{{ postListJson[num]['postTitle']|e }}</span>
|
||||
</div>
|
||||
<div class="listLabels">
|
||||
{% if postListJson[num]['commentNum']>0 %}<span class="Label" style="background-color:{{ blogBase['commentLabelColor'] }}">{{ postListJson[num]['commentNum'] }}</span>{% endif %}
|
||||
<span class="Label" style="background-color:{{ postListJson[num]['labelColor'] }}">{{ postListJson[num]['label'] }}</span>
|
||||
<span class="Label" style="background-color:{{ postListJson[num]['dateLabelColor'] }}">{{ postListJson[num]['createdDate'] }}</span>
|
||||
{% for label in postListJson[num]['labels'] -%}
|
||||
<span class="Label LabelName" style="background-color:{{ blogBase['labelColorDict'][label] }}"><object><a style="color:#fff" href="tag.html#{{ label }}">{{ label }}</a></object></span>
|
||||
{%- endfor %}
|
||||
<span class="Label LabelTime" style="background-color:{{ postListJson[num]['dateLabelColor'] }}">{{ postListJson[num]['createdDate'] }}</span>
|
||||
</div>
|
||||
</a>
|
||||
{%- endfor %}
|
||||
@ -89,13 +105,13 @@ h1 a:not([href]){color:inherit;text-decoration:none;vertical-align: bottom;font-
|
||||
{%- if blogBase['prevUrl']=='disabled' -%}
|
||||
<span class="previous_page" aria-disabled="true">{{ i18n['Previous'] }}</span>
|
||||
{% else -%}
|
||||
<a class="previous_page" rel="previous" href="{{ blogBase['prevUrl'] }}" aria-label="Previous Page">{{ i18n['Previous'] }}</a>
|
||||
<a class="previous_page" rel="previous" href="{{ blogBase['homeUrl'] }}{{ blogBase['prevUrl'] }}" aria-label="Previous Page">{{ i18n['Previous'] }}</a>
|
||||
{%- endif -%}
|
||||
|
||||
{%- if blogBase['nextUrl']=='disabled' -%}
|
||||
<span class="next_page" aria-disabled="true">{{ i18n['Next'] }}</span>
|
||||
{% else -%}
|
||||
<a class="next_page" rel="next" href="{{ blogBase['nextUrl'] }}" aria-label="Next Page">{{ i18n['Next'] }}</a>
|
||||
<a class="next_page" rel="next" href="{{ blogBase['homeUrl'] }}{{ blogBase['nextUrl'] }}" aria-label="Next Page">{{ i18n['Next'] }}</a>
|
||||
{%- endif -%}
|
||||
|
||||
</div>
|
||||
@ -105,7 +121,7 @@ h1 a:not([href]){color:inherit;text-decoration:none;vertical-align: bottom;font-
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
document.getElementById("searchSVG").setAttribute("d",IconList["search"]);
|
||||
document.getElementById("pathSearch").setAttribute("d",IconList["search"]);
|
||||
document.getElementById("pathRSS").setAttribute("d",IconList["rss"]);
|
||||
iconTOP=document.getElementsByClassName("svgTop1");
|
||||
iconPost=document.getElementsByClassName("svgTop0");
|
||||
@ -117,8 +133,13 @@ for(var i=0;i<iconPost.length;i++){
|
||||
iconPost[i].setAttribute("d",IconList["post"]);
|
||||
}
|
||||
|
||||
{% for key, value in blogBase['exlink'].items() %}
|
||||
document.getElementById("{{ key }}").setAttribute("d",value=IconList["{{ key }}"]);
|
||||
{%- endfor %}
|
||||
|
||||
{% for num in blogBase['singeListJson'] -%}
|
||||
document.getElementById("{{ blogBase['singeListJson'][num]['postTitle'] }}").setAttribute("d",value=IconList["{{ blogBase['singeListJson'][num]['label'] }}"]);
|
||||
document.getElementById("{{ blogBase['singeListJson'][num]['postTitle'] }}").setAttribute("d",value=IconList["{{ blogBase['singeListJson'][num]['labels'][0] }}"]);
|
||||
{%- endfor %}
|
||||
</script>
|
||||
{{ blogBase['indexScript'] }}
|
||||
{% endblock %}
|
||||
|
||||
@ -1,13 +1,20 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head %}
|
||||
<meta name="description" content="{{ blogBase['description'] }}">
|
||||
<meta property="og:title" content="{{ blogBase['postTitle'] }}">
|
||||
<meta property="og:description" content="{{ blogBase['description'] }}">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="{{ blogBase['postUrl'] }}">
|
||||
<meta property="og:image" content="{{ blogBase['ogImage'] }}">
|
||||
<title>{{ blogBase['postTitle'] }}</title>
|
||||
<link href="//unpkg.com/@wooorm/starry-night@2.1.1/style/both.css" rel="stylesheet" />
|
||||
{% if blogBase['highlight']==1 %}<link href="//unpkg.com/@wooorm/starry-night@2.1.1/style/both.css" rel="stylesheet" />{% endif %}
|
||||
{{ blogBase['head'] }}
|
||||
{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
<style>
|
||||
h1 a:not([href]){color:inherit;text-decoration:none;vertical-align: bottom;font-size:40px;}
|
||||
.postTitle{margin: auto 0;font-size:40px;font-weight:bold;}
|
||||
.title-right{display:flex;margin:auto 0 0 auto;}
|
||||
.title-right .circle{padding: 14px 16px;margin-right:8px;}
|
||||
#postBody{border-bottom: 1px solid var(--color-border-default);padding-bottom:36px;}
|
||||
@ -15,49 +22,64 @@ h1 a:not([href]){color:inherit;text-decoration:none;vertical-align: bottom;font-
|
||||
#cmButton{height:48px;margin-top:48px;}
|
||||
#comments{margin-top:64px;}
|
||||
.g-emoji{font-size:24px;}
|
||||
@media (max-width: 767px) {body {padding: 8px;}}
|
||||
@media (max-width: 600px) {
|
||||
body {padding: 8px;}
|
||||
.postTitle{font-size:24px;}
|
||||
}
|
||||
{% if blogBase['highlight']!=0 -%}
|
||||
.copy-feedback {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 50px;
|
||||
color: var(--color-fg-on-emphasis);
|
||||
background-color: var(--color-fg-muted);
|
||||
border-radius: 3px;
|
||||
padding: 5px 8px;
|
||||
font-size: 12px;
|
||||
}{% endif %}
|
||||
</style>
|
||||
{{ blogBase['style'] }}
|
||||
|
||||
{% endblock %}
|
||||
{% block header %}
|
||||
<h1>
|
||||
<a>{{ blogBase['postTitle'] }}</a>
|
||||
</h1>
|
||||
<h1 class="postTitle">{{ blogBase['postTitle'] }}</h1>
|
||||
<div class="title-right">
|
||||
|
||||
<a href="/"><button id="buttonHome" class="btn btn-invisible circle" title="{{ i18n['home'] }}">
|
||||
<a href="{{ blogBase['homeUrl'] }}" id="buttonHome" class="btn btn-invisible circle" title="{{ i18n['home'] }}">
|
||||
<svg class="octicon" width="16" height="16">
|
||||
<path id="pathHome" fill-rule="evenodd" d="{{ IconList['home'] }}"></path>
|
||||
<path id="pathHome" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button></a>
|
||||
|
||||
<a href="{{ blogBase['postSourceUrl'] }}" target="_blank"><button class="btn btn-invisible circle" title="Issue">
|
||||
</a>
|
||||
{% if blogBase['showPostSource']==1 %}
|
||||
<a href="{{ blogBase['postSourceUrl'] }}" target="_blank" class="btn btn-invisible circle" title="Issue">
|
||||
<svg class="octicon" width="16" height="16">
|
||||
<path id="pathIssue" fill-rule="evenodd" d="{{ IconList['github'] }}"></path>
|
||||
<path id="pathIssue" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button></a>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<button class="btn btn-invisible circle" onclick="modeSwitch();" title="{{ i18n['switchTheme'] }}">
|
||||
<a class="btn btn-invisible circle" onclick="modeSwitch();" title="{{ i18n['switchTheme'] }}" {%- if blogBase['themeMode']=='fix' -%}style="display:none;"{%- endif -%}>
|
||||
<svg class="octicon" width="16" height="16" >
|
||||
<path id="themeSwitch" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="markdown-body" id="postBody">
|
||||
{{ blogBase['postBody'] }}
|
||||
</div>
|
||||
<div class="markdown-body" id="postBody">{{ blogBase['postBody'] }}</div>
|
||||
<div style="font-size:small;margin-top:8px;float:right;">{{ blogBase['bottomText'] }}</div>
|
||||
{% if blogBase['needComment']==1 %}
|
||||
<button class="btn btn-block" type="button" onclick="openComments()" id="cmButton">{{ i18n['comments'] }}</button>
|
||||
<div class="comments" id="comments"></div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
|
||||
document.getElementById("pathHome").setAttribute("d",IconList["home"]);
|
||||
{% if blogBase['showPostSource']==1 %}document.getElementById("pathIssue").setAttribute("d",IconList["github"]);{% endif %}
|
||||
{% if blogBase['commentNum']>0 -%}
|
||||
cmButton=document.getElementById("cmButton");
|
||||
span=document.createElement("span");
|
||||
@ -66,6 +88,7 @@ h1 a:not([href]){color:inherit;text-decoration:none;vertical-align: bottom;font-
|
||||
cmButton.appendChild(span);
|
||||
{%- endif %}
|
||||
|
||||
{% if blogBase['needComment']==1 %}
|
||||
function openComments(){
|
||||
cm=document.getElementById("comments");
|
||||
cmButton=document.getElementById("cmButton");
|
||||
@ -78,8 +101,13 @@ function openComments(){
|
||||
script.setAttribute("src","https://utteranc.es/client.js");
|
||||
script.setAttribute("repo","{{ blogBase['repoName'] }}");
|
||||
script.setAttribute("issue-term","title");
|
||||
{% if blogBase['themeMode']=='manual' %}
|
||||
if(localStorage.getItem("meek_theme")=="dark"){script.setAttribute("theme","dark-blue");}
|
||||
else{script.setAttribute("theme","github-light");}
|
||||
else if(localStorage.getItem("meek_theme")=="light") {script.setAttribute("theme","github-light");}
|
||||
else{script.setAttribute("theme","preferred-color-scheme");}
|
||||
{% else %}
|
||||
script.setAttribute("theme","{{ blogBase['nightTheme'] }}");
|
||||
{% endif %}
|
||||
script.setAttribute("crossorigin","anonymous");
|
||||
script.setAttribute("async","");
|
||||
cm.appendChild(script);
|
||||
@ -98,9 +126,75 @@ function iFrameLoading(){
|
||||
}
|
||||
}
|
||||
}
|
||||
{%- endif %}
|
||||
|
||||
{% if blogBase['highlight']!=0 -%}
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const createClipboardHTML = (codeContent, additionalClasses = '') => `
|
||||
<pre class="notranslate"><code class="notranslate">${codeContent}</code></pre>
|
||||
<div class="clipboard-container position-absolute right-0 top-0 ${additionalClasses}">
|
||||
<clipboard-copy class="ClipboardButton btn m-2 p-0" role="button" style="display: inherit;">
|
||||
<svg height="16" width="16" class="octicon octicon-copy m-2"><path d="${IconList["copy"]}"></path></svg>
|
||||
<svg height="16" width="16" class="octicon octicon-check color-fg-success m-2 d-none"><path d="${IconList["check"]}"></path></svg>
|
||||
</clipboard-copy>
|
||||
<div class="copy-feedback">Copied!</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const handleCodeElements = (selector = '') => {
|
||||
document.querySelectorAll(selector).forEach(codeElement => {
|
||||
const codeContent = codeElement.innerHTML;
|
||||
const newStructure = document.createElement('div');
|
||||
newStructure.className = 'snippet-clipboard-content position-relative overflow-auto';
|
||||
newStructure.innerHTML = createClipboardHTML(codeContent);
|
||||
|
||||
const parentElement = codeElement.parentElement;
|
||||
if (selector.includes('highlight')) {
|
||||
parentElement.insertBefore(newStructure, codeElement.nextSibling);
|
||||
parentElement.removeChild(codeElement);
|
||||
} else {
|
||||
parentElement.parentElement.replaceChild(newStructure, parentElement);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleCodeElements('pre.notranslate > code.notranslate');
|
||||
handleCodeElements('div.highlight > pre.notranslate');
|
||||
|
||||
let currentFeedback = null;
|
||||
document.querySelectorAll('clipboard-copy').forEach(copyButton => {
|
||||
copyButton.addEventListener('click', () => {
|
||||
const codeContent = copyButton.closest('.snippet-clipboard-content').innerText;
|
||||
const tempTextArea = document.createElement('textarea');
|
||||
tempTextArea.value = codeContent;
|
||||
document.body.appendChild(tempTextArea);
|
||||
tempTextArea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(tempTextArea);
|
||||
|
||||
const copyIcon = copyButton.querySelector('.octicon-copy');
|
||||
const checkIcon = copyButton.querySelector('.octicon-check');
|
||||
const copyFeedback = copyButton.nextElementSibling;
|
||||
|
||||
if (currentFeedback && currentFeedback !== copyFeedback) {currentFeedback.style.display = 'none';}
|
||||
currentFeedback = copyFeedback;
|
||||
|
||||
copyIcon.classList.add('d-none');
|
||||
checkIcon.classList.remove('d-none');
|
||||
copyFeedback.style.display = 'block';
|
||||
copyButton.style.borderColor = 'var(--color-success-fg)';
|
||||
|
||||
setTimeout(() => {
|
||||
copyIcon.classList.remove('d-none');
|
||||
checkIcon.classList.add('d-none');
|
||||
copyFeedback.style.display = 'none';
|
||||
copyButton.style.borderColor = '';
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
});
|
||||
{%- endif %}
|
||||
|
||||
</script>
|
||||
|
||||
{{ blogBase['script'] }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
221
templates/tag.html
Normal file
221
templates/tag.html
Normal file
@ -0,0 +1,221 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block head %}
|
||||
<meta name="description" content="{{ blogBase['title'] }} search page">
|
||||
<title>{{ blogBase['title'] }} - Tag</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
<style>
|
||||
.tagTitle{margin:auto 0;font-size:40px;font-weight:bold;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}
|
||||
.title-right{display:flex;margin:auto 0 0 auto;}
|
||||
.title-right .circle{padding: 14px 16px;margin-right:8px;}
|
||||
|
||||
.subnav-search{width:222px;margin-top:8px;margin-right:8px;}
|
||||
.subnav-search-input{width:160px;border-top-right-radius:0px;border-bottom-right-radius:0px;}
|
||||
.subnav-search button{padding:5px 8px;border-top-left-radius:0px;border-bottom-left-radius:0px;}
|
||||
|
||||
.SideNav-icon{margin-right:16px}
|
||||
.Label{color: #fff;margin-left:4px;}
|
||||
#taglabel .Label {margin-bottom:8px;}
|
||||
.Counter{color:#fff;background-color:rgba(234, 238, 242, 0.5)}
|
||||
|
||||
.genTime{float: right;}
|
||||
.d-flex{min-width:0;}
|
||||
.listTitle{overflow:hidden;white-space:nowrap;text-overflow: ellipsis;max-width: 100%;}
|
||||
.listLabels{white-space:nowrap;}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
body { padding: 8px;}
|
||||
.tagTitle{display:none;}
|
||||
.LabelTime{display:none;}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<span class="tagTitle"><span>Loading</span><span class="AnimatedEllipsis"></span></span>
|
||||
<div class="title-right">
|
||||
<div class="subnav-search">
|
||||
<input type="search" class="form-control subnav-search-input float-left" aria-label="Search site" value="" style="height:32px;">
|
||||
<button class="btn float-left" type="submit" onclick="javascript:searchShow()">{{ i18n['Search'] }}</button>
|
||||
<svg class="subnav-search-icon octicon octicon-search" width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
|
||||
<path id="searchSVG" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<a href="{{ blogBase['homeUrl'] }}" id="buttonHome" class="btn btn-invisible circle" title="{{ i18n['home'] }}">
|
||||
<svg class="octicon" width="16" height="16">
|
||||
<path id="pathHome" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="btn btn-invisible circle" onclick="modeSwitch()" title="{{ i18n['switchTheme'] }}" {%- if blogBase['themeMode']=='fix' -%}style="display:none;"{%- endif -%}>
|
||||
<svg class="octicon" width="16" height="16" >
|
||||
<path id="themeSwitch" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="taglabel" style="margin-bottom:8px;"></div>
|
||||
<nav class="SideNav"></nav>
|
||||
<div class="notFind" style="display:none;font-size:24px;margin:8px;">Not Find</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
document.getElementById("pathHome").setAttribute("d",IconList["home"]);
|
||||
document.getElementById("searchSVG").setAttribute("d",IconList["search"]);
|
||||
|
||||
tagList=[];
|
||||
labelsCount={};
|
||||
jsonData='';
|
||||
let requestJson="postList.json"
|
||||
let request=new XMLHttpRequest();
|
||||
request.open("GET",requestJson);
|
||||
request.responseType='text';
|
||||
request.send();
|
||||
request.onload=function(){
|
||||
jsonData=JSON.parse(request.response);
|
||||
console.log(jsonData);
|
||||
showList(labelsCount);
|
||||
setClassDisplay(decodeURI(window.location.hash.slice(1)));
|
||||
}
|
||||
|
||||
function showList(labelsCount){
|
||||
let SideNav=document.getElementsByClassName("SideNav")[0];
|
||||
SideNav.classList.add("border");
|
||||
let taglabel=document.getElementById("taglabel");
|
||||
|
||||
jsonData['labelColorDict']["All"]="#000";
|
||||
labelsCount["All"]=0;
|
||||
for (let key in jsonData) {
|
||||
if (key !== 'labelColorDict' && Array.isArray(jsonData[key]['labels'])) {
|
||||
labelsCount["All"]++;
|
||||
for (let label of jsonData[key]['labels']) {
|
||||
labelsCount[label] = (labelsCount[label] || 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let sortedLabelsList = Object.keys(labelsCount).sort((a, b) => labelsCount[b] - labelsCount[a]);
|
||||
for (let label of sortedLabelsList) {
|
||||
tagList.push(label);
|
||||
let showLabels = document.createElement("button");
|
||||
showLabels.setAttribute("class", "Label");
|
||||
showLabels.setAttribute("style", "background-color:" + jsonData['labelColorDict'][label]+";padding:4px;");
|
||||
showLabels.innerHTML=" "+label+" ";
|
||||
showLabels.setAttribute("onclick", "javascript:updateShowTag('" + label + "');");
|
||||
|
||||
let LabelNum=document.createElement("span");
|
||||
LabelNum.setAttribute("class","Counter");
|
||||
LabelNum.innerHTML=labelsCount[label];
|
||||
showLabels.appendChild(LabelNum);
|
||||
taglabel.appendChild(showLabels);
|
||||
}
|
||||
|
||||
for(i in jsonData){
|
||||
if(i!='labelColorDict'){
|
||||
let div=document.createElement("div");
|
||||
div.setAttribute("class","lists "+jsonData[i]['labels'].join(" "));
|
||||
let item=document.createElement("a");
|
||||
item.setAttribute("class","SideNav-item d-flex flex-items-center flex-justify-between");
|
||||
item.setAttribute("href",jsonData[i]['postUrl']);
|
||||
|
||||
let center=document.createElement("div");
|
||||
center.setAttribute("class","d-flex flex-items-center");
|
||||
|
||||
svg=document.createElementNS('http://www.w3.org/2000/svg','svg');
|
||||
path=document.createElementNS("http://www.w3.org/2000/svg","path");
|
||||
span=document.createElement("span");
|
||||
svg.setAttributeNS(null,"class","SideNav-icon octicon");
|
||||
svg.setAttributeNS(null,"style","width:16px;height:16px");
|
||||
path.setAttributeNS(null, "d", IconList["post"]);
|
||||
svg.appendChild(path);
|
||||
|
||||
let title=document.createElement("span");
|
||||
title.setAttribute("class","listTitle");
|
||||
title.innerHTML=jsonData[i]['postTitle'];
|
||||
center.appendChild(svg);
|
||||
center.appendChild(title);
|
||||
|
||||
let listLabels=document.createElement("div");
|
||||
listLabels.setAttribute("class","listLabels");
|
||||
|
||||
for(label of jsonData[i]['labels']){
|
||||
let LabelName=document.createElement("span");
|
||||
LabelName.setAttribute("class","Label LabelName");
|
||||
LabelName.setAttribute("style","background-color:"+jsonData['labelColorDict'][label]);
|
||||
LabelName.innerHTML=label;
|
||||
listLabels.appendChild(LabelName);
|
||||
}
|
||||
let LabelTime=document.createElement("span");
|
||||
LabelTime.setAttribute("class","Label LabelTime");
|
||||
LabelTime.setAttribute("style","background-color:"+jsonData[i]['dateLabelColor']);
|
||||
LabelTime.innerHTML=jsonData[i]['createdDate'];
|
||||
listLabels.appendChild(LabelTime);
|
||||
|
||||
item.appendChild(center);
|
||||
item.appendChild(listLabels);
|
||||
div.appendChild(item);
|
||||
SideNav.appendChild(div);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateShowTag(label){
|
||||
if(window.location.hash.slice(1)!=encodeURI(label)){
|
||||
window.location.hash="#"+(label);
|
||||
setClassDisplay(label);
|
||||
}
|
||||
}
|
||||
|
||||
function setClassDisplay(label){
|
||||
let lists = document.getElementsByClassName("lists");
|
||||
let tagTitle = document.getElementsByClassName("tagTitle")[0];
|
||||
tagTitle.innerHTML="Tag #"+label;
|
||||
document.title=label+" - {{ blogBase['title'] }}";
|
||||
document.getElementsByClassName("subnav-search-input")[0].value='';
|
||||
if(label=="All"){
|
||||
for(let i = 0; i < lists.length; i++){lists[i].style.display='block';}
|
||||
document.getElementsByClassName("notFind")[0].style.display='none';
|
||||
}
|
||||
else if(tagList.indexOf(label)!=-1){
|
||||
for(let i = 0; i < lists.length; i++){
|
||||
lists[i].style.display='none';
|
||||
}
|
||||
|
||||
let labels = document.getElementsByClassName(label);
|
||||
for(let i = 0; i < labels.length; i++){
|
||||
labels[i].style.display='block';
|
||||
}
|
||||
document.getElementsByClassName("notFind")[0].style.display='none';
|
||||
}
|
||||
else{
|
||||
document.getElementsByClassName("subnav-search-input")[0].value=label;
|
||||
searchShow();
|
||||
}
|
||||
}
|
||||
|
||||
function searchShow(){
|
||||
let lists = document.getElementsByClassName("lists");
|
||||
let tagTitle = document.getElementsByClassName("tagTitle")[0];
|
||||
let searchInput = document.getElementsByClassName("subnav-search-input")[0].value;
|
||||
tagTitle.innerHTML="Search #"+searchInput;
|
||||
if(searchInput==''){document.title="Search - {{ blogBase['title'] }}";}
|
||||
else{document.title=searchInput+" - {{ blogBase['title'] }}";}
|
||||
let a=0;
|
||||
window.location.hash="#"+(searchInput);
|
||||
for(let i = 0; i < lists.length; i++){
|
||||
if(lists[i].childNodes[0].childNodes[0].childNodes[1].innerHTML.toUpperCase().indexOf(searchInput.toUpperCase())==-1){lists[i].style.display='none';}
|
||||
else{lists[i].style.display='block';a=a+1;}
|
||||
}
|
||||
if(a==0){
|
||||
let notFind=document.getElementsByClassName("notFind")[0];
|
||||
notFind.style.display='block';
|
||||
notFind.innerHTML='Not Find "'+searchInput+'"';
|
||||
}
|
||||
else{document.getElementsByClassName("notFind")[0].style.display='none';}
|
||||
}
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
Loading…
Reference in New Issue
Block a user