zensical 0.0.9__cp310-abi3-musllinux_1_2_i686.whl → 0.0.17__cp310-abi3-musllinux_1_2_i686.whl

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.

Potentially problematic release.


This version of zensical might be problematic. Click here for more details.

Files changed (310) hide show
  1. zensical/__init__.py +6 -6
  2. zensical/__main__.py +2 -2
  3. zensical/bootstrap/.github/workflows/docs.yml +1 -0
  4. zensical/bootstrap/zensical.toml +6 -6
  5. zensical/config.py +223 -201
  6. zensical/extensions/__init__.py +2 -2
  7. zensical/extensions/emoji.py +22 -27
  8. zensical/extensions/links.py +21 -25
  9. zensical/extensions/preview.py +29 -41
  10. zensical/extensions/search.py +83 -83
  11. zensical/extensions/utilities/__init__.py +2 -2
  12. zensical/extensions/utilities/filter.py +5 -10
  13. zensical/main.py +35 -48
  14. zensical/markdown.py +82 -21
  15. zensical/templates/.icons/lucide/LICENSE +39 -0
  16. zensical/templates/.icons/lucide/ampersand.svg +1 -1
  17. zensical/templates/.icons/lucide/anchor.svg +1 -1
  18. zensical/templates/.icons/lucide/balloon.svg +1 -0
  19. zensical/templates/.icons/lucide/birdhouse.svg +1 -0
  20. zensical/templates/.icons/lucide/book-search.svg +1 -0
  21. zensical/templates/.icons/lucide/brush-cleaning.svg +1 -1
  22. zensical/templates/.icons/lucide/bubbles.svg +1 -1
  23. zensical/templates/.icons/lucide/calendar-fold.svg +1 -1
  24. zensical/templates/.icons/lucide/calendars.svg +1 -0
  25. zensical/templates/.icons/lucide/cannabis-off.svg +1 -0
  26. zensical/templates/.icons/lucide/chess-bishop.svg +1 -0
  27. zensical/templates/.icons/lucide/chess-king.svg +1 -0
  28. zensical/templates/.icons/lucide/chess-knight.svg +1 -0
  29. zensical/templates/.icons/lucide/chess-pawn.svg +1 -0
  30. zensical/templates/.icons/lucide/chess-queen.svg +1 -0
  31. zensical/templates/.icons/lucide/chess-rook.svg +1 -0
  32. zensical/templates/.icons/lucide/circle-pile.svg +1 -0
  33. zensical/templates/.icons/lucide/clock-check.svg +1 -0
  34. zensical/templates/.icons/lucide/cloud-backup.svg +1 -0
  35. zensical/templates/.icons/lucide/cloud-sync.svg +1 -0
  36. zensical/templates/.icons/lucide/file-archive.svg +1 -1
  37. zensical/templates/.icons/lucide/file-audio-2.svg +1 -1
  38. zensical/templates/.icons/lucide/file-audio.svg +1 -1
  39. zensical/templates/.icons/lucide/file-axis-3-d.svg +1 -1
  40. zensical/templates/.icons/lucide/file-axis-3d.svg +1 -1
  41. zensical/templates/.icons/lucide/file-badge-2.svg +1 -1
  42. zensical/templates/.icons/lucide/file-badge.svg +1 -1
  43. zensical/templates/.icons/lucide/file-bar-chart-2.svg +1 -1
  44. zensical/templates/.icons/lucide/file-bar-chart.svg +1 -1
  45. zensical/templates/.icons/lucide/file-box.svg +1 -1
  46. zensical/templates/.icons/lucide/file-braces-corner.svg +1 -0
  47. zensical/templates/.icons/lucide/file-braces.svg +1 -0
  48. zensical/templates/.icons/lucide/file-chart-column-increasing.svg +1 -1
  49. zensical/templates/.icons/lucide/file-chart-column.svg +1 -1
  50. zensical/templates/.icons/lucide/file-chart-line.svg +1 -1
  51. zensical/templates/.icons/lucide/file-chart-pie.svg +1 -1
  52. zensical/templates/.icons/lucide/file-check-2.svg +1 -1
  53. zensical/templates/.icons/lucide/file-check-corner.svg +1 -0
  54. zensical/templates/.icons/lucide/file-check.svg +1 -1
  55. zensical/templates/.icons/lucide/file-clock.svg +1 -1
  56. zensical/templates/.icons/lucide/file-code-2.svg +1 -1
  57. zensical/templates/.icons/lucide/file-code-corner.svg +1 -0
  58. zensical/templates/.icons/lucide/file-code.svg +1 -1
  59. zensical/templates/.icons/lucide/file-cog-2.svg +1 -1
  60. zensical/templates/.icons/lucide/file-cog.svg +1 -1
  61. zensical/templates/.icons/lucide/file-diff.svg +1 -1
  62. zensical/templates/.icons/lucide/file-digit.svg +1 -1
  63. zensical/templates/.icons/lucide/file-down.svg +1 -1
  64. zensical/templates/.icons/lucide/file-edit.svg +1 -1
  65. zensical/templates/.icons/lucide/file-exclamation-point.svg +1 -0
  66. zensical/templates/.icons/lucide/file-headphone.svg +1 -0
  67. zensical/templates/.icons/lucide/file-heart.svg +1 -1
  68. zensical/templates/.icons/lucide/file-image.svg +1 -1
  69. zensical/templates/.icons/lucide/file-input.svg +1 -1
  70. zensical/templates/.icons/lucide/file-json-2.svg +1 -1
  71. zensical/templates/.icons/lucide/file-json.svg +1 -1
  72. zensical/templates/.icons/lucide/file-key-2.svg +1 -1
  73. zensical/templates/.icons/lucide/file-key.svg +1 -1
  74. zensical/templates/.icons/lucide/file-line-chart.svg +1 -1
  75. zensical/templates/.icons/lucide/file-lock-2.svg +1 -1
  76. zensical/templates/.icons/lucide/file-lock.svg +1 -1
  77. zensical/templates/.icons/lucide/file-minus-2.svg +1 -1
  78. zensical/templates/.icons/lucide/file-minus-corner.svg +1 -0
  79. zensical/templates/.icons/lucide/file-minus.svg +1 -1
  80. zensical/templates/.icons/lucide/file-music.svg +1 -1
  81. zensical/templates/.icons/lucide/file-output.svg +1 -1
  82. zensical/templates/.icons/lucide/file-pen-line.svg +1 -1
  83. zensical/templates/.icons/lucide/file-pen.svg +1 -1
  84. zensical/templates/.icons/lucide/file-pie-chart.svg +1 -1
  85. zensical/templates/.icons/lucide/file-play.svg +1 -1
  86. zensical/templates/.icons/lucide/file-plus-2.svg +1 -1
  87. zensical/templates/.icons/lucide/file-plus-corner.svg +1 -0
  88. zensical/templates/.icons/lucide/file-plus.svg +1 -1
  89. zensical/templates/.icons/lucide/file-question-mark.svg +1 -1
  90. zensical/templates/.icons/lucide/file-question.svg +1 -1
  91. zensical/templates/.icons/lucide/file-scan.svg +1 -1
  92. zensical/templates/.icons/lucide/file-search-2.svg +1 -1
  93. zensical/templates/.icons/lucide/file-search-corner.svg +1 -0
  94. zensical/templates/.icons/lucide/file-search.svg +1 -1
  95. zensical/templates/.icons/lucide/file-signal.svg +1 -0
  96. zensical/templates/.icons/lucide/file-signature.svg +1 -1
  97. zensical/templates/.icons/lucide/file-sliders.svg +1 -1
  98. zensical/templates/.icons/lucide/file-spreadsheet.svg +1 -1
  99. zensical/templates/.icons/lucide/file-symlink.svg +1 -1
  100. zensical/templates/.icons/lucide/file-terminal.svg +1 -1
  101. zensical/templates/.icons/lucide/file-text.svg +1 -1
  102. zensical/templates/.icons/lucide/file-type-2.svg +1 -1
  103. zensical/templates/.icons/lucide/file-type-corner.svg +1 -0
  104. zensical/templates/.icons/lucide/file-type.svg +1 -1
  105. zensical/templates/.icons/lucide/file-up.svg +1 -1
  106. zensical/templates/.icons/lucide/file-user.svg +1 -1
  107. zensical/templates/.icons/lucide/file-video-2.svg +1 -1
  108. zensical/templates/.icons/lucide/file-video-camera.svg +1 -1
  109. zensical/templates/.icons/lucide/file-video.svg +1 -1
  110. zensical/templates/.icons/lucide/file-volume-2.svg +1 -1
  111. zensical/templates/.icons/lucide/file-volume.svg +1 -1
  112. zensical/templates/.icons/lucide/file-warning.svg +1 -1
  113. zensical/templates/.icons/lucide/file-x-2.svg +1 -1
  114. zensical/templates/.icons/lucide/file-x-corner.svg +1 -0
  115. zensical/templates/.icons/lucide/file-x.svg +1 -1
  116. zensical/templates/.icons/lucide/file.svg +1 -1
  117. zensical/templates/.icons/lucide/files.svg +1 -1
  118. zensical/templates/.icons/lucide/fingerprint-pattern.svg +1 -0
  119. zensical/templates/.icons/lucide/fishing-hook.svg +1 -0
  120. zensical/templates/.icons/lucide/flashlight-off.svg +1 -1
  121. zensical/templates/.icons/lucide/flashlight.svg +1 -1
  122. zensical/templates/.icons/lucide/folder-git-2.svg +1 -1
  123. zensical/templates/.icons/lucide/form.svg +1 -0
  124. zensical/templates/.icons/lucide/gamepad-directional.svg +1 -0
  125. zensical/templates/.icons/lucide/git-branch-minus.svg +1 -0
  126. zensical/templates/.icons/lucide/hd.svg +1 -0
  127. zensical/templates/.icons/lucide/helicopter.svg +1 -0
  128. zensical/templates/.icons/lucide/layers-plus.svg +1 -0
  129. zensical/templates/.icons/lucide/memory-stick.svg +1 -1
  130. zensical/templates/.icons/lucide/microchip.svg +1 -1
  131. zensical/templates/.icons/lucide/mouse-pointer-2-off.svg +1 -0
  132. zensical/templates/.icons/lucide/paint-bucket.svg +1 -1
  133. zensical/templates/.icons/lucide/plug.svg +1 -1
  134. zensical/templates/.icons/lucide/ruler-dimension-line.svg +1 -1
  135. zensical/templates/.icons/lucide/scale.svg +1 -1
  136. zensical/templates/.icons/lucide/scissors-square-dashed-bottom.svg +1 -1
  137. zensical/templates/.icons/lucide/scissors-square.svg +1 -1
  138. zensical/templates/.icons/lucide/scooter.svg +1 -0
  139. zensical/templates/.icons/lucide/search-alert.svg +1 -0
  140. zensical/templates/.icons/lucide/shredder.svg +1 -1
  141. zensical/templates/.icons/lucide/solar-panel.svg +1 -0
  142. zensical/templates/.icons/lucide/square-bottom-dashed-scissors.svg +1 -1
  143. zensical/templates/.icons/lucide/square-scissors.svg +1 -1
  144. zensical/templates/.icons/lucide/sticker.svg +1 -1
  145. zensical/templates/.icons/lucide/sticky-note.svg +1 -1
  146. zensical/templates/.icons/lucide/stone.svg +1 -0
  147. zensical/templates/.icons/lucide/thermometer-sun.svg +1 -1
  148. zensical/templates/.icons/lucide/thumbs-down.svg +1 -1
  149. zensical/templates/.icons/lucide/thumbs-up.svg +1 -1
  150. zensical/templates/.icons/lucide/tickets-plane.svg +1 -1
  151. zensical/templates/.icons/lucide/tickets.svg +1 -1
  152. zensical/templates/.icons/lucide/toolbox.svg +1 -0
  153. zensical/templates/.icons/lucide/van.svg +1 -0
  154. zensical/templates/.icons/lucide/waves-arrow-down.svg +1 -0
  155. zensical/templates/.icons/lucide/waves-arrow-up.svg +1 -0
  156. zensical/templates/.icons/lucide/weight-tilde.svg +1 -0
  157. zensical/templates/.icons/octicons/boolean-off-16.svg +1 -0
  158. zensical/templates/.icons/octicons/boolean-off-24.svg +1 -0
  159. zensical/templates/.icons/octicons/boolean-on-16.svg +1 -0
  160. zensical/templates/.icons/octicons/boolean-on-24.svg +1 -0
  161. zensical/templates/.icons/octicons/compose-16.svg +1 -0
  162. zensical/templates/.icons/octicons/compose-24.svg +1 -0
  163. zensical/templates/.icons/octicons/crosshairs-16.svg +1 -0
  164. zensical/templates/.icons/octicons/crosshairs-24.svg +1 -0
  165. zensical/templates/.icons/octicons/dice-16.svg +1 -0
  166. zensical/templates/.icons/octicons/dice-24.svg +1 -0
  167. zensical/templates/.icons/octicons/exclamation-16.svg +1 -0
  168. zensical/templates/.icons/octicons/exclamation-24.svg +1 -0
  169. zensical/templates/.icons/octicons/file-check-16.svg +1 -0
  170. zensical/templates/.icons/octicons/file-check-24.svg +1 -0
  171. zensical/templates/.icons/octicons/flowchart-16.svg +1 -0
  172. zensical/templates/.icons/octicons/flowchart-24.svg +1 -0
  173. zensical/templates/.icons/octicons/focus-center-16.svg +1 -0
  174. zensical/templates/.icons/octicons/focus-center-24.svg +1 -0
  175. zensical/templates/.icons/octicons/git-branch-check-16.svg +1 -0
  176. zensical/templates/.icons/octicons/git-branch-check-24.svg +1 -0
  177. zensical/templates/.icons/octicons/graph-bar-horizontal-16.svg +1 -0
  178. zensical/templates/.icons/octicons/graph-bar-horizontal-24.svg +1 -0
  179. zensical/templates/.icons/octicons/graph-bar-vertical-16.svg +1 -0
  180. zensical/templates/.icons/octicons/graph-bar-vertical-24.svg +1 -0
  181. zensical/templates/.icons/octicons/inbox-fill-16.svg +1 -0
  182. zensical/templates/.icons/octicons/inbox-fill-24.svg +1 -0
  183. zensical/templates/.icons/octicons/node-16.svg +1 -0
  184. zensical/templates/.icons/octicons/node-24.svg +1 -0
  185. zensical/templates/.icons/octicons/pencil-ai-16.svg +1 -0
  186. zensical/templates/.icons/octicons/pencil-ai-24.svg +1 -0
  187. zensical/templates/.icons/octicons/smiley-frown-16.svg +1 -0
  188. zensical/templates/.icons/octicons/smiley-frown-24.svg +1 -0
  189. zensical/templates/.icons/octicons/smiley-frustrated-16.svg +1 -0
  190. zensical/templates/.icons/octicons/smiley-frustrated-24.svg +1 -0
  191. zensical/templates/.icons/octicons/smiley-grin-16.svg +1 -0
  192. zensical/templates/.icons/octicons/smiley-grin-24.svg +1 -0
  193. zensical/templates/.icons/octicons/smiley-neutral-16.svg +1 -0
  194. zensical/templates/.icons/octicons/smiley-neutral-24.svg +1 -0
  195. zensical/templates/.icons/octicons/spacing-large-16.svg +1 -0
  196. zensical/templates/.icons/octicons/spacing-large-24.svg +1 -0
  197. zensical/templates/.icons/octicons/spacing-medium-16.svg +1 -0
  198. zensical/templates/.icons/octicons/spacing-medium-24.svg +1 -0
  199. zensical/templates/.icons/octicons/spacing-small-16.svg +1 -0
  200. zensical/templates/.icons/octicons/spacing-small-24.svg +1 -0
  201. zensical/templates/.icons/octicons/split-view-16.svg +1 -0
  202. zensical/templates/.icons/octicons/split-view-24.svg +1 -0
  203. zensical/templates/.icons/octicons/unwrap-16.svg +1 -0
  204. zensical/templates/.icons/octicons/unwrap-24.svg +1 -0
  205. zensical/templates/.icons/octicons/vscode-16.svg +1 -1
  206. zensical/templates/.icons/octicons/vscode-32.svg +1 -1
  207. zensical/templates/.icons/octicons/vscode-48.svg +1 -1
  208. zensical/templates/.icons/octicons/wrap-16.svg +1 -0
  209. zensical/templates/.icons/octicons/wrap-24.svg +1 -0
  210. zensical/templates/.icons/simple/acode.svg +1 -0
  211. zensical/templates/.icons/simple/apacheavro.svg +1 -0
  212. zensical/templates/.icons/simple/appimage.svg +1 -0
  213. zensical/templates/.icons/simple/appmanager.svg +1 -0
  214. zensical/templates/.icons/simple/autentique.svg +1 -0
  215. zensical/templates/.icons/simple/b4x.svg +1 -0
  216. zensical/templates/.icons/simple/bioconductor.svg +1 -0
  217. zensical/templates/.icons/simple/coolify.svg +1 -0
  218. zensical/templates/.icons/simple/cursor.svg +1 -0
  219. zensical/templates/.icons/simple/dash0.svg +1 -0
  220. zensical/templates/.icons/simple/dodopayments.svg +1 -0
  221. zensical/templates/.icons/simple/elk.svg +1 -0
  222. zensical/templates/.icons/simple/fishaudio.svg +1 -0
  223. zensical/templates/.icons/simple/ghostty.svg +1 -0
  224. zensical/templates/.icons/simple/glance.svg +1 -0
  225. zensical/templates/.icons/simple/hashcat.svg +1 -0
  226. zensical/templates/.icons/simple/kando.svg +1 -0
  227. zensical/templates/.icons/simple/labex.svg +1 -0
  228. zensical/templates/.icons/simple/listenhub.svg +1 -0
  229. zensical/templates/.icons/simple/luanti.svg +1 -0
  230. zensical/templates/.icons/simple/maas.svg +1 -1
  231. zensical/templates/.icons/simple/mailbox.svg +1 -0
  232. zensical/templates/.icons/simple/mangacollec.svg +1 -0
  233. zensical/templates/.icons/simple/mdblist.svg +1 -0
  234. zensical/templates/.icons/simple/minimax.svg +1 -0
  235. zensical/templates/.icons/simple/newgrounds.svg +1 -0
  236. zensical/templates/.icons/simple/nodegui.svg +1 -0
  237. zensical/templates/.icons/simple/openrouter.svg +1 -0
  238. zensical/templates/.icons/simple/passbolt.svg +1 -0
  239. zensical/templates/.icons/simple/plane.svg +1 -0
  240. zensical/templates/.icons/simple/postiz.svg +1 -0
  241. zensical/templates/.icons/simple/qlty.svg +1 -0
  242. zensical/templates/.icons/simple/rekaui.svg +1 -0
  243. zensical/templates/.icons/simple/retroachievements.svg +1 -0
  244. zensical/templates/.icons/simple/root.svg +1 -0
  245. zensical/templates/.icons/simple/setuptools.svg +1 -0
  246. zensical/templates/.icons/simple/tanstack.svg +1 -0
  247. zensical/templates/.icons/simple/textual.svg +1 -0
  248. zensical/templates/assets/javascripts/LICENSE +29 -0
  249. zensical/templates/assets/javascripts/bundle.8ffeb9c9.min.js +3 -0
  250. zensical/templates/assets/javascripts/workers/search.e2d2d235.min.js +1 -0
  251. zensical/templates/assets/stylesheets/classic/main.9a39631f.min.css +1 -0
  252. zensical/templates/assets/stylesheets/modern/main.d4922b3c.min.css +1 -0
  253. zensical/templates/base.html +4 -4
  254. zensical/zensical.abi3.so +0 -0
  255. zensical/zensical.pyi +7 -13
  256. {zensical-0.0.9.dist-info → zensical-0.0.17.dist-info}/METADATA +9 -5
  257. {zensical-0.0.9.dist-info → zensical-0.0.17.dist-info}/RECORD +262 -172
  258. {zensical-0.0.9.dist-info → zensical-0.0.17.dist-info}/WHEEL +1 -1
  259. {zensical-0.0.9.dist-info → zensical-0.0.17.dist-info}/licenses/LICENSE.md +1 -1
  260. zensical.libs/libgcc_s-f5fcfe20.so.1 +0 -0
  261. zensical/templates/.icons/simple/aerlingus.svg +0 -1
  262. zensical/templates/.icons/simple/aerospike.svg +0 -1
  263. zensical/templates/.icons/simple/aew.svg +0 -1
  264. zensical/templates/.icons/simple/affinity.svg +0 -1
  265. zensical/templates/.icons/simple/affinitydesigner.svg +0 -1
  266. zensical/templates/.icons/simple/affinityphoto.svg +0 -1
  267. zensical/templates/.icons/simple/affinitypublisher.svg +0 -1
  268. zensical/templates/.icons/simple/alfaromeo.svg +0 -1
  269. zensical/templates/.icons/simple/allocine.svg +0 -1
  270. zensical/templates/.icons/simple/alteryx.svg +0 -1
  271. zensical/templates/.icons/simple/altiumdesigner.svg +0 -1
  272. zensical/templates/.icons/simple/alx.svg +0 -1
  273. zensical/templates/.icons/simple/authy.svg +0 -1
  274. zensical/templates/.icons/simple/canva.svg +0 -1
  275. zensical/templates/.icons/simple/codepen.svg +0 -1
  276. zensical/templates/.icons/simple/cognizant.svg +0 -1
  277. zensical/templates/.icons/simple/dbt.svg +0 -1
  278. zensical/templates/.icons/simple/flipkart.svg +0 -1
  279. zensical/templates/.icons/simple/googlefit.svg +0 -1
  280. zensical/templates/.icons/simple/heroku.svg +0 -1
  281. zensical/templates/.icons/simple/informatica.svg +0 -1
  282. zensical/templates/.icons/simple/invision.svg +0 -1
  283. zensical/templates/.icons/simple/jaguar.svg +0 -1
  284. zensical/templates/.icons/simple/landrover.svg +0 -1
  285. zensical/templates/.icons/simple/logitech.svg +0 -1
  286. zensical/templates/.icons/simple/logitechg.svg +0 -1
  287. zensical/templates/.icons/simple/mailboxdotorg.svg +0 -1
  288. zensical/templates/.icons/simple/minetest.svg +0 -1
  289. zensical/templates/.icons/simple/mulesoft.svg +0 -1
  290. zensical/templates/.icons/simple/musescore.svg +0 -1
  291. zensical/templates/.icons/simple/nexusmods.svg +0 -1
  292. zensical/templates/.icons/simple/openai.svg +0 -1
  293. zensical/templates/.icons/simple/pocket.svg +0 -1
  294. zensical/templates/.icons/simple/quip.svg +0 -1
  295. zensical/templates/.icons/simple/salesforce.svg +0 -1
  296. zensical/templates/.icons/simple/scribd.svg +0 -1
  297. zensical/templates/.icons/simple/sendgrid.svg +0 -1
  298. zensical/templates/.icons/simple/shutterstock.svg +0 -1
  299. zensical/templates/.icons/simple/slack.svg +0 -1
  300. zensical/templates/.icons/simple/tunein.svg +0 -1
  301. zensical/templates/.icons/simple/twilio.svg +0 -1
  302. zensical/templates/.icons/simple/walmart.svg +0 -1
  303. zensical/templates/.icons/simple/warnerbros.svg +0 -1
  304. zensical/templates/.icons/simple/westerndigital.svg +0 -1
  305. zensical/templates/assets/javascripts/bundle.21aa498e.min.js +0 -3
  306. zensical/templates/assets/javascripts/workers/search.5e1f2129.min.js +0 -1
  307. zensical/templates/assets/stylesheets/classic/main.6eec86b3.min.css +0 -1
  308. zensical/templates/assets/stylesheets/modern/main.2644c6b7.min.css +0 -1
  309. zensical.libs/libgcc_s-27e5a392.so.1 +0 -0
  310. {zensical-0.0.9.dist-info → zensical-0.0.17.dist-info}/entry_points.txt +0 -0
