This commit is contained in:
2026-03-11 15:12:17 +00:00
parent cbedcd67f6
commit 1570f79b62
11 changed files with 3638 additions and 410 deletions

View File

@@ -50,6 +50,10 @@ from core.views import (
urlpatterns = [
path("__debug__/", include("debug_toolbar.urls")),
path(
"favicon.ico",
RedirectView.as_view(url=f"{settings.STATIC_URL}favicon.ico", permanent=False),
),
path("", base.Home.as_view(), name="home"),
path("admin/", admin.site.urls),
path(

View File

@@ -0,0 +1,87 @@
# Frontend Asset Vendoring Playbook
This is the repeatable process used in GIA to self-host third-party frontend assets, pin versions, record provenance, and keep template SRI hashes maintainable.
## Goals
- Avoid runtime CDN dependencies for core UI assets.
- Pin exact upstream versions and package integrity in source control.
- Record provenance, license, and SRI for each third-party asset.
- Keep a simple manifest-driven workflow that can be copied into other projects.
## Files
- `tools/frontend_assets/package.json`
- Exact npm dependency pins for vendored frontend libraries.
- `tools/frontend_assets/package-lock.json`
- npm lockfile integrity and tarball resolution source of truth.
- `tools/frontend_assets/asset-manifest.json`
- Maps upstream package files and URL bundles into local `core/static/` targets.
- `scripts/vendor_frontend_assets.py`
- Copies npm assets, downloads non-npm bundles, computes local SHA-256 and SRI SHA-512, and emits inventory reports.
- `artifacts/frontend_libraries.md`
- Human-readable inventory with versions, licenses, official URLs, tarballs, upstream integrity, and local SHA/SRI.
- `artifacts/frontend_libraries.json`
- Machine-readable inventory for template updates and audits.
## Standard Workflow
1. Add or update exact versions in `tools/frontend_assets/package.json`.
2. Confirm each package is the official distribution, not a typosquat or community fork.
3. Update `tools/frontend_assets/asset-manifest.json` with the exact files to vendor.
4. Run `npm install --ignore-scripts` in `tools/frontend_assets/`.
5. Run `./genv/bin/python scripts/vendor_frontend_assets.py`.
6. Copy the emitted `sri_sha512` values into template tags in `core/templates/base.html`.
7. Restart the stack so `collectstatic` republishes the new files.
8. Verify in the browser that assets load from local static URLs with no CDN fallbacks.
## Provenance Rules
- Prefer official project docs or official extension listings before trusting npm search results.
- For Bulma extensions, treat `https://bulma.io/extensions/` as the discovery source and verify the linked package/repo matches the npm package name.
- Confirm npm metadata matches the expected project:
- homepage
- repository
- maintainers
- license
- publish recency
- Keep exact versions in `package.json`; do not use ranges.
- Use `package-lock.json` as the immutable record of tarball URLs and upstream integrity.
- If a library is not safely available from npm, vendor it from the official upstream URL as a `url_bundle`.
- Do not silently substitute a “free” or differently-scoped package if the templates rely on another asset family.
## GIA Decisions
- Font Awesome:
- GIA templates use icon classes that are not safely replaceable with `@fortawesome/fontawesome-free`.
- The existing Font Awesome Pro `site-assets` v6.1.1 bundle is self-hosted under `core/static/vendor/fontawesome/` instead.
- jQuery:
- Latest npm is 4.x, but GIA stays on `3.7.1` to avoid breaking older plugins.
- Bulma extensions:
- `bulma-calendar`, `bulma-tagsinput`, `bulma-switch`, `bulma-slider`, and `bulma-tooltip` were matched against Bulma's official extensions page before pinning.
- `bulma-calendar` and `bulma-tooltip` are deprecated on npm, but Bulma still points to the Wikiki ecosystem for these extensions, so they were kept and documented instead of replaced ad hoc.
## Theme Strategy
- `core/static/css/gia-theme.css` is now the project override layer instead of an inline `<style>` block in `base.html`.
- This is the low-friction step that works with the current stack immediately.
- If we want a true Bulma theme build later, add `sass` to `tools/frontend_assets`, create a `gia-bulma.scss` entrypoint that sets Bulma theme variables before importing Bulma, and vendor the compiled output through the same manifest process.
## Commands
```bash
cd /code/xf/GIA/tools/frontend_assets
npm install --ignore-scripts
cd /code/xf/GIA
./genv/bin/python scripts/vendor_frontend_assets.py
bash ./scripts/quadlet/manage.sh restart
./genv/bin/python manage.py check
```
## Verification Checklist
- `core/templates/base.html` only references local static URLs for vendored libraries.
- Each included stylesheet/script in `base.html` has an `integrity="sha512-..."` attribute.
- Browser network panel shows local asset hits and no unexpected CDN requests.
- `artifacts/frontend_libraries.md` reflects the exact deployed asset set.

View File

@@ -0,0 +1,469 @@
{
"entries": [
{
"id": "bulma_css",
"kind": "npm_file",
"package": "bulma",
"version": "1.0.4",
"license": "MIT",
"homepage": "https://bulma.io",
"official_url": "https://bulma.io",
"purpose": "Bulma core stylesheet",
"notes": "",
"resolved": "https://registry.npmjs.org/bulma/-/bulma-1.0.4.tgz",
"dist_integrity": "sha512-Ffb6YGXDiZYX3cqvSbHWqQ8+LkX6tVoTcZuVB3lm93sbAVXlO0D6QlOTMnV6g18gILpAXqkG2z9hf9z4hCjz2g==",
"source_path": "css/bulma.min.css",
"targets": [
{
"path": "core/static/css/bulma.min.css",
"sha256": "67fa26df1ca9e95d8f2adc7c04fa1b15fa3d24257470ebc10cc68b9aab914bee",
"sri_sha512": "sha512-yh2RE0wZCVZeysGiqTwDTO/dKelCbS9bP2L94UvOFtl/FKXcNAje3Y2oBg/ZMZ3LS1sicYk4dYVGtDex75fvvA=="
}
]
},
{
"id": "bulma_tooltip_css",
"kind": "npm_file",
"package": "bulma-tooltip",
"version": "3.0.2",
"license": "MIT",
"homepage": "https://github.com/Wikiki/bulma-tooltip#readme",
"official_url": "https://wikiki.github.io/elements/tooltip",
"purpose": "Official Bulma tooltip extension from Bulma's extensions page",
"notes": "",
"resolved": "https://registry.npmjs.org/bulma-tooltip/-/bulma-tooltip-3.0.2.tgz",
"dist_integrity": "sha512-CsT3APjhlZScskFg38n8HYL8oYNUHQtcu4sz6ERarxkUpBRbk9v0h/5KAvXeKapVSn2dp9l7bOGit5SECP8EWQ==",
"source_path": "dist/css/bulma-tooltip.min.css",
"targets": [
{
"path": "core/static/css/bulma-tooltip.min.css",
"sha256": "5c79d12a40b3532aaec159faa0b85fd3d500e192467761b71e0bda0fd04f3076",
"sri_sha512": "sha512-SNDNIUvSYhnqDV9FFXaH/e0xZ6NzkG4Qm5dafLLf0PCMkzICKaOmMTgI3y2t2jZK+hAtP6A7UBcFqjWMhsujIg=="
}
]
},
{
"id": "bulma_slider_css",
"kind": "npm_file",
"package": "bulma-slider",
"version": "2.0.5",
"license": "MIT",
"homepage": "https://github.com/Wikiki/bulma-slider#readme",
"official_url": "https://wikiki.github.io/form/slider",
"purpose": "Official Bulma slider extension from Bulma's extensions page",
"notes": "",
"resolved": "https://registry.npmjs.org/bulma-slider/-/bulma-slider-2.0.5.tgz",
"dist_integrity": "sha512-6woD/1E7q1o5bfEaQjNqpWZaCItC1oHe9bN15WYB2ELqz2gDaJYZkf+rlozGpAYOXQGDQGCCv3y+QuKjx6sQuw==",
"source_path": "dist/css/bulma-slider.min.css",
"targets": [
{
"path": "core/static/css/bulma-slider.min.css",
"sha256": "f9d952627d388b8ba267e1388d6923274cf9e62e758d459c5a045f3933e9dc8a",
"sri_sha512": "sha512-9o5SkCRCA9thttRH3Gb5QXLxKdRiuRLdO6ToEPwRHGLXjrhTZwFj0rEHjrCcJvDN9/aNaWMpGOIEA2vZsHmEqw=="
}
]
},
{
"id": "bulma_slider_js",
"kind": "npm_file",
"package": "bulma-slider",
"version": "2.0.5",
"license": "MIT",
"homepage": "https://github.com/Wikiki/bulma-slider#readme",
"official_url": "https://wikiki.github.io/form/slider",
"purpose": "Official Bulma slider extension runtime",
"notes": "",
"resolved": "https://registry.npmjs.org/bulma-slider/-/bulma-slider-2.0.5.tgz",
"dist_integrity": "sha512-6woD/1E7q1o5bfEaQjNqpWZaCItC1oHe9bN15WYB2ELqz2gDaJYZkf+rlozGpAYOXQGDQGCCv3y+QuKjx6sQuw==",
"source_path": "dist/js/bulma-slider.min.js",
"targets": [
{
"path": "core/static/js/bulma-slider.min.js",
"sha256": "db68ebe154a25597913c5635f31500fe7a32e5a205fb9a98c9642d0c2de47d9e",
"sri_sha512": "sha512-WLKXHCsMXTSIPsmQShJRE6K4IzwvNkhwxr/Oo8N3z+kzjhGleHibspmWLTawNMdl2z9E23XK20+yvUTDZ+zeNQ=="
}
]
},
{
"id": "bulma_calendar_css",
"kind": "npm_file",
"package": "bulma-calendar",
"version": "7.1.1",
"license": "MIT",
"homepage": "https://doc.mh-s.de/bulma-calendar",
"official_url": "https://wikiki.github.io/components/calendar",
"purpose": "Official Bulma calendar extension from Bulma's extensions page",
"notes": "",
"resolved": "https://registry.npmjs.org/bulma-calendar/-/bulma-calendar-7.1.1.tgz",
"dist_integrity": "sha512-E08i25KOfqMKBndgDF3y3eoQ0dUzVkgV9R53EDRM65GQUQKLzt8gcXVJYs3mYnpq6L3DiLuUt47Fl09tSv9OpA==",
"source_path": "src/demo/assets/css/bulma-calendar.min.css",
"targets": [
{
"path": "core/static/css/bulma-calendar.min.css",
"sha256": "d18b488ca52584bcd6ea3fb84bf06380e47a3cd18660a235617da017d13ab269",
"sri_sha512": "sha512-IOnJQkgQpezPDPTJcRiWD7YVI3sF2RYzYDl4isbDT2geSaEHRQ615UN/8GhJbSkvqkKRZu8SBCQ7XwKMqsqLFQ=="
}
]
},
{
"id": "bulma_calendar_js",
"kind": "npm_file",
"package": "bulma-calendar",
"version": "7.1.1",
"license": "MIT",
"homepage": "https://doc.mh-s.de/bulma-calendar",
"official_url": "https://wikiki.github.io/components/calendar",
"purpose": "Official Bulma calendar extension runtime",
"notes": "",
"resolved": "https://registry.npmjs.org/bulma-calendar/-/bulma-calendar-7.1.1.tgz",
"dist_integrity": "sha512-E08i25KOfqMKBndgDF3y3eoQ0dUzVkgV9R53EDRM65GQUQKLzt8gcXVJYs3mYnpq6L3DiLuUt47Fl09tSv9OpA==",
"source_path": "src/demo/assets/js/bulma-calendar.min.js",
"targets": [
{
"path": "core/static/js/bulma-calendar.min.js",
"sha256": "58160c87c4d17f9d98ec366fe019492acde50efbc0297af7045547952b306680",
"sri_sha512": "sha512-kkEtEtypXzruevjkoxhyEkqkZBtlhK7s8zt7IV2yPabgBwy5xbKL9uWeCS37ldS9AaNTSnveWTu4ivUvGMJUWA=="
}
]
},
{
"id": "bulma_tagsinput_css",
"kind": "npm_file",
"package": "bulma-tagsinput",
"version": "2.0.0",
"license": "MIT",
"homepage": "https://github.com/Wikiki/bulma-tagsinput#readme",
"official_url": "https://wikiki.github.io/form/tagsinput",
"purpose": "Official Bulma tagsinput extension from Bulma's extensions page",
"notes": "",
"resolved": "https://registry.npmjs.org/bulma-tagsinput/-/bulma-tagsinput-2.0.0.tgz",
"dist_integrity": "sha512-BFvd0oaxgeWHOEh3d4cgETy5vpSSjRRBA9w+8TWEuhjFQg38Rb+3vjDCavL+udpdjf+dRV0SK5T4kYCXTOrz5A==",
"source_path": "dist/css/bulma-tagsinput.min.css",
"targets": [
{
"path": "core/static/css/bulma-tagsinput.min.css",
"sha256": "8d1de24619c05ddf9045638b52059ab492d4887ce74119eed545d66af859da89",
"sri_sha512": "sha512-NWTkcDRubZ3pyXbZZLQBILuVsRFs8c6QGgnfe4dm5/d6yp50U+xdoCDLIcSo51fFy/GXH0O2Oed1Z1sF1faxDA=="
}
]
},
{
"id": "bulma_tagsinput_js",
"kind": "npm_file",
"package": "bulma-tagsinput",
"version": "2.0.0",
"license": "MIT",
"homepage": "https://github.com/Wikiki/bulma-tagsinput#readme",
"official_url": "https://wikiki.github.io/form/tagsinput",
"purpose": "Official Bulma tagsinput extension runtime",
"notes": "",
"resolved": "https://registry.npmjs.org/bulma-tagsinput/-/bulma-tagsinput-2.0.0.tgz",
"dist_integrity": "sha512-BFvd0oaxgeWHOEh3d4cgETy5vpSSjRRBA9w+8TWEuhjFQg38Rb+3vjDCavL+udpdjf+dRV0SK5T4kYCXTOrz5A==",
"source_path": "dist/js/bulma-tagsinput.min.js",
"targets": [
{
"path": "core/static/js/bulma-tagsinput.min.js",
"sha256": "b355aa94ec519e374d7edf569e3dbde8bbe30ff3a193cb96f2930ee7815939d6",
"sri_sha512": "sha512-Je6J++MjmmpxF30JCmRwM2KiK3uWQBQtqiNCjwzEMJKExLaa0BqerlYNa/fJAl5Rra4hMgRZF2fzg+V2vjE4Kw=="
}
]
},
{
"id": "bulma_switch_css",
"kind": "npm_file",
"package": "bulma-switch",
"version": "2.0.4",
"license": "MIT",
"homepage": "https://github.com/Wikiki/bulma-switch#readme",
"official_url": "https://wikiki.github.io/form/switch",
"purpose": "Official Bulma switch extension from Bulma's extensions page",
"notes": "",
"resolved": "https://registry.npmjs.org/bulma-switch/-/bulma-switch-2.0.4.tgz",
"dist_integrity": "sha512-kMu4H0Pr0VjvfsnT6viRDCgptUq0Rvy7y7PX6q+IHg1xUynsjszPjhAdal5ysAlCG5HNO+5YXxeiu92qYGQolw==",
"source_path": "dist/css/bulma-switch.min.css",
"targets": [
{
"path": "core/static/css/bulma-switch.min.css",
"sha256": "f0460ddebdd95425a50590908503a170f5ff08b28bd53573c71791fc7cd1e6f5",
"sri_sha512": "sha512-zjrHYubQoNgDVqVKTyGjKcvIeQlduZTvXCvcBwQ0iqJYKLKiz9cuFAN7e98zfKqCTpI/EgFRBRcTwJw20yAFuw=="
}
]
},
{
"id": "gridstack_css",
"kind": "npm_file",
"package": "gridstack",
"version": "12.4.2",
"license": "MIT",
"homepage": "http://gridstackjs.com/",
"official_url": "https://gridstackjs.com/",
"purpose": "GridStack stylesheet",
"notes": "",
"resolved": "https://registry.npmjs.org/gridstack/-/gridstack-12.4.2.tgz",
"dist_integrity": "sha512-aXbJrQpi3LwpYXYOr4UriPM5uc/dPcjK01SdOE5PDpx2vi8tnLhU7yBg/1i4T59UhNkG/RBfabdFUObuN+gMnw==",
"source_path": "dist/gridstack.min.css",
"targets": [
{
"path": "core/static/css/gridstack.min.css",
"sha256": "55e9d4ea6d8c6f8f1ea8a449b8af18d8571487c0afc6b433cccf877047cb8457",
"sri_sha512": "sha512-ttQfsDTO64bamkJHeLDf0kzMP1NKfkootudPWS2V8Pwy+9z1wexSYjIT6/HXGg/bmtD+DRwsUnQoYEB0yePjbw=="
}
]
},
{
"id": "gridstack_js",
"kind": "npm_file",
"package": "gridstack",
"version": "12.4.2",
"license": "MIT",
"homepage": "http://gridstackjs.com/",
"official_url": "https://gridstackjs.com/",
"purpose": "GridStack bundle used by the dashboard",
"notes": "",
"resolved": "https://registry.npmjs.org/gridstack/-/gridstack-12.4.2.tgz",
"dist_integrity": "sha512-aXbJrQpi3LwpYXYOr4UriPM5uc/dPcjK01SdOE5PDpx2vi8tnLhU7yBg/1i4T59UhNkG/RBfabdFUObuN+gMnw==",
"source_path": "dist/gridstack-all.js",
"targets": [
{
"path": "core/static/js/gridstack-all.js",
"sha256": "e055ca4eb8bdc65f14f38c966ec31960c4c8dc6dc42e91f421bd629808185518",
"sri_sha512": "sha512-djBPxwvBhDep1SvOhliatweHMORhVO3HabrfBjaW6nYsa7UcJYHty31x42m4HBSJXcJSQdoEgRPLVYGGIuIaDQ=="
}
]
},
{
"id": "jquery_js",
"kind": "npm_file",
"package": "jquery",
"version": "3.7.1",
"license": "MIT",
"homepage": "https://jquery.com",
"official_url": "https://jquery.com",
"purpose": "Latest jQuery 3.x release for compatibility with legacy plugins",
"notes": "The latest npm release is jQuery 4.x, but this project still vendors 3.7.1 to avoid breaking older plugins.",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
"dist_integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
"source_path": "dist/jquery.min.js",
"targets": [
{
"path": "core/static/js/jquery.min.js",
"sha256": "fc9a93dd241f6b045cbff0481cf4e1901becd0e12fb45166a8f17f95823f0b1a",
"sri_sha512": "sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g=="
}
]
},
{
"id": "htmx_js",
"kind": "npm_file",
"package": "htmx.org",
"version": "2.0.8",
"license": "0BSD",
"homepage": "https://htmx.org/",
"official_url": "https://htmx.org/",
"purpose": "htmx runtime",
"notes": "",
"resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.8.tgz",
"dist_integrity": "sha512-fm297iru0iWsNJlBrjvtN7V9zjaxd+69Oqjh4F/Vq9Wwi2kFisLcrLCiv5oBX0KLfOX/zG8AUo9ROMU5XUB44Q==",
"source_path": "dist/htmx.min.js",
"targets": [
{
"path": "core/static/js/htmx.min.js",
"sha256": "22283ef68cb7545914f0a88a1bdedc7256a703d1d580c1d255217d0a50d31313",
"sri_sha512": "sha512-CGXFnDNv5q48ciFeIyWFcfZhqYW0sSBiPO+HZDO3XLM+p8xjhezz5CCxtkXVDKfCbvF+iUhel7xoeSp19o7x7g=="
}
]
},
{
"id": "hyperscript_js",
"kind": "npm_file",
"package": "hyperscript.org",
"version": "0.9.14",
"license": "BSD 2-Clause",
"homepage": "https://hyperscript.org/",
"official_url": "https://hyperscript.org/",
"purpose": "_hyperscript runtime",
"notes": "",
"resolved": "https://registry.npmjs.org/hyperscript.org/-/hyperscript.org-0.9.14.tgz",
"dist_integrity": "sha512-ugmojsQQUMmXcnwaXYiYf8L3GbeANy/m59EmE/0Z6C5eQ52fOuSrvFkuEIejG9BdpbYB4iTtoYGqV99eYqDVMA==",
"source_path": "dist/_hyperscript.min.js",
"targets": [
{
"path": "core/static/js/hyperscript.min.js",
"sha256": "3e834a3ffc0334fee54ecff4e37a6ae951cd83e6daa96651ca7cfd8f751ad4d2",
"sri_sha512": "sha512-l43sZzpnAddmYhJyfPrgv46XhJvA95gsA28/+eW4XZLSekQ8wlP68i9f22KGkRjY0HNiZrLc5MXGo4z/tM2QNA=="
}
]
},
{
"id": "magnet_js",
"kind": "npm_file",
"package": "@lf2com/magnet.js",
"version": "2.0.1",
"license": "MIT",
"homepage": "https://github.com/lf2com/magnet.js",
"official_url": "https://github.com/lf2com/magnet.js",
"purpose": "Magnet.js drag attraction component",
"notes": "",
"resolved": "https://registry.npmjs.org/@lf2com/magnet.js/-/magnet.js-2.0.1.tgz",
"dist_integrity": "sha512-MDgv1s0aNOuftuhY9c9Ve6Yadkmn7G+Ww91cVciyHHMhPPdxTxX3XUSJXFYD3VraGFzcnI4uilik9/I76AsJEg==",
"source_path": "dist/magnet.min.js",
"targets": [
{
"path": "core/static/js/magnet.min.js",
"sha256": "05ff8858b5fb7b3ad2a618212571162e2108f580b08527716f4e63c648dcccb1",
"sri_sha512": "sha512-aoQ3V4iCM8zTcdMDSUTRG1K9wqZzmDSisuaCLQexk9DdFy92oWvTUoAfCVLnGzzJClst8PmtasZg219REwyNkw=="
}
]
},
{
"id": "fontawesome_bundle",
"kind": "url_bundle",
"version": "6.1.1",
"license": "Font Awesome Pro commercial terms via site-assets.fontawesome.com",
"homepage": "https://fontawesome.com",
"official_url": "https://fontawesome.com",
"purpose": "Existing Font Awesome asset family, self-hosted locally to preserve the currently used icon set",
"notes": "",
"entry_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/all.css",
"downloaded_files": [
{
"relative_path": "css/all.css",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/all.css"
},
{
"relative_path": "css/../webfonts/fa-brands-400.woff2",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-brands-400.woff2"
},
{
"relative_path": "css/../webfonts/fa-brands-400.ttf",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-brands-400.ttf"
},
{
"relative_path": "css/../webfonts/fa-duotone-900.woff2",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-duotone-900.woff2"
},
{
"relative_path": "css/../webfonts/fa-duotone-900.ttf",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-duotone-900.ttf"
},
{
"relative_path": "css/../webfonts/fa-light-300.woff2",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-light-300.woff2"
},
{
"relative_path": "css/../webfonts/fa-light-300.ttf",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-light-300.ttf"
},
{
"relative_path": "css/../webfonts/fa-regular-400.woff2",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-regular-400.woff2"
},
{
"relative_path": "css/../webfonts/fa-regular-400.ttf",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-regular-400.ttf"
},
{
"relative_path": "css/../webfonts/fa-solid-900.woff2",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-solid-900.woff2"
},
{
"relative_path": "css/../webfonts/fa-solid-900.ttf",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-solid-900.ttf"
},
{
"relative_path": "css/../webfonts/fa-thin-100.woff2",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-thin-100.woff2"
},
{
"relative_path": "css/../webfonts/fa-thin-100.ttf",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-thin-100.ttf"
},
{
"relative_path": "css/../webfonts/fa-v4compatibility.woff2",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-v4compatibility.woff2"
},
{
"relative_path": "css/../webfonts/fa-v4compatibility.ttf",
"download_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-v4compatibility.ttf"
}
],
"targets": [
{
"path": "core/static/vendor/fontawesome/css/all.css",
"sha256": "a35f901d01118e5649091bd03ac5784a7db52e111fb3806524c412f3d1dcfc5d",
"sri_sha512": "sha512-UKBBxJ5N3/MYiSsYTlEsARsp4vELKVRIklED4Mb6wpuVFOgy5Blt+sXUdz1TDReqWsm64xxBA2QoBJRCxI0x5Q=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-brands-400.woff2",
"sha256": "3701cbff3acccd80b1f2eede4311050514f7a64c2039eb77a77368fcd6e3de28",
"sri_sha512": "sha512-50+1yWldN03if6k/4jyncfSZyT44ev20Q7jmIGEiKG7v2qeB1nBOcVF9HD0mjSvAxmpS3+RDzoPfqbB4GfCkJg=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-brands-400.ttf",
"sha256": "06f6dc20f535066f0e069105329ad69d526d7a16215e7ff8af3dbc2cd0894186",
"sri_sha512": "sha512-klNly6qz+BwYPusuoZ0BIE01WW0CbrJszi5GrgZ6yFXQCrxAQRus6vMMATIxwPNYOYXu+UOd2KbRAsenhIkAGQ=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-duotone-900.woff2",
"sha256": "6f28dce91f45bc4687582137bb5d82d9771efc774e3b2b83c30018469d191ad8",
"sri_sha512": "sha512-Sz0g1G5m7symrIy2LfJOdASOrJQUo+IDIMpLKhFKm4RM7MLjV6wqlVGtDf2lEqE/tSzU5LQTtsc3djfZwDXCFQ=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-duotone-900.ttf",
"sha256": "6687687c70d502ff006750978f55d6d5108c364eec78db9b845adcfc838b78a4",
"sri_sha512": "sha512-1301N0n8/h7Kjp6E7LbEe7LWCyDPkfvYn9COlQ6ISbQTP/uPThs8NLhSr+nQskAPzjDoMqfojg0EyNfsD84s/w=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-light-300.woff2",
"sha256": "515954fe1dc163277d36b51f79fe56265f6b6cf79f99e307bbf6e52b477b9c87",
"sri_sha512": "sha512-xdrnQ7rYHIz80KJgGizTu80jCcWF4tGt/inACAoWT3dl3BleiIjq/g90RA42wJNcLpz3n8JAM1Z0ayUGROP5RQ=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-light-300.ttf",
"sha256": "7c41f14e1e8bbe7780049512c631b5301936a985dc6bbadd74a0cbc05549769c",
"sri_sha512": "sha512-i3tSu80Wfsx/12vQQziHhYQLjBNYLGQZfeXfyurZhCBibO+lq6kX1zARiktltXxBszu513BIKbnfzLTRYamoMQ=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-regular-400.woff2",
"sha256": "121b176974226dbc9b1ab227becb657d40b88d2bb7010a746c2360c31d7c373e",
"sri_sha512": "sha512-qioT43fXB5q4Bbpn8sPQE9OIZLjKD0c0lVmpm6KmT8k34LM6gkRcOOMi1BOl2lohFG/7p9tzKfTP5G563BQq1g=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-regular-400.ttf",
"sha256": "daa07950214eae5603ebb5a582a694da7b31a4c93f3bf38e9f616122860d83a5",
"sri_sha512": "sha512-1Ttr+eWt+Z7Na3gRZklbYEnBYGbQUATusZ4oeYLamI4iIjN8ILNV/upwk/h8qIJ2lf/Mukkj5zBVhUdXWJWLwQ=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-solid-900.woff2",
"sha256": "f350c708b5e7748a452b4b98600fa49127166d995686e260ccafb58d51a4ea62",
"sri_sha512": "sha512-Ph1xTLhfMycYSW+wUN8oL3Ggl56nGIS95EHiKWggcL/GbMNjPdib1Hreb1D4COlMxdiGCkk43nspQnpDuTjgQg=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-solid-900.ttf",
"sha256": "bbab2beb558f176d68d3e6002e4ea608633f7e6347dc6245dc67f8ad1c9ca18a",
"sri_sha512": "sha512-c/IBq1JAYcB6z9QnrVqSJREvhrp3NudGg8phmemZ1P41RTIc/z2txqbzP/dWVZYzZl9mPxDnDNLGTFWIQsAfPQ=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-thin-100.woff2",
"sha256": "92fb7777eb1a6a9c8e94048403db3e197e5e541bfd8142255e74ac69141081b2",
"sri_sha512": "sha512-U61hRcHKBdwKMsPzon6yDsO7ReY5gCHX3uztO+D48ynrqy4ZESWdoBtKohmJuXxry3epUhLQLsMd62nb6zmzhA=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-thin-100.ttf",
"sha256": "b8ef1ff99503a5611531d09bf5815261c1fb2f8291ae562711a7d7b651f92b9f",
"sri_sha512": "sha512-kFuJup+kEGELeelH6CXGKPaQ+cJfpLbOtbFFae6CwZfAidwsoM74wYu1Kzwb+pgeK9ODZc7SQMXpFWj5Mn7KtQ=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-v4compatibility.woff2",
"sha256": "4468a4ec439fecc93d86936bc3b5b5db7d0b98ce173faefd18938b2cef79245b",
"sri_sha512": "sha512-DzAaC0v4WatRDbN7LVoGXD5pmuOmveGoBimQLfnQFz74+L+hhZVatKhENPHAVkeNtRTBQ63B8w0fOlrQ3bERuw=="
},
{
"path": "core/static/vendor/fontawesome/css/../webfonts/fa-v4compatibility.ttf",
"sha256": "0f40286f817bb931f65fc1a5963f1450af855547fa9d2c02a009ad389fcc7d95",
"sri_sha512": "sha512-emfG/sIso1lhi9tNNmqIGP2xNU8SP47qPspS7g8V7ltbotbznl/wy00DxyjrtfxhYw3OfYYlxtyiGyoyRAFXTg=="
}
]
}
]
}

View File

@@ -0,0 +1,315 @@
# Frontend Library Inventory
This report is generated by `scripts/vendor_frontend_assets.py` from `tools/frontend_assets/asset-manifest.json`.
## bulma_css
- Source: `bulma`
- Version: `1.0.4`
- Official URL: https://bulma.io
- Homepage: https://bulma.io
- License: `MIT`
- Purpose: Bulma core stylesheet
- Resolved tarball: `https://registry.npmjs.org/bulma/-/bulma-1.0.4.tgz`
- Upstream package integrity: `sha512-Ffb6YGXDiZYX3cqvSbHWqQ8+LkX6tVoTcZuVB3lm93sbAVXlO0D6QlOTMnV6g18gILpAXqkG2z9hf9z4hCjz2g==`
- Local targets:
- `core/static/css/bulma.min.css`
- SHA-256: `67fa26df1ca9e95d8f2adc7c04fa1b15fa3d24257470ebc10cc68b9aab914bee`
- SRI sha512: `sha512-yh2RE0wZCVZeysGiqTwDTO/dKelCbS9bP2L94UvOFtl/FKXcNAje3Y2oBg/ZMZ3LS1sicYk4dYVGtDex75fvvA==`
## bulma_tooltip_css
- Source: `bulma-tooltip`
- Version: `3.0.2`
- Official URL: https://wikiki.github.io/elements/tooltip
- Homepage: https://github.com/Wikiki/bulma-tooltip#readme
- License: `MIT`
- Purpose: Official Bulma tooltip extension from Bulma's extensions page
- Resolved tarball: `https://registry.npmjs.org/bulma-tooltip/-/bulma-tooltip-3.0.2.tgz`
- Upstream package integrity: `sha512-CsT3APjhlZScskFg38n8HYL8oYNUHQtcu4sz6ERarxkUpBRbk9v0h/5KAvXeKapVSn2dp9l7bOGit5SECP8EWQ==`
- Local targets:
- `core/static/css/bulma-tooltip.min.css`
- SHA-256: `5c79d12a40b3532aaec159faa0b85fd3d500e192467761b71e0bda0fd04f3076`
- SRI sha512: `sha512-SNDNIUvSYhnqDV9FFXaH/e0xZ6NzkG4Qm5dafLLf0PCMkzICKaOmMTgI3y2t2jZK+hAtP6A7UBcFqjWMhsujIg==`
## bulma_slider_css
- Source: `bulma-slider`
- Version: `2.0.5`
- Official URL: https://wikiki.github.io/form/slider
- Homepage: https://github.com/Wikiki/bulma-slider#readme
- License: `MIT`
- Purpose: Official Bulma slider extension from Bulma's extensions page
- Resolved tarball: `https://registry.npmjs.org/bulma-slider/-/bulma-slider-2.0.5.tgz`
- Upstream package integrity: `sha512-6woD/1E7q1o5bfEaQjNqpWZaCItC1oHe9bN15WYB2ELqz2gDaJYZkf+rlozGpAYOXQGDQGCCv3y+QuKjx6sQuw==`
- Local targets:
- `core/static/css/bulma-slider.min.css`
- SHA-256: `f9d952627d388b8ba267e1388d6923274cf9e62e758d459c5a045f3933e9dc8a`
- SRI sha512: `sha512-9o5SkCRCA9thttRH3Gb5QXLxKdRiuRLdO6ToEPwRHGLXjrhTZwFj0rEHjrCcJvDN9/aNaWMpGOIEA2vZsHmEqw==`
## bulma_slider_js
- Source: `bulma-slider`
- Version: `2.0.5`
- Official URL: https://wikiki.github.io/form/slider
- Homepage: https://github.com/Wikiki/bulma-slider#readme
- License: `MIT`
- Purpose: Official Bulma slider extension runtime
- Resolved tarball: `https://registry.npmjs.org/bulma-slider/-/bulma-slider-2.0.5.tgz`
- Upstream package integrity: `sha512-6woD/1E7q1o5bfEaQjNqpWZaCItC1oHe9bN15WYB2ELqz2gDaJYZkf+rlozGpAYOXQGDQGCCv3y+QuKjx6sQuw==`
- Local targets:
- `core/static/js/bulma-slider.min.js`
- SHA-256: `db68ebe154a25597913c5635f31500fe7a32e5a205fb9a98c9642d0c2de47d9e`
- SRI sha512: `sha512-WLKXHCsMXTSIPsmQShJRE6K4IzwvNkhwxr/Oo8N3z+kzjhGleHibspmWLTawNMdl2z9E23XK20+yvUTDZ+zeNQ==`
## bulma_calendar_css
- Source: `bulma-calendar`
- Version: `7.1.1`
- Official URL: https://wikiki.github.io/components/calendar
- Homepage: https://doc.mh-s.de/bulma-calendar
- License: `MIT`
- Purpose: Official Bulma calendar extension from Bulma's extensions page
- Resolved tarball: `https://registry.npmjs.org/bulma-calendar/-/bulma-calendar-7.1.1.tgz`
- Upstream package integrity: `sha512-E08i25KOfqMKBndgDF3y3eoQ0dUzVkgV9R53EDRM65GQUQKLzt8gcXVJYs3mYnpq6L3DiLuUt47Fl09tSv9OpA==`
- Local targets:
- `core/static/css/bulma-calendar.min.css`
- SHA-256: `d18b488ca52584bcd6ea3fb84bf06380e47a3cd18660a235617da017d13ab269`
- SRI sha512: `sha512-IOnJQkgQpezPDPTJcRiWD7YVI3sF2RYzYDl4isbDT2geSaEHRQ615UN/8GhJbSkvqkKRZu8SBCQ7XwKMqsqLFQ==`
## bulma_calendar_js
- Source: `bulma-calendar`
- Version: `7.1.1`
- Official URL: https://wikiki.github.io/components/calendar
- Homepage: https://doc.mh-s.de/bulma-calendar
- License: `MIT`
- Purpose: Official Bulma calendar extension runtime
- Resolved tarball: `https://registry.npmjs.org/bulma-calendar/-/bulma-calendar-7.1.1.tgz`
- Upstream package integrity: `sha512-E08i25KOfqMKBndgDF3y3eoQ0dUzVkgV9R53EDRM65GQUQKLzt8gcXVJYs3mYnpq6L3DiLuUt47Fl09tSv9OpA==`
- Local targets:
- `core/static/js/bulma-calendar.min.js`
- SHA-256: `58160c87c4d17f9d98ec366fe019492acde50efbc0297af7045547952b306680`
- SRI sha512: `sha512-kkEtEtypXzruevjkoxhyEkqkZBtlhK7s8zt7IV2yPabgBwy5xbKL9uWeCS37ldS9AaNTSnveWTu4ivUvGMJUWA==`
## bulma_tagsinput_css
- Source: `bulma-tagsinput`
- Version: `2.0.0`
- Official URL: https://wikiki.github.io/form/tagsinput
- Homepage: https://github.com/Wikiki/bulma-tagsinput#readme
- License: `MIT`
- Purpose: Official Bulma tagsinput extension from Bulma's extensions page
- Resolved tarball: `https://registry.npmjs.org/bulma-tagsinput/-/bulma-tagsinput-2.0.0.tgz`
- Upstream package integrity: `sha512-BFvd0oaxgeWHOEh3d4cgETy5vpSSjRRBA9w+8TWEuhjFQg38Rb+3vjDCavL+udpdjf+dRV0SK5T4kYCXTOrz5A==`
- Local targets:
- `core/static/css/bulma-tagsinput.min.css`
- SHA-256: `8d1de24619c05ddf9045638b52059ab492d4887ce74119eed545d66af859da89`
- SRI sha512: `sha512-NWTkcDRubZ3pyXbZZLQBILuVsRFs8c6QGgnfe4dm5/d6yp50U+xdoCDLIcSo51fFy/GXH0O2Oed1Z1sF1faxDA==`
## bulma_tagsinput_js
- Source: `bulma-tagsinput`
- Version: `2.0.0`
- Official URL: https://wikiki.github.io/form/tagsinput
- Homepage: https://github.com/Wikiki/bulma-tagsinput#readme
- License: `MIT`
- Purpose: Official Bulma tagsinput extension runtime
- Resolved tarball: `https://registry.npmjs.org/bulma-tagsinput/-/bulma-tagsinput-2.0.0.tgz`
- Upstream package integrity: `sha512-BFvd0oaxgeWHOEh3d4cgETy5vpSSjRRBA9w+8TWEuhjFQg38Rb+3vjDCavL+udpdjf+dRV0SK5T4kYCXTOrz5A==`
- Local targets:
- `core/static/js/bulma-tagsinput.min.js`
- SHA-256: `b355aa94ec519e374d7edf569e3dbde8bbe30ff3a193cb96f2930ee7815939d6`
- SRI sha512: `sha512-Je6J++MjmmpxF30JCmRwM2KiK3uWQBQtqiNCjwzEMJKExLaa0BqerlYNa/fJAl5Rra4hMgRZF2fzg+V2vjE4Kw==`
## bulma_switch_css
- Source: `bulma-switch`
- Version: `2.0.4`
- Official URL: https://wikiki.github.io/form/switch
- Homepage: https://github.com/Wikiki/bulma-switch#readme
- License: `MIT`
- Purpose: Official Bulma switch extension from Bulma's extensions page
- Resolved tarball: `https://registry.npmjs.org/bulma-switch/-/bulma-switch-2.0.4.tgz`
- Upstream package integrity: `sha512-kMu4H0Pr0VjvfsnT6viRDCgptUq0Rvy7y7PX6q+IHg1xUynsjszPjhAdal5ysAlCG5HNO+5YXxeiu92qYGQolw==`
- Local targets:
- `core/static/css/bulma-switch.min.css`
- SHA-256: `f0460ddebdd95425a50590908503a170f5ff08b28bd53573c71791fc7cd1e6f5`
- SRI sha512: `sha512-zjrHYubQoNgDVqVKTyGjKcvIeQlduZTvXCvcBwQ0iqJYKLKiz9cuFAN7e98zfKqCTpI/EgFRBRcTwJw20yAFuw==`
## gridstack_css
- Source: `gridstack`
- Version: `12.4.2`
- Official URL: https://gridstackjs.com/
- Homepage: http://gridstackjs.com/
- License: `MIT`
- Purpose: GridStack stylesheet
- Resolved tarball: `https://registry.npmjs.org/gridstack/-/gridstack-12.4.2.tgz`
- Upstream package integrity: `sha512-aXbJrQpi3LwpYXYOr4UriPM5uc/dPcjK01SdOE5PDpx2vi8tnLhU7yBg/1i4T59UhNkG/RBfabdFUObuN+gMnw==`
- Local targets:
- `core/static/css/gridstack.min.css`
- SHA-256: `55e9d4ea6d8c6f8f1ea8a449b8af18d8571487c0afc6b433cccf877047cb8457`
- SRI sha512: `sha512-ttQfsDTO64bamkJHeLDf0kzMP1NKfkootudPWS2V8Pwy+9z1wexSYjIT6/HXGg/bmtD+DRwsUnQoYEB0yePjbw==`
## gridstack_js
- Source: `gridstack`
- Version: `12.4.2`
- Official URL: https://gridstackjs.com/
- Homepage: http://gridstackjs.com/
- License: `MIT`
- Purpose: GridStack bundle used by the dashboard
- Resolved tarball: `https://registry.npmjs.org/gridstack/-/gridstack-12.4.2.tgz`
- Upstream package integrity: `sha512-aXbJrQpi3LwpYXYOr4UriPM5uc/dPcjK01SdOE5PDpx2vi8tnLhU7yBg/1i4T59UhNkG/RBfabdFUObuN+gMnw==`
- Local targets:
- `core/static/js/gridstack-all.js`
- SHA-256: `e055ca4eb8bdc65f14f38c966ec31960c4c8dc6dc42e91f421bd629808185518`
- SRI sha512: `sha512-djBPxwvBhDep1SvOhliatweHMORhVO3HabrfBjaW6nYsa7UcJYHty31x42m4HBSJXcJSQdoEgRPLVYGGIuIaDQ==`
## jquery_js
- Source: `jquery`
- Version: `3.7.1`
- Official URL: https://jquery.com
- Homepage: https://jquery.com
- License: `MIT`
- Purpose: Latest jQuery 3.x release for compatibility with legacy plugins
- Resolved tarball: `https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz`
- Upstream package integrity: `sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==`
- Notes: The latest npm release is jQuery 4.x, but this project still vendors 3.7.1 to avoid breaking older plugins.
- Local targets:
- `core/static/js/jquery.min.js`
- SHA-256: `fc9a93dd241f6b045cbff0481cf4e1901becd0e12fb45166a8f17f95823f0b1a`
- SRI sha512: `sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==`
## htmx_js
- Source: `htmx.org`
- Version: `2.0.8`
- Official URL: https://htmx.org/
- Homepage: https://htmx.org/
- License: `0BSD`
- Purpose: htmx runtime
- Resolved tarball: `https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.8.tgz`
- Upstream package integrity: `sha512-fm297iru0iWsNJlBrjvtN7V9zjaxd+69Oqjh4F/Vq9Wwi2kFisLcrLCiv5oBX0KLfOX/zG8AUo9ROMU5XUB44Q==`
- Local targets:
- `core/static/js/htmx.min.js`
- SHA-256: `22283ef68cb7545914f0a88a1bdedc7256a703d1d580c1d255217d0a50d31313`
- SRI sha512: `sha512-CGXFnDNv5q48ciFeIyWFcfZhqYW0sSBiPO+HZDO3XLM+p8xjhezz5CCxtkXVDKfCbvF+iUhel7xoeSp19o7x7g==`
## hyperscript_js
- Source: `hyperscript.org`
- Version: `0.9.14`
- Official URL: https://hyperscript.org/
- Homepage: https://hyperscript.org/
- License: `BSD 2-Clause`
- Purpose: _hyperscript runtime
- Resolved tarball: `https://registry.npmjs.org/hyperscript.org/-/hyperscript.org-0.9.14.tgz`
- Upstream package integrity: `sha512-ugmojsQQUMmXcnwaXYiYf8L3GbeANy/m59EmE/0Z6C5eQ52fOuSrvFkuEIejG9BdpbYB4iTtoYGqV99eYqDVMA==`
- Local targets:
- `core/static/js/hyperscript.min.js`
- SHA-256: `3e834a3ffc0334fee54ecff4e37a6ae951cd83e6daa96651ca7cfd8f751ad4d2`
- SRI sha512: `sha512-l43sZzpnAddmYhJyfPrgv46XhJvA95gsA28/+eW4XZLSekQ8wlP68i9f22KGkRjY0HNiZrLc5MXGo4z/tM2QNA==`
## magnet_js
- Source: `@lf2com/magnet.js`
- Version: `2.0.1`
- Official URL: https://github.com/lf2com/magnet.js
- Homepage: https://github.com/lf2com/magnet.js
- License: `MIT`
- Purpose: Magnet.js drag attraction component
- Resolved tarball: `https://registry.npmjs.org/@lf2com/magnet.js/-/magnet.js-2.0.1.tgz`
- Upstream package integrity: `sha512-MDgv1s0aNOuftuhY9c9Ve6Yadkmn7G+Ww91cVciyHHMhPPdxTxX3XUSJXFYD3VraGFzcnI4uilik9/I76AsJEg==`
- Local targets:
- `core/static/js/magnet.min.js`
- SHA-256: `05ff8858b5fb7b3ad2a618212571162e2108f580b08527716f4e63c648dcccb1`
- SRI sha512: `sha512-aoQ3V4iCM8zTcdMDSUTRG1K9wqZzmDSisuaCLQexk9DdFy92oWvTUoAfCVLnGzzJClst8PmtasZg219REwyNkw==`
## fontawesome_bundle
- Source: `https://site-assets.fontawesome.com/releases/v6.1.1/css/all.css`
- Version: `6.1.1`
- Official URL: https://fontawesome.com
- Homepage: https://fontawesome.com
- License: `Font Awesome Pro commercial terms via site-assets.fontawesome.com`
- Purpose: Existing Font Awesome asset family, self-hosted locally to preserve the currently used icon set
- Downloaded bundle files:
- `css/all.css` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/all.css`
- `css/../webfonts/fa-brands-400.woff2` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-brands-400.woff2`
- `css/../webfonts/fa-brands-400.ttf` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-brands-400.ttf`
- `css/../webfonts/fa-duotone-900.woff2` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-duotone-900.woff2`
- `css/../webfonts/fa-duotone-900.ttf` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-duotone-900.ttf`
- `css/../webfonts/fa-light-300.woff2` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-light-300.woff2`
- `css/../webfonts/fa-light-300.ttf` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-light-300.ttf`
- `css/../webfonts/fa-regular-400.woff2` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-regular-400.woff2`
- `css/../webfonts/fa-regular-400.ttf` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-regular-400.ttf`
- `css/../webfonts/fa-solid-900.woff2` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-solid-900.woff2`
- `css/../webfonts/fa-solid-900.ttf` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-solid-900.ttf`
- `css/../webfonts/fa-thin-100.woff2` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-thin-100.woff2`
- `css/../webfonts/fa-thin-100.ttf` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-thin-100.ttf`
- `css/../webfonts/fa-v4compatibility.woff2` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-v4compatibility.woff2`
- `css/../webfonts/fa-v4compatibility.ttf` from `https://site-assets.fontawesome.com/releases/v6.1.1/css/webfonts/fa-v4compatibility.ttf`
- Local targets:
- `core/static/vendor/fontawesome/css/all.css`
- SHA-256: `a35f901d01118e5649091bd03ac5784a7db52e111fb3806524c412f3d1dcfc5d`
- SRI sha512: `sha512-UKBBxJ5N3/MYiSsYTlEsARsp4vELKVRIklED4Mb6wpuVFOgy5Blt+sXUdz1TDReqWsm64xxBA2QoBJRCxI0x5Q==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-brands-400.woff2`
- SHA-256: `3701cbff3acccd80b1f2eede4311050514f7a64c2039eb77a77368fcd6e3de28`
- SRI sha512: `sha512-50+1yWldN03if6k/4jyncfSZyT44ev20Q7jmIGEiKG7v2qeB1nBOcVF9HD0mjSvAxmpS3+RDzoPfqbB4GfCkJg==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-brands-400.ttf`
- SHA-256: `06f6dc20f535066f0e069105329ad69d526d7a16215e7ff8af3dbc2cd0894186`
- SRI sha512: `sha512-klNly6qz+BwYPusuoZ0BIE01WW0CbrJszi5GrgZ6yFXQCrxAQRus6vMMATIxwPNYOYXu+UOd2KbRAsenhIkAGQ==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-duotone-900.woff2`
- SHA-256: `6f28dce91f45bc4687582137bb5d82d9771efc774e3b2b83c30018469d191ad8`
- SRI sha512: `sha512-Sz0g1G5m7symrIy2LfJOdASOrJQUo+IDIMpLKhFKm4RM7MLjV6wqlVGtDf2lEqE/tSzU5LQTtsc3djfZwDXCFQ==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-duotone-900.ttf`
- SHA-256: `6687687c70d502ff006750978f55d6d5108c364eec78db9b845adcfc838b78a4`
- SRI sha512: `sha512-1301N0n8/h7Kjp6E7LbEe7LWCyDPkfvYn9COlQ6ISbQTP/uPThs8NLhSr+nQskAPzjDoMqfojg0EyNfsD84s/w==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-light-300.woff2`
- SHA-256: `515954fe1dc163277d36b51f79fe56265f6b6cf79f99e307bbf6e52b477b9c87`
- SRI sha512: `sha512-xdrnQ7rYHIz80KJgGizTu80jCcWF4tGt/inACAoWT3dl3BleiIjq/g90RA42wJNcLpz3n8JAM1Z0ayUGROP5RQ==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-light-300.ttf`
- SHA-256: `7c41f14e1e8bbe7780049512c631b5301936a985dc6bbadd74a0cbc05549769c`
- SRI sha512: `sha512-i3tSu80Wfsx/12vQQziHhYQLjBNYLGQZfeXfyurZhCBibO+lq6kX1zARiktltXxBszu513BIKbnfzLTRYamoMQ==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-regular-400.woff2`
- SHA-256: `121b176974226dbc9b1ab227becb657d40b88d2bb7010a746c2360c31d7c373e`
- SRI sha512: `sha512-qioT43fXB5q4Bbpn8sPQE9OIZLjKD0c0lVmpm6KmT8k34LM6gkRcOOMi1BOl2lohFG/7p9tzKfTP5G563BQq1g==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-regular-400.ttf`
- SHA-256: `daa07950214eae5603ebb5a582a694da7b31a4c93f3bf38e9f616122860d83a5`
- SRI sha512: `sha512-1Ttr+eWt+Z7Na3gRZklbYEnBYGbQUATusZ4oeYLamI4iIjN8ILNV/upwk/h8qIJ2lf/Mukkj5zBVhUdXWJWLwQ==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-solid-900.woff2`
- SHA-256: `f350c708b5e7748a452b4b98600fa49127166d995686e260ccafb58d51a4ea62`
- SRI sha512: `sha512-Ph1xTLhfMycYSW+wUN8oL3Ggl56nGIS95EHiKWggcL/GbMNjPdib1Hreb1D4COlMxdiGCkk43nspQnpDuTjgQg==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-solid-900.ttf`
- SHA-256: `bbab2beb558f176d68d3e6002e4ea608633f7e6347dc6245dc67f8ad1c9ca18a`
- SRI sha512: `sha512-c/IBq1JAYcB6z9QnrVqSJREvhrp3NudGg8phmemZ1P41RTIc/z2txqbzP/dWVZYzZl9mPxDnDNLGTFWIQsAfPQ==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-thin-100.woff2`
- SHA-256: `92fb7777eb1a6a9c8e94048403db3e197e5e541bfd8142255e74ac69141081b2`
- SRI sha512: `sha512-U61hRcHKBdwKMsPzon6yDsO7ReY5gCHX3uztO+D48ynrqy4ZESWdoBtKohmJuXxry3epUhLQLsMd62nb6zmzhA==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-thin-100.ttf`
- SHA-256: `b8ef1ff99503a5611531d09bf5815261c1fb2f8291ae562711a7d7b651f92b9f`
- SRI sha512: `sha512-kFuJup+kEGELeelH6CXGKPaQ+cJfpLbOtbFFae6CwZfAidwsoM74wYu1Kzwb+pgeK9ODZc7SQMXpFWj5Mn7KtQ==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-v4compatibility.woff2`
- SHA-256: `4468a4ec439fecc93d86936bc3b5b5db7d0b98ce173faefd18938b2cef79245b`
- SRI sha512: `sha512-DzAaC0v4WatRDbN7LVoGXD5pmuOmveGoBimQLfnQFz74+L+hhZVatKhENPHAVkeNtRTBQ63B8w0fOlrQ3bERuw==`
- `core/static/vendor/fontawesome/css/../webfonts/fa-v4compatibility.ttf`
- SHA-256: `0f40286f817bb931f65fc1a5963f1450af855547fa9d2c02a009ad389fcc7d95`
- SRI sha512: `sha512-emfG/sIso1lhi9tNNmqIGP2xNU8SP47qPspS7g8V7ltbotbznl/wy00DxyjrtfxhYw3OfYYlxtyiGyoyRAFXTg==`

View File

@@ -8,124 +8,135 @@
<head>
<script>
(function () {
var storedTheme = "light";
var storedTheme = null;
try {
storedTheme = localStorage.getItem("theme") || "light";
storedTheme = localStorage.getItem("theme");
} catch (error) {
}
var resolvedTheme = storedTheme === "dark" ? "dark" : "light";
document.documentElement.dataset.theme = resolvedTheme;
if (resolvedTheme === "dark") {
document.documentElement.style.backgroundColor = "#090d16";
document.documentElement.style.color = "#f8fafc";
if (storedTheme === "dark" || storedTheme === "light") {
document.documentElement.dataset.theme = storedTheme;
} else {
document.documentElement.style.backgroundColor = "#f3f4f6";
document.documentElement.style.color = "#1b2433";
document.documentElement.removeAttribute("data-theme");
}
var faviconHref = resolvedTheme === "dark"
? "{% static 'favicon-dark.svg' %}"
: "{% static 'favicon-light.svg' %}";
document.querySelectorAll("link[data-gia-favicon]").forEach(function (link) {
link.setAttribute("href", faviconHref);
});
document.querySelectorAll(".js-theme-logo").forEach(function (image) {
image.setAttribute("src", faviconHref);
});
})();
</script>
<style>
html[data-theme="dark"],
html[data-theme="dark"] body {
background: #090d16 !important;
color: #f8fafc !important;
}
html[data-theme="light"],
html[data-theme="light"] body {
background: #f3f4f6 !important;
color: #1b2433 !important;
}
</style>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block browser_title %}{% firstof page_browser_title page_title as explicit_title %}{% if explicit_title %}{{ explicit_title }} · GIA{% else %}{% with route_value=request.resolver_match.url_name|default:request.path_info|humanize_route %}{% if route_value %}{{ route_value }} · GIA{% else %}GIA{% endif %}{% endwith %}{% endif %}{% endblock %}</title>
<link rel="icon" type="image/svg+xml" href="{% static 'favicon-light.svg' %}" data-gia-favicon>
<link rel="shortcut icon" href="{% static 'favicon-light.svg' %}" data-gia-favicon>
<link rel="icon" type="image/png" href="{% static 'favicon.png' %}?v=sq3" data-gia-favicon="icon">
<link rel="shortcut icon" href="{% static 'favicon.png' %}?v=sq3" data-gia-favicon="shortcut">
<link rel="manifest" href="{% static 'manifest.webmanifest' %}">
<link rel="stylesheet" href="{% static 'css/bulma.min.css' %}">
<link rel="stylesheet" href="{% static 'css/bulma-tooltip.min.css' %}">
<link rel="stylesheet" href="https://site-assets.fontawesome.com/releases/v6.1.1/css/all.css">
<link rel="stylesheet" href="{% static 'css/bulma-slider.min.css' %}">
<link rel="stylesheet" href="{% static 'css/bulma-calendar.min.css' %}">
<link rel="stylesheet" href="{% static 'css/bulma-tagsinput.min.css' %}">
<link rel="stylesheet" href="{% static 'css/bulma-switch.min.css' %}">
<link rel="stylesheet" href="{% static 'css/gridstack.min.css' %}">
<script src="{% static 'js/bulma-calendar.min.js' %}" integrity="sha384-DThNif0xGXbopX7+PE+UabkuClfI/zELNhaVqoGLutaWB76dyMw0vIQBGmUxSfVQ" crossorigin="anonymous"></script>
<script src="{% static 'js/bulma-slider.min.js' %}" integrity="sha384-wbyps8iLG8QzJE02viYc/27BtT5HSa11+b5V7QPR1/huVuA8f4LRTNGc82qAIeIZ" crossorigin="anonymous"></script>
<script src="{% static 'js/htmx.min.js' %}" integrity="sha384-cZuAZ+ZbwkNRnrKi05G/fjBX+azI9DNOkNYysZ0I/X5ZFgsmMiBXgDZof30F5ofc" crossorigin="anonymous"></script>
<script defer src="{% static 'js/remove-me.js' %}" integrity="sha384-6fHcFNoQ8QEI3ZDgw9Z/A6Brk64gF7AnFbLgdrumo8/kBbsKQ/wo7wPegj5WkzuG" crossorigin="anonymous"></script>
<script defer src="{% static 'js/hyperscript.min.js' %}" integrity="sha384-6GYN8BDHOJkkru6zcpGOUa//1mn+5iZ/MyT6mq34WFIpuOeLF52kSi721q0SsYF9" crossorigin="anonymous"></script>
<script src="{% static 'js/bulma-tagsinput.min.js' %}"></script>
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'js/gridstack-all.js' %}"></script>
<script defer src="{% static 'js/magnet.min.js' %}"></script>
{# Managed by tools/frontend_assets/asset-manifest.json and scripts/vendor_frontend_assets.py. #}
<link rel="preload" href="{% static 'vendor/fontawesome/webfonts/fa-solid-900.woff2' %}" as="font" type="font/woff2" integrity="sha512-Ph1xTLhfMycYSW+wUN8oL3Ggl56nGIS95EHiKWggcL/GbMNjPdib1Hreb1D4COlMxdiGCkk43nspQnpDuTjgQg==" crossorigin="anonymous">
<link rel="preload" href="{% static 'vendor/fontawesome/webfonts/fa-regular-400.woff2' %}" as="font" type="font/woff2" integrity="sha512-qioT43fXB5q4Bbpn8sPQE9OIZLjKD0c0lVmpm6KmT8k34LM6gkRcOOMi1BOl2lohFG/7p9tzKfTP5G563BQq1g==" crossorigin="anonymous">
<link rel="stylesheet" href="{% static 'css/bulma.min.css' %}" integrity="sha512-yh2RE0wZCVZeysGiqTwDTO/dKelCbS9bP2L94UvOFtl/FKXcNAje3Y2oBg/ZMZ3LS1sicYk4dYVGtDex75fvvA==" crossorigin="anonymous">
<link rel="stylesheet" href="{% static 'css/bulma-tooltip.min.css' %}" integrity="sha512-SNDNIUvSYhnqDV9FFXaH/e0xZ6NzkG4Qm5dafLLf0PCMkzICKaOmMTgI3y2t2jZK+hAtP6A7UBcFqjWMhsujIg==" crossorigin="anonymous">
<link rel="stylesheet" href="{% static 'vendor/fontawesome/css/all.css' %}" integrity="sha512-UKBBxJ5N3/MYiSsYTlEsARsp4vELKVRIklED4Mb6wpuVFOgy5Blt+sXUdz1TDReqWsm64xxBA2QoBJRCxI0x5Q==" crossorigin="anonymous">
<link rel="stylesheet" href="{% static 'css/bulma-slider.min.css' %}" integrity="sha512-9o5SkCRCA9thttRH3Gb5QXLxKdRiuRLdO6ToEPwRHGLXjrhTZwFj0rEHjrCcJvDN9/aNaWMpGOIEA2vZsHmEqw==" crossorigin="anonymous">
<link rel="stylesheet" href="{% static 'css/bulma-calendar.min.css' %}" integrity="sha512-IOnJQkgQpezPDPTJcRiWD7YVI3sF2RYzYDl4isbDT2geSaEHRQ615UN/8GhJbSkvqkKRZu8SBCQ7XwKMqsqLFQ==" crossorigin="anonymous">
<link rel="stylesheet" href="{% static 'css/bulma-tagsinput.min.css' %}" integrity="sha512-NWTkcDRubZ3pyXbZZLQBILuVsRFs8c6QGgnfe4dm5/d6yp50U+xdoCDLIcSo51fFy/GXH0O2Oed1Z1sF1faxDA==" crossorigin="anonymous">
<link rel="stylesheet" href="{% static 'css/bulma-switch.min.css' %}" integrity="sha512-zjrHYubQoNgDVqVKTyGjKcvIeQlduZTvXCvcBwQ0iqJYKLKiz9cuFAN7e98zfKqCTpI/EgFRBRcTwJw20yAFuw==" crossorigin="anonymous">
<link rel="stylesheet" href="{% static 'css/gridstack.min.css' %}" integrity="sha512-ttQfsDTO64bamkJHeLDf0kzMP1NKfkootudPWS2V8Pwy+9z1wexSYjIT6/HXGg/bmtD+DRwsUnQoYEB0yePjbw==" crossorigin="anonymous">
<link rel="stylesheet" href="{% static 'css/gia-theme.css' %}" integrity="sha512-J/sff1E15uvn7df8Yv/C3Y+CE75bnxZF4vj4wEtfuIaXhECzC0cJTpocvTt7jdB5iMRry8MQ3ZH6Pg/ckh4QNA==" crossorigin="anonymous">
<script src="{% static 'js/bulma-calendar.min.js' %}" integrity="sha512-kkEtEtypXzruevjkoxhyEkqkZBtlhK7s8zt7IV2yPabgBwy5xbKL9uWeCS37ldS9AaNTSnveWTu4ivUvGMJUWA==" crossorigin="anonymous"></script>
<script src="{% static 'js/bulma-slider.min.js' %}" integrity="sha512-WLKXHCsMXTSIPsmQShJRE6K4IzwvNkhwxr/Oo8N3z+kzjhGleHibspmWLTawNMdl2z9E23XK20+yvUTDZ+zeNQ==" crossorigin="anonymous"></script>
<script src="{% static 'js/htmx.min.js' %}" integrity="sha512-CGXFnDNv5q48ciFeIyWFcfZhqYW0sSBiPO+HZDO3XLM+p8xjhezz5CCxtkXVDKfCbvF+iUhel7xoeSp19o7x7g==" crossorigin="anonymous"></script>
<script defer src="{% static 'js/remove-me.js' %}" integrity="sha512-uhE4kDw2+tXdJPDKSttOEYhVnwYq310+d5DMQnTjafJ58QLlYPyx0RTCNbjcrTiGfCjAhaQob4AumEUa2m3TaQ==" crossorigin="anonymous"></script>
<script defer src="{% static 'js/hyperscript.min.js' %}" integrity="sha512-l43sZzpnAddmYhJyfPrgv46XhJvA95gsA28/+eW4XZLSekQ8wlP68i9f22KGkRjY0HNiZrLc5MXGo4z/tM2QNA==" crossorigin="anonymous"></script>
<script src="{% static 'js/bulma-tagsinput.min.js' %}" integrity="sha512-Je6J++MjmmpxF30JCmRwM2KiK3uWQBQtqiNCjwzEMJKExLaa0BqerlYNa/fJAl5Rra4hMgRZF2fzg+V2vjE4Kw==" crossorigin="anonymous"></script>
<script src="{% static 'js/jquery.min.js' %}" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous"></script>
<script src="{% static 'js/gridstack-all.js' %}" integrity="sha512-djBPxwvBhDep1SvOhliatweHMORhVO3HabrfBjaW6nYsa7UcJYHty31x42m4HBSJXcJSQdoEgRPLVYGGIuIaDQ==" crossorigin="anonymous"></script>
<script defer src="{% static 'js/magnet.min.js' %}" integrity="sha512-aoQ3V4iCM8zTcdMDSUTRG1K9wqZzmDSisuaCLQexk9DdFy92oWvTUoAfCVLnGzzJClst8PmtasZg219REwyNkw==" crossorigin="anonymous"></script>
<script>
(function () {
function applyFavicon(theme) {
var href = theme === "dark"
? "{% static 'favicon-dark.svg' %}"
: "{% static 'favicon-light.svg' %}";
document.querySelectorAll("link[data-gia-favicon]").forEach(function (link) {
link.setAttribute("href", href);
});
document.querySelectorAll(".js-theme-logo").forEach(function (image) {
image.setAttribute("src", href);
});
}
var FAVICON_VERSION = "sq3";
var STORAGE_KEY = "theme";
var THEME_DARK = "dark";
var THEME_LIGHT = "light";
var root = document.documentElement;
var darkMedia = window.matchMedia("(prefers-color-scheme: dark)");
function applyTheme(mode) {
var validMode = mode === "dark" ? "dark" : "light";
document.documentElement.dataset.theme = validMode;
applyFavicon(validMode);
function getStoredTheme() {
try {
localStorage.setItem("theme", validMode);
var raw = localStorage.getItem(STORAGE_KEY);
if (raw === THEME_DARK || raw === THEME_LIGHT) {
return raw;
}
} catch (error) {
}
return null;
}
function getResolvedTheme() {
var stored = getStoredTheme();
if (stored === THEME_DARK || stored === THEME_LIGHT) {
return stored;
}
return darkMedia.matches ? THEME_DARK : THEME_LIGHT;
}
function applyFavicon() {
var href = "{% static 'favicon.png' %}?v=" + FAVICON_VERSION;
var iconLink = document.querySelector("link[data-gia-favicon='icon']");
if (iconLink) {
iconLink.setAttribute("href", href);
}
var shortcutLink = document.querySelector("link[data-gia-favicon='shortcut']");
if (shortcutLink) {
shortcutLink.setAttribute("href", href);
}
}
function persistTheme(mode) {
try {
localStorage.setItem(STORAGE_KEY, mode);
} catch (error) {
}
}
function updateToggleUI(mode) {
document.querySelectorAll(".js-theme-toggle").forEach(function (button) {
var nextMode = validMode === "dark" ? "light" : "dark";
button.setAttribute("data-theme-mode", validMode);
button.setAttribute("aria-label", "Theme: " + validMode + ". Click to switch to " + nextMode + ".");
button.setAttribute("title", "Theme: " + validMode);
var nextMode = mode === THEME_DARK ? THEME_LIGHT : THEME_DARK;
button.setAttribute("data-theme-mode", mode);
button.setAttribute("aria-label", "Theme: " + mode + ". Click to switch to " + nextMode + ".");
button.setAttribute("title", "Theme: " + mode);
var label = button.querySelector("[data-theme-label]");
if (label) {
label.textContent = validMode === "dark" ? "Dark" : "Light";
label.textContent = mode === THEME_DARK ? "Dark" : "Light";
}
});
}
function cycleTheme() {
var currentTheme = "light";
try {
currentTheme = localStorage.getItem("theme") || "light";
} catch (error) {
function applyTheme(mode, shouldPersist) {
var validMode = mode === THEME_DARK ? THEME_DARK : THEME_LIGHT;
root.dataset.theme = validMode;
applyFavicon();
updateToggleUI(validMode);
if (shouldPersist !== false) {
persistTheme(validMode);
}
if (currentTheme === "dark") {
applyTheme("light");
return;
}
applyTheme("dark");
}
try {
applyTheme(localStorage.getItem("theme") || "light");
} catch (error) {
applyTheme("light");
function cycleTheme() {
var currentTheme = root.dataset.theme === THEME_DARK ? THEME_DARK : THEME_LIGHT;
applyTheme(currentTheme === THEME_DARK ? THEME_LIGHT : THEME_DARK, true);
}
applyTheme(getResolvedTheme(), false);
if (darkMedia && darkMedia.addEventListener) {
darkMedia.addEventListener("change", function () {
if (!getStoredTheme()) {
applyTheme(getResolvedTheme(), false);
}
});
}
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll(".js-theme-toggle").forEach(function (button) {
button.addEventListener("click", cycleTheme);
});
applyTheme(document.documentElement.dataset.theme || "light");
applyTheme(getResolvedTheme(), false);
});
})();
@@ -188,325 +199,6 @@
});
});
</script>
<style>
.icon { border-bottom: 0px !important;}
.wrap {
word-wrap: break-word;
}
.nowrap-parent {
white-space: nowrap;
}
.nowrap-child {
display: inline-block;
}
.htmx-indicator{
opacity:0;
transition: opacity 500ms ease-in;
}
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator{
opacity:1
}
.tooltiptext {
visibility: hidden;
background-color: black;
color: #fff;
text-align: center;
padding: 5px 0;
border-radius: 6px;
position: absolute;
z-index: 1;
}
.rounded-tooltip:hover .tooltiptext {
visibility: visible;
}
.table {
background: transparent !important;
}
tr {
transition: all 0.2s ease-in-out;
}
a.panel-block {
transition: all 0.2s ease-in-out;
}
tr:hover,
a.panel-block:hover {
cursor:pointer;
background-color:rgba(221, 224, 255, 0.3) !important;
}
@media (prefers-color-scheme: dark) {
:root {
--background-color: rgba(84, 84, 84, 0.9) !important;
--modal-color: rgba(81, 81, 81, 0.9) !important;
}
}
:root {
--background-color-light: rgba(210, 210, 210, 0.9) !important;
--background-color-dark: rgba(84, 84, 84, 0.9) !important;
--background-color-modal-light: rgba(250, 250, 250, 0.5) !important;
--background-color-modal-dark: rgba(210, 210, 210, 0.9) !important;
}
[data-theme="light"] {
--background-color: var(--background-color-light);
--modal-color: var(--background-color-modal-light);
}
[data-theme="dark"] {
--background-color: var(--background-color-dark);
--modal-color: var(--background-color-modal-dark);
}
.panel, .box, .modal {
background-color: var(--modal-color) !important;
}
.box {
border: 1px solid rgba(25, 33, 52, 0.08);
box-shadow: 0 18px 48px rgba(20, 28, 45, 0.08);
}
.modal, .modal.box{
background-color: var(--background-color) !important;
}
.modal-background{
background-color:rgba(255, 255, 255, 0.3) !important;
}
#modals-here .modal-background {
background-color: rgba(0, 0, 0, 0.34) !important;
}
#modals-here .modal-content > .box {
background-color: rgba(255, 255, 255, 0.97) !important;
color: inherit;
}
#modals-here .modal-content .input,
#modals-here .modal-content .textarea,
#modals-here .modal-content .select select {
background-color: rgba(255, 255, 255, 0.98) !important;
}
[data-theme="dark"] #modals-here .modal-content > .box {
background-color: rgba(45, 45, 45, 0.97) !important;
}
[data-theme="dark"] #modals-here .modal-content .input,
[data-theme="dark"] #modals-here .modal-content .textarea,
[data-theme="dark"] #modals-here .modal-content .select select {
background-color: rgba(33, 33, 33, 0.98) !important;
}
.has-background-grey-lighter{
background-color:rgba(219, 219, 219, 0.5) !important;
}
.navbar {
background-color:rgba(0, 0, 0, 0.03) !important;
}
.gia-brand-shell {
display: inline-flex;
align-items: center;
}
.gia-brand-logo {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.45rem 0.75rem;
border-radius: 16px;
background: rgba(255, 255, 255, 0.82);
border: 1px solid rgba(21, 28, 39, 0.08);
box-shadow: 0 10px 24px rgba(21, 28, 39, 0.08);
}
.gia-brand-logo img {
display: block;
}
[data-theme="dark"] .gia-brand-logo {
background: rgba(255, 255, 255, 0.96);
border-color: rgba(255, 255, 255, 0.82);
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.34);
}
.section > .container.gia-page-shell,
.section > .container {
max-width: 1340px;
}
.gia-page-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.gia-page-header .title,
.gia-page-header .subtitle {
max-width: 72ch;
}
.gia-page-header .subtitle {
margin-bottom: 0;
}
.table thead th {
position: sticky;
top: 0;
background: rgba(248, 250, 252, 0.96) !important;
color: #1b1f2a !important;
backdrop-filter: blur(6px);
z-index: 1;
}
[data-theme="dark"] .table thead th {
background: rgba(44, 44, 44, 0.96) !important;
color: #f3f5f8 !important;
}
.table td,
.table th {
vertical-align: top;
}
.help {
max-width: 78ch;
}
.button.is-light {
border-color: rgba(27, 38, 59, 0.12);
}
.input,
.textarea,
.select select {
border-color: rgba(27, 38, 59, 0.18);
box-shadow: none;
}
.input:focus,
.textarea:focus,
.select select:focus {
border-color: rgba(27, 99, 214, 0.8);
box-shadow: 0 0 0 0.125em rgba(27, 99, 214, 0.14);
}
.grid-stack-item-content,
.floating-window {
display: flex !important;
flex-direction: column !important;
overflow-x: hidden !important;
overflow-y: hidden !important;
}
.panel {
display: flex !important;
flex-direction: column !important;
overflow: hidden;
}
.panel-block {
overflow-y:auto;
overflow-x:auto;
min-height: 90%;
display: block;
}
.floating-window {
max-height: 300px;
z-index: 9000;
position: absolute;
top: 50px;
left: 50px;
}
.floating-window .panel {
background-color:rgba(250, 250, 250, 0.8) !important;
}
.float-right {
float: right;
padding: 0 5px;
}
.grid-stack-item:hover .ui-resizable-handle {
display: block !important;
}
.ui-resizable-handle {
z-index: 39 !important;
}
.osint-table-shell {
border: 1px solid rgba(127, 127, 127, 0.2);
border-radius: 14px;
padding: 0.9rem;
background: rgba(255, 255, 255, 0.45);
}
.osint-table-toolbar {
margin-bottom: 0.75rem;
}
.osint-results-table-wrap {
border-radius: 10px;
overflow: auto;
}
.task-ui-badge {
background: #f5f5f5 !important;
border: 1px solid #dbdbdb !important;
color: #1f1f1f !important;
font-size: 0.75rem;
line-height: 1.5;
padding: 0.25em 0.75em;
font-weight: 500;
}
.osint-results-table th {
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.osint-sort-link {
color: inherit;
display: inline-flex;
align-items: center;
gap: 0.25rem;
}
.osint-sort-link:hover {
color: #3273dc;
}
.osint-search-form .button.is-fullwidth {
width: 100%;
}
.navbar-dropdown .navbar-item.is-current-route {
background-color: rgba(50, 115, 220, 0.14) !important;
color: #1f4f99 !important;
font-weight: 600;
}
.theme-toggle-button {
display: inline-flex;
align-items: center;
justify-content: center;
}
.brand-theme-toggle {
min-width: 0;
padding: 0;
border: 0 !important;
background: transparent !important;
box-shadow: none !important;
line-height: 1;
width: 3.15rem;
height: 3.15rem;
}
.brand-theme-logo {
display: block;
width: 3.15rem;
height: 3.15rem;
}
.security-page-tabs a {
transition: background-color 0.15s ease-in-out, color 0.15s ease-in-out;
}
.reduced-motion,
.reduced-motion * {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
</style>
</head>
{% get_accessibility_settings request.user as a11y_settings %}
<body{% if a11y_settings and a11y_settings.disable_animations %} class="reduced-motion"{% endif %}>
@@ -517,12 +209,28 @@
<span class="gia-brand-shell">
{% if user.is_authenticated %}
<button class="button is-light theme-toggle-button gia-brand-logo brand-theme-toggle js-theme-toggle" type="button" data-theme-mode="light" aria-label="Theme toggle">
<img class="brand-theme-logo js-theme-logo" src="{% static 'favicon-light.svg' %}" alt="" aria-hidden="true">
<svg class="brand-theme-logo" viewBox="0 0 213.35 150.85" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
<g transform="translate(38.831 -7.4316)">
<g transform="matrix(.99287 0 0 .99911 1.2367 -30.308)">
<path class="brand-theme-stroke" d="m-32.645 113.03a115.16 122.5 0 0 1 99.73-61.25 115.16 122.5 0 0 1 99.73 61.25" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="15.42"/>
<path class="brand-theme-stroke" d="m67.006 89.591a42.374 42.374 0 0 1 39.148 26.158 42.374 42.374 0 0 1-9.1855 46.179 42.374 42.374 0 0 1-46.179 9.1855 42.374 42.374 0 0 1-26.158-39.148" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16.251"/>
<circle cx="67.003" cy="131.96" r="13.151" fill="#740101"/>
</g>
</g>
</svg>
<span class="is-sr-only" data-theme-label>Auto</span>
</button>
{% else %}
<a href="{% url 'home' %}" class="gia-brand-logo" aria-label="GIA home">
<img class="brand-theme-logo" src="{% static 'favicon-light.svg' %}" alt="logo">
<svg class="brand-theme-logo" viewBox="0 0 213.35 150.85" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
<g transform="translate(38.831 -7.4316)">
<g transform="matrix(.99287 0 0 .99911 1.2367 -30.308)">
<path class="brand-theme-stroke" d="m-32.645 113.03a115.16 122.5 0 0 1 99.73-61.25 115.16 122.5 0 0 1 99.73 61.25" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="15.42"/>
<path class="brand-theme-stroke" d="m67.006 89.591a42.374 42.374 0 0 1 39.148 26.158 42.374 42.374 0 0 1-9.1855 46.179 42.374 42.374 0 0 1-46.179 9.1855 42.374 42.374 0 0 1-26.158-39.148" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16.251"/>
<circle cx="67.003" cy="131.96" r="13.151" fill="#740101"/>
</g>
</g>
</svg>
</a>
{% endif %}
</span>

View File

@@ -6,7 +6,11 @@ upstream django {
server {
listen 9999;
location = /favicon.ico { access_log off; log_not_found off; }
location = /favicon.ico {
alias /conf/static/favicon.ico;
access_log off;
log_not_found off;
}
location /static/ {
root /conf;

View File

@@ -6,7 +6,11 @@ upstream django {
server {
listen 9999;
location = /favicon.ico { access_log off; log_not_found off; }
location = /favicon.ico {
alias /conf/static/favicon.ico;
access_log off;
log_not_found off;
}
location /static/ {
root /conf;

View File

@@ -0,0 +1,267 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import base64
import hashlib
import json
import re
import shutil
import sys
from pathlib import Path, PurePosixPath
from typing import Any
from urllib.parse import urljoin
from urllib.request import Request, urlopen
URL_PATTERN = re.compile(r"url\(([^)]+)\)")
def load_json(path: Path) -> dict[str, Any]:
return json.loads(path.read_text(encoding="utf-8"))
def ensure_parent(path: Path) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
def sri(path: Path, algorithm: str) -> str:
digest = hashlib.new(algorithm, path.read_bytes()).digest()
return f"{algorithm}-" + base64.b64encode(digest).decode("ascii")
def sha256_hex(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def read_package_lock(path: Path) -> dict[str, Any]:
if not path.exists():
raise FileNotFoundError(
f"Missing {path}. Run `npm install --ignore-scripts` in {path.parent} first."
)
return load_json(path)
def lock_entry(lock_data: dict[str, Any], package_name: str) -> dict[str, Any]:
packages = lock_data.get("packages", {})
key = "node_modules/" + package_name
entry = packages.get(key)
if not entry:
raise KeyError(f"Package {package_name!r} not found in package-lock.json")
return entry
def read_package_metadata(package_root: Path) -> dict[str, Any]:
package_json = package_root / "package.json"
if not package_json.exists():
raise FileNotFoundError(f"Missing package metadata: {package_json}")
return load_json(package_json)
def package_root(npm_root: Path, package_name: str) -> Path:
return npm_root / "node_modules" / Path(*package_name.split("/"))
def normalize_manifest_path(path: str) -> str:
pure = PurePosixPath(path)
normalized = pure.as_posix()
if normalized.startswith("../") or normalized == "..":
raise ValueError(f"Refusing to write outside target root: {path}")
return normalized
def copy_file(source: Path, targets: list[Path]) -> None:
if not source.exists():
raise FileNotFoundError(f"Missing vendored source file: {source}")
for target in targets:
ensure_parent(target)
shutil.copy2(source, target)
def fetch_bytes(url: str) -> bytes:
request = Request(url, headers={"User-Agent": "GIA frontend asset vendoring"})
with urlopen(request) as response:
return response.read()
def download_url_bundle(
asset: dict[str, Any],
repo_root: Path,
) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
target_roots = [repo_root / entry for entry in asset["target_roots"]]
entry_target = normalize_manifest_path(asset["entry_target"])
entry_url = asset["entry_url"]
css_bytes = fetch_bytes(entry_url)
text = css_bytes.decode("utf-8")
downloaded: dict[str, bytes] = {entry_target: css_bytes}
for match in URL_PATTERN.finditer(text):
raw_ref = match.group(1).strip().strip("'\"")
if not raw_ref or raw_ref.startswith("data:"):
continue
resolved_url = urljoin(entry_url, raw_ref)
target_rel = normalize_manifest_path(
PurePosixPath(entry_target).parent.joinpath(raw_ref).as_posix()
)
downloaded[target_rel] = fetch_bytes(resolved_url)
report_files: list[dict[str, Any]] = []
copied_targets: list[dict[str, Any]] = []
for relative_path, content in downloaded.items():
for target_root in target_roots:
target = target_root / relative_path
ensure_parent(target)
target.write_bytes(content)
copied_targets.append(
{
"path": str(target.relative_to(repo_root)),
"sha256": sha256_hex(target),
"sri_sha512": sri(target, "sha512"),
}
)
report_files.append(
{
"relative_path": relative_path,
"download_url": urljoin(entry_url, relative_path)
if relative_path != entry_target
else entry_url,
}
)
return report_files, copied_targets
def vendor_npm_file(
asset: dict[str, Any],
repo_root: Path,
npm_root: Path,
lock_data: dict[str, Any],
) -> dict[str, Any]:
pkg_name = asset["package"]
pkg_root = package_root(npm_root, pkg_name)
metadata = read_package_metadata(pkg_root)
source = pkg_root / asset["source_path"]
targets = [repo_root / target for target in asset["targets"]]
copy_file(source, targets)
copied_targets = [
{
"path": str(target.relative_to(repo_root)),
"sha256": sha256_hex(target),
"sri_sha512": sri(target, "sha512"),
}
for target in targets
]
lock = lock_entry(lock_data, pkg_name)
return {
"id": asset["id"],
"kind": asset["kind"],
"package": pkg_name,
"version": metadata["version"],
"license": metadata.get("license", ""),
"homepage": metadata.get("homepage", asset.get("official_url", "")),
"official_url": asset.get("official_url", ""),
"purpose": asset.get("purpose", ""),
"notes": asset.get("notes", ""),
"resolved": lock.get("resolved", ""),
"dist_integrity": lock.get("integrity", ""),
"source_path": asset["source_path"],
"targets": copied_targets,
}
def vendor_url_bundle(asset: dict[str, Any], repo_root: Path) -> dict[str, Any]:
downloaded_files, copied_targets = download_url_bundle(asset, repo_root)
return {
"id": asset["id"],
"kind": asset["kind"],
"version": asset["version"],
"license": asset.get("license", ""),
"homepage": asset.get("official_url", ""),
"official_url": asset.get("official_url", ""),
"purpose": asset.get("purpose", ""),
"notes": asset.get("notes", ""),
"entry_url": asset["entry_url"],
"downloaded_files": downloaded_files,
"targets": copied_targets,
}
def markdown_report(entries: list[dict[str, Any]]) -> str:
lines = [
"# Frontend Library Inventory",
"",
"This report is generated by `scripts/vendor_frontend_assets.py` from `tools/frontend_assets/asset-manifest.json`.",
"",
]
for entry in entries:
title = entry["id"]
package_or_url = entry.get("package") or entry.get("entry_url", "")
lines.extend(
[
f"## {title}",
"",
f"- Source: `{package_or_url}`",
f"- Version: `{entry.get('version', 'n/a')}`",
f"- Official URL: {entry.get('official_url') or entry.get('homepage') or 'n/a'}",
f"- Homepage: {entry.get('homepage') or 'n/a'}",
f"- License: `{entry.get('license') or 'n/a'}`",
f"- Purpose: {entry.get('purpose') or 'n/a'}",
]
)
if entry.get("resolved"):
lines.append(f"- Resolved tarball: `{entry['resolved']}`")
if entry.get("dist_integrity"):
lines.append(f"- Upstream package integrity: `{entry['dist_integrity']}`")
if entry.get("notes"):
lines.append(f"- Notes: {entry['notes']}")
if entry.get("downloaded_files"):
lines.append("- Downloaded bundle files:")
for file_entry in entry["downloaded_files"]:
lines.append(
f" - `{file_entry['relative_path']}` from `{file_entry['download_url']}`"
)
lines.append("- Local targets:")
for target in entry["targets"]:
lines.append(f" - `{target['path']}`")
lines.append(f" - SHA-256: `{target['sha256']}`")
lines.append(f" - SRI sha512: `{target['sri_sha512']}`")
lines.extend(["", ""])
return "\n".join(lines).rstrip() + "\n"
def main() -> int:
parser = argparse.ArgumentParser(description="Vendor pinned frontend assets into Django static files.")
parser.add_argument(
"--manifest",
default="tools/frontend_assets/asset-manifest.json",
help="Path to the asset manifest, relative to the repo root.",
)
args = parser.parse_args()
repo_root = Path(__file__).resolve().parent.parent
manifest_path = repo_root / args.manifest
manifest = load_json(manifest_path)
npm_root = repo_root / manifest["npm_root"]
lock_data = read_package_lock(npm_root / "package-lock.json")
entries: list[dict[str, Any]] = []
for asset in manifest["assets"]:
if asset["kind"] == "npm_file":
entries.append(vendor_npm_file(asset, repo_root, npm_root, lock_data))
elif asset["kind"] == "url_bundle":
entries.append(vendor_url_bundle(asset, repo_root))
else:
raise ValueError(f"Unsupported asset kind: {asset['kind']}")
report_json_path = repo_root / manifest["report_json"]
report_markdown_path = repo_root / manifest["report_markdown"]
ensure_parent(report_json_path)
ensure_parent(report_markdown_path)
report_json_path.write_text(json.dumps({"entries": entries}, indent=2) + "\n", encoding="utf-8")
report_markdown_path.write_text(markdown_report(entries), encoding="utf-8")
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,187 @@
{
"schema_version": 1,
"npm_root": "tools/frontend_assets",
"report_markdown": "artifacts/frontend_libraries.md",
"report_json": "artifacts/frontend_libraries.json",
"assets": [
{
"id": "bulma_css",
"kind": "npm_file",
"package": "bulma",
"source_path": "css/bulma.min.css",
"targets": [
"core/static/css/bulma.min.css"
],
"purpose": "Bulma core stylesheet",
"official_url": "https://bulma.io"
},
{
"id": "bulma_tooltip_css",
"kind": "npm_file",
"package": "bulma-tooltip",
"source_path": "dist/css/bulma-tooltip.min.css",
"targets": [
"core/static/css/bulma-tooltip.min.css"
],
"purpose": "Official Bulma tooltip extension from Bulma's extensions page",
"official_url": "https://wikiki.github.io/elements/tooltip"
},
{
"id": "bulma_slider_css",
"kind": "npm_file",
"package": "bulma-slider",
"source_path": "dist/css/bulma-slider.min.css",
"targets": [
"core/static/css/bulma-slider.min.css"
],
"purpose": "Official Bulma slider extension from Bulma's extensions page",
"official_url": "https://wikiki.github.io/form/slider"
},
{
"id": "bulma_slider_js",
"kind": "npm_file",
"package": "bulma-slider",
"source_path": "dist/js/bulma-slider.min.js",
"targets": [
"core/static/js/bulma-slider.min.js"
],
"purpose": "Official Bulma slider extension runtime",
"official_url": "https://wikiki.github.io/form/slider"
},
{
"id": "bulma_calendar_css",
"kind": "npm_file",
"package": "bulma-calendar",
"source_path": "src/demo/assets/css/bulma-calendar.min.css",
"targets": [
"core/static/css/bulma-calendar.min.css"
],
"purpose": "Official Bulma calendar extension from Bulma's extensions page",
"official_url": "https://wikiki.github.io/components/calendar"
},
{
"id": "bulma_calendar_js",
"kind": "npm_file",
"package": "bulma-calendar",
"source_path": "src/demo/assets/js/bulma-calendar.min.js",
"targets": [
"core/static/js/bulma-calendar.min.js"
],
"purpose": "Official Bulma calendar extension runtime",
"official_url": "https://wikiki.github.io/components/calendar"
},
{
"id": "bulma_tagsinput_css",
"kind": "npm_file",
"package": "bulma-tagsinput",
"source_path": "dist/css/bulma-tagsinput.min.css",
"targets": [
"core/static/css/bulma-tagsinput.min.css"
],
"purpose": "Official Bulma tagsinput extension from Bulma's extensions page",
"official_url": "https://wikiki.github.io/form/tagsinput"
},
{
"id": "bulma_tagsinput_js",
"kind": "npm_file",
"package": "bulma-tagsinput",
"source_path": "dist/js/bulma-tagsinput.min.js",
"targets": [
"core/static/js/bulma-tagsinput.min.js"
],
"purpose": "Official Bulma tagsinput extension runtime",
"official_url": "https://wikiki.github.io/form/tagsinput"
},
{
"id": "bulma_switch_css",
"kind": "npm_file",
"package": "bulma-switch",
"source_path": "dist/css/bulma-switch.min.css",
"targets": [
"core/static/css/bulma-switch.min.css"
],
"purpose": "Official Bulma switch extension from Bulma's extensions page",
"official_url": "https://wikiki.github.io/form/switch"
},
{
"id": "gridstack_css",
"kind": "npm_file",
"package": "gridstack",
"source_path": "dist/gridstack.min.css",
"targets": [
"core/static/css/gridstack.min.css"
],
"purpose": "GridStack stylesheet",
"official_url": "https://gridstackjs.com/"
},
{
"id": "gridstack_js",
"kind": "npm_file",
"package": "gridstack",
"source_path": "dist/gridstack-all.js",
"targets": [
"core/static/js/gridstack-all.js"
],
"purpose": "GridStack bundle used by the dashboard",
"official_url": "https://gridstackjs.com/"
},
{
"id": "jquery_js",
"kind": "npm_file",
"package": "jquery",
"source_path": "dist/jquery.min.js",
"targets": [
"core/static/js/jquery.min.js"
],
"purpose": "Latest jQuery 3.x release for compatibility with legacy plugins",
"official_url": "https://jquery.com",
"notes": "The latest npm release is jQuery 4.x, but this project still vendors 3.7.1 to avoid breaking older plugins."
},
{
"id": "htmx_js",
"kind": "npm_file",
"package": "htmx.org",
"source_path": "dist/htmx.min.js",
"targets": [
"core/static/js/htmx.min.js"
],
"purpose": "htmx runtime",
"official_url": "https://htmx.org/"
},
{
"id": "hyperscript_js",
"kind": "npm_file",
"package": "hyperscript.org",
"source_path": "dist/_hyperscript.min.js",
"targets": [
"core/static/js/hyperscript.min.js"
],
"purpose": "_hyperscript runtime",
"official_url": "https://hyperscript.org/"
},
{
"id": "magnet_js",
"kind": "npm_file",
"package": "@lf2com/magnet.js",
"source_path": "dist/magnet.min.js",
"targets": [
"core/static/js/magnet.min.js"
],
"purpose": "Magnet.js drag attraction component",
"official_url": "https://github.com/lf2com/magnet.js"
},
{
"id": "fontawesome_bundle",
"kind": "url_bundle",
"version": "6.1.1",
"entry_url": "https://site-assets.fontawesome.com/releases/v6.1.1/css/all.css",
"entry_target": "css/all.css",
"target_roots": [
"core/static/vendor/fontawesome"
],
"official_url": "https://fontawesome.com",
"license": "Font Awesome Pro commercial terms via site-assets.fontawesome.com",
"purpose": "Existing Font Awesome asset family, self-hosted locally to preserve the currently used icon set"
}
]
}

2160
tools/frontend_assets/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
{
"name": "gia-frontend-assets",
"private": true,
"version": "1.0.0",
"description": "Pinned frontend asset sources for vendoring into Django static files.",
"license": "UNLICENSED",
"scripts": {
"vendor": "../../genv/bin/python ../../scripts/vendor_frontend_assets.py"
},
"dependencies": {
"@lf2com/magnet.js": "2.0.1",
"bulma": "1.0.4",
"bulma-calendar": "7.1.1",
"bulma-slider": "2.0.5",
"bulma-switch": "2.0.4",
"bulma-tagsinput": "2.0.0",
"bulma-tooltip": "3.0.2",
"gridstack": "12.4.2",
"htmx.org": "2.0.8",
"hyperscript.org": "0.9.14",
"jquery": "3.7.1"
}
}