mkdocs-document-dates 3.2__tar.gz → 3.2.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.
Files changed (45) hide show
  1. mkdocs_document_dates-3.2.1/LICENSE +22 -0
  2. {mkdocs_document_dates-3.2/mkdocs_document_dates.egg-info → mkdocs_document_dates-3.2.1}/PKG-INFO +9 -8
  3. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/README.md +7 -6
  4. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/cache_manager.py +3 -7
  5. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/plugin.py +99 -49
  6. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/config/user.config.css +6 -7
  7. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/config/user.config.js +13 -5
  8. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/core/core.css +15 -8
  9. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/core/core.js +58 -10
  10. mkdocs_document_dates-3.2.1/mkdocs_document_dates/static/core/timeago-load.js +18 -0
  11. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/utils.py +45 -70
  12. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1/mkdocs_document_dates.egg-info}/PKG-INFO +9 -8
  13. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/setup.py +3 -3
  14. mkdocs_document_dates-3.2/LICENSE +0 -11
  15. mkdocs_document_dates-3.2/mkdocs_document_dates/static/core/timeago-load.js +0 -5
  16. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/MANIFEST.in +0 -0
  17. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/__init__.py +0 -0
  18. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/hooks/pre-commit +0 -0
  19. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/hooks_installer.py +0 -0
  20. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/core/timeago.full.min.js +0 -0
  21. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/core/timeago.min.js +0 -0
  22. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/languages/ar.json +0 -0
  23. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/languages/de.json +0 -0
  24. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/languages/en.json +0 -0
  25. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/languages/es.json +0 -0
  26. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/languages/fr.json +0 -0
  27. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/languages/ja.json +0 -0
  28. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/languages/ko.json +0 -0
  29. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/languages/ru.json +0 -0
  30. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/languages/zh.json +0 -0
  31. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/languages/zh_TW.json +0 -0
  32. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/tippy/backdrop.css +0 -0
  33. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/tippy/light.css +0 -0
  34. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/tippy/material.css +0 -0
  35. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/tippy/popper.min.js +0 -0
  36. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/tippy/scale.css +0 -0
  37. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/tippy/shift-away.css +0 -0
  38. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/tippy/tippy.css +0 -0
  39. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates/static/tippy/tippy.umd.min.js +0 -0
  40. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates.egg-info/SOURCES.txt +0 -0
  41. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates.egg-info/dependency_links.txt +0 -0
  42. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates.egg-info/entry_points.txt +0 -0
  43. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates.egg-info/requires.txt +0 -0
  44. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/mkdocs_document_dates.egg-info/top_level.txt +0 -0
  45. {mkdocs_document_dates-3.2 → mkdocs_document_dates-3.2.1}/setup.cfg +0 -0
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Aaron Wang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs-document-dates
3
- Version: 3.2
4
- Summary: An easy-to-use, lightweight MkDocs plugin for displaying the exact creation time, last modification time and author info of markdown documents.
3
+ Version: 3.2.1
4
+ Summary: A new generation MkDocs plugin for displaying exact meta-info of documents, such as creation time, last update time, authors, email, etc.
5
5
  Home-page: https://github.com/jaywhj/mkdocs-document-dates
6
6
  Author: Aaron Wang
7
7
  Author-email: aaronwqt@gmail.com
@@ -30,7 +30,7 @@ English | [简体中文](README_zh.md)
30
30
 
31
31
 
32
32
 
33
- An easy-to-use, lightweight MkDocs plugin for displaying the <mark>exact</mark> creation time, last modification time and author info of markdown documents.
33
+ A new generation MkDocs plugin for displaying exact meta-info of documents, such as **creation time, last update time, authors, email**, etc.
34
34
 
35
35
  ## Features
36
36
 
@@ -43,6 +43,7 @@ An easy-to-use, lightweight MkDocs plugin for displaying the <mark>exact</mark>
43
43
  - Intelligent repositioning to always float optimally in view
44
44
  - Supports automatic theme switching following Material's light/dark color scheme
45
45
  - Multi-language support, cross-platform support (Windows, macOS, Linux)
46
+ - Ultimate build efficiency O(1), typically less than 0.2 seconds, regardless of whether the number of documents is 1,000 or 10,000
46
47
 
47
48
  ## Showcases
48
49
 
@@ -75,7 +76,7 @@ plugins:
75
76
  time_format: '%H:%M:%S' # Time format strings (valid only if type=datetime)
76
77
  exclude: # List of excluded files
77
78
  - temp.md # Exclude specific file