@@ -1,7 +1,7 @@
1
- # Copyright (c) 2025 Zensical and contributors
1
+ # Copyright (c) 2025-2026 Zensical and contributors
2
2
 
3
3
  # SPDX-License-Identifier: MIT
4
- # Third-party contributions licensed under DCO
4
+ # All contributions are certified under the DCO
5
5
 
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to
@@ -1,7 +1,7 @@
1
- # Copyright (c) 2025 Zensical and contributors
1
+ # Copyright (c) 2025-2026 Zensical and contributors
2
2
 
3
3
  # SPDX-License-Identifier: MIT
4
- # Third-party contributions licensed under DCO
4
+ # All contributions are certified under the DCO
5
5
 
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to
@@ -26,21 +26,22 @@ from __future__ import annotations
26
26
  import codecs
27
27
  import functools
28
28
  import os
29
-
30
29
  from glob import iglob
31
- from markdown import Markdown
32
- from pymdownx import emoji, twemoji_db
30
+ from typing import TYPE_CHECKING
33
31
  from xml.etree.ElementTree import Element
34
32
 
33
+ from pymdownx import emoji, twemoji_db
34
+
35
+ if TYPE_CHECKING:
36
+ from markdown import Markdown
37
+
35
38
  # -----------------------------------------------------------------------------
