picata 0.0.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 (93) hide show
  1. picata-0.0.1/.gitignore +29 -0
  2. picata-0.0.1/LICENSE.md +24 -0
  3. picata-0.0.1/PKG-INFO +87 -0
  4. picata-0.0.1/README.md +59 -0
  5. picata-0.0.1/components/HelloWorld.tsx +11 -0
  6. picata-0.0.1/entrypoint.tsx +268 -0
  7. picata-0.0.1/manage.py +15 -0
  8. picata-0.0.1/picata/__init__.py +1 -0
  9. picata-0.0.1/picata/apps.py +33 -0
  10. picata-0.0.1/picata/blocks.py +175 -0
  11. picata-0.0.1/picata/helpers/__init__.py +70 -0
  12. picata-0.0.1/picata/helpers/wagtail.py +61 -0
  13. picata-0.0.1/picata/log_utils.py +47 -0
  14. picata-0.0.1/picata/middleware.py +54 -0
  15. picata-0.0.1/picata/migrations/0001_initial.py +264 -0
  16. picata-0.0.1/picata/migrations/0002_alter_article_content_alter_basicpage_content.py +112 -0
  17. picata-0.0.1/picata/migrations/0003_alter_article_content_alter_basicpage_content.py +104 -0
  18. picata-0.0.1/picata/migrations/0004_alter_article_content_alter_basicpage_content.py +105 -0
  19. picata-0.0.1/picata/migrations/0005_socialsettings.py +48 -0
  20. picata-0.0.1/picata/migrations/0006_alter_article_content.py +71 -0
  21. picata-0.0.1/picata/migrations/0007_splitviewpage.py +69 -0
  22. picata-0.0.1/picata/migrations/0008_alter_splitviewpage_content.py +96 -0
  23. picata-0.0.1/picata/migrations/0009_alter_splitviewpage_content.py +111 -0
  24. picata-0.0.1/picata/migrations/0010_alter_splitviewpage_content.py +105 -0
  25. picata-0.0.1/picata/migrations/0011_alter_splitviewpage_options_and_more.py +113 -0
  26. picata-0.0.1/picata/migrations/0012_alter_splitviewpage_content.py +109 -0
  27. picata-0.0.1/picata/migrations/0013_alter_article_content.py +43 -0
  28. picata-0.0.1/picata/migrations/0014_alter_article_content_alter_article_summary.py +24 -0
  29. picata-0.0.1/picata/migrations/0015_alter_article_options_article_tagline_and_more.py +28 -0
  30. picata-0.0.1/picata/migrations/0016_alter_article_options_alter_articletag_options_and_more.py +33 -0
  31. picata-0.0.1/picata/migrations/0017_articletagrelation_alter_article_tags_and_more.py +35 -0
  32. picata-0.0.1/picata/migrations/0018_rename_articletag_pagetag_and_more.py +21 -0
  33. picata-0.0.1/picata/migrations/0019_rename_name_plural_articletype__name_plural.py +18 -0
  34. picata-0.0.1/picata/migrations/0020_rename__name_plural_articletype__pluralised_name.py +18 -0
  35. picata-0.0.1/picata/migrations/0021_rename_article_type_article_page_type.py +18 -0
  36. picata-0.0.1/picata/migrations/0022_homepage.py +28 -0
  37. picata-0.0.1/picata/migrations/__init__.py +0 -0
  38. picata-0.0.1/picata/models.py +486 -0
  39. picata-0.0.1/picata/settings/__init__.py +1 -0
  40. picata-0.0.1/picata/settings/base.py +345 -0
  41. picata-0.0.1/picata/settings/dev.py +94 -0
  42. picata-0.0.1/picata/settings/mypy.py +7 -0
  43. picata-0.0.1/picata/settings/prod.py +12 -0
  44. picata-0.0.1/picata/settings/test.py +6 -0
  45. picata-0.0.1/picata/static/picata/ada-profile.jpg +0 -0
  46. picata-0.0.1/picata/static/picata/ada-social-bear.jpg +0 -0
  47. picata-0.0.1/picata/static/picata/favicon.ico +0 -0
  48. picata-0.0.1/picata/static/picata/fonts/Bitter-Light.ttf +0 -0
  49. picata-0.0.1/picata/static/picata/fonts/Bitter-LightItalic.ttf +0 -0
  50. picata-0.0.1/picata/static/picata/fonts/FiraCode-Light.ttf +0 -0
  51. picata-0.0.1/picata/static/picata/fonts/FiraCode-SemiBold.ttf +0 -0
  52. picata-0.0.1/picata/static/picata/fonts/Sacramento-Regular.ttf +0 -0
  53. picata-0.0.1/picata/static/picata/fonts/ZillaSlab-Bold.ttf +0 -0
  54. picata-0.0.1/picata/static/picata/fonts/ZillaSlab-BoldItalic.ttf +0 -0
  55. picata-0.0.1/picata/static/picata/fonts/ZillaSlab-Light.ttf +0 -0
  56. picata-0.0.1/picata/static/picata/fonts/ZillaSlab-LightItalic.ttf +0 -0
  57. picata-0.0.1/picata/static/picata/fonts/ZillaSlabHighlight-Bold.ttf +0 -0
  58. picata-0.0.1/picata/static/picata/icons.svg +56 -0
  59. picata-0.0.1/picata/templates/picata/3_column.html +28 -0
  60. picata-0.0.1/picata/templates/picata/404.html +11 -0
  61. picata-0.0.1/picata/templates/picata/500.html +13 -0
  62. picata-0.0.1/picata/templates/picata/_post_list.html +24 -0
  63. picata-0.0.1/picata/templates/picata/article.html +20 -0
  64. picata-0.0.1/picata/templates/picata/base.html +135 -0
  65. picata-0.0.1/picata/templates/picata/basic_page.html +10 -0
  66. picata-0.0.1/picata/templates/picata/blocks/icon_link_item.html +7 -0
  67. picata-0.0.1/picata/templates/picata/blocks/icon_link_list.html +4 -0
  68. picata-0.0.1/picata/templates/picata/blocks/icon_link_list_stream.html +3 -0
  69. picata-0.0.1/picata/templates/picata/dl_view.html +18 -0
  70. picata-0.0.1/picata/templates/picata/home_page.html +21 -0
  71. picata-0.0.1/picata/templates/picata/post_listing.html +17 -0
  72. picata-0.0.1/picata/templates/picata/previews/3col.html +73 -0
  73. picata-0.0.1/picata/templates/picata/previews/dl.html +10 -0
  74. picata-0.0.1/picata/templates/picata/previews/split.html +10 -0
  75. picata-0.0.1/picata/templates/picata/previews/theme_gallery.html +158 -0
  76. picata-0.0.1/picata/templates/picata/search_results.html +28 -0
  77. picata-0.0.1/picata/templates/picata/split_view.html +15 -0
  78. picata-0.0.1/picata/templates/picata/tags/site_menu.html +8 -0
  79. picata-0.0.1/picata/templatetags/__init__.py +1 -0
  80. picata-0.0.1/picata/templatetags/absolute_static.py +15 -0
  81. picata-0.0.1/picata/templatetags/menu_tags.py +42 -0
  82. picata-0.0.1/picata/templatetags/stringify.py +23 -0
  83. picata-0.0.1/picata/transformers.py +60 -0
  84. picata-0.0.1/picata/typing/__init__.py +19 -0
  85. picata-0.0.1/picata/typing/wagtail.py +31 -0
  86. picata-0.0.1/picata/urls.py +48 -0
  87. picata-0.0.1/picata/validators.py +36 -0
  88. picata-0.0.1/picata/views.py +80 -0
  89. picata-0.0.1/picata/wagtail_hooks.py +43 -0
  90. picata-0.0.1/picata/wsgi.py +15 -0
  91. picata-0.0.1/pygments.sass +382 -0
  92. picata-0.0.1/pyproject.toml +143 -0
  93. picata-0.0.1/styles.sass +300 -0
