mkdocs-document-dates 3.6.0__tar.gz → 3.7.0__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 (48) hide show
  1. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/LICENSE +1 -1
  2. mkdocs_document_dates-3.7.0/MANIFEST.in +9 -0
  3. {mkdocs_document_dates-3.6.0/mkdocs_document_dates.egg-info → mkdocs_document_dates-3.7.0}/PKG-INFO +19 -30
  4. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/README.md +13 -15
  5. mkdocs_document_dates-3.7.0/mkdocs_document_dates/__init__.py +1 -0
  6. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/cache_manager.py +85 -5
  7. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/hooks/pre-commit +1 -1
  8. mkdocs_document_dates-3.7.0/mkdocs_document_dates/hooks_installer.py +98 -0
  9. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/plugin.py +38 -11
  10. mkdocs_document_dates-3.7.0/mkdocs_document_dates/static/.DS_Store +0 -0
  11. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/core/core.css +2 -1
  12. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/core/core.js +5 -12
  13. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/core/utils.js +12 -0
  14. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/templates/recently_updated_group.html +30 -7
  15. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/utils.py +16 -7
  16. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0/mkdocs_document_dates.egg-info}/PKG-INFO +19 -30
  17. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates.egg-info/SOURCES.txt +1 -0
  18. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates.egg-info/entry_points.txt +1 -0
  19. mkdocs_document_dates-3.7.0/pyproject.toml +48 -0
  20. mkdocs_document_dates-3.7.0/setup.py +50 -0
  21. mkdocs_document_dates-3.6.0/MANIFEST.in +0 -2
  22. mkdocs_document_dates-3.6.0/mkdocs_document_dates/__init__.py +0 -6
  23. mkdocs_document_dates-3.6.0/mkdocs_document_dates/hooks_installer.py +0 -120
  24. mkdocs_document_dates-3.6.0/pyproject.toml +0 -4
  25. mkdocs_document_dates-3.6.0/setup.py +0 -69
  26. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/config/user.config.css +0 -0
  27. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/config/user.config.js +0 -0
  28. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/core/default.config.js +0 -0
  29. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/core/md5.min.js +0 -0
  30. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/core/timeago.full.min.js +0 -0
  31. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/core/timeago.min.js +0 -0
  32. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/fonts/material-icons.css +0 -0
  33. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/fonts/materialicons.woff2 +0 -0
  34. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/templates/recently_updated_detail.html +0 -0
  35. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/templates/recently_updated_grid.html +0 -0
  36. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/templates/recently_updated_list.html +0 -0
  37. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/tippy/backdrop.css +0 -0
  38. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/tippy/light.css +0 -0
  39. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/tippy/material.css +0 -0
  40. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/tippy/popper.min.js +0 -0
  41. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/tippy/scale.css +0 -0
  42. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/tippy/shift-away.css +0 -0
  43. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/tippy/tippy.css +0 -0
  44. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates/static/tippy/tippy.umd.min.js +0 -0
  45. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates.egg-info/dependency_links.txt +0 -0
  46. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates.egg-info/requires.txt +0 -0
  47. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/mkdocs_document_dates.egg-info/top_level.txt +0 -0
  48. {mkdocs_document_dates-3.6.0 → mkdocs_document_dates-3.7.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Aaron Wang
3
+ Copyright (c) 2026 Aaron Wang
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,9 @@
1
+ global-exclude tests/*
2
+ global-exclude tests/**/*
3
+
4
+ global-exclude .DS_Store
5
+ global-exclude __pycache__/*
6
+ global-exclude *.py[cod]
7
+
8
+ graft mkdocs_document_dates/hooks
9
+ graft mkdocs_document_dates/static
@@ -1,28 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs-document-dates
3
- Version: 3.6.0
3
+ Version: 3.7.0
4
4
  Summary: A new generation MkDocs plugin for displaying exact creation date, last updated date, authors, email of documents
5
- Home-page: https://github.com/jaywhj/mkdocs-document-dates
6
- Author: Aaron Wang
7
- Author-email: aaronwqt@gmail.com
8
- License: MIT
5
+ Author-email: Aaron Wang <aaronwqt@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/jaywhj/mkdocs-document-dates
8
+ Project-URL: Repository, https://github.com/jaywhj/mkdocs-document-dates
9
+ Project-URL: Documentation, https://jaywhj.netlify.app/document-dates-en
9
10
  Classifier: Programming Language :: Python :: 3
10
11
  Classifier: Operating System :: OS Independent
11
12
  Requires-Python: >=3.7
12
13
  Description-Content-Type: text/markdown
13
14
  License-File: LICENSE
14
15
  Requires-Dist: mkdocs>=1.1.0
15
- Dynamic: author
16
- Dynamic: author-email
17
- Dynamic: classifier
18
- Dynamic: description
19
- Dynamic: description-content-type
20
- Dynamic: home-page
21
- Dynamic: license
22
16
  Dynamic: license-file
23
- Dynamic: requires-dist
24
- Dynamic: requires-python
25
- Dynamic: summary
26
17
 
27
18
  # mkdocs-document-dates
28
19
 
@@ -36,7 +27,7 @@ A new generation MkDocs plugin for displaying exact **creation date, last update
36
27
 
37
28
  ## Features
38
29
 
39
- - [x] Always displays **exact** meta information of the document and works in any environment (no-Git, Git environments, Docker, all CI/CD build systems, etc.)
30
+ - [x] Works in any environment: no-Git, Git environments, Docker, all CI/CD build systems, etc.
40
31
  - [x] Support list display of recently updated documents (in descending order of update date)
41
32
  - [x] Support for manually specifying date and author in `Front Matter`
42
33
  - [x] Support for multiple date formats (date, datetime, timeago)
@@ -67,41 +58,39 @@ plugins:
67
58
  - document-dates
68
59
  ```
69
60
 
70
- Or, full configuration:
61
+ Or, common configuration:
71
62
 
72
63
  ```yaml
73
64
  plugins:
74
65
  - document-dates:
75
66
  position: top # Display position: top(after title) bottom(end of document), default: top
76
67
  type: date # Date type: date datetime timeago, default: date
77
- exclude: # List of excluded files
68
+ exclude: # List of excluded files (support unix shell-style wildcards)
78
69
  - temp.md # Example: exclude the specified file
79
70
  - 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)
71
+ - '*/index.md' # Example: exclude all index.md files in any subfolders
82
72
  ```
83
73
 
84
74
  ## Customization Settings
85
75
 
86
76
  In addition to the above basic configuration, the plug-in also provides a wealth of customization options to meet a variety of individual needs:
87
77
 
88
- - [Specify Datetime](https://jaywhj.netlify.app/document-dates-en#Specify-Datetime): Introduces the mechanism for obtaining document dates and methods for personalized customization, you can manually specify the creation date and last updated date for each document
89
- - [Specify Author](https://jaywhj.netlify.app/document-dates-en#Specify-Author): Introduces the mechanism for obtaining document authors and methods for personalized customization, you can manually specify the author information for each document, such as name, link, avatar, email, etc.
90
- - [Specify Avatar](https://jaywhj.netlify.app/document-dates-en#Specify-Avatar): You can manually specify the avatar for each author, support local file path and URL path
91
- - [Configuration Structure and Style](https://jaywhj.netlify.app/document-dates-en#Structure-and-Style): You can freely configure the plugin's display structure in mkdocs.yml or Front Matter. You can quickly set the plugin styles through preset entrances, such as icons, themes, colors, fonts, animations, dividing line and so on
92
- - [Use Template Variables](https://jaywhj.netlify.app/document-dates-en#Use-Template-Variables): Can be used to optimize `sitemap.xml` for site SEO; Can be used to re-customize plug-ins, etc.
93
- - [Add Recently Updated Module](https://jaywhj.netlify.app/document-dates-en#Add-Recently-Updated-Module): Enable list of recently updated documents (in descending order of update date), this is ideal for sites with a large number of documents, so that readers can quickly see what's new
94
- - [Add Localization Language](https://jaywhj.netlify.app/document-dates-en#Add-Localization-Language): More localization languages for `timeago` and `tooltip`
95
- - [Other Tips](https://jaywhj.netlify.app/document-dates-en#Other-Tips): Introducing the Do's of using plugin in Docker
78
+ - [Date & Time](https://jaywhj.netlify.app/document-dates-en#Date--Time): Introduces the mechanism for obtaining document dates and methods for personalized customization, support for manually specifying the creation date and last updated date for each document
79
+ - [Author](https://jaywhj.netlify.app/document-dates-en#Author): Introduces the mechanism for obtaining document authors and methods for personalized customization, support for manually specifying the author information for each document, such as name, link, avatar, email, etc.
80
+ - [Avatar](https://jaywhj.netlify.app/document-dates-en#Avatar): You can manually specify the avatar for each author, support local file path and URL path
81
+ - [Structure and Style](https://jaywhj.netlify.app/document-dates-en#Structure-and-Style): You can freely configure the plugin's display structure in mkdocs.yml or Front Matter. You can quickly set the plugin styles through preset entrances, such as icons, themes, colors, fonts, animations, dividing line and so on
82
+ - [Template Variables](https://jaywhj.netlify.app/document-dates-en#Template-Variables): Can be used to optimize `sitemap.xml` for site SEO
83
+ - [Recently Updated Module](https://jaywhj.netlify.app/document-dates-en#Recently-Updated-Module): Enable list of recently updated documents (in descending order of update date), this is ideal for sites with a large number of documents, so that readers can quickly see what's new
84
+ - [Localization Language](https://jaywhj.netlify.app/document-dates-en#Localization-Language): More localization languages for `timeago` and `tooltip`
96
85
  - [Development Stories](https://jaywhj.netlify.app/document-dates-en#Development-Stories): Describes the origin of the plug-in, the difficulties and solutions encountered in development, and the principles and directions of product design
97
86
 
98
87
  See the documentation for details: https://jaywhj.netlify.app/document-dates-en
99
88
 
100
- <br />
89
+ ![recently-updated](recently-updated-en.gif)
101
90
 
102
91
  ## Other Projects
103
92
 
104
- - [**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).
93
+ - [**MaterialX**](https://github.com/jaywhj/mkdocs-materialx), the next generation of mkdocs-material. Build beautiful sites the way you already know and love. Based on `mkdocs-material-9.7.1` and is named `X`, it provides ongoing maintenance and updates (since mkdocs-material will stop being maintained).
105
94
  Updates have been released that refactor and add a lot of new features, see https://github.com/jaywhj/mkdocs-materialx/releases/
106
95
 
107
96
  <br />
@@ -10,7 +10,7 @@ A new generation MkDocs plugin for displaying exact **creation date, last update
10
10
 
11
11
  ## Features
12
12
 
13
- - [x] Always displays **exact** meta information of the document and works in any environment (no-Git, Git environments, Docker, all CI/CD build systems, etc.)
13
+ - [x] Works in any environment: no-Git, Git environments, Docker, all CI/CD build systems, etc.
14
14
  - [x] Support list display of recently updated documents (in descending order of update date)
15
15
  - [x] Support for manually specifying date and author in `Front Matter`
16
16
  - [x] Support for multiple date formats (date, datetime, timeago)
@@ -41,41 +41,39 @@ plugins:
41
41
  - document-dates
42
42
  ```
43
43
 
44
- Or, full configuration:
44
+ Or, common configuration:
45
45
 
46
46
  ```yaml
47
47
  plugins:
48
48
  - document-dates:
49
49
  position: top # Display position: top(after title) bottom(end of document), default: top
50
50
  type: date # Date type: date datetime timeago, default: date
51
- exclude: # List of excluded files
51
+ exclude: # List of excluded files (support unix shell-style wildcards)
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)
54
+ - '*/index.md' # Example: exclude all index.md files in any subfolders
56
55
  ```
57
56
 
58
57
  ## Customization Settings
59
58
 
60
59
  In addition to the above basic configuration, the plug-in also provides a wealth of customization options to meet a variety of individual needs:
61
60
 
62
- - [Specify Datetime](https://jaywhj.netlify.app/document-dates-en#Specify-Datetime): Introduces the mechanism for obtaining document dates and methods for personalized customization, you can manually specify the creation date and last updated date for each document
63
- - [Specify Author](https://jaywhj.netlify.app/document-dates-en#Specify-Author): Introduces the mechanism for obtaining document authors and methods for personalized customization, you can manually specify the author information for each document, such as name, link, avatar, email, etc.
64
- - [Specify Avatar](https://jaywhj.netlify.app/document-dates-en#Specify-Avatar): You can manually specify the avatar for each author, support local file path and URL path
65
- - [Configuration Structure and Style](https://jaywhj.netlify.app/document-dates-en#Structure-and-Style): You can freely configure the plugin's display structure in mkdocs.yml or Front Matter. You can quickly set the plugin styles through preset entrances, such as icons, themes, colors, fonts, animations, dividing line and so on
66
- - [Use Template Variables](https://jaywhj.netlify.app/document-dates-en#Use-Template-Variables): Can be used to optimize `sitemap.xml` for site SEO; Can be used to re-customize plug-ins, etc.
67
- - [Add Recently Updated Module](https://jaywhj.netlify.app/document-dates-en#Add-Recently-Updated-Module): Enable list of recently updated documents (in descending order of update date), this is ideal for sites with a large number of documents, so that readers can quickly see what's new
68
- - [Add Localization Language](https://jaywhj.netlify.app/document-dates-en#Add-Localization-Language): More localization languages for `timeago` and `tooltip`
69
- - [Other Tips](https://jaywhj.netlify.app/document-dates-en#Other-Tips): Introducing the Do's of using plugin in Docker
61
+ - [Date & Time](https://jaywhj.netlify.app/document-dates-en#Date--Time): Introduces the mechanism for obtaining document dates and methods for personalized customization, support for manually specifying the creation date and last updated date for each document
62
+ - [Author](https://jaywhj.netlify.app/document-dates-en#Author): Introduces the mechanism for obtaining document authors and methods for personalized customization, support for manually specifying the author information for each document, such as name, link, avatar, email, etc.
63
+ - [Avatar](https://jaywhj.netlify.app/document-dates-en#Avatar): You can manually specify the avatar for each author, support local file path and URL path
64
+ - [Structure and Style](https://jaywhj.netlify.app/document-dates-en#Structure-and-Style): You can freely configure the plugin's display structure in mkdocs.yml or Front Matter. You can quickly set the plugin styles through preset entrances, such as icons, themes, colors, fonts, animations, dividing line and so on
65
+ - [Template Variables](https://jaywhj.netlify.app/document-dates-en#Template-Variables): Can be used to optimize `sitemap.xml` for site SEO
66
+ - [Recently Updated Module](https://jaywhj.netlify.app/document-dates-en#Recently-Updated-Module): Enable list of recently updated documents (in descending order of update date), this is ideal for sites with a large number of documents, so that readers can quickly see what's new
67
+ - [Localization Language](https://jaywhj.netlify.app/document-dates-en#Localization-Language): More localization languages for `timeago` and `tooltip`
70
68
  - [Development Stories](https://jaywhj.netlify.app/document-dates-en#Development-Stories): Describes the origin of the plug-in, the difficulties and solutions encountered in development, and the principles and directions of product design
71
69
 
72
70
  See the documentation for details: https://jaywhj.netlify.app/document-dates-en
73
71
 
74
- <br />
72
+ ![recently-updated](recently-updated-en.gif)
75
73
 
76
74
  ## Other Projects
77
75
 
78
- - [**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).
76
+ - [**MaterialX**](https://github.com/jaywhj/mkdocs-materialx), the next generation of mkdocs-material. Build beautiful sites the way you already know and love. Based on `mkdocs-material-9.7.1` and is named `X`, it provides ongoing maintenance and updates (since mkdocs-material will stop being maintained).
79
77
  Updates have been released that refactor and add a lot of new features, see https://github.com/jaywhj/mkdocs-materialx/releases/
80
78
 
81
79
  <br />
@@ -0,0 +1 @@
1
+ """MkDocs Document Dates Plugin."""
@@ -1,16 +1,92 @@
1
1
  import logging
2
+ import os
2
3
  import subprocess
3
4
  from pathlib import Path
5
+ from logging.handlers import RotatingFileHandler
6
+ from typing import Optional
4
7
  from .utils import read_jsonl_cache, write_jsonl_cache, get_file_creation_time, get_git_first_commit_time
5
8
 
6
9
  logger = logging.getLogger("mkdocs.plugins.document_dates")
7
- logger.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, CRITICAL
10
+ _LOGGING_CONFIGURED = False
11
+
12
+
13
+ def _default_log_file() -> Path:
14
+ try:
15
+ git_root = Path(subprocess.check_output(
16
+ ['git', 'rev-parse', '--show-toplevel'],
17
+ env=_clean_git_env(),
18
+ encoding='utf-8'
19
+ ).strip())
20
+ base_dir = git_root
21
+ except Exception:
22
+ base_dir = Path.cwd()
23
+ return base_dir / "mkdocs_document_dates.log"
24
+
25
+ def configure_file_logging(log_file: Optional[Path] = None, level: int = logging.DEBUG) -> Optional[Path]:
26
+ global _LOGGING_CONFIGURED
27
+ if _LOGGING_CONFIGURED:
28
+ return log_file
29
+
30
+ env_log_file = os.getenv("MKDOCS_DOCUMENT_DATES_LOG_FILE")
31
+ if log_file is None and env_log_file:
32
+ log_file = Path(env_log_file).expanduser()
33
+
34
+ if log_file is None:
35
+ return None
36
+
37
+ log_file.parent.mkdir(parents=True, exist_ok=True)
38
+
39
+ handler = RotatingFileHandler(
40
+ log_file,
41
+ maxBytes=5 * 1024 * 1024,
42
+ backupCount=3,
43
+ encoding="utf-8",
44
+ )
45
+ handler.setLevel(level)
46
+ handler.setFormatter(logging.Formatter(
47
+ fmt="%(asctime)s [%(filename)s:%(lineno)d] %(message)s",
48
+ datefmt="%Y-%m-%d %H:%M:%S",
49
+ ))
50
+
51
+ logger.setLevel(level)
52
+ logger.addHandler(handler)
53
+ logger.propagate = False
54
+ _LOGGING_CONFIGURED = True
55
+ logger.debug(f"File logging enabled: {log_file}")
56
+ return log_file
57
+
58
+ def _env_truthy(name: str) -> bool:
59
+ value = os.getenv(name)
60
+ if value is None:
61
+ return False
62
+ value = value.strip().lower()
63
+ return value not in ("", "0", "false", "no", "off")
64
+
65
+
66
+ def _clean_git_env():
67
+ env = os.environ.copy()
68
+
69
+ for k in [
70
+ "GIT_DIR",
71
+ "GIT_WORK_TREE",
72
+ "GIT_COMMON_DIR",
73
+ "GIT_INDEX_FILE",
74
+ "GIT_PREFIX",
75
+ "GIT_SUPER_PREFIX",
76
+ "GIT_CEILING_DIRECTORIES",
77
+ ]:
78
+ env.pop(k, None)
79
+
80
+ env["GIT_OPTIONAL_LOCKS"] = "0"
81
+
82
+ return env
8
83
 
9
84
  def find_mkdocs_projects():
10
85
  projects = []
11
86
  try:
12
87
  git_root = Path(subprocess.check_output(
13
88
  ['git', 'rev-parse', '--show-toplevel'],
89
+ env=_clean_git_env(),
14
90
  encoding='utf-8'
15
91
  ).strip())
16
92
 
@@ -39,7 +115,7 @@ def setup_gitattributes(docs_dir: Path):
39
115
  content += '\n'
40
116
  content += f"{union_merge_line}\n"
41
117
  gitattributes_path.write_text(content, encoding='utf-8')
42
- subprocess.run(["git", "add", str(gitattributes_path)], check=True)
118
+ subprocess.run(["git", "add", str(gitattributes_path)], cwd=docs_dir, env=_clean_git_env(), check=True)
43
119
  logger.info(f"Updated .gitattributes file: {gitattributes_path}")
44
120
  return True
45
121
  except (IOError, OSError) as e:
@@ -49,8 +125,12 @@ def setup_gitattributes(docs_dir: Path):
49
125
  return False
50
126
 
51
127
  def update_cache():
52
- global_updated = False
128
+ if os.getenv("MKDOCS_DOCUMENT_DATES_LOG_FILE"):
129
+ configure_file_logging()
130
+ elif _env_truthy("MKDOCS_DOCUMENT_DATES_DEBUG"):
131
+ configure_file_logging(_default_log_file())
53
132
 
133
+ global_updated = False
54
134
  for project_dir in find_mkdocs_projects():
55
135
  try:
56
136
  project_updated = False
@@ -65,7 +145,7 @@ def update_cache():
65
145
 
66
146
  # 获取docs目录下已跟踪(tracked)的markdown文件
67
147
  cmd = ["git", "ls-files", "*.md"]
68
- result = subprocess.run(cmd, cwd=docs_dir, capture_output=True, encoding='utf-8')
148
+ result = subprocess.run(cmd, cwd=docs_dir, env=_clean_git_env(), capture_output=True, encoding='utf-8')
69
149
  tracked_files = result.stdout.splitlines() if result.stdout else []
70
150
 
71
151
  if not tracked_files:
@@ -115,4 +195,4 @@ def update_cache():
115
195
 
116
196
 
117
197
  if __name__ == "__main__":
118
- update_cache()
198
+ update_cache()
@@ -4,7 +4,7 @@ try:
4
4
  from mkdocs_document_dates.cache_manager import update_cache
5
5
  except Exception:
6
6
  import sys
7
- # 正常退出(0 状态码),不影响 git 的后续动作
7
+ # Normal exit (0 status code), don't affect subsequent git actions
8
8
  sys.exit(0)
9
9
 
10
10
  if __name__ == "__main__":
@@ -0,0 +1,98 @@
1
+ import os
2
+ import sys
3
+ import subprocess
4
+ from pathlib import Path
5
+
6
+ DEFAULT_HOOKS_DIRNAME = ".githooks"
7
+
8
+
9
+ class HookInstallError(Exception):
10
+ pass
11
+
12
+
13
+ def get_repo_root() -> Path:
14
+ result = subprocess.run(
15
+ ["git", "rev-parse", "--show-toplevel"],
16
+ capture_output=True,
17
+ text=True,
18
+ )
19
+ if result.returncode != 0 or not result.stdout.strip():
20
+ raise HookInstallError("This command must be run inside a Git repo. Please cd into your project directory and try again.")
21
+ return Path(result.stdout.strip())
22
+
23
+
24
+ def ensure_hooks_dir(repo_root: Path) -> Path:
25
+ hooks_dir = repo_root / DEFAULT_HOOKS_DIRNAME
26
+ try:
27
+ hooks_dir.mkdir(parents=True, exist_ok=True)
28
+ os.chmod(hooks_dir, 0o755)
29
+ except Exception as e:
30
+ raise HookInstallError(f"Failed to create hooks directory: {hooks_dir} ({e})")
31
+ return hooks_dir
32
+
33
+
34
+ def write_hooks(source_dir: Path, target_dir: Path):
35
+ if not source_dir.exists():
36
+ raise HookInstallError(f"Hooks source directory not found: {source_dir}")
37
+
38
+ shebang = f"#!{sys.executable}"
39
+
40
+ try:
41
+ for file in source_dir.iterdir():
42
+ if file.name.startswith(".") or not file.is_file():
43
+ continue
44
+
45
+ content = file.read_text(encoding="utf-8")
46
+
47
+ if content.startswith("#!"):
48
+ os.linesep
49
+ content = shebang + "\n" + content.split("\n", 1)[1]
50
+ else:
51
+ content = shebang + "\n" + content
52
+
53
+ target = target_dir / file.name
54
+ target.write_text(content, encoding="utf-8")
55
+ os.chmod(target, 0o755)
56
+
57
+ except Exception as e:
58
+ raise HookInstallError(f"Failed to write hook files: {e}")
59
+
60
+
61
+ def set_hooks_path(repo_root: Path, hooks_dir: Path):
62
+ rel_path = hooks_dir.relative_to(repo_root).as_posix()
63
+
64
+ # # 配置自定义合并驱动
65
+ # script_path = hooks_dir / 'json_merge_driver.py'
66
+ # subprocess.run(['git', 'config', 'merge.custom_json_merge.name', 'Custom JSON merge driver'], check=True)
67
+ # subprocess.run(['git', 'config', 'merge.custom_json_merge.driver', f'"{sys.executable}" "{script_path}" %O %A %B'], check=True)
68
+
69
+ result = subprocess.run(
70
+ ["git", "config", "core.hooksPath", rel_path],
71
+ cwd=repo_root,
72
+ capture_output=True,
73
+ text=True,
74
+ )
75
+ if result.returncode != 0:
76
+ raise HookInstallError(result.stderr.strip() or "Failed to set hooksPath")
77
+
78
+
79
+ def install():
80
+ try:
81
+ repo_root = get_repo_root()
82
+ hooks_dir = ensure_hooks_dir(repo_root)
83
+
84
+ source_dir = Path(__file__).parent / "hooks"
85
+
86
+ write_hooks(source_dir, hooks_dir)
87
+ set_hooks_path(repo_root, hooks_dir)
88
+
89
+ print(f"✔ Hooks installed successfully: {hooks_dir}")
90
+
91
+ except HookInstallError as e:
92
+ print(f"✖ Installation failed: {e}", file=sys.stderr)
93
+ except Exception as e:
94
+ print(f"✖ Unexpected error: {e}", file=sys.stderr)
95
+
96
+
97
+ if __name__ == "__main__":
98
+ install()
@@ -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
@@ -43,11 +43,13 @@ class DocumentDatesPlugin(BasePlugin):
43
43
 
44
44
  def __init__(self):
45
45
  super().__init__()
46
+
46
47
  self.dates_cache = {}
47
48
  self.last_updated_dates = {}
48
49
  self.authors_yml = {}
49
50
  self.recent_docs_html = None
50
51
  self.recent_enable = False
52
+ self._exclude_patterns = []
51
53
 
52
54
  def on_config(self, config):
53
55
  docs_dir_path = Path(config['docs_dir'])
@@ -96,7 +98,14 @@ class DocumentDatesPlugin(BasePlugin):
96
98
  # https://cdn.jsdelivr.net/npm/timeago.js@4.0.2/dist/timeago.min.js
97
99
  # https://cdnjs.cloudflare.com/ajax/libs/timeago.js/4.0.2/timeago.full.min.js
98
100
  if self.config['type'] == 'timeago':
99
- config['extra_javascript'].insert(0, 'assets/document_dates/core/timeago.min.js')
101
+ scripts = config.get('extra_javascript') or []
102
+ has_timeago = any(
103
+ str(item).endswith('timeago.min.js') or str(item).endswith('timeago.full.min.js')
104
+ for item in scripts
105
+ )
106
+ if not has_timeago:
107
+ scripts.insert(0, 'assets/document_dates/core/timeago.min.js')
108
+ config['extra_javascript'] = scripts
100
109
 
101
110
  """
102
111
  Tippy.js, for Tooltip
@@ -138,6 +147,8 @@ class DocumentDatesPlugin(BasePlugin):
138
147
  'assets/document_dates/core/core.js'
139
148
  ])
140
149
 
150
+ self._exclude_patterns = compile_exclude_patterns(self.config['exclude'])
151
+
141
152
  return config
142
153
 
143
154
  def on_page_markdown(self, markdown, page: Page, config, files):
@@ -157,14 +168,19 @@ class DocumentDatesPlugin(BasePlugin):
157
168
 
158
169
  # 获取作者信息
159
170
  authors = self._get_author_info(rel_path, page, config)
160
-
161
- # 在排除前暴露 meta 信息给前端使用
162
- page.meta['document_dates_created'] = created.isoformat()
163
- page.meta['document_dates_updated'] = updated.isoformat()
164
- page.meta['document_dates_authors'] = authors
165
-
171
+
172
+ # MaterialX 的数据规范给 meta 填充数据
173
+ mx = page.meta.setdefault("_mx", {})
174
+ mx["document_dates"] = {
175
+ "dates": {
176
+ "created": created.isoformat(),
177
+ "updated": updated.isoformat(),
178
+ },
179
+ "authors": authors
180
+ }
181
+
166
182
  # 检查是否需要排除
167
- if is_excluded(rel_path, self.config['exclude']):
183
+ if is_excluded(rel_path, self._exclude_patterns):
168
184
  return markdown
169
185
 
170
186
  # 生成日期和作者信息 HTML
@@ -187,7 +203,8 @@ class DocumentDatesPlugin(BasePlugin):
187
203
  limit = recently_updated_config.get('limit', 10)
188
204
 
189
205
  # 获取最近更新的文档数据
190
- recently_updated_docs = get_recently_updated_files(self.last_updated_dates, files, exclude_list, limit, self.recent_enable)
206
+ recent_exclude_patterns = compile_exclude_patterns(exclude_list)
207
+ recently_updated_docs = get_recently_updated_files(self.last_updated_dates, files, recent_exclude_patterns, limit, self.recent_enable)
191
208
 
192
209
  # 将数据注入到 config['extra'] 中供全局访问
193
210
  if 'extra' not in config:
@@ -198,6 +215,16 @@ class DocumentDatesPlugin(BasePlugin):
198
215
  if self.recent_enable:
199
216
  self.recent_docs_html = self._render_recently_updated_html(recently_updated_docs)
200
217
 
218
+ # # 便捷访问日期数据函数
219
+ # def mdd_access(page, domain):
220
+ # return (
221
+ # page.meta.get("_mx", {})
222
+ # .get("document_dates", {})
223
+ # .get(domain, {})
224
+ # )
225
+
226
+ # env.globals["mdd"] = mdd_access
227
+
201
228
  return env
202
229
 
203
230
  def on_post_page(self, output, page, config):
@@ -477,4 +504,4 @@ class DocumentDatesPlugin(BasePlugin):
477
504
 
478
505
  pos = next_newline + 1
479
506
 
480
- return '', length
507
+ return '', length
@@ -68,6 +68,7 @@
68
68
  display: flex;
69
69
  align-items: center;
70
70
  flex-shrink: 0;
71
+ margin-right: 1.4rem;
71
72
  }
72
73
  .dd-item {
73
74
  display: inline-flex;
@@ -142,8 +143,8 @@
142
143
  display: block;
143
144
 
144
145
  /* Fix bug in Safari: rounded corners may blink to show square corners */
145
- will-change: transform;
146
146
  transform: translateZ(0);
147
+ will-change: transform;
147
148
  }
148
149
  .avatar-wrapper:hover {
149
150
  transform: scale(1.08);
@@ -163,16 +163,9 @@ function applyTimeagoToTimes(timeNodes, rawLocale) {
163
163
 
164
164
  // 处理数据加载
165
165
  function processDataLoading() {
166
- // 获取 locale,优先级:用户主动选择 > 服务端显式配置 > 用户浏览器语言 > 站点HTML语言 > 默认英语
167
- const rawLocale =
168
- ddUtils.getSavedLanguage() ||
169
- // ddpEl.getAttribute('locale') ||
170
- navigator.language ||
171
- navigator.userLanguage ||
172
- document.documentElement.lang ||
173
- 'en';
174
-
175
166
  document.querySelectorAll('.document-dates-plugin').forEach(ddpEl => {
167
+ const rawLocale = ddUtils.getCurrentLocale(ddpEl);
168
+
176
169
  // 处理 time 元素(使用 timeago 时)
177
170
  applyTimeagoToTimes(ddpEl.querySelectorAll('time'), rawLocale);
178
171
 
@@ -192,6 +185,7 @@ function processDataLoading() {
192
185
  });
193
186
 
194
187
  // 处理其他 timeago 时间
188
+ const rawLocale = ddUtils.getCurrentLocale();
195
189
  applyTimeagoToTimes(document.querySelectorAll('time.dd-timeago'), rawLocale);
196
190
  }
197
191
 
@@ -386,13 +380,13 @@ function initLayoutSwitcher() {
386
380
  grid.classList.toggle('is-list', savedLayout === 'list');
387
381
  grid.classList.toggle('is-detail', savedLayout === 'detail');
388
382
 
383
+
389
384
  // 查找或创建切换器容器
390
385
  let switcher = grid.previousElementSibling;
391
386
  if (!switcher || !switcher.classList.contains('article-layout-switcher')) {
392
387
  // 如果模板中没写,可以动态注入,但建议写在模板里以保证 UI 一致性
393
388
  return;
394
389
  }
395
-
396
390
  const listBtn = switcher.querySelector('.layout-list-btn');
397
391
  const detailBtn = switcher.querySelector('.layout-detail-btn');
398
392
  const gridBtn = switcher.querySelector('.layout-grid-btn');
@@ -402,9 +396,9 @@ function initLayoutSwitcher() {
402
396
  if (detailBtn) detailBtn.classList.toggle('is-active', layout === 'detail');
403
397
  if (gridBtn) gridBtn.classList.toggle('is-active', layout === 'grid');
404
398
  };
405
-
406
399
  updateActiveBtn(savedLayout);
407
400
 
401
+
408
402
  const setLayout = (layout) => {
409
403
  grid.classList.remove('is-list', 'is-detail');
410
404
  if (layout !== 'grid') {
@@ -413,7 +407,6 @@ function initLayoutSwitcher() {
413
407
  localStorage.setItem('dd_recent_docs_layout', layout);
414
408
  updateActiveBtn(layout);
415
409
  };
416
-
417
410
  if (listBtn) {
418
411
  listBtn.onclick = () => {
419
412
  setLayout('list');
@@ -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 {