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.
- 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)
|