@@ -0,0 +1,29 @@
1
+ .env
2
+ secrets.tfvars
3
+
4
+ .venv/
5
+ node_modules/
6
+
7
+ .terraform
8
+ terraform.tfstate
9
+ terraform.tfstate.*
10
+ terraform.tfstate.d/
11
+ infra/dot_env.tfvars
12
+
13
+ build/
14
+ lib/
15
+ media/
16
+ static/
17
+ !src/picata/static/
18
+ src/db.sqlite3
19
+ snapshots/*.tgz
20
+
21
+ logs/
22
+ *.log
23
+
24
+ __pycache__
25
+ *.swp
26
+ .junk/
27
+ .DS_Store
28
+
29
+ .dmypy.json
@@ -0,0 +1,24 @@
1
+ # The MIT License (MIT)
2
+
3
+ Copyright © `2024` `Ada Wright <ada@hpk.io>`
4
+
5
+ Permission is hereby granted, free of charge, to any person
6
+ obtaining a copy of this software and associated documentation
7
+ files (the “Software”), to deal in the Software without
8
+ restriction, including without limitation the rights to use,
9
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the
11
+ Software is furnished to do so, subject to the following
12
+ conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ OTHER DEALINGS IN THE SOFTWARE.
picata-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,87 @@
1
+ Metadata-Version: 2.4
2
+ Name: picata
3
+ Version: 0.0.1
4
+ Summary: Ada's Wagtail-based CMS & blog
5
+ Project-URL: Documentation, https://github.com/hipikat/picata#readme
6
+ Project-URL: Issues, https://github.com/hipikat/picata/issues
7
+ Project-URL: Source, https://github.com/hipikat/picata
8
+ Author-email: Ada Wright <ada@hpk.io>
9
+ License-Expression: MIT
10
+ License-File: LICENSE.md
11
+ Keywords: blog,cms,django,wagtail
12
+ Classifier: Development Status :: 2 - Pre-Alpha
13
+ Classifier: Framework :: Django CMS
14
+ Classifier: Framework :: Wagtail :: 6
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: Implementation :: CPython
18
+ Requires-Python: >=3.13
19
+ Requires-Dist: gunicorn~=23.0.0
20
+ Requires-Dist: lxml~=5.3.0
21
+ Requires-Dist: psutil~=6.1.0
22
+ Requires-Dist: psycopg~=3.2.3
23
+ Requires-Dist: pygments~=2.18.0
24
+ Requires-Dist: python-slugify~=8.0.4
25
+ Requires-Dist: wagtail-modeladmin~=2.1.0
26
+ Requires-Dist: wagtail~=6.2
27
+ Description-Content-Type: text/markdown
28
+
29
+ # Ada's website
30
+
31
+ Wherein I set up a little website, and learn a bunch of stuff as I go.
32
+
33
+ ## What it's made of
34
+
35
+ ### Inside the box
36
+
37
+ - [Wagtail](https://wagtail.org) (on [Django](https://www.djangoproject.com)) is the web framework
38
+ <!-- - [Tailwind CSS](https://tailwindcss.com) for styling -->
39
+
40
+ ### Holding things together
41
+
42
+ - [UV](https://github.com/astral-sh/uv) for all Python project management
43
+ - [Just](https://just.systems) as a command runner
44
+ - [OpenTofu](https://opentofu.org) for DevOps
45
+ - [Postgres](https://www.postgresql.org) for the database
46
+ - [Docker](https://www.docker.com) for local development
47
+
48
+ ## Quickstart
49
+
50
+ ### Requirements
51
+
52
+ - On a Mac:
53
+
54
+ ```shell
55
+ brew install colima docker
56
+ ```
57
+
58
+ ### Run a development server
59
+
60
+ ```shell
61
+ just tofu workspace select dev
62
+ just tofu apply
63
+ ```
64
+
65
+ This will spin up a box on DigitalOcean using the settings defined in
66
+ [infra/variables.tf](infra/variables.tf), and create a DNS A record at
67
+ (workspace).for.(tld), (i.e. dev.for.hpk.io) pointing to the box. The variables
68
+ `do_token` and `ssh_fingerprint` should be defined in
69
+ [infra/secrets.tfvars](infra/secrets.tfvars). Workspace-specific variables are
70
+ defined in infra/envs/(workspace).tfvars; e.g.
71
+ [infra/envs/dev.tfvars](infra/envs/dev.tfvars) defines the 'tags' list for the
72
+ box as `[development]` and sets `cloud_init_config` to point to the
73
+ [cloud-init](https://cloud-init.io) script
74
+ [config/cloud-init-dev.yml](config/cloud-init-dev.yml).
75
+
76
+ The development cloud-init script will:
77
+
78
+ - Install the system packages [`just`](https://just.systems), [`zsh`](https://www.zsh.org),
79
+ [`gunicorn`](https://gunicorn.org), and `tree`
80
+ - Create a 'wagtail' user, with UID 1500
81
+ - Create the 'ada' user, and:
82
+ - install their SSH public keys,
83
+ - install their dotfiles,
84
+ - add them to the 'sudo' and 'wagtail' groups
85
+ - Install [Node](http://nodejs.org) on the system, from the `TF_VAR_NODE_VERSION`
86
+ defined in [.env](.env)
87
+ - Checkout this repository into `/app`, setting the owner and group to 'wagtail'.
picata-0.0.1/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # Ada's website
2
+
3
+ Wherein I set up a little website, and learn a bunch of stuff as I go.
4
+
5
+ ## What it's made of
6
+
7
+ ### Inside the box
8
+
9
+ - [Wagtail](https://wagtail.org) (on [Django](https://www.djangoproject.com)) is the web framework
10
+ <!-- - [Tailwind CSS](https://tailwindcss.com) for styling -->
11
+
12
+ ### Holding things together
13
+
14
+ - [UV](https://github.com/astral-sh/uv) for all Python project management
15
+ - [Just](https://just.systems) as a command runner
16
+ - [OpenTofu](https://opentofu.org) for DevOps
17
+ - [Postgres](https://www.postgresql.org) for the database
18
+ - [Docker](https://www.docker.com) for local development
19
+
20
+ ## Quickstart
21
+
22
+ ### Requirements
23
+
24
+ - On a Mac:
25
+
26
+ ```shell
27
+ brew install colima docker
28
+ ```
29
+
30
+ ### Run a development server
31
+
32
+ ```shell
33
+ just tofu workspace select dev
34
+ just tofu apply
35
+ ```
36
+
37
+ This will spin up a box on DigitalOcean using the settings defined in
38
+ [infra/variables.tf](infra/variables.tf), and create a DNS A record at
39
+ (workspace).for.(tld), (i.e. dev.for.hpk.io) pointing to the box. The variables
40
+ `do_token` and `ssh_fingerprint` should be defined in
41
+ [infra/secrets.tfvars](infra/secrets.tfvars). Workspace-specific variables are
42
+ defined in infra/envs/(workspace).tfvars; e.g.
43
+ [infra/envs/dev.tfvars](infra/envs/dev.tfvars) defines the 'tags' list for the
44
+ box as `[development]` and sets `cloud_init_config` to point to the
45
+ [cloud-init](https://cloud-init.io) script
46
+ [config/cloud-init-dev.yml](config/cloud-init-dev.yml).
47
+
48
+ The development cloud-init script will:
49
+
50
+ - Install the system packages [`just`](https://just.systems), [`zsh`](https://www.zsh.org),
51
+ [`gunicorn`](https://gunicorn.org), and `tree`
52
+ - Create a 'wagtail' user, with UID 1500
53
+ - Create the 'ada' user, and:
54
+ - install their SSH public keys,
55
+ - install their dotfiles,
56
+ - add them to the 'sudo' and 'wagtail' groups
57
+ - Install [Node](http://nodejs.org) on the system, from the `TF_VAR_NODE_VERSION`
58
+ defined in [.env](.env)
59
+ - Checkout this repository into `/app`, setting the owner and group to 'wagtail'.
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+
3
+ type Props = {
4
+ name: string;
5
+ };
6
+
7
+ const HelloWorld: React.FC<Props> = ({ name }) => {
8
+ return <h1>Hello, {name}!</h1>;
9
+ };
10
+
11
+ export default HelloWorld;
@@ -0,0 +1,268 @@
1
+ import "./styles.sass";
2
+
3
+ const THEMES = {
4
+ light: "fl",
5
+ dark: "ad",
6
+ };
7
+
8
+ // Set listeners on data-set-theme attributes to change the theme
9
+ import { themeChange } from "theme-change";
10
+ themeChange();
11
+
12
+ //
13
+ // Theme "reset to system defaults", and "light"/"dark" data-theme-mode logic
14
+ //
15
+ function initializeThemeReset() {
16
+ const themeReset = document.querySelector<HTMLSpanElement>("#theme-reset");
17
+ const themeButtons = document.querySelectorAll<HTMLButtonElement>("[data-set-theme]");
18
+
19
+ const updateThemeMode = () => {
20
+ const theme = document.documentElement.getAttribute("data-theme");
21
+ if (theme === THEMES.dark || theme === THEMES.light) {
22
+ document.documentElement.setAttribute(
23
+ "data-theme-mode",
24
+ theme === THEMES.dark ? "dark" : "light",
25
+ );
26
+ } else {
27
+ const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
28
+ document.documentElement.setAttribute("data-theme-mode", prefersDark ? "dark" : "light");
29
+ }
30
+ };
31
+
32
+ const updateThemeResetButtonVisibility = () => {
33
+ if (themeReset) {
34
+ const isThemeSet = document.documentElement.hasAttribute("data-theme");
35
+ if (isThemeSet) {
36
+ themeReset.classList.remove("hidden", "pointer-events-none");
37
+ } else {
38
+ themeReset.classList.add("hidden", "pointer-events-none");
39
+ }
40
+ }
41
+ };
42
+
43
+ // Initialize on page load
44
+ updateThemeMode();
45
+ updateThemeResetButtonVisibility();
46
+
47
+ // Add a listener for system preference changes
48
+ const prefersDarkQuery = window.matchMedia("(prefers-color-scheme: dark)");
49
+ prefersDarkQuery.addEventListener("change", () => {
50
+ if (!document.documentElement.getAttribute("data-theme")) {
51
+ updateThemeMode();
52
+ }
53
+ });
54
+
55
+ // Monitor changes to the data-theme attribute
56
+ const themeChangeObserver = new MutationObserver(() => {
57
+ updateThemeMode();
58
+ updateThemeResetButtonVisibility();
59
+ });
60
+ themeChangeObserver.observe(document.documentElement, {
61
+ attributes: true,
62
+ attributeFilter: ["data-theme"],
63
+ });
64
+
65
+ // Add click listener for the reset button
66
+ if (themeReset) {
67
+ themeReset.addEventListener("click", () => {
68
+ document.documentElement.removeAttribute("data-theme");
69
+ localStorage.removeItem("theme");
70
+
71
+ themeButtons.forEach((button) => {
72
+ button.classList.remove("btn-active");
73
+ });
74
+
75
+ updateThemeMode();
76
+ updateThemeResetButtonVisibility();
77
+ });
78
+ } else {
79
+ console.error("Could not find #theme-reset element.");
80
+ }
81
+
82
+ // Add listeners to theme buttons to toggle "btn-active" class
83
+ themeButtons.forEach((button) => {
84
+ button.addEventListener("click", () => {
85
+ const newTheme = button.getAttribute("data-set-theme");
86
+ if (newTheme) {
87
+ document.documentElement.setAttribute("data-theme", newTheme);
88
+ localStorage.setItem("theme", newTheme);
89
+ }
90
+
91
+ themeButtons.forEach((btn) => btn.classList.remove("btn-active"));
92
+ button.classList.add("btn-active");
93
+
94
+ updateThemeMode();
95
+ });
96
+ });
97
+ }
98
+
99
+ //
100
+ // Search field toggling logic
101
+ //
102
+ function initializeSearchFieldToggle() {
103
+ const searchToggleButton = document.getElementById("search-toggle") as HTMLButtonElement | null;
104
+ const searchField = document.getElementById("search-field") as HTMLElement | null;
105
+
106
+ if (!searchToggleButton || !searchField) {
107
+ console.error("Search toggle or search field elements not found.");
108
+ return;
109
+ }
110
+
111
+ searchToggleButton.addEventListener("click", () => {
112
+ const isVisible = searchField.classList.toggle("search-visible");
113
+ searchField.classList.toggle("search-hidden", !isVisible);
114
+ searchField.setAttribute("tabindex", isVisible ? "0" : "-1");
115
+ searchToggleButton.classList.toggle("!rounded-r-none", isVisible);
116
+ searchToggleButton.classList.toggle("!rounded-r-full", !isVisible);
117
+ searchToggleButton.setAttribute("aria-expanded", isVisible.toString());
118
+ if (isVisible) {
119
+ searchField.focus();
120
+ }
121
+ });
122
+ }
123
+
124
+ //
125
+ // Apply shadows to the right of code blocks when they overflow their container
126
+ //
127
+ function initializeCodeBlockOverflowWatchers(): void {
128
+ const pygmentsDivs: NodeListOf<HTMLDivElement> = document.querySelectorAll(".pygments");
129
+
130
+ const applyOverflowClass = (div: HTMLDivElement) => {
131
+ const pre = div.querySelector("pre");
132
+ if (!pre) return;
133
+
134
+ if (pre.scrollWidth > pre.clientWidth) {
135
+ div.classList.add("shadow-fade-right");
136
+ } else {
137
+ div.classList.remove("shadow-fade-right");
138
+ }
139
+ };
140
+
141
+ // Apply initial shadows
142
+ pygmentsDivs.forEach(applyOverflowClass);
143
+
144
+ // Add resize listener to recheck on window resize
145
+ window.addEventListener("resize", () => {
146
+ pygmentsDivs.forEach(applyOverflowClass);
147
+ });
148
+ }
149
+
150
+ //
151
+ // Create the nested list of internal page links for a 'Page Contents' container
152
+ //
153
+ function renderPageContents(): void {
154
+ const tocContainer = document.querySelector("main nav .toc");
155
+ if (!tocContainer) return;
156
+
157
+ // Create the header for the navigation
158
+ const tocHeader = document.createElement("h2");
159
+ tocHeader.textContent = "In this page";
160
+ tocContainer.appendChild(tocHeader);
161
+
162
+ // Create the root list
163
+ const tocList = document.createElement("ul");
164
+ tocContainer.appendChild(tocList);
165
+
166
+ // Stack to track the current list level
167
+ const listStack: HTMLUListElement[] = [tocList];
168
+ let currentLevel = 1;
169
+
170
+ // Find all anchor-linked headings
171
+ const headings = document.querySelectorAll<HTMLElement>(
172
+ "h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]",
173
+ );
174
+ headings.forEach((heading) => {
175
+ const headingLevel = parseInt(heading.tagName.substring(1)); // Extract the heading level (e.g., "1" for "H1")
176
+
177
+ // Adjust the stack to match the heading level
178
+ while (headingLevel > currentLevel) {
179
+ // Create intermediate sub-lists for skipped levels
180
+ const newList = document.createElement("ul");
181
+ const lastItem = listStack[listStack.length - 1].lastElementChild;
182
+
183
+ if (lastItem) {
184
+ lastItem.appendChild(newList);
185
+ listStack.push(newList);
186
+ } else {
187
+ // If no previous item exists, append directly to the current list
188
+ listStack[listStack.length - 1].appendChild(newList);
189
+ listStack.push(newList);
190
+ }
191
+ currentLevel++;
192
+ }
193
+
194
+ while (headingLevel < currentLevel) {
195
+ // Pop back to the parent list
196
+ listStack.pop();
197
+ currentLevel--;
198
+ }
199
+
200
+ // Add the heading to the current list
201
+ const listItem = document.createElement("li");
202
+ const link = document.createElement("a");
203
+
204
+ // Get heading text without pilcrow
205
+ link.href = `#${heading.id}`;
206
+ link.textContent = heading.textContent?.replace("¶", "").trim() || "Untitled"; // Remove pilcrow
207
+ listItem.appendChild(link);
208
+
209
+ listStack[listStack.length - 1].appendChild(listItem);
210
+ });
211
+ }
212
+
213
+ // function enableStickyTOC(): void {
214
+ // const tocContainer = document.querySelector<HTMLElement>(".toc > div");
215
+ // if (!tocContainer) return;
216
+
217
+ // const parentContainer = tocContainer.parentElement;
218
+ // if (!parentContainer) return;
219
+
220
+ // const offsetTop = 16; // Equivalent to Tailwind's `top-4`
221
+ // const marginRight = 16; // Equivalent to Tailwind's `-mr-4`
222
+
223
+ // const initialTop = parentContainer.getBoundingClientRect().top + window.scrollY;
224
+ // const parentStyles = getComputedStyle(parentContainer);
225
+
226
+ // window.addEventListener("scroll", () => {
227
+ // const currentScroll = window.scrollY;
228
+ // const stickyStart = initialTop - offsetTop;
229
+
230
+ // if (currentScroll >= stickyStart) {
231
+ // tocContainer.classList.add("is-fixed");
232
+
233
+ // tocContainer.style.position = "fixed";
234
+ // tocContainer.style.top = `${offsetTop}px`;
235
+ // tocContainer.style.maxHeight = `calc(100vh - ${offsetTop}px)`;
236
+ // tocContainer.style.overflowY = "auto";
237
+
238
+ // // Dynamically calculate width and right offset
239
+ // const parentWidth = parentContainer.getBoundingClientRect().width;
240
+ // tocContainer.style.width = `${parentWidth}px`;
241
+ // tocContainer.style.padding = parentStyles.padding; // Preserve padding
242
+ // tocContainer.style.right = `${marginRight}px`; // Apply negative right margin
243
+ // } else {
244
+ // tocContainer.classList.remove("is-fixed");
245
+
246
+ // tocContainer.style.position = "relative";
247
+ // tocContainer.style.top = "initial";
248
+ // tocContainer.style.maxHeight = "initial";
249
+ // tocContainer.style.overflowY = "initial";
250
+
251
+ // // Reset dynamically applied styles
252
+ // tocContainer.style.width = "initial";
253
+ // tocContainer.style.padding = "initial";
254
+ // tocContainer.style.right = "initial"; // Reset right offset
255
+ // }
256
+ // });
257
+ // }
258
+
259
+ //
260
+ // Main DOMContentLoaded Listener
261
+ //
262
+ document.addEventListener("DOMContentLoaded", () => {
263
+ initializeThemeReset();
264
+ initializeSearchFieldToggle();
265
+ initializeCodeBlockOverflowWatchers();
266
+ renderPageContents();
267
+ // enableStickyTOC();
268
+ });
picata-0.0.1/manage.py ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env python
2
+ """Entry-point for Django management commands."""
3
+
4
+ from os import environ
5
+ from sys import argv
6
+
7
+ if __name__ == "__main__":
8
+ environ.setdefault("DJANGO_SETTINGS_MODULE", "hpk.settings.dev")
9
+
10
+ if len(argv) >= 2: # noqa: PLR2004
11
+ environ.setdefault("DJANGO_MANAGEMENT_COMMAND", argv[1])
12
+
13
+ from django.core.management import execute_from_command_line
14
+
15
+ execute_from_command_line(argv)
@@ -0,0 +1 @@
1
+ """Main "umbrella" package for custom code running hpk.io."""
@@ -0,0 +1,33 @@
1
+ """Application configuration for the hpk Django app."""
2
+
3
+ from django.apps import AppConfig
4
+
5
+
6
+ class Config(AppConfig):
7
+ """Configuration class for the hpk Django application."""
8
+
9
+ default_auto_field = "django.db.models.BigAutoField"
10
+ name = "hpk"
11
+
12
+ def ready(self) -> None:
13
+ """Configure Wagtail admin with custom models, and register document transformers."""
14
+ #
15
+ # Register the 'custom article type' model with the Wagtail admin
16
+ from wagtail_modeladmin.options import modeladmin_register
17
+
18
+ from hpk.models import ArticleTypeAdmin
19
+
20
+ modeladmin_register(ArticleTypeAdmin)
21
+
22
+ # Add document transformers to the HTMLProcessingMiddleware
23
+ from hpk.middleware import HTMLProcessingMiddleware
24
+ from hpk.transformers import AnchorInserter, add_heading_ids
25
+
26
+ ## Add ids to all headings missing them within html > body > main
27
+ HTMLProcessingMiddleware.add_transformer(add_heading_ids)
28
+
29
+ ## Add anchored pillcrows to headings in designated pages
30
+ anchor_inserter = AnchorInserter(
31
+ root="//main/article", targets=".//h1 | .//h2 | .//h3 | .//h4 | .//h5 | .//h6"
32
+ )
33
+ HTMLProcessingMiddleware.add_transformer(anchor_inserter)