36
39
  # Functions
37
40
  # -----------------------------------------------------------------------------
38
41
 
39
42
 
40
- def twemoji(options: object, md: Markdown):
41
- """
42
- Create twemoji index.
43
- """
43
+ def twemoji(options: dict, md: Markdown) -> dict: # noqa: ARG001
44
+ """Create twemoji index."""
44
45
  paths = options.get("custom_icons", [])[:]
45
46
  return _load_twemoji_index(tuple(paths))
46
47
 
@@ -53,14 +54,12 @@ def to_svg(
53
54
  alt: str,
54
55
  title: str,
55
56
  category: str,
56
- options: object,
57
+ options: dict,
57
58
  md: Markdown,
58
- ):
59
- """
60
- Load icon.
61
- """
59
+ ) -> Element[str]:
60
+ """Load icon."""
62
61
  if not uc:
63
- icons = md.inlinePatterns["emoji"].emoji_index["emoji"]
62
+ icons = md.inlinePatterns["emoji"].emoji_index["emoji"] # type: ignore[attr-defined]
64
63
 
65
64
  # Create and return element to host icon
66
65
  el = Element("span", {"class": options.get("classes", index)})
@@ -78,20 +77,16 @@ def to_svg(
78
77
  # -----------------------------------------------------------------------------
79
78
 
80
79
 
81
- @functools.lru_cache(maxsize=None)
82
- def _load(file: str):
83
- """
84
- Load icon from file.
85
- """
80
+ @functools.cache
81
+ def _load(file: str) -> str:
82
+ """Load icon from file."""
86
83
  with codecs.open(file, encoding="utf-8") as f:
87
84
  return f.read()
88
85
 
89
86
 
90
- @functools.lru_cache(maxsize=None)
91
- def _load_twemoji_index(paths):
92
- """
93
- Load twemoji index and add icons.
94
- """
87
+ @functools.cache
88
+ def _load_twemoji_index(paths: tuple[str, ...]) -> dict:
89
+ """Load twemoji index and add icons."""
95
90
  index = {
96
91
  "name": "twemoji",
97
92
  "emoji": twemoji_db.emoji,
@@ -106,8 +101,8 @@ def _load_twemoji_index(paths):
106
101
 
107
102
  # Index icons provided by the theme and via custom icons
108
103
  glob = os.path.join(base, "**", "*.svg")
109
- glob = iglob(os.path.normpath(glob), recursive=True)
110
- for file in glob:
104
+ svgs = iglob(os.path.normpath(glob), recursive=True)
105
+ for file in svgs:
111
106
  icon = file[len(base) + 1 : -4].replace(os.path.sep, "-")
112
107
 
113
108
  # Add icon to index
@@ -1,7 +1,7 @@
1
- # Copyright (c) 2025 Zensical and contributors
1
+ # Copyright (c) 2025-2026 Zensical and contributors
2
2
 
3
3
  # SPDX-License-Identifier: MIT
4
- # Third-party contributions licensed under DCO
4
+ # All contributions are certified under the DCO
5
5
 
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to
@@ -23,12 +23,17 @@
23
23
 
24
24
  from __future__ import annotations
25
25
 
26
+ from pathlib import PurePosixPath
27
+ from typing import TYPE_CHECKING
28
+ from urllib.parse import urlparse
29
+
26
30
  from markdown import Extension, Markdown
27
31
  from markdown.treeprocessors import Treeprocessor
28
32
  from markdown.util import AMP_SUBSTITUTE
29
- from pathlib import PurePosixPath
30
- from xml.etree.ElementTree import Element
31
- from urllib.parse import urlparse
33
+
34
+ if TYPE_CHECKING:
35
+ from xml.etree.ElementTree import Element
36
+
32
37
 
33
38
  # -----------------------------------------------------------------------------
34
39
  # Classes
@@ -36,8 +41,7 @@ from urllib.parse import urlparse
36
41
 
37
42
 
38
43
  class LinksProcessor(Treeprocessor):
39
- """
40
- Tree processor to replace links in Markdown with URLs.
44
+ """Tree processor to replace links in Markdown with URLs.
41
45
 
42
46
  Note that we view this as a bandaid until we can do processing on proper
43
47
  HTML ASTs in Rust. In the meantime, we just replace them as we find them.
@@ -50,7 +54,7 @@ class LinksProcessor(Treeprocessor):
50
54
  self.path = path # Current page
51
55
  self.use_directory_urls = use_directory_urls
52
56
 
53
- def run(self, root: Element):
57
+ def run(self, root: Element) -> None:
54
58
  # Now, we determine whether the current page is an index page, as we
55
59
  # must apply slightly different handling in case of directory URLs
56
60
  current_is_index = get_name(self.path) in ("index.md", "README.md")
@@ -64,7 +68,7 @@ class LinksProcessor(Treeprocessor):
64
68
  # Extract value - Python Markdown does some weird stuff where it
65
69
  # replaces mailto: links with double encoded entities. MkDocs just
66
70
  # skips if it detects that, so we do the same.
67
- value = el.get(key)
71
+ value = el.get(key, "")
68
72
  if AMP_SUBSTITUTE in value:
69
73
  continue
70
74
 
@@ -101,21 +105,15 @@ class LinksProcessor(Treeprocessor):
101
105
 
102
106
 
103
107
  class LinksExtension(Extension):
104
- """
105
- A Markdown extension to resolve links to other Markdown files.
106
- """
108
+ """A Markdown extension to resolve links to other Markdown files."""
107
109
 
108
110
  def __init__(self, path: str, use_directory_urls: bool):
109
- """
110
- Initialize the extension.
111
- """
111
+ """Initialize the extension."""
112
112
  self.path = path # Current page
113
113
  self.use_directory_urls = use_directory_urls
114
114
 
115
- def extendMarkdown(self, md: Markdown):
116
- """
117
- Register Markdown extension.
118
- """
115
+ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802
116
+ """Register Markdown extension."""
119
117
  md.registerExtension(self)
120
118
 
121
119
  # Create and register treeprocessor - we use the same priority as the
@@ -123,7 +121,7 @@ class LinksExtension(Extension):
123
121
  # after our treeprocessor, so we can check the original Markdown URIs
124
122
  # before they are resolved to URLs.
125
123
  processor = LinksProcessor(md, self.path, self.use_directory_urls)
126
- md.treeprocessors.register(processor, "relpath", 0)
124
+ md.treeprocessors.register(processor, "zrelpath", 0)
127
125
 
128
126
 
129
127
  # -----------------------------------------------------------------------------
@@ -132,8 +130,6 @@ class LinksExtension(Extension):
132
130
 
133
131
 
134
132
  def get_name(path: str) -> str:
135
- """
136
- Get the name of a file from a given path.
137
- """
138
- path = PurePosixPath(path)
139
- return path.name
133
+ """Get the name of a file from a given path."""
134
+ pure_path = PurePosixPath(path)
135
+ return pure_path.name
@@ -1,7 +1,7 @@
1
- # Copyright (c) 2025 Zensical and contributors
1
+ # Copyright (c) 2025-2026 Zensical and contributors
2
2
 
3
3
  # SPDX-License-Identifier: MIT
4
- # Third-party contributions licensed under DCO
4
+ # All contributions are certified under the DCO
5
5
 
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to
@@ -24,14 +24,17 @@
24
24
  from __future__ import annotations
25
25
 
26
26
  import posixpath
27
+ from typing import TYPE_CHECKING, Any
28
+ from urllib.parse import urlparse
27
29
 
28
30
  from markdown import Extension, Markdown
29
31
  from markdown.treeprocessors import Treeprocessor
30
- from urllib.parse import urlparse
31
- from xml.etree.ElementTree import Element
32
32
 
33
- from .links import LinksProcessor
34
- from .utilities.filter import Filter
33
+ from zensical.extensions.links import LinksProcessor
34
+ from zensical.extensions.utilities.filter import Filter
35
+
36
+ if TYPE_CHECKING:
37
+ from xml.etree.ElementTree import Element
35
38
 
36
39
  # -----------------------------------------------------------------------------
37
40
  # Classes
@@ -39,25 +42,20 @@ from .utilities.filter import Filter
39
42
 
40
43
 
41
44
  class PreviewProcessor(Treeprocessor):
42
- """
43
- A Markdown treeprocessor to enable instant previews on links.
45
+ """A Markdown treeprocessor to enable instant previews on links.
44
46
 
45
47
  Note that this treeprocessor is dependent on the `links` treeprocessor
46
48
  registered programmatically before rendering a page.
47
49
  """
48
50
 
49
51
  def __init__(self, md: Markdown, config: dict):
50
- """
51
- Initialize the treeprocessor.
52
- """
52
+ """Initialize the treeprocessor."""
53
53
  super().__init__(md)
54
54
  self.config = config
55
55
 
56
- def run(self, root: Element):
57
- """
58
- Run the treeprocessor.
59
- """
60
- at = self.md.treeprocessors.get_index_for_name("relpath")
56
+ def run(self, root: Element) -> None:
57
+ """Run the treeprocessor."""
58
+ at = self.md.treeprocessors.get_index_for_name("zrelpath")
61
59
 
62
60
  # Hack: Python Markdown has no notion of where it is, i.e., which file
63
61
  # is being processed. This seems to be a deliberate design decision, as
@@ -84,9 +82,10 @@ class PreviewProcessor(Treeprocessor):
84
82
  # Walk through all configurations - @todo refactor so that we don't
85
83
  # iterate multiple times over the same elements
86
84
  for configuration in configurations:
87
- if not configuration.get("sources"):
88
- if not configuration.get("targets"):
89
- continue
85
+ if not configuration.get("sources") and not configuration.get(
86
+ "targets"
87
+ ):
88
+ continue
90
89
 
91
90
  # Skip if page should not be considered
92
91
  filter = get_filter(configuration, "sources")
@@ -123,8 +122,7 @@ class PreviewProcessor(Treeprocessor):
123
122
 
124
123
 
125
124
  class PreviewExtension(Extension):
126
- """
127
- A Markdown extension to enable instant previews on links.
125
+ """A Markdown extension to enable instant previews on links.
128
126
 
129
127
  This extensions allows to automatically add the `data-preview` attribute to
130
128
  internal links matching specific criteria, so Material for MkDocs renders a
@@ -132,10 +130,8 @@ class PreviewExtension(Extension):
132
130
  add previews to links in a programmatic way.
133
131
  """
134
132
 
135
- def __init__(self, *args, **kwargs):
136
- """
137
- Initialize the extension.
138
- """
133
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
134
+ """Initialize the extension."""
139
135
  self.config = {
140
136
  "configurations": [[], "Filter configurations"],
141
137
  "sources": [{}, "Link sources"],
@@ -143,10 +139,8 @@ class PreviewExtension(Extension):
143
139
  }
144
140
  super().__init__(*args, **kwargs)
145
141
 
146
- def extendMarkdown(self, md: Markdown):
147
- """
148
- Register Markdown extension.
149
- """
142
+ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802
143
+ """Register Markdown extension."""
150
144
  md.registerExtension(self)
151
145
 
152
146
  # Create and register treeprocessor - we use the same priority as the
@@ -162,17 +156,13 @@ class PreviewExtension(Extension):
162
156
  # -----------------------------------------------------------------------------
163
157
 
164
158
 
165
- def get_filter(settings: dict, key: str):
166
- """
167
- Get file filter from settings.
168
- """
169
- return Filter(config=settings.get(key)) # type: ignore
159
+ def get_filter(settings: dict, key: str) -> Filter:
160
+ """Get file filter from settings."""
161
+ return Filter(config=settings.get(key, {}))
170
162
 
171
163
 
172
164
  def resolve(processor_path: str, url_path: str) -> str:
173
- """
174
- Resolve a relative URL path against the processor path.
175
- """
165
+ """Resolve a relative URL path against the processor path."""
176
166
  # Remove the file name from the processor path to get the directory
177
167
  base_path = posixpath.dirname(processor_path)
178
168
 
@@ -194,8 +184,6 @@ def resolve(processor_path: str, url_path: str) -> str:
194
184
  return posixpath.join(*base_segments)
195
185
 
196
186
 
197
- def makeExtension(**kwargs):
198
- """
199
- Register Markdown extension.
200
- """
187
+ def makeExtension(**kwargs: Any) -> PreviewExtension: # noqa: N802
188
+ """Register Markdown extension."""
201
189
  return PreviewExtension(**kwargs)
@@ -1,7 +1,7 @@
1
- # Copyright (c) 2025 Zensical and contributors
1
+ # Copyright (c) 2025-2026 Zensical and contributors
2
2
 
3
3
  # SPDX-License-Identifier: MIT
4
- # Third-party contributions licensed under DCO
4
+ # All contributions are certified under the DCO
5
5
 
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to
@@ -23,8 +23,9 @@
23
23
 
24
24
  from html import escape
25
25
  from html.parser import HTMLParser
26
+ from typing import Any
26
27
 
27
- from markdown import Extension
28
+ from markdown import Extension, Markdown
28
29
  from markdown.postprocessors import Postprocessor
29
30
 
30
31
  # -----------------------------------------------------------------------------
@@ -33,17 +34,14 @@ from markdown.postprocessors import Postprocessor
33
34
 
34
35
 
35
36
  class SearchProcessor(Postprocessor):
36
- """
37
- Post processor that extracts searchable content from the rendered HTML.
38
- """
37
+ """Post processor to extract searchable content from the rendered HTML."""
39
38
 
40
- def __init__(self, md):
39
+ def __init__(self, md: Markdown) -> None:
41
40
  super().__init__(md)
42
- self.data = []
41
+ self.data: list[dict[str, Any]] = []
43
42
 
44
- def run(self, html):
43
+ def run(self, html: str) -> str:
45
44
  """Process the rendered HTML and extract text length."""
46
-
47
45
  # Divide page content into sections
48
46
  parser = Parser()
49
47
  parser.feed(html)
@@ -76,17 +74,17 @@ class SearchProcessor(Postprocessor):
76
74
  class SearchExtension(Extension):
77
75
  """Markdown extension for search indexing."""
78
76
 
79
- def __init__(self, **kwargs):
77
+ def __init__(self, **kwargs: Any) -> None:
80
78
  self.config = {"keep": [set(), "Set of HTML tags to keep in output"]}
81
79
  super().__init__(**kwargs)
82
80
 
83
- def extendMarkdown(self, md):
81
+ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802
84
82
  """Register the PostProcessor with Markdown."""
85
83
  processor = SearchProcessor(md)
86
84
  md.postprocessors.register(processor, "search", 0)
87
85
 
88
86
 
89
- def makeExtension(**kwargs):
87
+ def makeExtension(**kwargs: Any) -> SearchExtension: # noqa: N802
90
88
  """Factory function for creating the extension."""
91
89
  return SearchExtension(**kwargs)
92
90
 
@@ -96,13 +94,16 @@ def makeExtension(**kwargs):
96
94
 
97
95
  # HTML element
98
96
  class Element:
99
- """
97
+ """HTML element.
98
+
100
99
  An element with attributes, essentially a small wrapper object for the
101
100
  parser to access attributes in other callbacks than handle_starttag.
102
101
  """
103
102
 
104
103
  # Initialize HTML element
105
- def __init__(self, tag, attrs=None):
104
+ def __init__(
105
+ self, tag: str, attrs: dict[str, str | None] | None = None
106
+ ) -> None:
106
107
  self.tag = tag
107
108
  self.attrs = attrs or {}
108
109
 
@@ -111,18 +112,17 @@ class Element:
111
112
  return self.tag
112
113
 
113
114
  # Support comparison (compare by tag only)
114
- def __eq__(self, other):
115
- if other is Element:
115
+ def __eq__(self, other: object) -> bool:
116
+ if isinstance(other, Element):
116
117
  return self.tag == other.tag
117
- else:
118
- return self.tag == other
118
+ return self.tag == other
119
119
 
120
120
  # Support set operations
121
121
  def __hash__(self):
122
122
  return hash(self.tag)
123
123
 
124
124
  # Check whether the element should be excluded
125
- def is_excluded(self):
125
+ def is_excluded(self) -> bool:
126
126
  return "data-search-exclude" in self.attrs
127
127
 
128
128
 
@@ -131,31 +131,31 @@ class Element:
131
131
 
132
132
  # HTML section
133
133
  class Section:
134
- """
134
+ """HTML section.
135
+
135
136
  A block of text with markup, preceded by a title (with markup), i.e., a
136
137
  headline with a certain level (h1-h6). Internally used by the parser.
137
138
  """
138
139
 
139
140
  # Initialize HTML section
140
- def __init__(self, el, level, depth=0):
141
+ def __init__(self, el: Element, level: int, depth: int = 0) -> None:
141
142
  self.el = el
142
- self.depth = depth
143
+ self.depth: int | float = depth
143
144
  self.level = level
144
145
 
145
146
  # Initialize section data
146
- self.text = []
147
- self.title = []
148
- self.id = None
147
+ self.text: list[str] = []
148
+ self.title: list[str] = []
149
+ self.id: str | None = None
149
150
 
150
151
  # String representation
151
152
  def __repr__(self):
152
153
  if self.id:
153
- return "#".join([self.el.tag, self.id])
154
- else:
155
- return self.el.tag
154
+ return f"{self.el.tag}#{self.id}"
155
+ return self.el.tag
156
156
 
157
157
  # Check whether the section should be excluded
158
- def is_excluded(self):
158
+ def is_excluded(self) -> bool:
159
159
  return self.el.is_excluded()
160
160
 
161
161
 
@@ -164,7 +164,8 @@ class Section:
164
164
 
165
165
  # HTML parser
166
166
  class Parser(HTMLParser):
167
- """
167
+ """Section divider.
168
+
168
169
  This parser divides the given string of HTML into a list of sections, each
169
170
  of which are preceded by a h1-h6 level heading. A white- and blacklist of
170
171
  tags dictates which tags should be preserved as part of the index, and
@@ -172,31 +173,31 @@ class Parser(HTMLParser):
172
173
  """
173
174
 
174
175
  # Initialize HTML parser
175
- def __init__(self, *args, **kwargs):
176
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
176
177
  super().__init__(*args, **kwargs)
177
178
 
178
179
  # Tags to skip
179
- self.skip = set(
180
- [
181
- "object", # Objects
182
- "script", # Scripts
183
- "style", # Styles
184
- ]
185
- )
180
+ self.skip: set[str | Element] = {
181
+ "object", # Objects
182
+ "script", # Scripts
183
+ "style", # Styles
184
+ }
186
185
 
187
186
  # Current context and section
188
- self.context = []
189
- self.section = None
187
+ self.context: list[Element] = []
188
+ self.section: Section | None = None
190
189
 
191
190
  # All parsed sections
192
- self.data = []
191
+ self.data: list[Section] = []
193
192
 
194
193
  # Called at the start of every HTML tag
195
- def handle_starttag(self, tag, attrs):
196
- attrs = dict(attrs)
194
+ def handle_starttag(
195
+ self, tag: str, attrs: list[tuple[str, str | None]]
196
+ ) -> None:
197
+ attrs_dict = dict(attrs)
197
198
 
198
199
  # Ignore self-closing tags
199
- el = Element(tag, attrs)
200
+ el = Element(tag, attrs_dict)
200
201
  if tag not in void:
201
202
  self.context.append(el)
202
203
  else:
@@ -205,7 +206,7 @@ class Parser(HTMLParser):
205
206
  # Handle heading
206
207
  if tag in ([f"h{x}" for x in range(1, 7)]):
207
208
  depth = len(self.context)
208
- if "id" in attrs:
209
+ if "id" in attrs_dict:
209
210
  # Ensure top-level section
210
211
  if tag != "h1" and not self.data:
211
212
  self.section = Section(Element("hx"), 1, depth)
@@ -214,7 +215,7 @@ class Parser(HTMLParser):
214
215
  # Set identifier, if not first section
215
216
  self.section = Section(el, int(tag[1:2]), depth)
216
217
  if self.data:
217
- self.section.id = attrs["id"]
218
+ self.section.id = attrs_dict["id"]
218
219
 
219
220
  # Append section to list
220
221
  self.data.append(self.section)
@@ -225,7 +226,7 @@ class Parser(HTMLParser):
225
226
  self.data.append(self.section)
226
227
 
227
228
  # Handle special cases to skip
228
- for key, value in attrs.items():
229
+ for key, value in attrs_dict.items():
229
230
  # Skip block if explicitly excluded from search
230
231
  if key == "data-search-exclude":
231
232
  self.skip.add(el)
@@ -247,7 +248,7 @@ class Parser(HTMLParser):
247
248
  data.append(f"<{tag}>")
248
249
 
249
250
  # Called at the end of every HTML tag
250
- def handle_endtag(self, tag):
251
+ def handle_endtag(self, tag: str) -> None:
251
252
  if not self.context or self.context[-1] != tag:
252
253
  return
253
254
 
@@ -255,6 +256,7 @@ class Parser(HTMLParser):
255
256
  # a headline is nested in another element. In that case, we close the
256
257
  # current section, continuing to append data to the previous section,
257
258
  # which could also be a nested section – see https://bit.ly/3IxxIJZ
259
+ assert self.section is not None # noqa: S101
258
260
  if self.section.depth > len(self.context):
259
261
  for section in reversed(self.data):
260
262
  if section.depth <= len(self.context):
@@ -295,7 +297,7 @@ class Parser(HTMLParser):
295
297
  data.append(f"</{tag}>")
296
298
 
297
299
  # Called for the text contents of each tag
298
- def handle_data(self, data):
300
+ def handle_data(self, data: str) -> None:
299
301
  if self.skip.intersection(self.context):
300
302
  return
301
303
 
@@ -324,9 +326,11 @@ class Parser(HTMLParser):
324
326
 
325
327
  # Collapse adjacent whitespace
326
328
  elif data.isspace():
327
- if not self.section.text or not self.section.text[-1].isspace():
328
- self.section.text.append(data)
329
- elif "pre" in self.context:
329
+ if (
330
+ not self.section.text
331
+ or not self.section.text[-1].isspace()
332
+ or "pre" in self.context
333
+ ):
330
334
  self.section.text.append(data)
331
335
 
332
336
  # Handle everything else
@@ -339,35 +343,31 @@ class Parser(HTMLParser):
339
343
  # -----------------------------------------------------------------------------
340
344
 
341
345
  # Tags to keep
342
- keep = set(
343
- [
344
- "p",
345
- "code",
346
- "pre",
347
- "li",
348
- "ol",
349
- "ul",
350
- "sub",
351
- "sup",
352
- ]
353
- )
346
+ keep = {
347
+ "p",
348
+ "code",
349
+ "pre",
350
+ "li",
351
+ "ol",
352
+ "ul",
353
+ "sub",
354
+ "sup",
355
+ }
354
356
 
355
357
  # Tags that are self-closing
356
- void = set(
357
- [
358
- "area",
359
- "base",
360
- "br",
361
- "col",
362
- "embed",
363
- "hr",
364
- "img",
365
- "input",
366
- "link",
367
- "meta",
368
- "param",
369
- "source",
370
- "track",
371
- "wbr",
372
- ]
373
- )
358
+ void = {
359
+ "area",
360
+ "base",
361
+ "br",
362
+ "col",
363
+ "embed",
364
+ "hr",
365
+ "img",
366
+ "input",
367
+ "link",
368
+ "meta",
369
+ "param",
370
+ "source",
371
+ "track",
372
+ "wbr",
373
+ }