mkdocs-document-dates 3.5.2__tar.gz → 3.6.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mkdocs_document_dates-3.5.2/mkdocs_document_dates.egg-info → mkdocs_document_dates-3.6.1}/PKG-INFO +8 -4
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/README.md +7 -3
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/plugin.py +13 -22
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/core/core.js +84 -14
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/core/utils.js +12 -0
- mkdocs_document_dates-3.6.1/mkdocs_document_dates/static/templates/recently_updated_detail.html +99 -0
- mkdocs_document_dates-3.6.1/mkdocs_document_dates/static/templates/recently_updated_grid.html +106 -0
- mkdocs_document_dates-3.6.1/mkdocs_document_dates/static/templates/recently_updated_group.html +208 -0
- mkdocs_document_dates-3.5.2/mkdocs_document_dates/static/templates/recently_updated.html → mkdocs_document_dates-3.6.1/mkdocs_document_dates/static/templates/recently_updated_list.html +23 -15
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/utils.py +220 -9
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1/mkdocs_document_dates.egg-info}/PKG-INFO +8 -4
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates.egg-info/SOURCES.txt +4 -1
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/setup.py +1 -1
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/LICENSE +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/MANIFEST.in +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/__init__.py +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/cache_manager.py +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/hooks/pre-commit +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/hooks_installer.py +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/config/user.config.css +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/config/user.config.js +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/core/core.css +1 -1
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/core/default.config.js +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/core/md5.min.js +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/core/timeago.full.min.js +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/core/timeago.min.js +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/fonts/material-icons.css +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/fonts/materialicons.woff2 +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/tippy/backdrop.css +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/tippy/light.css +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/tippy/material.css +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/tippy/popper.min.js +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/tippy/scale.css +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/tippy/shift-away.css +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/tippy/tippy.css +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/static/tippy/tippy.umd.min.js +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates.egg-info/dependency_links.txt +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates.egg-info/entry_points.txt +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates.egg-info/requires.txt +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates.egg-info/top_level.txt +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/pyproject.toml +0 -0
- {mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/setup.cfg +0 -0
{mkdocs_document_dates-3.5.2/mkdocs_document_dates.egg-info → mkdocs_document_dates-3.6.1}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mkdocs-document-dates
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.6.1
|
|
4
4
|
Summary: A new generation MkDocs plugin for displaying exact creation date, last updated date, authors, email of documents
|
|
5
5
|
Home-page: https://github.com/jaywhj/mkdocs-document-dates
|
|
6
6
|
Author: Aaron Wang
|
|
@@ -67,7 +67,7 @@ plugins:
|
|
|
67
67
|
- document-dates
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
Or,
|
|
70
|
+
Or, common configuration:
|
|
71
71
|
|
|
72
72
|
```yaml
|
|
73
73
|
plugins:
|
|
@@ -77,8 +77,6 @@ plugins:
|
|
|
77
77
|
exclude: # List of excluded files
|
|
78
78
|
- temp.md # Example: exclude the specified file
|
|
79
79
|
- blog/* # Example: exclude all files in blog folder, including subfolders
|
|
80
|
-
date_format: '%Y-%m-%d' # Date format strings (e.g., %Y-%m-%d, %b %d, %Y)
|
|
81
|
-
time_format: '%H:%M:%S' # Time format strings (valid only if type=datetime)
|
|
82
80
|
```
|
|
83
81
|
|
|
84
82
|
## Customization Settings
|
|
@@ -97,6 +95,12 @@ In addition to the above basic configuration, the plug-in also provides a wealth
|
|
|
97
95
|
|
|
98
96
|
See the documentation for details: https://jaywhj.netlify.app/document-dates-en
|
|
99
97
|
|
|
98
|
+

|
|
99
|
+
|
|
100
|
+
## Other Projects
|
|
101
|
+
|
|
102
|
+
- [**MaterialX**](https://github.com/jaywhj/mkdocs-materialx), the next generation of mkdocs-material, is based on `mkdocs-material-9.7.0` and is named `X`. I'll be maintaining this branch continuously (since mkdocs-material will stop being maintained).
|
|
103
|
+
Updates have been released that refactor and add a lot of new features, see https://github.com/jaywhj/mkdocs-materialx/releases/
|
|
100
104
|
|
|
101
105
|
<br />
|
|
102
106
|
|
|
@@ -41,7 +41,7 @@ plugins:
|
|
|
41
41
|
- document-dates
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Or,
|
|
44
|
+
Or, common configuration:
|
|
45
45
|
|
|
46
46
|
```yaml
|
|
47
47
|
plugins:
|
|
@@ -51,8 +51,6 @@ plugins:
|
|
|
51
51
|
exclude: # List of excluded files
|
|
52
52
|
- temp.md # Example: exclude the specified file
|
|
53
53
|
- blog/* # Example: exclude all files in blog folder, including subfolders
|
|
54
|
-
date_format: '%Y-%m-%d' # Date format strings (e.g., %Y-%m-%d, %b %d, %Y)
|
|
55
|
-
time_format: '%H:%M:%S' # Time format strings (valid only if type=datetime)
|
|
56
54
|
```
|
|
57
55
|
|
|
58
56
|
## Customization Settings
|
|
@@ -71,6 +69,12 @@ In addition to the above basic configuration, the plug-in also provides a wealth
|
|
|
71
69
|
|
|
72
70
|
See the documentation for details: https://jaywhj.netlify.app/document-dates-en
|
|
73
71
|
|
|
72
|
+

|
|
73
|
+
|
|
74
|
+
## Other Projects
|
|
75
|
+
|
|
76
|
+
- [**MaterialX**](https://github.com/jaywhj/mkdocs-materialx), the next generation of mkdocs-material, is based on `mkdocs-material-9.7.0` and is named `X`. I'll be maintaining this branch continuously (since mkdocs-material will stop being maintained).
|
|
77
|
+
Updates have been released that refactor and add a lot of new features, see https://github.com/jaywhj/mkdocs-materialx/releases/
|
|
74
78
|
|
|
75
79
|
<br />
|
|
76
80
|
|
|
@@ -10,7 +10,7 @@ from mkdocs.config import config_options
|
|
|
10
10
|
from mkdocs.structure.pages import Page
|
|
11
11
|
from mkdocs.utils import get_relative_url
|
|
12
12
|
from urllib.parse import urlparse
|
|
13
|
-
from .utils import get_file_creation_time, load_git_metadata, load_git_last_updated_date, read_jsonl_cache,is_excluded, get_recently_updated_files
|
|
13
|
+
from .utils import get_file_creation_time, load_git_metadata, load_git_last_updated_date, read_jsonl_cache, compile_exclude_patterns, is_excluded, get_recently_updated_files
|
|
14
14
|
|
|
15
15
|
logger = logging.getLogger("mkdocs.plugins.document_dates")
|
|
16
16
|
logger.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
|
@@ -46,9 +46,9 @@ class DocumentDatesPlugin(BasePlugin):
|
|
|
46
46
|
self.dates_cache = {}
|
|
47
47
|
self.last_updated_dates = {}
|
|
48
48
|
self.authors_yml = {}
|
|
49
|
-
self.github_username = None
|
|
50
49
|
self.recent_docs_html = None
|
|
51
50
|
self.recent_enable = False
|
|
51
|
+
self._exclude_patterns = []
|
|
52
52
|
|
|
53
53
|
def on_config(self, config):
|
|
54
54
|
docs_dir_path = Path(config['docs_dir'])
|
|
@@ -139,6 +139,8 @@ class DocumentDatesPlugin(BasePlugin):
|
|
|
139
139
|
'assets/document_dates/core/core.js'
|
|
140
140
|
])
|
|
141
141
|
|
|
142
|
+
self._exclude_patterns = compile_exclude_patterns(self.config['exclude'])
|
|
143
|
+
|
|
142
144
|
return config
|
|
143
145
|
|
|
144
146
|
def on_page_markdown(self, markdown, page: Page, config, files):
|
|
@@ -165,7 +167,7 @@ class DocumentDatesPlugin(BasePlugin):
|
|
|
165
167
|
page.meta['document_dates_authors'] = authors
|
|
166
168
|
|
|
167
169
|
# 检查是否需要排除
|
|
168
|
-
if is_excluded(rel_path, self.
|
|
170
|
+
if is_excluded(rel_path, self._exclude_patterns):
|
|
169
171
|
return markdown
|
|
170
172
|
|
|
171
173
|
# 生成日期和作者信息 HTML
|
|
@@ -186,10 +188,10 @@ class DocumentDatesPlugin(BasePlugin):
|
|
|
186
188
|
# 获取配置
|
|
187
189
|
exclude_list = recently_updated_config.get('exclude', [])
|
|
188
190
|
limit = recently_updated_config.get('limit', 10)
|
|
189
|
-
template_path = recently_updated_config.get('template')
|
|
190
191
|
|
|
191
192
|
# 获取最近更新的文档数据
|
|
192
|
-
|
|
193
|
+
recent_exclude_patterns = compile_exclude_patterns(exclude_list)
|
|
194
|
+
recently_updated_docs = get_recently_updated_files(self.last_updated_dates, files, recent_exclude_patterns, limit, self.recent_enable)
|
|
193
195
|
|
|
194
196
|
# 将数据注入到 config['extra'] 中供全局访问
|
|
195
197
|
if 'extra' not in config:
|
|
@@ -198,8 +200,7 @@ class DocumentDatesPlugin(BasePlugin):
|
|
|
198
200
|
|
|
199
201
|
# 渲染HTML
|
|
200
202
|
if self.recent_enable:
|
|
201
|
-
|
|
202
|
-
self.recent_docs_html = self._render_recently_updated_html(docs_dir, template_path, recently_updated_docs)
|
|
203
|
+
self.recent_docs_html = self._render_recently_updated_html(recently_updated_docs)
|
|
203
204
|
|
|
204
205
|
return env
|
|
205
206
|
|
|
@@ -233,20 +234,10 @@ class DocumentDatesPlugin(BasePlugin):
|
|
|
233
234
|
logger.info(f"Error parsing .authors.yml: {e}")
|
|
234
235
|
|
|
235
236
|
|
|
236
|
-
def _render_recently_updated_html(self,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
# 选择模板路径
|
|
242
|
-
if template_path and user_full_path.is_file():
|
|
243
|
-
template_dir = user_full_path.parent
|
|
244
|
-
template_file = user_full_path.name
|
|
245
|
-
else:
|
|
246
|
-
# 默认模板路径
|
|
247
|
-
default_template_path = Path(__file__).parent / 'static' / 'templates' / 'recently_updated.html'
|
|
248
|
-
template_dir = default_template_path.parent
|
|
249
|
-
template_file = default_template_path.name
|
|
237
|
+
def _render_recently_updated_html(self, recently_updated_data):
|
|
238
|
+
default_template_path = Path(__file__).parent / 'static' / 'templates' / 'recently_updated_group.html'
|
|
239
|
+
template_dir = default_template_path.parent
|
|
240
|
+
template_file = default_template_path.name
|
|
250
241
|
|
|
251
242
|
# 加载模板
|
|
252
243
|
env = Environment(
|
|
@@ -490,4 +481,4 @@ class DocumentDatesPlugin(BasePlugin):
|
|
|
490
481
|
|
|
491
482
|
pos = next_newline + 1
|
|
492
483
|
|
|
493
|
-
return '', length
|
|
484
|
+
return '', length
|
|
@@ -144,25 +144,30 @@ const iconKeyMap = {
|
|
|
144
144
|
doc_author: 'author',
|
|
145
145
|
doc_authors: 'authors'
|
|
146
146
|
};
|
|
147
|
+
|
|
148
|
+
function applyTimeagoToTimes(timeNodes, rawLocale) {
|
|
149
|
+
if (typeof timeago === 'undefined') {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (!timeNodes || !timeNodes.length) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const tLocale = ddUtils.resolveTimeagoLocale(rawLocale);
|
|
156
|
+
timeNodes.forEach(timeEl => {
|
|
157
|
+
const dt = timeEl.getAttribute('datetime');
|
|
158
|
+
if (dt) {
|
|
159
|
+
timeEl.textContent = timeago.format(dt, tLocale);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
147
164
|
// 处理数据加载
|
|
148
165
|
function processDataLoading() {
|
|
149
166
|
document.querySelectorAll('.document-dates-plugin').forEach(ddpEl => {
|
|
150
|
-
|
|
151
|
-
const rawLocale =
|
|
152
|
-
ddUtils.getSavedLanguage() ||
|
|
153
|
-
ddpEl.getAttribute('locale') ||
|
|
154
|
-
navigator.language ||
|
|
155
|
-
navigator.userLanguage ||
|
|
156
|
-
document.documentElement.lang ||
|
|
157
|
-
'en';
|
|
167
|
+
const rawLocale = ddUtils.getCurrentLocale(ddpEl);
|
|
158
168
|
|
|
159
169
|
// 处理 time 元素(使用 timeago 时)
|
|
160
|
-
|
|
161
|
-
const tLocale = ddUtils.resolveTimeagoLocale(rawLocale);
|
|
162
|
-
ddpEl.querySelectorAll('time').forEach(timeEl => {
|
|
163
|
-
timeEl.textContent = timeago.format(timeEl.getAttribute('datetime'), tLocale);
|
|
164
|
-
});
|
|
165
|
-
}
|
|
170
|
+
applyTimeagoToTimes(ddpEl.querySelectorAll('time'), rawLocale);
|
|
166
171
|
|
|
167
172
|
// 动态处理 tooltip 内容
|
|
168
173
|
const langData = TooltipLanguage.get(rawLocale);
|
|
@@ -178,6 +183,10 @@ function processDataLoading() {
|
|
|
178
183
|
}
|
|
179
184
|
});
|
|
180
185
|
});
|
|
186
|
+
|
|
187
|
+
// 处理其他 timeago 时间
|
|
188
|
+
const rawLocale = ddUtils.getCurrentLocale();
|
|
189
|
+
applyTimeagoToTimes(document.querySelectorAll('time.dd-timeago'), rawLocale);
|
|
181
190
|
}
|
|
182
191
|
|
|
183
192
|
// 供外部使用:更新文档日期和 tippy 内容到指定语言(可持久化)
|
|
@@ -359,6 +368,65 @@ function handleDocumentDatesAutoWrap() {
|
|
|
359
368
|
});
|
|
360
369
|
}
|
|
361
370
|
|
|
371
|
+
// 最近更新 - 布局切换器 (Layout Switcher)
|
|
372
|
+
function initLayoutSwitcher() {
|
|
373
|
+
const grids = document.querySelectorAll('.article-grid');
|
|
374
|
+
if (!grids.length) return;
|
|
375
|
+
|
|
376
|
+
const savedLayout = localStorage.getItem('dd_recent_docs_layout') || 'grid';
|
|
377
|
+
|
|
378
|
+
grids.forEach(grid => {
|
|
379
|
+
// 应用初始布局
|
|
380
|
+
grid.classList.toggle('is-list', savedLayout === 'list');
|
|
381
|
+
grid.classList.toggle('is-detail', savedLayout === 'detail');
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
// 查找或创建切换器容器
|
|
385
|
+
let switcher = grid.previousElementSibling;
|
|
386
|
+
if (!switcher || !switcher.classList.contains('article-layout-switcher')) {
|
|
387
|
+
// 如果模板中没写,可以动态注入,但建议写在模板里以保证 UI 一致性
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const listBtn = switcher.querySelector('.layout-list-btn');
|
|
391
|
+
const detailBtn = switcher.querySelector('.layout-detail-btn');
|
|
392
|
+
const gridBtn = switcher.querySelector('.layout-grid-btn');
|
|
393
|
+
|
|
394
|
+
const updateActiveBtn = (layout) => {
|
|
395
|
+
if (listBtn) listBtn.classList.toggle('is-active', layout === 'list');
|
|
396
|
+
if (detailBtn) detailBtn.classList.toggle('is-active', layout === 'detail');
|
|
397
|
+
if (gridBtn) gridBtn.classList.toggle('is-active', layout === 'grid');
|
|
398
|
+
};
|
|
399
|
+
updateActiveBtn(savedLayout);
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
const setLayout = (layout) => {
|
|
403
|
+
grid.classList.remove('is-list', 'is-detail');
|
|
404
|
+
if (layout !== 'grid') {
|
|
405
|
+
grid.classList.add(`is-${layout}`);
|
|
406
|
+
}
|
|
407
|
+
localStorage.setItem('dd_recent_docs_layout', layout);
|
|
408
|
+
updateActiveBtn(layout);
|
|
409
|
+
};
|
|
410
|
+
if (listBtn) {
|
|
411
|
+
listBtn.onclick = () => {
|
|
412
|
+
setLayout('list');
|
|
413
|
+
listBtn.blur();
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
if (detailBtn) {
|
|
417
|
+
detailBtn.onclick = () => {
|
|
418
|
+
setLayout('detail');
|
|
419
|
+
detailBtn.blur();
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
if (gridBtn) {
|
|
423
|
+
gridBtn.onclick = () => {
|
|
424
|
+
setLayout('grid');
|
|
425
|
+
gridBtn.blur();
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
}
|
|
362
430
|
|
|
363
431
|
/*
|
|
364
432
|
入口
|
|
@@ -367,6 +435,7 @@ let datesAutoWrapObserver = null;
|
|
|
367
435
|
function initPluginFeatures() {
|
|
368
436
|
tippyManager.initialize();
|
|
369
437
|
processDataLoading();
|
|
438
|
+
initLayoutSwitcher();
|
|
370
439
|
AvatarService.init().then(() => {
|
|
371
440
|
loadAvatars();
|
|
372
441
|
});
|
|
@@ -381,6 +450,7 @@ function initPluginFeatures() {
|
|
|
381
450
|
});
|
|
382
451
|
});
|
|
383
452
|
document.querySelectorAll('.document-dates-plugin').forEach(ddpEl => datesAutoWrapObserver.observe(ddpEl));
|
|
453
|
+
setTimeout(handleDocumentDatesAutoWrap, 100);
|
|
384
454
|
}
|
|
385
455
|
|
|
386
456
|
// 兼容 Material 主题的 'navigation.instant' 属性
|
|
@@ -44,6 +44,18 @@ window.ddUtils = {
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
|
|
47
|
+
// 获取当前语言环境,优先级:用户选择 > 元素配置 > 浏览器语言 > 页面语言 > 默认 'en'
|
|
48
|
+
getCurrentLocale(el) {
|
|
49
|
+
return (
|
|
50
|
+
this.getSavedLanguage() ||
|
|
51
|
+
(el ? el.getAttribute('locale') : null) ||
|
|
52
|
+
navigator.language ||
|
|
53
|
+
navigator.userLanguage ||
|
|
54
|
+
document.documentElement.lang ||
|
|
55
|
+
'en'
|
|
56
|
+
);
|
|
57
|
+
},
|
|
58
|
+
|
|
47
59
|
// 清除保存的语言设置
|
|
48
60
|
clearLanguage() {
|
|
49
61
|
try {
|
mkdocs_document_dates-3.6.1/mkdocs_document_dates/static/templates/recently_updated_detail.html
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<style>
|
|
2
|
+
.article-card {
|
|
3
|
+
border: 1px solid rgba(142, 142, 142, 0.2);
|
|
4
|
+
border-radius: 8px;
|
|
5
|
+
padding: 16px;
|
|
6
|
+
margin: 20px auto;
|
|
7
|
+
font-family: sans-serif;
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* --- 第一行:标题与时间 --- */
|
|
14
|
+
.card-header {
|
|
15
|
+
display: flex;
|
|
16
|
+
justify-content: space-between;
|
|
17
|
+
align-items: center;
|
|
18
|
+
text-decoration: none;
|
|
19
|
+
}
|
|
20
|
+
.card-title {
|
|
21
|
+
font-weight: 700;
|
|
22
|
+
line-height: 1.4;
|
|
23
|
+
color: var(--md-typeset-color, #1c1c1c);
|
|
24
|
+
margin-right: 16px;
|
|
25
|
+
}
|
|
26
|
+
.card-date {
|
|
27
|
+
font-size: 0.84em;
|
|
28
|
+
color: rgba(100, 100, 100, 0.4);
|
|
29
|
+
white-space: nowrap;
|
|
30
|
+
margin-right: 10px;
|
|
31
|
+
}
|
|
32
|
+
.card-header:hover .card-title {
|
|
33
|
+
color: var(--md-accent-fg-color, blue);
|
|
34
|
+
text-decoration: underline;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* --- 第二行:摘要与封面图 --- */
|
|
38
|
+
.card-body {
|
|
39
|
+
display: flex;
|
|
40
|
+
justify-content: space-between;
|
|
41
|
+
align-items: center;
|
|
42
|
+
margin-top: 12px;
|
|
43
|
+
}
|
|
44
|
+
.card-summary {
|
|
45
|
+
flex: 1;
|
|
46
|
+
color: rgba(142, 142, 142, 1);
|
|
47
|
+
font-size: 0.7rem;
|
|
48
|
+
line-height: 1.6;
|
|
49
|
+
letter-spacing: 0.02rem;
|
|
50
|
+
word-break: break-all;
|
|
51
|
+
|
|
52
|
+
/* 3 行文字截断,超出显示省略号 */
|
|
53
|
+
display: -webkit-box;
|
|
54
|
+
-webkit-box-orient: vertical;
|
|
55
|
+
-webkit-line-clamp: 3;
|
|
56
|
+
overflow: hidden;
|
|
57
|
+
}
|
|
58
|
+
.card-cover {
|
|
59
|
+
width: 120px;
|
|
60
|
+
height: 80px;
|
|
61
|
+
border-radius: 8px;
|
|
62
|
+
flex-shrink: 0;
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
justify-content: center;
|
|
66
|
+
overflow: hidden;
|
|
67
|
+
margin-left: 10px;
|
|
68
|
+
}
|
|
69
|
+
.card-cover a {
|
|
70
|
+
display: block;
|
|
71
|
+
width: 100%;
|
|
72
|
+
height: 100%;
|
|
73
|
+
}
|
|
74
|
+
.card-cover img {
|
|
75
|
+
width: 100%;
|
|
76
|
+
height: 100%;
|
|
77
|
+
object-fit: cover;
|
|
78
|
+
margin: 0 !important;
|
|
79
|
+
}
|
|
80
|
+
</style>
|
|
81
|
+
|
|
82
|
+
<div>
|
|
83
|
+
{%- for mtime, rel_path, title, url, cover, summary in recent_docs %}
|
|
84
|
+
<div class="article-card">
|
|
85
|
+
<a class="card-header" href="{{ url }}" target="_blank">
|
|
86
|
+
<div class="card-title">{{ title }}</div>
|
|
87
|
+
<time class="dd-timeago card-date" datetime="{{ mtime }}">{{ mtime[:10] }}</time>
|
|
88
|
+
</a>
|
|
89
|
+
<div class="card-body">
|
|
90
|
+
<div class="card-summary">{{ summary }}</div>
|
|
91
|
+
{%- if cover %}
|
|
92
|
+
<div class="card-cover">
|
|
93
|
+
<img src="{{ cover }}" alt="{{ title }}">
|
|
94
|
+
</div>
|
|
95
|
+
{%- endif %}
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
{%- endfor %}
|
|
99
|
+
</div>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<style>
|
|
2
|
+
/* --- 网格布局容器 --- */
|
|
3
|
+
.article-grid {
|
|
4
|
+
display: grid;
|
|
5
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
6
|
+
gap: 20px;
|
|
7
|
+
margin: 20px 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.article-card {
|
|
11
|
+
border: 1px solid rgba(142, 142, 142, 0.2);
|
|
12
|
+
border-radius: 8px;
|
|
13
|
+
padding: 16px;
|
|
14
|
+
font-family: sans-serif;
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* --- 第一行:标题与时间 --- */
|
|
21
|
+
.card-header {
|
|
22
|
+
display: flex;
|
|
23
|
+
justify-content: space-between;
|
|
24
|
+
align-items: center;
|
|
25
|
+
text-decoration: none;
|
|
26
|
+
}
|
|
27
|
+
.card-title {
|
|
28
|
+
font-weight: 700;
|
|
29
|
+
line-height: 1.4;
|
|
30
|
+
color: var(--md-typeset-color, #1c1c1c);
|
|
31
|
+
margin-right: 16px;
|
|
32
|
+
}
|
|
33
|
+
.card-date {
|
|
34
|
+
font-size: 0.84em;
|
|
35
|
+
color: rgba(100, 100, 100, 0.4);
|
|
36
|
+
white-space: nowrap;
|
|
37
|
+
margin-right: 10px;
|
|
38
|
+
}
|
|
39
|
+
.card-header:hover .card-title {
|
|
40
|
+
color: var(--md-accent-fg-color, blue);
|
|
41
|
+
text-decoration: underline;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* --- 第二行:摘要与封面图 --- */
|
|
45
|
+
.card-body {
|
|
46
|
+
display: flex;
|
|
47
|
+
justify-content: space-between;
|
|
48
|
+
align-items: center;
|
|
49
|
+
margin-top: 12px;
|
|
50
|
+
}
|
|
51
|
+
.card-summary {
|
|
52
|
+
flex: 1;
|
|
53
|
+
color: rgba(142, 142, 142, 1);
|
|
54
|
+
font-size: 0.7rem;
|
|
55
|
+
line-height: 1.6;
|
|
56
|
+
letter-spacing: 0.02rem;
|
|
57
|
+
word-break: break-all;
|
|
58
|
+
|
|
59
|
+
/* 3 行文字截断,超出显示省略号 */
|
|
60
|
+
display: -webkit-box;
|
|
61
|
+
-webkit-box-orient: vertical;
|
|
62
|
+
-webkit-line-clamp: 3;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
}
|
|
65
|
+
.card-cover {
|
|
66
|
+
width: 120px;
|
|
67
|
+
height: 80px;
|
|
68
|
+
border-radius: 8px;
|
|
69
|
+
flex-shrink: 0;
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
justify-content: center;
|
|
73
|
+
overflow: hidden;
|
|
74
|
+
margin-left: 10px;
|
|
75
|
+
}
|
|
76
|
+
.card-cover a {
|
|
77
|
+
display: block;
|
|
78
|
+
width: 100%;
|
|
79
|
+
height: 100%;
|
|
80
|
+
}
|
|
81
|
+
.card-cover img {
|
|
82
|
+
width: 100%;
|
|
83
|
+
height: 100%;
|
|
84
|
+
object-fit: cover;
|
|
85
|
+
margin: 0 !important;
|
|
86
|
+
}
|
|
87
|
+
</style>
|
|
88
|
+
|
|
89
|
+
<div class="article-grid">
|
|
90
|
+
{%- for mtime, rel_path, title, url, cover, summary in recent_docs %}
|
|
91
|
+
<div class="article-card">
|
|
92
|
+
<a class="card-header" href="{{ url }}" target="_blank">
|
|
93
|
+
<div class="card-title">{{ title }}</div>
|
|
94
|
+
<time class="dd-timeago card-date" datetime="{{ mtime }}">{{ mtime[:10] }}</time>
|
|
95
|
+
</a>
|
|
96
|
+
<div class="card-body">
|
|
97
|
+
<div class="card-summary">{{ summary }}</div>
|
|
98
|
+
{%- if cover %}
|
|
99
|
+
<div class="card-cover">
|
|
100
|
+
<img src="{{ cover }}" alt="{{ title }}">
|
|
101
|
+
</div>
|
|
102
|
+
{%- endif %}
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
{%- endfor %}
|
|
106
|
+
</div>
|
mkdocs_document_dates-3.6.1/mkdocs_document_dates/static/templates/recently_updated_group.html
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
<style>
|
|
2
|
+
.article-layout-switcher {
|
|
3
|
+
display: flex;
|
|
4
|
+
justify-content: flex-end;
|
|
5
|
+
/* margin-bottom: 20px; */
|
|
6
|
+
}
|
|
7
|
+
.article-layout-switcher button + button {
|
|
8
|
+
margin-left: 8px;
|
|
9
|
+
}
|
|
10
|
+
.article-layout-switcher button {
|
|
11
|
+
background: transparent;
|
|
12
|
+
border: none;
|
|
13
|
+
cursor: pointer;
|
|
14
|
+
padding: 4px;
|
|
15
|
+
border-radius: 4px;
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
color: var(--md-typeset-color, #1c1c1c);
|
|
19
|
+
opacity: 0.4;
|
|
20
|
+
transition: all 0.2s;
|
|
21
|
+
}
|
|
22
|
+
.article-layout-switcher button:hover {
|
|
23
|
+
background: rgba(142, 142, 142, 0.1);
|
|
24
|
+
opacity: 0.8;
|
|
25
|
+
}
|
|
26
|
+
.article-layout-switcher button.is-active {
|
|
27
|
+
opacity: 1;
|
|
28
|
+
color: var(--md-accent-fg-color, blue);
|
|
29
|
+
background: rgba(142, 142, 142, 0.1);
|
|
30
|
+
}
|
|
31
|
+
.article-layout-switcher .material-icons {
|
|
32
|
+
font-size: 20px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* --- 网格模式 (默认) --- */
|
|
36
|
+
.article-grid {
|
|
37
|
+
display: grid;
|
|
38
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
39
|
+
gap: 20px;
|
|
40
|
+
margin: 20px 0;
|
|
41
|
+
/* transition: all 0.2s ease; */
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* --- 详情模式 (is-detail) --- */
|
|
45
|
+
.article-grid.is-detail {
|
|
46
|
+
grid-template-columns: 1fr;
|
|
47
|
+
margin-left: auto;
|
|
48
|
+
margin-right: auto;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* --- 列表模式 (is-list) --- */
|
|
52
|
+
.article-grid.is-list {
|
|
53
|
+
margin-left: auto;
|
|
54
|
+
margin-right: auto;
|
|
55
|
+
|
|
56
|
+
border: 1px solid rgba(142, 142, 142, 0.2);
|
|
57
|
+
border-radius: 8px;
|
|
58
|
+
row-gap: 0;
|
|
59
|
+
padding: 11px;
|
|
60
|
+
}
|
|
61
|
+
.article-grid.is-list .card-body {
|
|
62
|
+
display: none;
|
|
63
|
+
}
|
|
64
|
+
.article-grid.is-list .article-card {
|
|
65
|
+
border: none;
|
|
66
|
+
border-radius: 0;
|
|
67
|
+
padding: 8px;
|
|
68
|
+
}
|
|
69
|
+
.article-grid.is-list .card-title {
|
|
70
|
+
font-weight: 400;
|
|
71
|
+
color: var(--md-typeset-a-color, #0077cc);
|
|
72
|
+
|
|
73
|
+
white-space: nowrap;
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
text-overflow: ellipsis;
|
|
76
|
+
min-width: 0;
|
|
77
|
+
}
|
|
78
|
+
.article-grid.is-list .card-date {
|
|
79
|
+
margin-right: 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
.article-card {
|
|
84
|
+
border: 1px solid rgba(142, 142, 142, 0.2);
|
|
85
|
+
border-radius: 8px;
|
|
86
|
+
padding: 16px;
|
|
87
|
+
font-family: sans-serif;
|
|
88
|
+
box-sizing: border-box;
|
|
89
|
+
display: flex;
|
|
90
|
+
flex-direction: column;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* --- 第一行:标题与时间 --- */
|
|
94
|
+
.card-header {
|
|
95
|
+
display: flex;
|
|
96
|
+
justify-content: space-between;
|
|
97
|
+
align-items: center;
|
|
98
|
+
text-decoration: none;
|
|
99
|
+
}
|
|
100
|
+
.card-title {
|
|
101
|
+
font-weight: 700;
|
|
102
|
+
line-height: 1.4;
|
|
103
|
+
color: var(--md-typeset-color, #1c1c1c);
|
|
104
|
+
margin-right: 12px;
|
|
105
|
+
}
|
|
106
|
+
.card-date {
|
|
107
|
+
font-size: 0.84em;
|
|
108
|
+
color: rgba(100, 100, 100, 0.4);
|
|
109
|
+
white-space: nowrap;
|
|
110
|
+
margin-right: 10px;
|
|
111
|
+
}
|
|
112
|
+
.card-header:hover {
|
|
113
|
+
text-decoration: none !important;
|
|
114
|
+
}
|
|
115
|
+
.card-header:hover .card-title {
|
|
116
|
+
color: var(--md-accent-fg-color, blue);
|
|
117
|
+
text-decoration: underline;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* --- 第二行:摘要与封面图 --- */
|
|
121
|
+
.card-body {
|
|
122
|
+
display: flex;
|
|
123
|
+
justify-content: space-between;
|
|
124
|
+
align-items: center;
|
|
125
|
+
margin-top: 12px;
|
|
126
|
+
}
|
|
127
|
+
.card-summary {
|
|
128
|
+
flex: 1;
|
|
129
|
+
color: rgba(142, 142, 142, 1);
|
|
130
|
+
font-size: 0.7rem;
|
|
131
|
+
line-height: 1.6;
|
|
132
|
+
letter-spacing: 0.02rem;
|
|
133
|
+
word-break: break-all;
|
|
134
|
+
|
|
135
|
+
/* 3 行文字截断,超出显示省略号 */
|
|
136
|
+
display: -webkit-box;
|
|
137
|
+
-webkit-box-orient: vertical;
|
|
138
|
+
-webkit-line-clamp: 3;
|
|
139
|
+
overflow: hidden;
|
|
140
|
+
}
|
|
141
|
+
.card-cover {
|
|
142
|
+
width: 120px;
|
|
143
|
+
height: 80px;
|
|
144
|
+
border-radius: 8px;
|
|
145
|
+
flex-shrink: 0;
|
|
146
|
+
display: flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
justify-content: center;
|
|
149
|
+
overflow: hidden;
|
|
150
|
+
margin-left: 10px;
|
|
151
|
+
transition: filter 0.3s ease;
|
|
152
|
+
|
|
153
|
+
/* Safari 圆角闪烁修复 */
|
|
154
|
+
transform: translateZ(0); /* 建立独立合成层 */
|
|
155
|
+
will-change: transform; /* 提前告诉浏览器 */
|
|
156
|
+
-webkit-mask-image: -webkit-radial-gradient(white, black); /* 强制圆角裁剪走 mask 管线 */
|
|
157
|
+
isolation: isolate;
|
|
158
|
+
}
|
|
159
|
+
.card-cover:hover {
|
|
160
|
+
filter: brightness(0.8);
|
|
161
|
+
}
|
|
162
|
+
.card-cover a {
|
|
163
|
+
display: block;
|
|
164
|
+
width: 100%;
|
|
165
|
+
height: 100%;
|
|
166
|
+
}
|
|
167
|
+
.card-cover img {
|
|
168
|
+
width: 100%;
|
|
169
|
+
height: 100%;
|
|
170
|
+
object-fit: cover;
|
|
171
|
+
margin: 0 !important;
|
|
172
|
+
|
|
173
|
+
/* 避免 layer 升降级 */
|
|
174
|
+
transform: scale(1.1) translateZ(0);
|
|
175
|
+
transition: transform 0.4s ease-out;
|
|
176
|
+
/* 防 Safari 重绘抖动 */
|
|
177
|
+
backface-visibility: hidden;
|
|
178
|
+
will-change: transform;
|
|
179
|
+
}
|
|
180
|
+
.card-cover:hover img {
|
|
181
|
+
transform: scale(1.1) translateX(4px) translateZ(0);
|
|
182
|
+
}
|
|
183
|
+
</style>
|
|
184
|
+
|
|
185
|
+
<div class="article-layout-switcher">
|
|
186
|
+
<button class="layout-list-btn"><span class="material-icons">view_list</span></button>
|
|
187
|
+
<button class="layout-detail-btn"><span class="material-icons">view_day</span></button>
|
|
188
|
+
<button class="layout-grid-btn"><span class="material-icons">view_module</span></button>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div class="article-grid">
|
|
192
|
+
{%- for mtime, rel_path, title, url, cover, summary in recent_docs %}
|
|
193
|
+
<div class="article-card">
|
|
194
|
+
<a class="card-header" href="{{ url }}" target="_blank">
|
|
195
|
+
<div class="card-title">{{ title }}</div>
|
|
196
|
+
<time class="dd-timeago card-date" datetime="{{ mtime }}">{{ mtime[:10] }}</time>
|
|
197
|
+
</a>
|
|
198
|
+
<div class="card-body">
|
|
199
|
+
<div class="card-summary">{{ summary }}</div>
|
|
200
|
+
{%- if cover %}
|
|
201
|
+
<div class="card-cover">
|
|
202
|
+
<img src="{{ cover }}" alt="{{ title }}">
|
|
203
|
+
</div>
|
|
204
|
+
{%- endif %}
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
{%- endfor %}
|
|
208
|
+
</div>
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
<style>
|
|
2
2
|
.recently-updated {
|
|
3
3
|
display: grid;
|
|
4
|
-
grid-template-columns: repeat(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
padding: 16px;
|
|
8
|
-
border: 1px solid rgba(142, 142, 142, 0.15);
|
|
4
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
5
|
+
padding: 11px;
|
|
6
|
+
border: 1px solid rgba(142, 142, 142, 0.2);
|
|
9
7
|
border-radius: 8px;
|
|
10
8
|
font-family: system-ui, sans-serif;
|
|
11
9
|
}
|
|
10
|
+
|
|
11
|
+
/* 屏幕宽度 675px 及以下时切换为 1 列 */
|
|
12
|
+
@media screen and (max-width: 675px) {
|
|
13
|
+
.recently-updated {
|
|
14
|
+
grid-template-columns: 1fr;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
12
17
|
.recently-updated-item {
|
|
13
18
|
display: flex;
|
|
14
19
|
align-items: center;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
font-size: 0.84em;
|
|
18
|
-
color: rgba(142, 142, 142, 0.5);
|
|
19
|
-
margin-right: 4px;
|
|
20
|
-
flex-shrink: 0;
|
|
21
|
-
width: 90px;
|
|
20
|
+
justify-content: space-between;
|
|
21
|
+
margin: 5px;
|
|
22
22
|
}
|
|
23
23
|
.recently-updated-item a {
|
|
24
24
|
overflow: hidden;
|
|
@@ -26,17 +26,25 @@
|
|
|
26
26
|
white-space: nowrap;
|
|
27
27
|
color: #0077cc;
|
|
28
28
|
text-decoration: none;
|
|
29
|
-
|
|
29
|
+
margin-left: 6px;
|
|
30
30
|
}
|
|
31
31
|
.recently-updated-item a:hover {
|
|
32
32
|
text-decoration: underline;
|
|
33
33
|
}
|
|
34
|
+
.recently-updated-item time {
|
|
35
|
+
font-size: 0.84em;
|
|
36
|
+
color: rgba(142, 142, 142, 0.4);
|
|
37
|
+
margin: 0 6px;
|
|
38
|
+
flex-shrink: 0;
|
|
39
|
+
text-align: right;
|
|
40
|
+
}
|
|
34
41
|
</style>
|
|
42
|
+
|
|
35
43
|
<div class="recently-updated">
|
|
36
|
-
{%- for mtime, rel_path, title, url in recent_docs %}
|
|
44
|
+
{%- for mtime, rel_path, title, url, cover, summary in recent_docs %}
|
|
37
45
|
<div class="recently-updated-item">
|
|
38
|
-
<span>{{ mtime[:10] }}</span>
|
|
39
46
|
<a href="{{ url }}" target="_blank">{{ title }}</a>
|
|
47
|
+
<time class="dd-timeago" datetime="{{ mtime }}">{{ mtime[:10] }}</time>
|
|
40
48
|
</div>
|
|
41
49
|
{%- endfor %}
|
|
42
50
|
</div>
|
|
@@ -4,6 +4,8 @@ import json
|
|
|
4
4
|
import heapq
|
|
5
5
|
import logging
|
|
6
6
|
import subprocess
|
|
7
|
+
import fnmatch
|
|
8
|
+
import re
|
|
7
9
|
from pathlib import Path
|
|
8
10
|
from datetime import datetime
|
|
9
11
|
from collections import defaultdict
|
|
@@ -13,15 +15,22 @@ logger = logging.getLogger("mkdocs.plugins.document_dates")
|
|
|
13
15
|
logger.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
def
|
|
18
|
+
def compile_exclude_patterns(exclude_list):
|
|
17
19
|
if not exclude_list:
|
|
20
|
+
return []
|
|
21
|
+
return [re.compile(fnmatch.translate(pattern)) for pattern in exclude_list]
|
|
22
|
+
|
|
23
|
+
def is_excluded(path, patterns):
|
|
24
|
+
if not patterns:
|
|
18
25
|
return False
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
first = patterns[0]
|
|
27
|
+
if isinstance(first, re.Pattern):
|
|
28
|
+
for regex in patterns:
|
|
29
|
+
if regex.match(path):
|
|
22
30
|
return True
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
else:
|
|
32
|
+
for pattern in patterns:
|
|
33
|
+
if fnmatch.fnmatch(path, pattern):
|
|
25
34
|
return True
|
|
26
35
|
return False
|
|
27
36
|
|
|
@@ -132,7 +141,8 @@ def load_git_last_updated_date(docs_dir_path: Path):
|
|
|
132
141
|
|
|
133
142
|
return doc_mtime_map
|
|
134
143
|
|
|
135
|
-
|
|
144
|
+
# 建议在 on_page_markdown 之后的全局事件中调用,因为需要读取 page.meta 中的信息
|
|
145
|
+
def get_recently_updated_files(existing_dates: dict, files: Files, exclude_list: list, limit: int = 10, recent_enable: bool = False):
|
|
136
146
|
recently_updated_results = []
|
|
137
147
|
if recent_enable:
|
|
138
148
|
files_meta = []
|
|
@@ -148,14 +158,23 @@ def get_recently_updated_files(existing_map: dict, files: Files, exclude_list: l
|
|
|
148
158
|
continue
|
|
149
159
|
|
|
150
160
|
# 优先从现有数据获取 mtime,如果不存在则 fallback 到文件系统 mtime
|
|
151
|
-
mtime =
|
|
161
|
+
mtime = existing_dates.get(rel_path, os.path.getmtime(file.abs_src_path))
|
|
152
162
|
|
|
153
163
|
# 获取文档标题和 URL
|
|
154
164
|
title = file.page.title if file.page and file.page.title else file.name
|
|
155
165
|
url = file.page.url if file.page and file.page.url else file.url
|
|
156
166
|
|
|
167
|
+
cover = ''
|
|
168
|
+
summary = ''
|
|
169
|
+
# authors = []
|
|
170
|
+
if file.page:
|
|
171
|
+
cover = file.page.meta.get('cover', '')
|
|
172
|
+
# authors = file.page.meta.get('document_dates_authors', [])
|
|
173
|
+
if file.page.file:
|
|
174
|
+
summary = extract_summary(file.page.file.content_string)
|
|
175
|
+
|
|
157
176
|
# 存储信息
|
|
158
|
-
files_meta.append((mtime, rel_path, title, url))
|
|
177
|
+
files_meta.append((mtime, rel_path, title, url, cover, summary))
|
|
159
178
|
# existing_map[rel_path] = mtime
|
|
160
179
|
|
|
161
180
|
# 构建最近更新列表
|
|
@@ -208,3 +227,195 @@ def write_jsonl_cache(jsonl_file: Path, dates_cache, tracked_files):
|
|
|
208
227
|
except Exception as e:
|
|
209
228
|
logger.warning(f"Failed to add JSONL cache file to git: {e}")
|
|
210
229
|
return False
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# ===== Extract Summary =====
|
|
233
|
+
#
|
|
234
|
+
# -------- block skip --------
|
|
235
|
+
# Fence
|
|
236
|
+
FENCE_RE = re.compile(r"^\s*([`~]{3,})")
|
|
237
|
+
|
|
238
|
+
# HTML comment
|
|
239
|
+
HTML_COMMENT_START = re.compile(r'<!--', re.I)
|
|
240
|
+
HTML_COMMENT_END = re.compile(r'-->', re.I)
|
|
241
|
+
|
|
242
|
+
# HTML
|
|
243
|
+
HTML_TAG_OPEN = re.compile(r'<\s*([a-zA-Z][\w\-]*)\b', re.I)
|
|
244
|
+
HTML_TAG_CLOSE_TEMPLATE = r"</\s*{}\s*>"
|
|
245
|
+
HTML_VOID_TAGS = {
|
|
246
|
+
"area", "base", "br", "col", "embed", "hr",
|
|
247
|
+
"img", "input", "link", "meta", "param",
|
|
248
|
+
"source", "track", "wbr"
|
|
249
|
+
}
|
|
250
|
+
HTML_VOID_CLOSE_RE = re.compile(r">", re.I)
|
|
251
|
+
|
|
252
|
+
# -------- inline skip --------
|
|
253
|
+
H1_TITLE = re.compile(r'^\s*# .+$', re.MULTILINE)
|
|
254
|
+
SINGLE_LINE_HTML_NOISE = re.compile(r'^</?[a-z][\w-]*[^>]*>$', re.I)
|
|
255
|
+
TABLE_ROW_RE = re.compile(r"^\s*\|.*\|\s*$")
|
|
256
|
+
INLINE_SKIP_RE = re.compile(
|
|
257
|
+
r"""
|
|
258
|
+
^\s*> | # quote
|
|
259
|
+
^\s*(?:!!!|\?\?\?) | # admonition
|
|
260
|
+
^\s*=== | # tab
|
|
261
|
+
^\s*\[.+?\]: # reference link, including footnote
|
|
262
|
+
""",
|
|
263
|
+
re.X,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# -------- inline replace --------
|
|
267
|
+
IMAGE_RE = re.compile(r'!\[[^\]]*\]\([^)]+\)')
|
|
268
|
+
LINK_RE = re.compile(r'\[([^\]]+)\]\([^)]+\)')
|
|
269
|
+
BRACE_RE = re.compile(r"\{[^}]*\}")
|
|
270
|
+
MD_SYNTAX_RE = re.compile(r'[`*_>#]+')
|
|
271
|
+
|
|
272
|
+
def clean_markdown(md: str) -> list:
|
|
273
|
+
|
|
274
|
+
lines = md.splitlines()
|
|
275
|
+
result = []
|
|
276
|
+
|
|
277
|
+
state = "NORMAL"
|
|
278
|
+
fence = ""
|
|
279
|
+
html_close_re = None
|
|
280
|
+
frontmatter_parsed = False
|
|
281
|
+
h1_parsed = False
|
|
282
|
+
math_delim = ""
|
|
283
|
+
|
|
284
|
+
for line in lines:
|
|
285
|
+
stripped = line.strip()
|
|
286
|
+
if not stripped:
|
|
287
|
+
continue
|
|
288
|
+
|
|
289
|
+
# ==================================================
|
|
290
|
+
# 1. Frontmatter
|
|
291
|
+
# ==================================================
|
|
292
|
+
if not frontmatter_parsed:
|
|
293
|
+
if state == "FRONTMATTER":
|
|
294
|
+
if stripped in ("---", "+++"):
|
|
295
|
+
state = "NORMAL"
|
|
296
|
+
frontmatter_parsed = True
|
|
297
|
+
continue
|
|
298
|
+
|
|
299
|
+
if state == "NORMAL" and stripped in ("---", "+++"):
|
|
300
|
+
state = "FRONTMATTER"
|
|
301
|
+
continue
|
|
302
|
+
|
|
303
|
+
# ==================================================
|
|
304
|
+
# 2. Fence Block
|
|
305
|
+
# ==================================================
|
|
306
|
+
if state == "FENCE":
|
|
307
|
+
if stripped.startswith(fence):
|
|
308
|
+
state = "NORMAL"
|
|
309
|
+
continue
|
|
310
|
+
|
|
311
|
+
if state == "NORMAL":
|
|
312
|
+
m = FENCE_RE.match(stripped)
|
|
313
|
+
if m:
|
|
314
|
+
fence = m.group(1)
|
|
315
|
+
state = "FENCE"
|
|
316
|
+
continue
|
|
317
|
+
|
|
318
|
+
# ==================================================
|
|
319
|
+
# 3. HTML Comment
|
|
320
|
+
# ==================================================
|
|
321
|
+
if state == "COMMENT":
|
|
322
|
+
if HTML_COMMENT_END.search(stripped):
|
|
323
|
+
state = "NORMAL"
|
|
324
|
+
continue
|
|
325
|
+
|
|
326
|
+
if state == "NORMAL" and HTML_COMMENT_START.search(stripped):
|
|
327
|
+
state = "COMMENT"
|
|
328
|
+
if HTML_COMMENT_END.search(stripped):
|
|
329
|
+
state = "NORMAL"
|
|
330
|
+
continue
|
|
331
|
+
|
|
332
|
+
# ==================================================
|
|
333
|
+
# 4. HTML Block
|
|
334
|
+
# ==================================================
|
|
335
|
+
if state == "HTML_BLOCK":
|
|
336
|
+
if html_close_re and html_close_re.search(stripped):
|
|
337
|
+
state = "NORMAL"
|
|
338
|
+
html_close_re = None
|
|
339
|
+
continue
|
|
340
|
+
|
|
341
|
+
if state == "NORMAL":
|
|
342
|
+
m = HTML_TAG_OPEN.match(stripped)
|
|
343
|
+
if m:
|
|
344
|
+
tag = m.group(1).lower()
|
|
345
|
+
is_void = tag in HTML_VOID_TAGS
|
|
346
|
+
|
|
347
|
+
# void tag:单行且以 > 结尾,视为直接结束,忽略该行
|
|
348
|
+
if stripped.endswith('>') and is_void:
|
|
349
|
+
continue
|
|
350
|
+
|
|
351
|
+
# 非 void tag:进入 HTML_BLOCK
|
|
352
|
+
state = "HTML_BLOCK"
|
|
353
|
+
if is_void:
|
|
354
|
+
html_close_re = HTML_VOID_CLOSE_RE
|
|
355
|
+
else:
|
|
356
|
+
html_close_re = re.compile(HTML_TAG_CLOSE_TEMPLATE.format(re.escape(tag)), re.I)
|
|
357
|
+
|
|
358
|
+
# same-line close: <div>...</div>
|
|
359
|
+
if html_close_re.search(stripped):
|
|
360
|
+
state = "NORMAL"
|
|
361
|
+
html_close_re = None
|
|
362
|
+
|
|
363
|
+
continue
|
|
364
|
+
|
|
365
|
+
# ==================================================
|
|
366
|
+
# 5. Math Block
|
|
367
|
+
# ==================================================
|
|
368
|
+
if state == "MATH":
|
|
369
|
+
if stripped == math_delim:
|
|
370
|
+
state = "NORMAL"
|
|
371
|
+
continue
|
|
372
|
+
|
|
373
|
+
if state == "NORMAL" and stripped in ("$$", "\\["):
|
|
374
|
+
math_delim = "$$" if stripped == "$$" else "\\]"
|
|
375
|
+
state = "MATH"
|
|
376
|
+
continue
|
|
377
|
+
|
|
378
|
+
# ==================================================
|
|
379
|
+
# 6. Inline Skip
|
|
380
|
+
# ==================================================
|
|
381
|
+
if state == "NORMAL":
|
|
382
|
+
if TABLE_ROW_RE.match(stripped):
|
|
383
|
+
continue
|
|
384
|
+
if INLINE_SKIP_RE.match(stripped):
|
|
385
|
+
continue
|
|
386
|
+
# 单行 HTML 噪音兜底
|
|
387
|
+
if SINGLE_LINE_HTML_NOISE.match(stripped):
|
|
388
|
+
continue
|
|
389
|
+
if not h1_parsed:
|
|
390
|
+
if H1_TITLE.match(stripped):
|
|
391
|
+
h1_parsed = True
|
|
392
|
+
continue
|
|
393
|
+
if stripped.startswith(('---', '***')):
|
|
394
|
+
continue
|
|
395
|
+
|
|
396
|
+
# ==================================================
|
|
397
|
+
# 7. Inline Replace
|
|
398
|
+
# ==================================================
|
|
399
|
+
text = stripped
|
|
400
|
+
text = IMAGE_RE.sub("", text)
|
|
401
|
+
text = LINK_RE.sub(r"\1", text)
|
|
402
|
+
text = BRACE_RE.sub("", text)
|
|
403
|
+
|
|
404
|
+
text = text.strip()
|
|
405
|
+
if text:
|
|
406
|
+
result.append(text)
|
|
407
|
+
|
|
408
|
+
# 提前熔断
|
|
409
|
+
if len(result) >= 10:
|
|
410
|
+
break
|
|
411
|
+
|
|
412
|
+
# 锁定 Frontmatter 状态,防止后续 --- 干扰
|
|
413
|
+
frontmatter_parsed = True
|
|
414
|
+
|
|
415
|
+
return result
|
|
416
|
+
# return "\n".join(result)
|
|
417
|
+
|
|
418
|
+
def extract_summary(markdown_text: str) -> str:
|
|
419
|
+
md_list = clean_markdown(markdown_text)
|
|
420
|
+
text = " ".join(md_list)
|
|
421
|
+
return MD_SYNTAX_RE.sub('', text).strip()
|
{mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1/mkdocs_document_dates.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mkdocs-document-dates
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.6.1
|
|
4
4
|
Summary: A new generation MkDocs plugin for displaying exact creation date, last updated date, authors, email of documents
|
|
5
5
|
Home-page: https://github.com/jaywhj/mkdocs-document-dates
|
|
6
6
|
Author: Aaron Wang
|
|
@@ -67,7 +67,7 @@ plugins:
|
|
|
67
67
|
- document-dates
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
Or,
|
|
70
|
+
Or, common configuration:
|
|
71
71
|
|
|
72
72
|
```yaml
|
|
73
73
|
plugins:
|
|
@@ -77,8 +77,6 @@ plugins:
|
|
|
77
77
|
exclude: # List of excluded files
|
|
78
78
|
- temp.md # Example: exclude the specified file
|
|
79
79
|
- blog/* # Example: exclude all files in blog folder, including subfolders
|
|
80
|
-
date_format: '%Y-%m-%d' # Date format strings (e.g., %Y-%m-%d, %b %d, %Y)
|
|
81
|
-
time_format: '%H:%M:%S' # Time format strings (valid only if type=datetime)
|
|
82
80
|
```
|
|
83
81
|
|
|
84
82
|
## Customization Settings
|
|
@@ -97,6 +95,12 @@ In addition to the above basic configuration, the plug-in also provides a wealth
|
|
|
97
95
|
|
|
98
96
|
See the documentation for details: https://jaywhj.netlify.app/document-dates-en
|
|
99
97
|
|
|
98
|
+

|
|
99
|
+
|
|
100
|
+
## Other Projects
|
|
101
|
+
|
|
102
|
+
- [**MaterialX**](https://github.com/jaywhj/mkdocs-materialx), the next generation of mkdocs-material, is based on `mkdocs-material-9.7.0` and is named `X`. I'll be maintaining this branch continuously (since mkdocs-material will stop being maintained).
|
|
103
|
+
Updates have been released that refactor and add a lot of new features, see https://github.com/jaywhj/mkdocs-materialx/releases/
|
|
100
104
|
|
|
101
105
|
<br />
|
|
102
106
|
|
|
@@ -26,7 +26,10 @@ mkdocs_document_dates/static/core/timeago.min.js
|
|
|
26
26
|
mkdocs_document_dates/static/core/utils.js
|
|
27
27
|
mkdocs_document_dates/static/fonts/material-icons.css
|
|
28
28
|
mkdocs_document_dates/static/fonts/materialicons.woff2
|
|
29
|
-
mkdocs_document_dates/static/templates/
|
|
29
|
+
mkdocs_document_dates/static/templates/recently_updated_detail.html
|
|
30
|
+
mkdocs_document_dates/static/templates/recently_updated_grid.html
|
|
31
|
+
mkdocs_document_dates/static/templates/recently_updated_group.html
|
|
32
|
+
mkdocs_document_dates/static/templates/recently_updated_list.html
|
|
30
33
|
mkdocs_document_dates/static/tippy/backdrop.css
|
|
31
34
|
mkdocs_document_dates/static/tippy/light.css
|
|
32
35
|
mkdocs_document_dates/static/tippy/material.css
|
|
File without changes
|
|
File without changes
|
{mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/__init__.py
RENAMED
|
File without changes
|
{mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/cache_manager.py
RENAMED
|
File without changes
|
{mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/hooks/pre-commit
RENAMED
|
File without changes
|
{mkdocs_document_dates-3.5.2 → mkdocs_document_dates-3.6.1}/mkdocs_document_dates/hooks_installer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -143,8 +143,8 @@
|
|
|
143
143
|
display: block;
|
|
144
144
|
|
|
145
145
|
/* Fix bug in Safari: rounded corners may blink to show square corners */
|
|
146
|
-
will-change: transform;
|
|
147
146
|
transform: translateZ(0);
|
|
147
|
+
will-change: transform;
|
|
148
148
|
}
|
|
149
149
|
.avatar-wrapper:hover {
|
|
150
150
|
transform: scale(1.08);
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|