Properly sort versions, add separator

This commit is contained in:
object-Object 2023-09-02 13:25:23 -04:00
parent a819d50945
commit 32cd536b69
10 changed files with 119 additions and 52 deletions

View file

@ -49,7 +49,7 @@ jobs:
outputs:
pages-url: ${{ steps.get-url.outputs.pages-url }}
matrix: ${{ steps.list-langs.outputs.matrix }}
matrix: ${{ fromJson(steps.list-langs.outputs.matrix) }}
release: ${{ steps.check-release.outputs.release }}
steps:
@ -120,7 +120,7 @@ jobs:
strategy:
fail-fast: false
matrix:
lang: ${{ fromJson(needs.build.outputs.matrix) }}
lang: ${{ needs.build.outputs.matrix }}
env:
GITHUB_PAGES_URL: ${{ needs.build.outputs.pages-url }}

2
.vscode/launch.json vendored
View file

@ -18,7 +18,7 @@
"request": "launch",
"module": "hexdoc.hexdoc",
"args": [
"doc/properties.toml", "-o", "out",// "--allow-missing"
"0.9.5/doc/properties.toml", "-o", "out", "--lang", "en_us",
],
"console": "integratedTerminal",
"justMyCode": false

View file

@ -15,4 +15,7 @@
"[html][jinja-html]": {
"editor.rulers": [120],
},
"files.associations": {
"*.js.jinja": "javascript"
}
}

View file

@ -6,10 +6,11 @@
- [x] GitHub Actions
- [x] Pypi releasing
- [x] API improvements (disable exporting, make it easier (possible?) to generate book for imported mods)
- [ ] Unit test for mock addon book
- [ ] Re-add edified wood recipe to [Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/items/edified.json](items/edified) when it actually exists
- [x] Language
- [x] Version
- [x] Don't delete sitemap markers
- [x] delete in dest, then copy, then traverse again to build sitemap from scratch
- [x] Dropdowns for ^
- [x] Dropdowns for ^
- [ ] Rewrite the JS code in TypeScript, because I'm going insane trying to modify this
- [ ] Unit test for mock addon book
- [ ] Re-add edified wood recipe to [Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/items/edified.json](items/edified) when it actually exists

View file

@ -40,7 +40,7 @@
aria-haspopup="true"
aria-expanded="false"
>{{ lang }} <span class="caret"></span></a>
<ul class="dropdown-menu" id="lang-dropdown"></ul>
<ul class="dropdown-menu dropdown-menu-left" id="lang-dropdown"></ul>
</li>
</ul>
</div>

View file

@ -16,5 +16,9 @@
<b>Entries which are blurred are spoilers</b>. Click to reveal them, but be aware that they may spoil endgame
progression. Alternatively, click <a href="?nospoiler">here</a> to get a version with all spoilers showing.
</p>
{% if not is_bleeding_edge %}
<div id="old-version-notice"></div>
{% endif %}
</blockquote>
</div>

View file