78
- - private/* # Exclude all files in private directory, including subdirectories
79
+ - private/* # Exclude all files in private folder, including subfolders
79
80
  show_author: true # Show author or not, default: true
80
81
  ```
81
82
 
@@ -83,7 +84,7 @@ plugins:
83
84
 
84
85
  The plugin will automatically get the exact time of the document, will automatically cache the creation time, but of course, you can also specify it manually in `Front Matter`
85
86
 
86
- Priority order: `Front Matter` > `Cache Files` > `File System Timestamps`
87
+ Priority order: `Front Matter` > `File System Timestamps(Cached)` > `Git Timestamps`
87
88
 
88
89
  ```yaml
89
90
  ---
@@ -101,7 +102,7 @@ modified: 2025-02-23
101
102
 
102
103
  The plugin will automatically get the author of the document, will parse the email and make a link, also you can specify it manually in `Front Matter`
103
104
 
104
- Priority order: `Front Matter` > `Git Author` > `site_author (mkdocs.yml)` > `PC Username`
105
+ Priority order: `Front Matter` > `Git Author` > `site_author(mkdocs.yml)` > `PC Username`
105
106
 
106
107
  ```yaml
107
108
  ---
@@ -117,7 +118,7 @@ email: e-name@gmail.com
117
118
 
118
119
  ## Customization
119
120
 
120
- The plugin supports deep customization, such as **icon style, theme color, font, animation, dividing line**, etc. Everything is customizable (I've already written the code, you just need to turn on the uncomment switch):
121
+ The plugin supports deep customization, such as **icon style, theme color, font, animation, dividing line**, etc. Everything is customizable (Find the file below and turn on the uncomment switch):
121
122
 
122
123
  | Category: | Location: |
123
124
  | :----------------------: | ---------------------------------------- |
@@ -146,7 +147,7 @@ The plugin supports deep customization, such as **icon style, theme color, font,
146
147
  ![06-change-theme](mkdocs_document_dates/demo_images/06-change-theme.png)
147
148
  ![08-pop-up-from-bottom](mkdocs_document_dates/demo_images/08-pop-up-from-bottom.png)
148
149
 
149
- ## Used in templates
150
+ ## Template Variables
150
151
 
151
152
  You can access the meta-info of a document in a template using the following variables:
152
153
 
@@ -4,7 +4,7 @@ English | [简体中文](README_zh.md)
4
4
 
5
5
 
6
6
 
7
- An easy-to-use, lightweight MkDocs plugin for displaying the <mark>exact</mark> creation time, last modification time and author info of markdown documents.
7
+ A new generation MkDocs plugin for displaying exact meta-info of documents, such as **creation time, last update time, authors, email**, etc.
8
8
 
9
9
  ## Features
10
10
 
@@ -17,6 +17,7 @@ An easy-to-use, lightweight MkDocs plugin for displaying the <mark>exact</mark>
17
17
  - Intelligent repositioning to always float optimally in view
18
18
  - Supports automatic theme switching following Material's light/dark color scheme
19
19
  - Multi-language support, cross-platform support (Windows, macOS, Linux)
20
+ - Ultimate build efficiency O(1), typically less than 0.2 seconds, regardless of whether the number of documents is 1,000 or 10,000
20
21
 
21
22
  ## Showcases
22
23
 
@@ -49,7 +50,7 @@ plugins:
49
50
  time_format: '%H:%M:%S' # Time format strings (valid only if type=datetime)
50
51
  exclude: # List of excluded files
51
52
  - temp.md # Exclude specific file
52
- - private/* # Exclude all files in private directory, including subdirectories
53
+ - private/* # Exclude all files in private folder, including subfolders
53
54
  show_author: true # Show author or not, default: true
54
55
  ```
55
56
 
@@ -57,7 +58,7 @@ plugins:
57
58
 
58
59
  The plugin will automatically get the exact time of the document, will automatically cache the creation time, but of course, you can also specify it manually in `Front Matter`
59
60
 
60
- Priority order: `Front Matter` > `Cache Files` > `File System Timestamps`
61
+ Priority order: `Front Matter` > `File System Timestamps(Cached)` > `Git Timestamps`
61
62
 
62
63
  ```yaml
63
64
  ---
@@ -75,7 +76,7 @@ modified: 2025-02-23
75
76
 
76
77
  The plugin will automatically get the author of the document, will parse the email and make a link, also you can specify it manually in `Front Matter`
77
78
 
78
- Priority order: `Front Matter` > `Git Author` > `site_author (mkdocs.yml)` > `PC Username`
79
+ Priority order: `Front Matter` > `Git Author` > `site_author(mkdocs.yml)` > `PC Username`
79
80
 
80
81
  ```yaml
81
82
  ---
@@ -91,7 +92,7 @@ email: e-name@gmail.com
91
92
 
92
93
  ## Customization
93
94
 
94
- The plugin supports deep customization, such as **icon style, theme color, font, animation, dividing line**, etc. Everything is customizable (I've already written the code, you just need to turn on the uncomment switch):
95
+ The plugin supports deep customization, such as **icon style, theme color, font, animation, dividing line**, etc. Everything is customizable (Find the file below and turn on the uncomment switch):
95
96
 
96
97
  | Category: | Location: |
97
98
  | :----------------------: | ---------------------------------------- |
@@ -120,7 +121,7 @@ The plugin supports deep customization, such as **icon style, theme color, font,
120
121
  ![06-change-theme](mkdocs_document_dates/demo_images/06-change-theme.png)
121
122
  ![08-pop-up-from-bottom](mkdocs_document_dates/demo_images/08-pop-up-from-bottom.png)
122
123
 
123
- ## Used in templates
124
+ ## Template Variables
124
125
 
125
126
  You can access the meta-info of a document in a template using the following variables:
126
127
 
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  import subprocess
3
3
  from pathlib import Path
4
- from .utils import read_json_cache, read_jsonl_cache, write_jsonl_cache, get_file_creation_time, get_git_first_commit_time
4
+ from .utils import read_json_cache, read_jsonl_cache, write_jsonl_cache, get_file_creation_time
5
5
 
6
6
  logger = logging.getLogger("mkdocs.plugins.document_dates")
7
7
  logger.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, CRITICAL
@@ -57,7 +57,7 @@ def update_cache():
57
57
 
58
58
  docs_dir = project_dir / 'docs'
59
59
  if not docs_dir.exists():
60
- logger.error(f"Document directory does not exist: {docs_dir}")
60
+ logger.warning(f"Document directory does not exist: {docs_dir}")
61
61
  continue
62
62
 
63
63
  # 设置.gitattributes文件
@@ -93,11 +93,7 @@ def update_cache():
93
93
  jsonl_dates_cache[rel_path] = json_dates_cache[rel_path]
94
94
  project_updated = True
95
95
  elif full_path.exists():
96
- created_time = get_file_creation_time(full_path)
97
- if not jsonl_cache_file.exists() and not json_cache_file.exists():
98
- git_time = get_git_first_commit_time(full_path)
99
- if git_time:
100
- created_time = min(created_time, git_time)
96
+ created_time = get_file_creation_time(full_path).astimezone()
101
97
  jsonl_dates_cache[rel_path] = {
102
98
  "created": created_time.isoformat()
103
99
  }
@@ -7,12 +7,30 @@ from pathlib import Path
7
7
  from mkdocs.plugins import BasePlugin
8
8
  from mkdocs.config import config_options
9
9
  from mkdocs.structure.pages import Page
10
- from .utils import Author, read_json_cache, read_jsonl_cache, check_git_repo, get_file_creation_time, get_git_first_commit_time, get_git_authors
10
+ from .utils import get_file_creation_time, load_git_cache, read_jsonl_cache
11
11
 
12
12
  logger = logging.getLogger("mkdocs.plugins.document_dates")
13
13
  logger.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, CRITICAL
14
14
 
15
15
 
16
+ class Author:
17
+ def __init__(self, name="", email="", **kwargs):
18
+ self.name = name
19
+ self.email = email
20
+ # 扩展属性
21
+ self.attributes = kwargs
22
+
23
+ def __getattr__(self, name):
24
+ return self.attributes.get(name)
25
+
26
+ def to_dict(self):
27
+ return {
28
+ 'name': self.name,
29
+ 'email': self.email,
30
+ **self.attributes
31
+ }
32
+
33
+
16
34
  class DocumentDatesPlugin(BasePlugin):
17
35
  config_scheme = (
18
36
  ('type', config_options.Type(str, default='date')),
@@ -34,7 +52,6 @@ class DocumentDatesPlugin(BasePlugin):
34
52
  super().__init__()
35
53
  self.translation = {}
36
54
  self.dates_cache = {}
37
- self.is_git_repo = False
38
55
 
39
56
  def on_config(self, config):
40
57
  try:
@@ -44,25 +61,23 @@ class DocumentDatesPlugin(BasePlugin):
44
61
  except Exception:
45
62
  self.config['locale'] = 'en'
46
63
 
47
- # 检查是否为 Git 仓库
48
- self.is_git_repo = check_git_repo()
49
-
50
64
  docs_dir_path = Path(config['docs_dir'])
51
65
 
52
66
  # 加载 json 语言文件
53
67
  self._load_translation(docs_dir_path)
54
68
 
55
- # 加载日期缓存
69
+ # 加载 git 缓存
70
+ self.dates_cache = load_git_cache(docs_dir_path)
71
+ # 加载 jsonl 缓存覆盖
56
72
  jsonl_cache_file = docs_dir_path / '.dates_cache.jsonl'
57
- self.dates_cache = read_jsonl_cache(jsonl_cache_file)
58
-
59
- # 兼容旧版缓存文件
60
- if not self.dates_cache:
61
- json_cache_file = docs_dir_path / '.dates_cache.json'
62
- self.dates_cache = read_json_cache(json_cache_file)
73
+ if jsonl_cache_file.exists():
74
+ jsonl_cache = read_jsonl_cache(jsonl_cache_file)
75
+ for filename, new_info in jsonl_cache.items():
76
+ if filename in self.dates_cache:
77
+ self.dates_cache[filename].update(new_info)
63
78
 
64
79
  """
65
- Tippy.js
80
+ Tippy.js, for Tooltip
66
81
  # core
67
82
  https://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js
68
83
  https://unpkg.com/tippy.js@6/dist/tippy.umd.min.js
@@ -83,7 +98,11 @@ class DocumentDatesPlugin(BasePlugin):
83
98
  for dir_name in ['tippy', 'core']:
84
99
  source_dir = Path(__file__).parent / 'static' / dir_name
85
100
  target_dir = dest_dir / dir_name
86
- shutil.copytree(source_dir, target_dir, dirs_exist_ok=True)
101
+ # shutil.copytree(source_dir, target_dir, dirs_exist_ok=True)
102
+ target_dir.mkdir(parents=True, exist_ok=True)
103
+ for item in source_dir.iterdir():
104
+ if item.is_file():
105
+ shutil.copy2(item, target_dir / item.name)
87
106
 
88
107
  # 复制配置文件模板到用户目录(如果不存在)
89
108
  config_files = ['user.config.css', 'user.config.js']
@@ -150,7 +169,7 @@ class DocumentDatesPlugin(BasePlugin):
150
169
  modified = self._get_file_modification_time(file_path)
151
170
 
152
171
  # 获取作者信息
153
- authors = self._get_author_info(file_path, page, config)
172
+ authors = self._get_author_info(rel_path, page, config)
154
173
 
155
174
  # 在排除前暴露 meta 信息给前端使用
156
175
  page.meta['document_dates_created'] = created.isoformat()
@@ -202,17 +221,14 @@ class DocumentDatesPlugin(BasePlugin):
202
221
 
203
222
  def _is_excluded(self, rel_path):
204
223
  for pattern in self.config['exclude']:
205
- # if fnmatch.fnmatch(rel_path, pattern):
206
- if self._matches_exclude_pattern(rel_path, pattern):
207
- return True
224
+ if pattern.endswith('*'):
225
+ if rel_path.startswith(pattern.partition('*')[0]):
226
+ return True
227
+ else:
228
+ if rel_path == pattern:
229
+ return True
208
230
  return False
209
231
 
210
- def _matches_exclude_pattern(self, rel_path: str, pattern: str):
211
- if '*' not in pattern:
212
- return rel_path == pattern
213
- else:
214
- return rel_path.startswith(pattern.partition('*')[0])
215
-
216
232
 
217
233
  def _find_meta_date(self, meta, field_names):
218
234
  for field in field_names:
@@ -229,29 +245,16 @@ class DocumentDatesPlugin(BasePlugin):
229
245
  # 优先从缓存中读取
230
246
  if rel_path in self.dates_cache:
231
247
  return datetime.fromisoformat(self.dates_cache[rel_path]['created'])
232
-
233
248
  # 从文件系统获取
234
- fs_time = get_file_creation_time(file_path)
235
-
236
- # 获取Git首次提交时间
237
- if self.is_git_repo:
238
- git_time = get_git_first_commit_time(file_path)
239
- # 取两者更早的时间
240
- if git_time:
241
- return min(fs_time, git_time)
242
- return fs_time
249
+ return get_file_creation_time(file_path).astimezone()
243
250
 
244
251
  def _get_file_modification_time(self, file_path):
245
- # 从git获取最后修改时间
246
- # if self.is_git_repo:
247
- # return get_git_last_commit_time(file_path)
248
-
249
252
  # 从文件系统获取最后修改时间
250
253
  stat = os.stat(file_path)
251
- return datetime.fromtimestamp(stat.st_mtime)
254
+ return datetime.fromtimestamp(stat.st_mtime).astimezone()
252
255
 
253
256
 
254
- def _get_author_info(self, file_path, page, config):
257
+ def _get_author_info(self, rel_path, page, config):
255
258
  if not self.config['show_author']:
256
259
  return None
257
260
  # 1. meta author
@@ -259,10 +262,10 @@ class DocumentDatesPlugin(BasePlugin):
259
262
  if authors:
260
263
  return authors
261
264
  # 2. git author
262
- if self.is_git_repo:
263
- authors = get_git_authors(file_path)
264
- if authors:
265
- return authors
265
+ if rel_path in self.dates_cache:
266
+ authors_list = self.dates_cache[rel_path].get('authors')
267
+ if authors_list:
268
+ return [Author(**dict) for dict in authors_list]
266
269
  # 3. site_author 或 PC username
267
270
  return [Author(name=config.get('site_author') or Path.home().name)]
268
271
 
@@ -334,7 +337,11 @@ class DocumentDatesPlugin(BasePlugin):
334
337
  if self.config['show_author'] and authors:
335
338
  if len(authors) == 1:
336
339
  author, = authors
337
- author_tooltip = f'<a href="mailto:{author.email}">{author.name}</a>' if author.email else author.name
340
+ if author.email:
341
+ # 使用 HTML 实体编码避免 Tippy.js 转义问题
342
+ author_tooltip = f'&lt;a href="mailto:{author.email}"&gt;{author.name}&lt;/a&gt;'
343
+ else:
344
+ author_tooltip = author.name
338
345
  html += (
339
346
  f"<span data-tippy-content='{self.translation.get('author', 'Author')}: {author_tooltip}'>"
340
347
  f"<span class='material-icons' data-icon='doc_author'></span>"
@@ -344,7 +351,13 @@ class DocumentDatesPlugin(BasePlugin):
344
351
  else:
345
352
  # 多个作者的情况
346
353
  authors_info = ', '.join(a.name for a in authors if a.name)
347
- authors_tooltip = ',&nbsp;'.join(f'<a href="mailto:{a.email}">{a.name}</a>' if a.email else a.name for a in authors)
354
+ authors_tooltip_parts = []
355
+ for a in authors:
356
+ if a.email:
357
+ authors_tooltip_parts.append(f'&lt;a href="mailto:{a.email}"&gt;{a.name}&lt;/a&gt;')
358
+ else:
359
+ authors_tooltip_parts.append(a.name)
360
+ authors_tooltip = ',&nbsp;'.join(authors_tooltip_parts)
348
361
  html += (
349
362
  f"<span data-tippy-content='{self.translation.get('authors', 'Authors')}: {authors_tooltip}'>"
350
363
  f"<span class='material-icons' data-icon='doc_authors'></span>"
@@ -361,9 +374,46 @@ class DocumentDatesPlugin(BasePlugin):
361
374
 
362
375
  def _insert_date_info(self, markdown: str, date_info: str):
363
376
  if self.config['position'] == 'top':
364
- before, _, after = markdown.lstrip().partition('\n')
365
- if before.startswith('# '):
366
- return f"{before}\n{date_info}\n{after}"
377
+ first_line, insert_pos = self.find_markdown_body_start(markdown)
378
+ if first_line.startswith(('# ', '<h1')):
379
+ return markdown[:insert_pos] + date_info + '\n' + markdown[insert_pos:]
367
380
  else:
368
381
  return f"{date_info}\n{markdown}"
369
382
  return f"{markdown}\n\n{date_info}"
383
+
384
+ def find_markdown_body_start(self, text: str):
385
+ pos = 0
386
+ length = len(text)
387
+ in_comment = False
388
+ WHITESPACE = {' ', '\t', '\r', '\n'}
389
+
390
+ while pos < length:
391
+ next_newline = text.find('\n', pos)
392
+ if next_newline == -1:
393
+ next_newline = length
394
+
395
+ start = pos
396
+ while start < next_newline and text[start] in WHITESPACE:
397
+ start += 1
398
+
399
+ if start < next_newline:
400
+ if not in_comment:
401
+ if text.startswith('<!--', start):
402
+ in_comment = True
403
+ start += 4
404
+
405
+ if in_comment:
406
+ comment_end = text.find('-->', start, next_newline)
407
+ if comment_end != -1:
408
+ in_comment = False
409
+ pos = comment_end + 3
410
+ continue
411
+ pos = next_newline + 1
412
+ continue
413
+
414
+ # 找到正文行
415
+ return text[start:next_newline], next_newline + 1 if next_newline < length else length
416
+
417
+ pos = next_newline + 1
418
+
419
+ return '', length
@@ -2,11 +2,11 @@
2
2
  including icons, fonts, colors, etc
3
3
  */
