File size: 2,287 Bytes
bf3b56a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
"""SEO page components: hero, FAQ, related Spaces, disclaimer, footer."""

import html as _html

from .config import SpaceConfig, COLLECTION_TITLE, COLLECTION_URL


def hero_html(config: SpaceConfig) -> str:
    """Single H1 + one-sentence outcome promise. The only H1 on the page."""
    return (
        '<div class="lf-page">'
        f"<h1>{_html.escape(config.h1)}</h1>"
        f'<p class="lf-tagline">{_html.escape(config.tagline)}</p>'
        "</div>"
    )


def section_html(heading: str, body_html: str, level: int = 2) -> str:
    tag = f"h{level}"
    return (
        '<div class="lf-page">'
        f"<{tag}>{_html.escape(heading)}</{tag}>{body_html}</div>"
    )


def faq_html(faqs: list[tuple[str, str]]) -> str:
    """Accessible <details> FAQ. faqs = [(question, answer_html)]."""
    items = "".join(
        f"<details><summary>{_html.escape(q)}</summary><p>{a}</p></details>"
        for q, a in faqs
    )
    return (
        '<div class="lf-page"><h2>Frequently Asked Questions</h2>'
        f'<div class="lf-faq">{items}</div></div>'
    )


def related_html(config: SpaceConfig) -> str:
    if not config.related:
        return ""
    cards = "".join(
        f'<a class="lf-related-card" href="{r.url}">'
        f'<div class="lf-related-title">{_html.escape(r.title)}</div>'
        f'<div class="lf-related-desc">{_html.escape(r.description)}</div></a>'
        for r in config.related
    )
    return (
        '<div class="lf-page"><h2>More Free D&amp;D Tools</h2>'
        f'<div class="lf-related">{cards}</div>'
        f'<p>Browse the full collection: <a href="{COLLECTION_URL}">'
        f"{_html.escape(COLLECTION_TITLE)}</a></p></div>"
    )


def disclaimer_html() -> str:
    return (
        '<p class="lf-disclaimer">'
        "This is an unofficial fan tool. Dungeons &amp; Dragons and D&amp;D are "
        "trademarks of Wizards of the Coast LLC. This tool is not affiliated with, "
        "endorsed, or sponsored by Wizards of the Coast. Portions of the results are "
        "generated by AI: review everything with your DM and adapt it to your table "
        "before play. This tool is built and maintained by "
        '<a href="https://loreify.ai">Loreify</a>, an AI session-notes app for D&amp;D '
        "campaigns.</p>"
    )