@ -101,6 +101,10 @@ canvas.spell-viz {
--dark-mode: 0;
}
.dropdown-menu .divider {
margin: 4px 0;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #201a20;
@ -142,9 +146,8 @@ canvas.spell-viz {
min-width: 120px;
}
.dropdown-menu-ltr {
left: 0;
right: auto;
.dropdown-menu .divider {
background-color: #080808;
}
.dropdown-menu > li > a {

View file

@ -40,7 +40,7 @@
</style>
</noscript>
<script>
<script type="module">
{% filter indent(6) %}
{%+ include "main.js.jinja" %}
{% endfilter %}

View file

@ -1,5 +1,7 @@
"use strict";
import semver from 'https://cdn.jsdelivr.net/npm/semver@7.5.4/+esm';
const speeds = [0, 0.25, 0.5, 1, 2, 4];
const scrollThreshold = 100;
const rfaQueue = [];
@ -299,6 +301,7 @@ const PAGE_URL = "{{ page_url }}";
const VERSION = "{{ version }}";
const LANG = "{{ lang }}";
// Creates an element in the form `<li><a href=${href}>${text}</a></li>`
function dropdownItem(text, href) {
let a = document.createElement("a");
a.href = href;
@ -309,39 +312,88 @@ function dropdownItem(text, href) {
return li;
}
function fillDropdowns(sitemap) {
let versionDropdown = document.getElementById("version-dropdown");
let langDropdown = document.getElementById("lang-dropdown");
function versionDropdownItem(sitemap, version) {
const {defaultPath, langPaths} = sitemap[version];
// versions, linking to the current lang if possible
for (const version of Object.keys(sitemap).sort().reverse()) {
let {defaultPath, langPaths} = sitemap[version];
// link to the current language if available, else link to the default language
let path;
if (langPaths.hasOwnProperty(LANG)) {
path = langPaths[LANG];
} else {
path = defaultPath;
}
let item = dropdownItem(version, ROOT_URL + path);
versionDropdown.appendChild(item);
// link to the current language if available, else link to the default language
let path;
if (langPaths.hasOwnProperty(LANG)) {
path = langPaths[LANG];
} else {
path = defaultPath;
}
// langs for the current version
let langPaths = sitemap[VERSION].langPaths;
for (const lang of Object.keys(langPaths).sort()) {
let item = dropdownItem(lang, ROOT_URL + langPaths[lang]);
langDropdown.appendChild(item);
}
return dropdownItem(version, ROOT_URL + path);
}
function versionDropdownItems(sitemap, versions) {
return versions.map((version) => (
versionDropdownItem(sitemap, version)
));
}
function dropdownSeparator() {
let li = document.createElement("li");
li.className = "divider";
li.setAttribute("role", "separator");
return li;
}
// Like array.filter(predicate), but also returns the items which didn't match the filter.
function partition(array, predicate) {
let matched = [];
let unmatched = [];
array.forEach((value, index) => {
if (predicate(value, index, array)) {
matched.push(value);
} else {
unmatched.push(value);
}
});
return [matched, unmatched];
}
function sortSitemapVersions(sitemap) {
let [versions, branches] = partition(Object.keys(sitemap), (v) => semver.valid(v) != null);
// branches ascending, versions descending
// eg. ["dev", "main"], ["0.10.0", "0.9.0"]
branches.sort();
versions.sort(semver.rcompare);
return [branches, versions];
}
// Fills the version dropdown menus and the "old version" message.
function addDropdowns(sitemap) {
let [branches, versions] = sortSitemapVersions(sitemap);
// versions
document.getElementById("version-dropdown").append(
...versionDropdownItems(sitemap, branches),
dropdownSeparator(),
...versionDropdownItems(sitemap, versions),
);
// languages for the current version
const langPaths = sitemap[VERSION].langPaths;
const langs = Object.keys(langPaths).sort();
document.getElementById("lang-dropdown").append(
...langs.map((lang) => dropdownItem(lang, ROOT_URL + langPaths[lang])),
);
// return sitemap for chaining, i guess
return sitemap
}
document.addEventListener("DOMContentLoaded", () => {
// fetch the sitemap from the root and use it to generate the navbar
fetch(`${ROOT_URL}/meta/sitemap.json`)
.then(r => r.json())
.then(fillDropdowns)
.then(addDropdowns)
.catch(e => console.error(e))
document.querySelectorAll("details.spell-collapsible").forEach(hookLoad);

View file

@ -1,22 +1,28 @@
import json
import shutil
from argparse import ArgumentParser
from collections import defaultdict
from pathlib import Path
from typing import Self, Sequence, TypedDict
from typing import Self, Sequence
from pydantic import Field, TypeAdapter
from hexdoc.hexdoc import MARKER_NAME, SitemapMarker
from hexdoc.utils import HexdocModel
from hexdoc.utils.model import DEFAULT_CONFIG
from hexdoc.utils.path import write_to_path
class SitemapItem(TypedDict):
defaultPath: str
langPaths: dict[str, str]
class SitemapItem(HexdocModel):
default_path: str = Field(alias="defaultPath", default="")
lang_paths: dict[str, str] = Field(alias="langPaths", default_factory=dict)
def add_marker(self, marker: SitemapMarker):
self.lang_paths[marker.lang] = marker.path
if marker.is_default_lang:
self.default_path = marker.path
def strip_empty_lines(text: str) -> str:
return "\n".join(s for s in text.splitlines() if s.strip())
Sitemap = dict[str, SitemapItem]
# CLI arguments
@ -70,19 +76,17 @@ def main():
shutil.copytree(args.src, args.dst, dirs_exist_ok=True)
# crawl the new tree to rebuild the sitemap
sitemap = defaultdict[str, SitemapItem](
lambda: SitemapItem(defaultPath="", langPaths={})
)
sitemap: Sitemap = defaultdict(SitemapItem)
for marker_path in args.dst.rglob(MARKER_NAME):
marker = SitemapMarker.load(marker_path)
version_item = sitemap[marker.version]
sitemap[marker.version].add_marker(marker)
version_item["langPaths"][marker.lang] = marker.path
if marker.is_default_lang:
version_item["defaultPath"] = marker.path
write_to_path(args.dst / "meta" / "sitemap.json", json.dumps(sitemap))
# dump the sitemap using a TypeAdapter so it serializes the items properly
ta = TypeAdapter(Sitemap, config=DEFAULT_CONFIG)
write_to_path(
args.dst / "meta" / "sitemap.json",
ta.dump_json(sitemap, by_alias=True),
)
if __name__ == "__main__":