4
4
  /*
5
- .document-dates-plugin {
5
+ .md-main .document-dates-plugin {
6
6
  color: rgba(109, 157, 204, 0.7);
7
7
  font-size: 0.75rem;
8
8
  }
9
- .document-dates-plugin .material-icons {
9
+ .md-main .document-dates-plugin .material-icons {
10
10
  font-size: 0.9rem;
11
11
  } */
12
12
 
@@ -34,13 +34,12 @@
34
34
 
35
35
 
36
36
  /* 2. Plug-in wrapper styles
37
- including divider, margin, padding, etc
37
+ including divider line, margin, padding, etc
38
+ for example, remove the divider line like below:
38
39
  */
39
40
  /*
40
- .document-dates-plugin-wrapper.document-dates-top {
41
- border-bottom: 1px solid rgba(142, 142, 142, 0.15);
42
- }
43
- .document-dates-plugin-wrapper.document-dates-bottom {
41
+ .md-main .document-dates-plugin-wrapper.document-dates-top,
42
+ .md-main .document-dates-plugin-wrapper.document-dates-bottom {
44
43
  border-bottom: none;
45
44
  } */
46
45
 
@@ -76,10 +76,18 @@ const localeFunc = (number, index) => {
76
76
  };
77
77
  const localeStr = 'whatever';
