Remove minification, format html, break the tests
This commit is contained in:
parent
5c0e410b76
commit
d78e057aa6
21 changed files with 360 additions and 286 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -6,6 +6,9 @@
|
|||
},
|
||||
"editor.rulers": [88],
|
||||
},
|
||||
"[html][jinja-html]": {
|
||||
"editor.rulers": [120],
|
||||
},
|
||||
"python.formatting.provider": "black",
|
||||
"python.analysis.typeCheckingMode": "strict", // god save us
|
||||
"python.analysis.diagnosticSeverityOverrides": {
|
||||
|
|
|
@ -23,7 +23,7 @@ blacklist = []
|
|||
|
||||
[template_args]
|
||||
title = "Hex Book"
|
||||
mod_name = "Hexcasting"
|
||||
mod_name = "Hex Casting"
|
||||
author = "petrak@, Alwinfy"
|
||||
description = "The Hex Book, all in one place."
|
||||
icon_href = "icon.png"
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from html import escape
|
||||
from typing import Any
|
||||
|
||||
from jinja2 import nodes
|
||||
|
@ -27,21 +26,12 @@ class IncludeRawExtension(Extension):
|
|||
return Markup(source[0])
|
||||
|
||||
|
||||
def hexdoc_minify(value: str) -> str:
|
||||
return "".join(line.strip() for line in value.splitlines())
|
||||
|
||||
|
||||
# TODO: remove this when we do the jinja breaking changes
|
||||
def hexdoc_escape(value: Any) -> str:
|
||||
return Markup(escape(str(value)))
|
||||
|
||||
|
||||
def hexdoc_block(value: Any) -> str:
|
||||
match value:
|
||||
case LocalizedStr() | str():
|
||||
# use Markup to tell Jinja not to escape this string for us
|
||||
lines = str(value).splitlines()
|
||||
return Markup("<br />".join(hexdoc_escape(line) for line in lines))
|
||||
return Markup("<br />".join(Markup.escape(line) for line in lines))
|
||||
case FormatTree():
|
||||
with HTMLStream() as out:
|
||||
with value.style.element(out):
|
||||
|
@ -60,4 +50,4 @@ def hexdoc_wrap(value: str, *args: str):
|
|||
attributes = " " + " ".join(attributes)
|
||||
else:
|
||||
attributes = ""
|
||||
return Markup(f"<{tag}{attributes}>{hexdoc_escape(value)}</{tag}>")
|
||||
return Markup(f"<{tag}{attributes}>{Markup.escape(value)}</{tag}>")
|
||||
|
|
|
@ -9,19 +9,18 @@ from jinja2 import Environment, FileSystemLoader, StrictUndefined
|
|||
# from jinja2.sandbox import SandboxedEnvironment
|
||||
from tap import Tap
|
||||
|
||||
from common.jinja_extensions import (
|
||||
IncludeRawExtension,
|
||||
hexdoc_block,
|
||||
hexdoc_escape,
|
||||
hexdoc_minify,
|
||||
hexdoc_wrap,
|
||||
)
|
||||
from common.jinja_extensions import IncludeRawExtension, hexdoc_block, hexdoc_wrap
|
||||
from common.properties import Properties
|
||||
from hexcasting.hex_book import HexBook
|
||||
|
||||
if sys.version_info < (3, 11):
|
||||
raise RuntimeError("Minimum Python version: 3.11")
|
||||
|
||||
|
||||
def strip_empty_lines(text: str) -> str:
|
||||
return "\n".join(s for s in text.splitlines() if s.strip())
|
||||
|
||||
|
||||
# CLI arguments
|
||||
class Args(Tap):
|
||||
"""example: main.py properties.toml -o out.html"""
|
||||
|
@ -51,20 +50,18 @@ def main(args: Args) -> None:
|
|||
extensions=[IncludeRawExtension],
|
||||
)
|
||||
env.filters |= dict( # for some reason, pylance doesn't like the {} here
|
||||
hexdoc_minify=hexdoc_minify,
|
||||
hexdoc_block=hexdoc_block,
|
||||
hexdoc_wrap=hexdoc_wrap,
|
||||
hexdoc_escape=hexdoc_escape,
|
||||
)
|
||||
|
||||
# load and render template
|
||||
template = env.get_template(props.template)
|
||||
docs = template.render(
|
||||
props.template_args
|
||||
| {
|
||||
"book": book,
|
||||
"props": props,
|
||||
}
|
||||
docs = strip_empty_lines(
|
||||
template.render(
|
||||
**props.template_args,
|
||||
book=book,
|
||||
props=props,
|
||||
)
|
||||
)
|
||||
|
||||
# if there's an output file specified, write to it
|
||||
|
|
14
doc/templates/book.html.jinja
vendored
14
doc/templates/book.html.jinja
vendored
|
@ -1,21 +1,21 @@
|
|||
<div class='container'>
|
||||
<div class="container">
|
||||
{# big landing text box - this is optional so addons can disable it if they want #}
|
||||
{% if show_landing_text %}
|
||||
<header class='jumbotron'>
|
||||
<h1 class='book-title'>{{ book.name }}</h1>
|
||||
<header class="jumbotron">
|
||||
<h1 class="book-title">{{ book.name }}</h1>
|
||||
{{ book.landing_text|hexdoc_block }}
|
||||
</header>
|
||||
{% endif %}
|
||||
|
||||
{# table of contents #}
|
||||
<nav>
|
||||
{% include "tableofcontents.html.jinja" %}
|
||||
{% include "table_of_contents.html.jinja" %}
|
||||
</nav>
|
||||
|
||||
{# actual book content (ie. all the categories) #}
|
||||
<main class='book-body'>
|
||||
{% for category in book.categories.values() %}
|
||||
<main class="book-body">
|
||||
{% for category in book.categories.values() +%}
|
||||
{% include "category.html.jinja" %}
|
||||
{% endfor -%}
|
||||
{% endfor +%}
|
||||
</main>
|
||||
</div>
|
||||
|
|
11
doc/templates/category.html.jinja
vendored
11
doc/templates/category.html.jinja
vendored
|
@ -1,11 +1,12 @@
|
|||
{% import "macros.html.jinja" as macros -%}
|
||||
{% import "macros.html.jinja" as macros %}
|
||||
|
||||
<section id='{{ category.id.path }}'>
|
||||
<section id="{{ category.id.path }}">
|
||||
{% call macros.maybe_spoilered(category) %}
|
||||
{{ macros.section_header(category, "h2", "category-title") }}
|
||||
{{- macros.section_header(category, "h2", "category-title") }}
|
||||
{{ category.description|hexdoc_block }}
|
||||
{% endcall %}
|
||||
{% for entry in category.entries if entry.id not in props.blacklist %}
|
||||
|
||||
{% for entry in category.entries if entry.id not in props.blacklist +%}
|
||||
{% include "entry.html.jinja" %}
|
||||
{% endfor %}
|
||||
{% endfor +%}
|
||||
</section>
|
||||
|
|
9
doc/templates/entry.html.jinja
vendored
9
doc/templates/entry.html.jinja
vendored
|
@ -1,10 +1,11 @@
|
|||
{% import "macros.html.jinja" as macros -%}
|
||||
|
||||
<div id='{{ entry.id.path }}'>
|
||||
<div id="{{ entry.id.path }}">
|
||||
{% call macros.maybe_spoilered(entry) %}
|
||||
{{ macros.section_header(entry, "h3", "entry-title") }}
|
||||
{% for page in entry.pages %}
|
||||
{%- include page.template -%}
|
||||
{{- macros.section_header(entry, "h3", "entry-title") }}
|
||||
|
||||
{% for page in entry.pages +%}
|
||||
{% include page.template %}
|
||||
{% endfor %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
|
|
34
doc/templates/macros.html.jinja
vendored
34
doc/templates/macros.html.jinja
vendored
|
@ -1,37 +1,37 @@
|
|||
{# jump to top icon in section headers #}
|
||||
{% macro jump_to_top() -%}
|
||||
<a href='#table-of-contents' class='permalink small' title='Jump to top'>
|
||||
<i class='bi bi-box-arrow-up'></i>
|
||||
</a>
|
||||
<a
|
||||
href="#table-of-contents"
|
||||
class="permalink small"
|
||||
title="Jump to top"
|
||||
><i class="bi bi-box-arrow-up"></i></a>
|
||||
{%- endmacro %}
|
||||
|
||||
{# link icon in section headers to get a permalink to that section #}
|
||||
{% macro permalink(href) -%}
|
||||
<a href='#{{ href }}' class='permalink small' title='Permalink'>
|
||||
<i class='bi bi-link-45deg'></i>
|
||||
</a>
|
||||
<a
|
||||
href="#{{ href }}"
|
||||
class="permalink small"
|
||||
title="Permalink"
|
||||
><i class="bi bi-link-45deg"></i></a>
|
||||
{%- endmacro %}
|
||||
|
||||
{# header for categories and entries #}
|
||||
{% macro section_header(value, header_tag, class_name) -%}
|
||||
<{{ header_tag }} class='{{ class_name }} page-header'>
|
||||
{{ value.name|hexdoc_escape }}
|
||||
{{ jump_to_top() }}
|
||||
{{ permalink(value.id.path) }}
|
||||
<{{ header_tag }} class="{{ class_name }} page-header">
|
||||
{{- value.name ~ jump_to_top() ~ permalink(value.id.path) -}}
|
||||
</{{ header_tag }}>
|
||||
{%- endmacro %}
|
||||
|
||||
{# link to value.id, with spoiler blur if value is a spoiler #}
|
||||
{% macro maybe_spoilered_link(value) -%}
|
||||
<a href='#{{ value.id.path }}' class='{{ "spoilered" if value.is_spoiler else "" }}'>
|
||||
{{- value.name|hexdoc_escape -}}
|
||||
</a>
|
||||
<a href="#{{ value.id.path }}"{{ ' class="spoilered"'|safe if value.is_spoiler }}>{{ value.name }}</a>
|
||||
{%- endmacro %}
|
||||
|
||||
{# macro block which spoiler blurs its content if value is a spoiler #}
|
||||
{% macro maybe_spoilered(value) -%}
|
||||
{% if value.is_spoiler %}
|
||||
<div class='spoilered'>
|
||||
<div class="spoilered">
|
||||
{{ caller() }}
|
||||
</div>
|
||||
{% else %}
|
||||
|
@ -41,11 +41,11 @@
|
|||
|
||||
{# shows the names of all the recipe results in a list of recipes #}
|
||||
{% macro recipe_block(recipes, result_attribute, description, separator) -%}
|
||||
<blockquote class='crafting-info'>
|
||||
<blockquote class="crafting-info">
|
||||
Depicted in the book: {{ description }} {{
|
||||
recipes
|
||||
|map(attribute='result.' ~ result_attribute)
|
||||
|map('hexdoc_wrap', 'code')
|
||||
|map(attribute="result." ~ result_attribute)
|
||||
|map("hexdoc_wrap", "code")
|
||||
|join(separator)
|
||||
}}.
|
||||
</blockquote>
|
||||
|
|
117
doc/templates/main.css.jinja
vendored
Normal file
117
doc/templates/main.css.jinja
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
details.spell-collapsible {
|
||||
display: inline-block;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 4px;
|
||||
padding: 0.5em 0.5em 0;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
summary.collapse-spell {
|
||||
font-weight: bold;
|
||||
margin: -0.5em -0.5em 0;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
details.spell-collapsible[open] {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
details[open] summary.collapse-spell {
|
||||
border-bottom: 1px solid #aaa;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
details .collapse-spell::before {
|
||||
content: "Click to show spell";
|
||||
}
|
||||
|
||||
details[open] .collapse-spell::before {
|
||||
content: "Click to hide spell";
|
||||
}
|
||||
|
||||
blockquote.crafting-info {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
a.permalink {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
a.permalink:hover {
|
||||
color: lightgray;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.5ex 0;
|
||||
}
|
||||
|
||||
p.fake-li {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p.fake-li::before {
|
||||
content: "\2022";
|
||||
margin: 1ex;
|
||||
}
|
||||
|
||||
.linkout::before {
|
||||
content: "Link: ";
|
||||
}
|
||||
|
||||
p.todo-note {
|
||||
font-style: italic;
|
||||
color: lightgray;
|
||||
}
|
||||
|
||||
.obfuscated {
|
||||
filter: blur(1em);
|
||||
}
|
||||
|
||||
.spoilered {
|
||||
filter: blur(1ex);
|
||||
-moz-transition: filter 0.04s linear;
|
||||
}
|
||||
|
||||
.spoilered:hover {
|
||||
filter: blur(0.5ex);
|
||||
}
|
||||
|
||||
.spoilered.unspoilered {
|
||||
filter: blur(0);
|
||||
}
|
||||
|
||||
canvas.spell-viz {
|
||||
--dot-color: #777f;
|
||||
--dot-color: #777f;
|
||||
--start-dot-color: #f009;
|
||||
--moving-dot-color: #0fa9;
|
||||
|
||||
--path-color: darkgray;
|
||||
--visited-path-color: #0c8;
|
||||
|
||||
--dot-scale: 0.0625;
|
||||
--moving-dot-scale: 0.125;
|
||||
--line-scale: 0.08333;
|
||||
--pausetext-scale: 0.5;
|
||||
--dark-mode: 0;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #201a20;
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.jumbotron {
|
||||
background-color: #323;
|
||||
}
|
||||
|
||||
canvas.spell-viz {
|
||||
/* hack */
|
||||
--dark-mode: 1;
|
||||
}
|
||||
}
|
148
doc/templates/main.html.jinja
vendored
148
doc/templates/main.html.jinja
vendored
|
@ -1,5 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="{{ description }}">
|
||||
|
@ -9,148 +10,33 @@
|
|||
<title>{{ title }}</title>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/css/bootstrap.min.css"
|
||||
integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
|
||||
integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
|
||||
|
||||
<style>
|
||||
summary { display: list-item; }
|
||||
|
||||
details.spell-collapsible {
|
||||
display: inline-block;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 4px;
|
||||
padding: .5em .5em 0;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
summary.collapse-spell {
|
||||
font-weight: bold;
|
||||
margin: -.5em -.5em 0;
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
details.spell-collapsible[open] {
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
details[open] summary.collapse-spell {
|
||||
border-bottom: 1px solid #aaa;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
details .collapse-spell::before {
|
||||
content: "Click to show spell";
|
||||
}
|
||||
details[open] .collapse-spell::before {
|
||||
content: "Click to hide spell";
|
||||
}
|
||||
blockquote.crafting-info {
|
||||
font-size: inherit;
|
||||
}
|
||||
a.permalink {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
a.permalink:hover {
|
||||
color: lightgray;
|
||||
}
|
||||
p {
|
||||
margin: 0.5ex 0;
|
||||
}
|
||||
p.fake-li {
|
||||
margin: 0;
|
||||
}
|
||||
p.fake-li::before {
|
||||
content: "\2022";
|
||||
margin: 1ex;
|
||||
}
|
||||
.linkout::before {
|
||||
content: "Link: ";
|
||||
}
|
||||
p.todo-note {
|
||||
font-style: italic;
|
||||
color: lightgray;
|
||||
}
|
||||
.obfuscated {
|
||||
filter: blur(1em);
|
||||
}
|
||||
.spoilered {
|
||||
filter: blur(1ex);
|
||||
-moz-transition: filter 0.04s linear;
|
||||
}
|
||||
.spoilered:hover {
|
||||
filter: blur(0.5ex);
|
||||
}
|
||||
.spoilered.unspoilered {
|
||||
filter: blur(0);
|
||||
}
|
||||
canvas.spell-viz {
|
||||
--dot-color: #777f;
|
||||
--dot-color: #777f;
|
||||
--start-dot-color: #f009;
|
||||
--moving-dot-color: #0fa9;
|
||||
|
||||
--path-color: darkgray;
|
||||
--visited-path-color: #0c8;
|
||||
|
||||
--dot-scale: 0.0625;
|
||||
--moving-dot-scale: 0.125;
|
||||
--line-scale: 0.08333;
|
||||
--pausetext-scale: 0.5;
|
||||
--dark-mode: 0;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #201a20;
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.jumbotron {
|
||||
background-color: #323;
|
||||
}
|
||||
|
||||
canvas.spell-viz {
|
||||
/* hack */
|
||||
--dark-mode: 1;
|
||||
}
|
||||
}
|
||||
|
||||
{% filter indent(6) %}
|
||||
{%+ include "main.css.jinja" %}
|
||||
{% endfilter %}
|
||||
</style>
|
||||
|
||||
<noscript>
|
||||
<style>
|
||||
<style>
|
||||
/* for accessibility */
|
||||
.spoilered {
|
||||
filter: none !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
</noscript>
|
||||
|
||||
<script>
|
||||
{# TODO: kinda hacky #}
|
||||
{% filter indent(6) %}
|
||||
{%+ include_raw "main.js" %}
|
||||
{% endfilter +%}
|
||||
|
||||
{% endfilter %}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="container" style="margin-top: 3em;">
|
||||
<blockquote>
|
||||
<h1>This is the online version of the {{ mod_name }} documentation.</h1>
|
||||
<p>Embedded images and patterns are included, but not crafting recipes or items. There's an in-game book for
|
||||
those.</p>
|
||||
{% if is_bleeding_edge %}
|
||||
<p>Additionally, this is built from the latest code on GitHub. It may describe <b>newer</b> features that you
|
||||
may not necessarily have, even on the latest CurseForge version!</p>
|
||||
{% endif %}
|
||||
<p><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>
|
||||
</blockquote>
|
||||
</div>
|
||||
{% filter hexdoc_minify %}
|
||||
{% include "book.html.jinja" %}
|
||||
{% endfilter +%}
|
||||
</body>
|
||||
<body>
|
||||
{% include "welcome.html.jinja" +%}
|
||||
{% include "book.html.jinja" +%}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
147
doc/templates/main.js
vendored
147
doc/templates/main.js
vendored
|
@ -1,8 +1,10 @@
|
|||
"use strict";
|
||||
|
||||
const speeds = [0, 0.25, 0.5, 1, 2, 4];
|
||||
const scrollThreshold = 100;
|
||||
const rfaQueue = [];
|
||||
const colorCache = new Map();
|
||||
|
||||
function getColorRGB(ctx, str) {
|
||||
if (!colorCache.has(str)) {
|
||||
ctx.fillStyle = str;
|
||||
|
@ -13,28 +15,45 @@ function getColorRGB(ctx, str) {
|
|||
}
|
||||
return colorCache.get(str);
|
||||
}
|
||||
|
||||
function startAngle(str) {
|
||||
switch (str) {
|
||||
case "east": return 0;
|
||||
case "north_east": return 1;
|
||||
case "north_west": return 2;
|
||||
case "west": return 3;
|
||||
case "south_west": return 4;
|
||||
case "south_east": return 5;
|
||||
default: return 0;
|
||||
case "east":
|
||||
return 0;
|
||||
case "north_east":
|
||||
return 1;
|
||||
case "north_west":
|
||||
return 2;
|
||||
case "west":
|
||||
return 3;
|
||||
case "south_west":
|
||||
return 4;
|
||||
case "south_east":
|
||||
return 5;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function offsetAngle(str) {
|
||||
switch (str) {
|
||||
case "w": return 0;
|
||||
case "q": return 1;
|
||||
case "a": return 2;
|
||||
case "s": return 3;
|
||||
case "d": return 4;
|
||||
case "e": return 5;
|
||||
default: return -1;
|
||||
case "w":
|
||||
return 0;
|
||||
case "q":
|
||||
return 1;
|
||||
case "a":
|
||||
return 2;
|
||||
case "s":
|
||||
return 3;
|
||||
case "d":
|
||||
return 4;
|
||||
case "e":
|
||||
return 5;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
function initializeElem(canvas) {
|
||||
const str = canvas.dataset.string;
|
||||
let angle = startAngle(canvas.dataset.start);
|
||||
|
@ -43,12 +62,13 @@ function initializeElem(canvas) {
|
|||
// build geometry
|
||||
const points = [[0, 0]];
|
||||
let lastPoint = points[0];
|
||||
let minPoint = lastPoint, maxPoint = lastPoint;
|
||||
let minPoint = lastPoint,
|
||||
maxPoint = lastPoint;
|
||||
for (const ch of "w" + str) {
|
||||
const addAngle = offsetAngle(ch);
|
||||
if (addAngle < 0) continue;
|
||||
angle = (angle + addAngle) % 6;
|
||||
const trueAngle = Math.PI / 3 * angle;
|
||||
const trueAngle = (Math.PI / 3) * angle;
|
||||
|
||||
const [lx, ly] = lastPoint;
|
||||
const newPoint = [lx + Math.cos(trueAngle), ly - Math.sin(trueAngle)];
|
||||
|
@ -62,13 +82,24 @@ function initializeElem(canvas) {
|
|||
maxPoint = [Math.max(max, newPoint[0]), Math.max(may, newPoint[1])];
|
||||
}
|
||||
const size = Math.min(canvas.width, canvas.height) * 0.8;
|
||||
const scale = size / Math.max(3, Math.max(maxPoint[1] - minPoint[1], maxPoint[0] - minPoint[0]));
|
||||
const center = [(minPoint[0] + maxPoint[0]) * 0.5, (minPoint[1] + maxPoint[1]) * 0.5];
|
||||
const truePoints = points.map(p => [canvas.width * 0.5 + scale * (p[0] - center[0]), canvas.height * 0.5 + scale * (p[1] - center[1])]);
|
||||
const scale =
|
||||
size /
|
||||
Math.max(3, Math.max(maxPoint[1] - minPoint[1], maxPoint[0] - minPoint[0]));
|
||||
const center = [
|
||||
(minPoint[0] + maxPoint[0]) * 0.5,
|
||||
(minPoint[1] + maxPoint[1]) * 0.5,
|
||||
];
|
||||
const truePoints = points.map((p) => [
|
||||
canvas.width * 0.5 + scale * (p[0] - center[0]),
|
||||
canvas.height * 0.5 + scale * (p[1] - center[1]),
|
||||
]);
|
||||
let uniqPoints = [];
|
||||
l1: for (const point of truePoints) {
|
||||
for (const pt of uniqPoints) {
|
||||
if (Math.abs(point[0] - pt[0]) < 0.00001 && Math.abs(point[1] - pt[1]) < 0.00001) {
|
||||
if (
|
||||
Math.abs(point[0] - pt[0]) < 0.00001 &&
|
||||
Math.abs(point[1] - pt[1]) < 0.00001
|
||||
) {
|
||||
continue l1;
|
||||
}
|
||||
}
|
||||
|
@ -83,14 +114,15 @@ function initializeElem(canvas) {
|
|||
let scrollTimeout = 1e309;
|
||||
let speedLevel = 3;
|
||||
let speedIncrement = 0;
|
||||
|
||||
function speedScale() {
|
||||
return speeds[speedLevel];
|
||||
}
|
||||
|
||||
const style = getComputedStyle(canvas);
|
||||
const getProp = n => style.getPropertyValue(n);
|
||||
const getProp = (n) => style.getPropertyValue(n);
|
||||
|
||||
const tick = dt => {
|
||||
const tick = (dt) => {
|
||||
scrollTimeout += dt;
|
||||
if (canvas.offsetParent === null) return;
|
||||
|
||||
|
@ -107,18 +139,24 @@ function initializeElem(canvas) {
|
|||
const pauseScale = scale * +getProp("--pausetext-scale");
|
||||
const bodyBg = scale * +getProp("--pausetext-scale");
|
||||
const darkMode = +getProp("--dark-mode");
|
||||
const bgColors = getColorRGB(context, getComputedStyle(document.body).backgroundColor);
|
||||
|
||||
const bgColors = getColorRGB(
|
||||
context,
|
||||
getComputedStyle(document.body).backgroundColor
|
||||
);
|
||||
|
||||
if (!perWorld) {
|
||||
progress += speed * dt * (progress > 0 ? speedScale() : Math.sqrt(speedScale()));
|
||||
progress +=
|
||||
speed * dt * (progress > 0 ? speedScale() : Math.sqrt(speedScale()));
|
||||
}
|
||||
if (progress >= truePoints.length - 1) {
|
||||
progress = negaProgress;
|
||||
}
|
||||
let ix = Math.floor(progress), frac = progress - ix, core = null, fadeColor = 0;
|
||||
let ix = Math.floor(progress),
|
||||
frac = progress - ix,
|
||||
core = null,
|
||||
fadeColor = 0;
|
||||
if (ix < 0) {
|
||||
const rawFade = 2 * progress / negaProgress - 1;
|
||||
const rawFade = (2 * progress) / negaProgress - 1;
|
||||
fadeColor = 1 - Math.abs(rawFade);
|
||||
context.strokeStyle = rawFade > 0 ? strokeVisitedStyle : strokeStyle;
|
||||
ix = rawFade > 0 ? truePoints.length - 2 : 0;
|
||||
|
@ -131,7 +169,6 @@ function initializeElem(canvas) {
|
|||
const [rx, ry] = truePoints[ix + 1];
|
||||
core = [lx + (rx - lx) * frac, ly + (ry - ly) * frac];
|
||||
|
||||
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
context.beginPath();
|
||||
context.lineWidth = strokeWidth;
|
||||
|
@ -151,8 +188,8 @@ function initializeElem(canvas) {
|
|||
|
||||
for (let i = 0; i < uniqPoints.length; i++) {
|
||||
context.beginPath();
|
||||
context.fillStyle = (i == 0 && !perWorld) ? startDotStyle : dotStyle;
|
||||
const radius = (i == 0 && !perWorld) ? movDotRadius : dotRadius;
|
||||
context.fillStyle = i == 0 && !perWorld ? startDotStyle : dotStyle;
|
||||
const radius = i == 0 && !perWorld ? movDotRadius : dotRadius;
|
||||
context.arc(uniqPoints[i][0], uniqPoints[i][1], radius, 0, 2 * Math.PI);
|
||||
context.fill();
|
||||
}
|
||||
|
@ -168,16 +205,22 @@ function initializeElem(canvas) {
|
|||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
if (scrollTimeout <= 2000) {
|
||||
context.fillStyle = `rgba(200, 200, 200, ${(2000 - scrollTimeout) / 1000})`;
|
||||
context.fillStyle = `rgba(200, 200, 200, ${
|
||||
(2000 - scrollTimeout) / 1000
|
||||
})`;
|
||||
context.font = `${pauseScale}px sans-serif`;
|
||||
context.fillText(speedScale() ? speedScale() + "x" : "Paused", 0.2 * scale, canvas.height - 0.2 * scale);
|
||||
context.fillText(
|
||||
speedScale() ? speedScale() + "x" : "Paused",
|
||||
0.2 * scale,
|
||||
canvas.height - 0.2 * scale
|
||||
);
|
||||
}
|
||||
};
|
||||
rfaQueue.push(tick);
|
||||
|
||||
// scrolling input
|
||||
if (!perWorld) {
|
||||
canvas.addEventListener("wheel", ev => {
|
||||
canvas.addEventListener("wheel", (ev) => {
|
||||
speedIncrement += ev.deltaY;
|
||||
const oldSpeedLevel = speedLevel;
|
||||
if (speedIncrement >= scrollThreshold) {
|
||||
|
@ -194,6 +237,7 @@ function initializeElem(canvas) {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
function hookLoad(elem) {
|
||||
let init = false;
|
||||
const canvases = elem.querySelectorAll("canvas");
|
||||
|
@ -204,22 +248,27 @@ function hookLoad(elem) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
function hookToggle(elem) {
|
||||
const details = Array.from(document.querySelectorAll("details." + elem.dataset.target));
|
||||
const details = Array.from(
|
||||
document.querySelectorAll("details." + elem.dataset.target)
|
||||
);
|
||||
elem.addEventListener("click", () => {
|
||||
if (details.some(x => x.open)) {
|
||||
details.forEach(x => x.open = false);
|
||||
if (details.some((x) => x.open)) {
|
||||
details.forEach((x) => (x.open = false));
|
||||
} else {
|
||||
details.forEach(x => x.open = true);
|
||||
details.forEach((x) => (x.open = true));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(document.location.search);
|
||||
|
||||
function hookSpoiler(elem) {
|
||||
if (params.get("nospoiler") !== null) {
|
||||
elem.classList.add("unspoilered");
|
||||
} else {
|
||||
const thunk = ev => {
|
||||
const thunk = (ev) => {
|
||||
if (!elem.classList.contains("unspoilered")) {
|
||||
ev.preventDefault();
|
||||
ev.stopImmediatePropagation();
|
||||
|
@ -232,21 +281,27 @@ function hookSpoiler(elem) {
|
|||
if (elem instanceof HTMLAnchorElement) {
|
||||
const href = elem.getAttribute("href");
|
||||
if (href.startsWith("#")) {
|
||||
elem.addEventListener("click", () => document.getElementById(href.substring(1)).querySelector(".spoilered").classList.add("unspoilered"));
|
||||
elem.addEventListener("click", () =>
|
||||
document
|
||||
.getElementById(href.substring(1))
|
||||
.querySelector(".spoilered")
|
||||
.classList.add("unspoilered")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.querySelectorAll('details.spell-collapsible').forEach(hookLoad);
|
||||
document.querySelectorAll('a.toggle-link').forEach(hookToggle);
|
||||
document.querySelectorAll('.spoilered').forEach(hookSpoiler);
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.querySelectorAll("details.spell-collapsible").forEach(hookLoad);
|
||||
document.querySelectorAll("a.toggle-link").forEach(hookToggle);
|
||||
document.querySelectorAll(".spoilered").forEach(hookSpoiler);
|
||||
function tick(prevTime, time) {
|
||||
const dt = time - prevTime;
|
||||
for (const q of rfaQueue) {
|
||||
q(dt);
|
||||
}
|
||||
requestAnimationFrame(t => tick(time, t));
|
||||
requestAnimationFrame((t) => tick(time, t));
|
||||
}
|
||||
requestAnimationFrame(t => tick(t, t));
|
||||
});
|
||||
requestAnimationFrame((t) => tick(t, t));
|
||||
});
|
||||
|
|
4
doc/templates/pages/ImagePage.html.jinja
vendored
4
doc/templates/pages/ImagePage.html.jinja
vendored
|
@ -1,9 +1,9 @@
|
|||
{% extends "pages/PageWithTitle.html.jinja" %}
|
||||
|
||||
{% block inner_body %}
|
||||
<p class='img-wrapper'>
|
||||
<p class="img-wrapper">
|
||||
{% for image in page.images %}
|
||||
<img src='{{ props.asset_url(image) }}'></img>
|
||||
<img src="{{ props.asset_url(image) }}"></img>
|
||||
{% endfor %}
|
||||
</p>
|
||||
{{ super() }}
|
||||
|
|
6
doc/templates/pages/LinkPage.html.jinja
vendored
6
doc/templates/pages/LinkPage.html.jinja
vendored
|
@ -1,8 +1,8 @@
|
|||
{% extends "pages/PageWithTitle.html.jinja" %}
|
||||
|
||||
{% block inner_body %}
|
||||
{{- super() }}
|
||||
<h4 class='linkout'>
|
||||
<a href='{{ page.url|hexdoc_escape }}'>{{ page.link_text }}</a>
|
||||
{{ super() }}
|
||||
<h4 class="linkout">
|
||||
<a href="{{ page.url }}">{{ page.link_text }}</a>
|
||||
</h4>
|
||||
{% endblock inner_body %}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{% extends "pages/PageWithPattern.html.jinja" %}
|
||||
|
||||
{% block title_attrs %} class='pattern-title'{% endblock %}
|
||||
{% block title_attrs %} class="pattern-title"{% endblock %}
|
||||
|
|
10
doc/templates/pages/Page.html.jinja
vendored
10
doc/templates/pages/Page.html.jinja
vendored
|
@ -1,12 +1,12 @@
|
|||
{% if page.anchor %}
|
||||
{# set variable to allow children to use this value too #}
|
||||
{% set page_anchor_id = entry.id.path ~ "@" ~ page.anchor -%}
|
||||
{% set page_anchor_id = entry.id.path ~ "@" ~ page.anchor %}
|
||||
|
||||
{# page content (not required because EmptyPage uses this template directly) -#}
|
||||
<div id='{{ page_anchor_id }}'>
|
||||
{% block body scoped %}{% endblock -%}
|
||||
{#- page content (not required because EmptyPage uses this template directly) #}
|
||||
<div id="{{ page_anchor_id }}">
|
||||
{% block body scoped %}{% endblock %}
|
||||
</div>
|
||||
{% else %}
|
||||
{{- self.body() }}
|
||||
{{ self.body() }}
|
||||
{% endif %}
|
||||
<br />
|
||||
|
|
16
doc/templates/pages/PageWithPattern.html.jinja
vendored
16
doc/templates/pages/PageWithPattern.html.jinja
vendored
|
@ -1,13 +1,17 @@
|
|||
{% extends "pages/PageWithTitle.html.jinja" %}
|
||||
|
||||
{% block inner_body %}
|
||||
<details class='spell-collapsible'>
|
||||
<summary class='collapse-spell'></summary>
|
||||
|
||||
<details class="spell-collapsible">
|
||||
<summary class="collapse-spell"></summary>
|
||||
{% for pattern in page.patterns %}
|
||||
<canvas class='spell-viz' width='216' height='216' data-string='{{ pattern.signature }}' data-start='{{ pattern.startdir.name.lower() }}' data-per-world='{{ pattern.is_per_world }}'>
|
||||
Your browser does not support visualizing patterns. Pattern code: {{ pattern.signature }}
|
||||
</canvas>
|
||||
<canvas
|
||||
class="spell-viz"
|
||||
width="216"
|
||||
height="216"
|
||||
data-string="{{ pattern.signature }}"
|
||||
data-start="{{ pattern.startdir.name.lower() }}"
|
||||
data-per-world="{{ pattern.is_per_world }}"
|
||||
>Your browser does not support visualizing patterns. Pattern code: {{ pattern.signature }}</canvas>
|
||||
{% endfor %}
|
||||
</details>
|
||||
{{ super() }}
|
||||
|
|
11
doc/templates/pages/PageWithTitle.html.jinja
vendored
11
doc/templates/pages/PageWithTitle.html.jinja
vendored
|
@ -5,15 +5,16 @@
|
|||
{% if page.title is not none %}
|
||||
{# need title_attrs for LookupPatternPage -#}
|
||||
<h4{% block title_attrs %}{% endblock %}>
|
||||
{{ page.title|hexdoc_escape }}
|
||||
{% if page_anchor_id is defined %} {# conditionally defined in Page #}
|
||||
{{ macros.permalink(page_anchor_id) }}
|
||||
{% endif %}
|
||||
{{- page.title -}}
|
||||
{# conditionally defined in Page #}
|
||||
{%- if page_anchor_id is defined %}
|
||||
{{- macros.permalink(page_anchor_id) -}}
|
||||
{% endif -%}
|
||||
</h4>
|
||||
{% endif %}
|
||||
|
||||
{# within a separate block so we can control if the text goes before or after page-specific content #}
|
||||
{% block inner_body %}
|
||||
{{- page.text|hexdoc_block }}
|
||||
{{ page.text|hexdoc_block }}
|
||||
{% endblock inner_body %}
|
||||
{% endblock body %}
|
||||
|
|
2
doc/templates/pages/SpotlightPage.html.jinja
vendored
2
doc/templates/pages/SpotlightPage.html.jinja
vendored
|
@ -1,6 +1,6 @@
|
|||
{% extends "pages/PageWithTitle.html.jinja" %}
|
||||
|
||||
{% block inner_body %}
|
||||
<h4 class='spotlight-title page-header'>{{ page.item }}</h4>
|
||||
<h4 class="spotlight-title page-header">{{ page.item }}</h4>
|
||||
{{ super() }}
|
||||
{% endblock inner_body %}
|
||||
|
|
24
doc/templates/table_of_contents.html.jinja
vendored
Normal file
24
doc/templates/table_of_contents.html.jinja
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
{% import "macros.html.jinja" as macros -%}
|
||||
|
||||
<h2 id="table-of-contents" class="page-header">
|
||||
Table of Contents<a
|
||||
href="javascript:void(0)"
|
||||
class="permalink toggle-link small"
|
||||
data-target="toc-category"
|
||||
title="Toggle all"
|
||||
><i class="bi bi-list-nested"></i></a>{{ macros.permalink("table-of-contents") }}
|
||||
</h2>
|
||||
|
||||
{% for category in book.categories.values() %}
|
||||
<details class="toc-category">
|
||||
{# category #}
|
||||
<summary>{{ macros.maybe_spoilered_link(category) }}</summary>
|
||||
|
||||
{# list of entries in the category #}
|
||||
<ul>
|
||||
{% for entry in category.entries %}
|
||||
<li>{{ macros.maybe_spoilered_link(entry) }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% endfor %}
|
25
doc/templates/tableofcontents.html.jinja
vendored
25
doc/templates/tableofcontents.html.jinja
vendored
|
@ -1,25 +0,0 @@
|
|||
{% import "macros.html.jinja" as macros %}
|
||||
|
||||
{# section header #}
|
||||
<h2 id='table-of-contents' class='page-header'>
|
||||
Table of Contents
|
||||
<a href='javascript:void(0)' class='permalink toggle-link small' data-target='toc-category' title='Toggle all'>
|
||||
<i class='bi bi-list-nested'></i>
|
||||
</a>
|
||||
{{ macros.permalink("table-of-contents") }}
|
||||
</h2>
|
||||
|
||||
{# table of contents #}
|
||||
{% for category in book.categories.values() %}
|
||||
<details class='toc-category'>
|
||||
{# category #}
|
||||
<summary>{{ macros.maybe_spoilered_link(category) }}</summary>
|
||||
|
||||
{# list of entries in the category #}
|
||||
<ul>
|
||||
{% for entry in category.entries %}
|
||||
<li>{{ macros.maybe_spoilered_link(entry) }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% endfor %}
|
20
doc/templates/welcome.html.jinja
vendored
Normal file
20
doc/templates/welcome.html.jinja
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
<div class="container" style="margin-top: 3em;">
|
||||
<blockquote>
|
||||
<h1>This is the online version of the {{ mod_name }} documentation.</h1>
|
||||
<p>
|
||||
Embedded images and patterns are included, but not crafting recipes or items. There's an in-game book for those.
|
||||
</p>
|
||||
|
||||
{% if is_bleeding_edge %}
|
||||
<p>
|
||||
Additionally, this is built from the latest code on GitHub.
|
||||
It may describe <b>newer</b> features that you may not necessarily have, even on the latest CurseForge version!
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<p>
|
||||
<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>
|
||||
</blockquote>
|
||||
</div>
|
Loading…
Reference in a new issue