picata 0.0.1__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- picata-0.0.1/.gitignore +29 -0
- picata-0.0.1/LICENSE.md +24 -0
- picata-0.0.1/PKG-INFO +87 -0
- picata-0.0.1/README.md +59 -0
- picata-0.0.1/components/HelloWorld.tsx +11 -0
- picata-0.0.1/entrypoint.tsx +268 -0
- picata-0.0.1/manage.py +15 -0
- picata-0.0.1/picata/__init__.py +1 -0
- picata-0.0.1/picata/apps.py +33 -0
- picata-0.0.1/picata/blocks.py +175 -0
- picata-0.0.1/picata/helpers/__init__.py +70 -0
- picata-0.0.1/picata/helpers/wagtail.py +61 -0
- picata-0.0.1/picata/log_utils.py +47 -0
- picata-0.0.1/picata/middleware.py +54 -0
- picata-0.0.1/picata/migrations/0001_initial.py +264 -0
- picata-0.0.1/picata/migrations/0002_alter_article_content_alter_basicpage_content.py +112 -0
- picata-0.0.1/picata/migrations/0003_alter_article_content_alter_basicpage_content.py +104 -0
- picata-0.0.1/picata/migrations/0004_alter_article_content_alter_basicpage_content.py +105 -0
- picata-0.0.1/picata/migrations/0005_socialsettings.py +48 -0
- picata-0.0.1/picata/migrations/0006_alter_article_content.py +71 -0
- picata-0.0.1/picata/migrations/0007_splitviewpage.py +69 -0
- picata-0.0.1/picata/migrations/0008_alter_splitviewpage_content.py +96 -0
- picata-0.0.1/picata/migrations/0009_alter_splitviewpage_content.py +111 -0
- picata-0.0.1/picata/migrations/0010_alter_splitviewpage_content.py +105 -0
- picata-0.0.1/picata/migrations/0011_alter_splitviewpage_options_and_more.py +113 -0
- picata-0.0.1/picata/migrations/0012_alter_splitviewpage_content.py +109 -0
- picata-0.0.1/picata/migrations/0013_alter_article_content.py +43 -0
- picata-0.0.1/picata/migrations/0014_alter_article_content_alter_article_summary.py +24 -0
- picata-0.0.1/picata/migrations/0015_alter_article_options_article_tagline_and_more.py +28 -0
- picata-0.0.1/picata/migrations/0016_alter_article_options_alter_articletag_options_and_more.py +33 -0
- picata-0.0.1/picata/migrations/0017_articletagrelation_alter_article_tags_and_more.py +35 -0
- picata-0.0.1/picata/migrations/0018_rename_articletag_pagetag_and_more.py +21 -0
- picata-0.0.1/picata/migrations/0019_rename_name_plural_articletype__name_plural.py +18 -0
- picata-0.0.1/picata/migrations/0020_rename__name_plural_articletype__pluralised_name.py +18 -0
- picata-0.0.1/picata/migrations/0021_rename_article_type_article_page_type.py +18 -0
- picata-0.0.1/picata/migrations/0022_homepage.py +28 -0
- picata-0.0.1/picata/migrations/__init__.py +0 -0
- picata-0.0.1/picata/models.py +486 -0
- picata-0.0.1/picata/settings/__init__.py +1 -0
- picata-0.0.1/picata/settings/base.py +345 -0
- picata-0.0.1/picata/settings/dev.py +94 -0
- picata-0.0.1/picata/settings/mypy.py +7 -0
- picata-0.0.1/picata/settings/prod.py +12 -0
- picata-0.0.1/picata/settings/test.py +6 -0
- picata-0.0.1/picata/static/picata/ada-profile.jpg +0 -0
- picata-0.0.1/picata/static/picata/ada-social-bear.jpg +0 -0
- picata-0.0.1/picata/static/picata/favicon.ico +0 -0
- picata-0.0.1/picata/static/picata/fonts/Bitter-Light.ttf +0 -0
- picata-0.0.1/picata/static/picata/fonts/Bitter-LightItalic.ttf +0 -0
- picata-0.0.1/picata/static/picata/fonts/FiraCode-Light.ttf +0 -0
- picata-0.0.1/picata/static/picata/fonts/FiraCode-SemiBold.ttf +0 -0
- picata-0.0.1/picata/static/picata/fonts/Sacramento-Regular.ttf +0 -0
- picata-0.0.1/picata/static/picata/fonts/ZillaSlab-Bold.ttf +0 -0
- picata-0.0.1/picata/static/picata/fonts/ZillaSlab-BoldItalic.ttf +0 -0
- picata-0.0.1/picata/static/picata/fonts/ZillaSlab-Light.ttf +0 -0
- picata-0.0.1/picata/static/picata/fonts/ZillaSlab-LightItalic.ttf +0 -0
- picata-0.0.1/picata/static/picata/fonts/ZillaSlabHighlight-Bold.ttf +0 -0
- picata-0.0.1/picata/static/picata/icons.svg +56 -0
- picata-0.0.1/picata/templates/picata/3_column.html +28 -0
- picata-0.0.1/picata/templates/picata/404.html +11 -0
- picata-0.0.1/picata/templates/picata/500.html +13 -0
- picata-0.0.1/picata/templates/picata/_post_list.html +24 -0
- picata-0.0.1/picata/templates/picata/article.html +20 -0
- picata-0.0.1/picata/templates/picata/base.html +135 -0
- picata-0.0.1/picata/templates/picata/basic_page.html +10 -0
- picata-0.0.1/picata/templates/picata/blocks/icon_link_item.html +7 -0
- picata-0.0.1/picata/templates/picata/blocks/icon_link_list.html +4 -0
- picata-0.0.1/picata/templates/picata/blocks/icon_link_list_stream.html +3 -0
- picata-0.0.1/picata/templates/picata/dl_view.html +18 -0
- picata-0.0.1/picata/templates/picata/home_page.html +21 -0
- picata-0.0.1/picata/templates/picata/post_listing.html +17 -0
- picata-0.0.1/picata/templates/picata/previews/3col.html +73 -0
- picata-0.0.1/picata/templates/picata/previews/dl.html +10 -0
- picata-0.0.1/picata/templates/picata/previews/split.html +10 -0
- picata-0.0.1/picata/templates/picata/previews/theme_gallery.html +158 -0
- picata-0.0.1/picata/templates/picata/search_results.html +28 -0
- picata-0.0.1/picata/templates/picata/split_view.html +15 -0
- picata-0.0.1/picata/templates/picata/tags/site_menu.html +8 -0
- picata-0.0.1/picata/templatetags/__init__.py +1 -0
- picata-0.0.1/picata/templatetags/absolute_static.py +15 -0
- picata-0.0.1/picata/templatetags/menu_tags.py +42 -0
- picata-0.0.1/picata/templatetags/stringify.py +23 -0
- picata-0.0.1/picata/transformers.py +60 -0
- picata-0.0.1/picata/typing/__init__.py +19 -0
- picata-0.0.1/picata/typing/wagtail.py +31 -0
- picata-0.0.1/picata/urls.py +48 -0
- picata-0.0.1/picata/validators.py +36 -0
- picata-0.0.1/picata/views.py +80 -0
- picata-0.0.1/picata/wagtail_hooks.py +43 -0
- picata-0.0.1/picata/wsgi.py +15 -0
- picata-0.0.1/pygments.sass +382 -0
- picata-0.0.1/pyproject.toml +143 -0
- picata-0.0.1/styles.sass +300 -0
picata-0.0.1/.gitignore
ADDED
@@ -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
|
picata-0.0.1/LICENSE.md
ADDED
@@ -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,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)
|