78
78
  timeago.register(localeStr, localeFunc);
79
-
80
- if (typeof timeago !== 'undefined') {
81
- document.querySelectorAll('.document-dates-plugin time').forEach(timeElement => {
82
- timeElement.textContent = timeago.format(timeElement.getAttribute('datetime'), localeStr);
83
- });
79
+ function formatTimeagoElements() {
80
+ if (typeof timeago !== 'undefined') {
81
+ document.querySelectorAll('.document-dates-plugin time').forEach(timeElement => {
82
+ timeElement.textContent = timeago.format(timeElement.getAttribute('datetime'), localeStr);
83
+ });
84
+ }
85
+ }
86
+ if (typeof window.document$ !== 'undefined' && !window.document$.isStopped) {
87
+ // Compatible with Material for MkDocs 'navigation.instant'
88
+ window.document$.subscribe(formatTimeagoElements);
89
+ } else {
90
+ // Fallback to standard DOMContentLoaded for other themes
91
+ formatTimeagoElements();
84
92
  }
85
93
  */
@@ -3,13 +3,17 @@
3
3
  including icons, fonts, colors, etc
4
4
  */
5
5
  .document-dates-plugin {
6
- color: rgba(142, 142, 142, 0.7);
7
- font-size: 0.75rem;
6
+ color: #666;
7
+ font-size: 0.9rem;
8
8
  padding: 0.2rem 0;
9
9
  display: flex;
10
10
  align-items: center;
11
11
  margin-bottom: 0.3rem;
12
12
  }
13
+ .md-main .document-dates-plugin {
14
+ color: rgba(142, 142, 142, 0.7);
15
+ font-size: 0.75rem;
16
+ }
13
17
  .document-dates-plugin span:not(:first-child) {
14
18
  margin-left: 1.5rem;
15
19
  }
@@ -18,9 +22,13 @@
18
22
  align-items: center;
19
23
  }
