picata 0.0.1__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
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)