20
24
  .document-dates-plugin .material-icons {
25
+ font-size: 1rem;
26
+ opacity: 0.85;
27
+ margin-right: 0.3rem;
28
+ }
29
+ .md-main .document-dates-plugin .material-icons {
21
30
  font-size: 0.9rem;
22
31
  opacity: 0.7;
23
- margin-right: 0.3rem;
24
32
  }
25
33
 
26
34
  /* Customized icons, just change the icon name.
@@ -48,16 +56,15 @@
48
56
  /* 2. Plug-in wrapper styles
49
57
  including divider, margin, padding, etc
50
58
  */
51
- .document-dates-plugin-wrapper.document-dates-top {
52
- margin: -1rem 0 1rem 0;
53
- padding-bottom: 0.3rem;
54
- border-bottom: 1px solid rgba(142, 142, 142, 0.15);
55
- }
59
+ .document-dates-plugin-wrapper.document-dates-top,
56
60
  .document-dates-plugin-wrapper.document-dates-bottom {
57
61
  margin: 1rem 0;
58
62
  padding-bottom: 0.3rem;
59
63
  border-bottom: 1px solid rgba(142, 142, 142, 0.15);
60
64
  }
65
+ .md-main .document-dates-plugin-wrapper.document-dates-top {
66
+ margin: -1rem 0 1rem 0;
67
+ }
61
68
 
62
69
  /* Hide the footnote divider immediately following the date information with the CSS adjacent sibling selector */
63
70
  .document-dates-plugin-wrapper + .footnote hr {
@@ -62,13 +62,20 @@ function getCurrentTheme() {
62
62
 
63
63
  // Main initialization
64
64
  async function init() {
65
- await executeHooks('beforeInit', { tippy_config });
65
+ // Create context object to pass to hooks and return from function
66
+ const context = { tippy_config };
67
+
68
+ // Execute beforeInit hooks
69
+ await executeHooks('beforeInit', context);
66
70
 
67
71
  // Configure the properties of the Tooltip here, available documents: https://atomiks.github.io/tippyjs/
68
72
  const tippyInstances = tippy('[data-tippy-content]', {
69
73
  ...tippy_config.tooltip,
70
74
  theme: getCurrentTheme() // Initialize Tooltip's theme based on Material's light/dark color scheme
71
75
  });
76
+
77
+ // Store instances in context
78
+ context.tippyInstances = tippyInstances;
72
79
 
73
80
  // Automatic theme switching. Set Tooltip's theme to change automatically with the Material's light/dark color scheme
74
81
  const observer = new MutationObserver((mutations) => {
@@ -86,25 +93,66 @@ async function init() {
86
93
  attributes: true,
87
94
  attributeFilter: ['data-md-color-scheme']
88
95
  });
89
-
90
- await executeHooks('afterInit', { tippyInstances });
96
+
97
+ // Store observer in context
98
+ context.observer = observer;
99
+
100
+ // Execute afterInit hooks
101
+ await executeHooks('afterInit', context);
102
+
103
+ // Return context with instances and observer for cleanup
104
+ return context;
91
105
  }
92
106
 
93
- // Singleton Initialization
107
+ // Initialization Manager
94
108
  const initManager = (() => {
95
- let initialized = false;
109
+ let tippyInstances = [];
110
+ let observer = null;
111
+
112
+ // Function to clean up previous instances
113
+ function cleanup() {
114
+ // Destroy previous tippy instances if they exist
115
+ if (tippyInstances.length > 0) {
116
+ tippyInstances.forEach(instance => instance.destroy());
117
+ tippyInstances = [];
118
+ }
119
+
120
+ // Disconnect previous observer if it exists
121
+ if (observer) {
122
+ observer.disconnect();
123
+ observer = null;
124
+ }
125
+ }
126
+
96
127
  return {
128
+ // This can be called multiple times, especially with navigation.instant
97
129
  initialize() {
98
- if (initialized) return;
99
- init();
100
- initialized = true;
130
+ // Clean up previous instances first
131
+ cleanup();
132
+
133
+ // Initialize new instances
134
+ init().then(context => {
135
+ if (context && context.tippyInstances) {
136
+ tippyInstances = context.tippyInstances;
137
+ }
138
+ if (context && context.observer) {
139
+ observer = context.observer;
140
+ }
141
+ });
101
142
  }
102
143
  };
103
144
  })();
104
145
 
105
- // Entrance
106
- document.addEventListener('DOMContentLoaded', initManager.initialize);
107
146
 
147
+ // Entrance - Compatible with Material for MkDocs 'navigation.instant'
148
+ // Check if Material for MkDocs document$ observable is available
149
+ if (typeof window.document$ !== 'undefined' && !window.document$.isStopped) {
150
+ // Use Material's document$ observable for both initial load and navigation.instant
151
+ window.document$.subscribe(initManager.initialize);
152
+ } else {
153
+ // Fallback to standard DOMContentLoaded for other themes
154
+ document.addEventListener('DOMContentLoaded', initManager.initialize);
155
+ }
108
156
 
109
157
 
110
158
  // Export API
@@ -0,0 +1,18 @@
1
+ // Function to format timeago elements
2
+ function formatTimeagoElements() {
3
+ if (typeof timeago !== 'undefined') {
4
+ document.querySelectorAll('.document-dates-plugin time').forEach(timeElement => {
5
+ timeElement.textContent = timeago.format(timeElement.getAttribute('datetime'), timeElement.getAttribute('locale'));
6
+ });
7
+ }
8
+ }
9
+
10
+ // Compatible with Material for MkDocs 'navigation.instant'
11
+ // Check if Material for MkDocs document$ observable is available
12
+ if (typeof window.document$ !== 'undefined' && !window.document$.isStopped) {
13
+ // Use Material's document$ observable for both initial load and navigation.instant
14
+ window.document$.subscribe(formatTimeagoElements);
15
+ } else {
16
+ // Fallback to standard DOMContentLoaded for other themes
17
+ formatTimeagoElements();
18
+ }
@@ -5,27 +5,54 @@ import logging
5
5
  import subprocess
6
6
  from pathlib import Path
7
7
  from datetime import datetime
8
+ from collections import defaultdict
8
9
 
9
10
  logger = logging.getLogger("mkdocs.plugins.document_dates")
10
11
  logger.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, CRITICAL
11
12
 
12
- class Author:
13
- def __init__(self, name="", email="", **kwargs):
14
- self.name = name
15
- self.email = email
16
- # 扩展属性
17
- self.attributes = kwargs
18
-
19
- def __getattr__(self, name):
20
- return self.attributes.get(name)
21
-
22
- def to_dict(self):
23
- return {
24
- 'name': self.name,
25
- 'email': self.email,
26
- **self.attributes
27
- }
28
13
 
14
+ def load_git_cache(docs_dir_path: Path):
15
+ dates_cache = {}
16
+ try:
17
+ cmd = ['git', 'log', '--reverse', '--no-merges', '--name-only', '--format=%an|%ae|%aI', '--', '*.md']
18
+ process = subprocess.run(cmd, cwd=docs_dir_path, capture_output=True, text=True)
19
+ if process.returncode == 0:
20
+ git_root = Path(subprocess.check_output(
21
+ ['git', 'rev-parse', '--show-toplevel'],
22
+ cwd=docs_dir_path,
23
+ text=True, encoding='utf-8'
24
+ ).strip())
25
+ docs_prefix = docs_dir_path.relative_to(git_root).as_posix()
26
+
27
+ authors_dict = defaultdict(set)
28
+ first_commit = {}
29
+ current_commit = None
30
+
31
+ for line in process.stdout.splitlines():
32
+ line = line.strip()
33
+ if not line:
34
+ continue
35
+ if '|' in line:
36
+ name, email, created = line.split('|', 2)
37
+ current_commit = {'name': name, 'email': email, 'created': created}
38
+ elif line.endswith('.md') and current_commit:
39
+ if line.startswith(docs_prefix + '/'):
40
+ line = line[len(docs_prefix) + 1:]
41
+ authors_dict[line].add((current_commit['name'], current_commit['email']))
42
+ if line not in first_commit:
43
+ first_commit[line] = current_commit['created']
44
+
45
+ for file_path in sorted(first_commit):
46
+ dates_cache[file_path] = {
47
+ 'created': first_commit[file_path],
48
+ 'authors': [
49
+ {'name': name, 'email': email}
50
+ for name, email in sorted(authors_dict[file_path])
51
+ ]
52
+ }
53
+ except Exception as e:
54
+ logger.info(f"Error getting git info in {docs_dir_path}: {e}")
55
+ return dates_cache
29
56
 
30
57
  def get_file_creation_time(file_path):
31
58
  try:
@@ -44,58 +71,6 @@ def get_file_creation_time(file_path):
44
71
  logger.error(f"Failed to get file creation time for {file_path}: {e}")
45
72
  return datetime.now()
46
73
 
47
- def check_git_repo():
48
- try:
49
- check_git = subprocess.run(['git', 'rev-parse', '--is-inside-work-tree'], capture_output=True, text=True)
50
- if check_git.returncode == 0:
51
- return True
52
- except Exception as e:
53
- logger.info(f"Not a Git repository: {str(e)}")
54
- return False
55
-
56
- def get_git_first_commit_time(file_path):
57
- try:
58
- # git log --reverse --format="%aI" -- {file_path} | head -n 1
59
- cmd_list = ['git', 'log', '--reverse', '--format=%aI', '--', file_path]
60
- process = subprocess.run(cmd_list, capture_output=True, text=True)
61
- if process.returncode == 0 and process.stdout.strip():
62
- first_line = process.stdout.partition('\n')[0].strip()
63
- return datetime.fromisoformat(first_line).replace(tzinfo=None)
64
- except Exception as e:
65
- logger.info(f"Error getting git first commit time for {file_path}: {e}")
66
- return None
67
-
68
- def get_git_last_commit_time(file_path):
69
- try:
70
- cmd_list = ['git', 'log', '-1', '--format=%aI', '--', file_path]
71
- process = subprocess.run(cmd_list, capture_output=True, text=True)
72
- if process.returncode == 0 and process.stdout.strip():
73
- git_time = process.stdout.strip()
74
- return datetime.fromisoformat(git_time).replace(tzinfo=None)
75
- except Exception as e:
76
- logger.info(f"Error getting git last commit time for {file_path}: {e}")
77
- return None
78
-
79
- def get_git_authors(file_path):
80
- try:
81
- # 为了兼容性,不采用管道命令,在 python 中处理去重
82
- # git log --format="%an|%ae" -- {file_path} | sort | uniq
83
- # git log --format="%an|%ae" -- {file_path} | grep -vE "bot|noreply|ci|github-actions|dependabot|renovate" | sort | uniq
84
- cmd_list = ['git', 'log', '--format=%an|%ae', '--', file_path]
85
- process = subprocess.run(cmd_list, capture_output=True, text=True)
86
- if process.returncode == 0 and process.stdout.strip():
87
- # 使用字典去重和存储作者
88
- authors_map = {}
89
- for line in process.stdout.splitlines():
90
- if not line.strip() or line in authors_map:
91
- continue
92
- name, email = line.split('|')
93
- authors_map[line] = Author(name=name, email=email)
94
- return list(authors_map.values()) or None
95
- except Exception as e:
96
- logger.warning(f"Failed to get git author info: {str(e)}")
97
- return None
98
-
99
74
  def read_json_cache(cache_file: Path):
100
75
  dates_cache = {}
101
76
  if cache_file.exists():
@@ -141,7 +116,7 @@ def write_jsonl_cache(jsonl_file: Path, dates_cache, tracked_files):
141
116
  logger.info(f"Successfully updated JSONL cache file: {jsonl_file}")
142
117
  return True
143
118
  except (IOError, json.JSONDecodeError) as e:
144
- logger.error(f"Failed to write JSONL cache file {jsonl_file}: {e}")
119
+ logger.warning(f"Failed to write JSONL cache file {jsonl_file}: {e}")
145
120
  except Exception as e:
146
- logger.error(f"Failed to add JSONL cache file to git: {e}")
121
+ logger.warning(f"Failed to add JSONL cache file to git: {e}")
147
122
  return False
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs-document-dates
3
- Version: 3.2
4
- Summary: An easy-to-use, lightweight MkDocs plugin for displaying the exact creation time, last modification time and author info of markdown documents.
3
+ Version: 3.2.1
4
+ Summary: A new generation MkDocs plugin for displaying exact meta-info of documents, such as creation time, last update time, authors, email, etc.
5
5
  Home-page: https://github.com/jaywhj/mkdocs-document-dates
6
6
  Author: Aaron Wang
7
7
  Author-email: aaronwqt@gmail.com
@@ -30,7 +30,7 @@ English | [简体中文](README_zh.md)
30
30
 
31
31
 
32
32
 
33
- An easy-to-use, lightweight MkDocs plugin for displaying the <mark>exact</mark> creation time, last modification time and author info of markdown documents.
33
+ A new generation MkDocs plugin for displaying exact meta-info of documents, such as **creation time, last update time, authors, email**, etc.
34
34
 
35
35
  ## Features
36
36
 
@@ -43,6 +43,7 @@ An easy-to-use, lightweight MkDocs plugin for displaying the <mark>exact</mark>
43
43
  - Intelligent repositioning to always float optimally in view
44
44
  - Supports automatic theme switching following Material's light/dark color scheme
45
45
  - Multi-language support, cross-platform support (Windows, macOS, Linux)
46
+ - Ultimate build efficiency O(1), typically less than 0.2 seconds, regardless of whether the number of documents is 1,000 or 10,000
46
47
 
47
48
  ## Showcases
48
49
 
@@ -75,7 +76,7 @@ plugins:
75
76
  time_format: '%H:%M:%S' # Time format strings (valid only if type=datetime)
76
77
  exclude: # List of excluded files
77
78
  - temp.md # Exclude specific file
78
- - private/* # Exclude all files in private directory, including subdirectories
79
+ - private/* # Exclude all files in private folder, including subfolders
79
80
  show_author: true # Show author or not, default: true
80
81
  ```
81
82
 
@@ -83,7 +84,7 @@ plugins:
83
84
 
84
85
  The plugin will automatically get the exact time of the document, will automatically cache the creation time, but of course, you can also specify it manually in `Front Matter`
85
86
 
86
- Priority order: `Front Matter` > `Cache Files` > `File System Timestamps`
87
+ Priority order: `Front Matter` > `File System Timestamps(Cached)` > `Git Timestamps`
87
88
 
88
89
  ```yaml
89
90
  ---
@@ -101,7 +102,7 @@ modified: 2025-02-23
101
102
 
102
103
  The plugin will automatically get the author of the document, will parse the email and make a link, also you can specify it manually in `Front Matter`
103
104
 
104
- Priority order: `Front Matter` > `Git Author` > `site_author (mkdocs.yml)` > `PC Username`
105
+ Priority order: `Front Matter` > `Git Author` > `site_author(mkdocs.yml)` > `PC Username`
105
106
 
106
107
  ```yaml
107
108
  ---
@@ -117,7 +118,7 @@ email: e-name@gmail.com
117
118
 
118
119
  ## Customization
119
120
 
120
- The plugin supports deep customization, such as **icon style, theme color, font, animation, dividing line**, etc. Everything is customizable (I've already written the code, you just need to turn on the uncomment switch):
121
+ The plugin supports deep customization, such as **icon style, theme color, font, animation, dividing line**, etc. Everything is customizable (Find the file below and turn on the uncomment switch):
121
122
 
122
123
  | Category: | Location: |
123
124
  | :----------------------: | ---------------------------------------- |
@@ -146,7 +147,7 @@ The plugin supports deep customization, such as **icon style, theme color, font,
146
147
  ![06-change-theme](mkdocs_document_dates/demo_images/06-change-theme.png)
147
148
  ![08-pop-up-from-bottom](mkdocs_document_dates/demo_images/08-pop-up-from-bottom.png)
148
149
 
149
- ## Used in templates
150
+ ## Template Variables
150
151
 
151
152
  You can access the meta-info of a document in a template using the following variables:
152
153
 
@@ -26,9 +26,9 @@ try:
26
26
  with open("README.md", "r", encoding="utf-8") as fh:
27
27
  long_description = fh.read()
28
28
  except FileNotFoundError:
29
- long_description = "An easy-to-use, lightweight MkDocs plugin for displaying the exact creation time, last modification time and author info of markdown documents."
29
+ long_description = "A new generation MkDocs plugin for displaying exact meta-info of documents, such as creation time, last update time, authors, email, etc."
30
30
 
31
- VERSION = '3.2'
31
+ VERSION = '3.2.1'
32
32
 
33
33
  setup(
34
34
  name="mkdocs-document-dates",
@@ -36,7 +36,7 @@ setup(
36
36
  author="Aaron Wang",
37
37
  author_email="aaronwqt@gmail.com",
38
38
  license="MIT",
39
- description="An easy-to-use, lightweight MkDocs plugin for displaying the exact creation time, last modification time and author info of markdown documents.",
39
+ description="A new generation MkDocs plugin for displaying exact meta-info of documents, such as creation time, last update time, authors, email, etc.",
40
40
  long_description=long_description,
41
41
  long_description_content_type="text/markdown",
42
42
  url="https://github.com/jaywhj/mkdocs-document-dates",
@@ -1,11 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Aaron Wang
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
@@ -1,5 +0,0 @@
1
- if (typeof timeago !== 'undefined') {
2
- document.querySelectorAll('.document-dates-plugin time').forEach(timeElement => {
3
- timeElement.textContent = timeago.format(timeElement.getAttribute('datetime'), timeElement.getAttribute('locale'));
4
- });
5
- }