mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-11 12:33:53 -06:00
make_demo.py records a second variant: the full-screen interface at the 640x480 screen of an R36S, on an ArkOS root made with bwrap, started as the Tools menu launcher starts it (RETROBIOS_PAD=gptokeyb), driven by the keys gptokeyb sends for the buttons. demo.json holds one entry per variant with its alt text. No Windows variant: under pwsh on Linux the installer's summary gives --dest /mnt/e/bios back. The quick install box of Get your files gets an "On the device" tab with the handheld recording, the terminal recording under the others but Windows, and, under the text of each computer tab, a drawing of opening the terminal and pasting (sitesteps.py, inline SVG in the theme's colours, title and desc). The terminal steps name the physical action.
270 lines
10 KiB
Python
270 lines
10 KiB
Python
"""What the install box of Get your files shows under each tab.
|
|
|
|
Two things per tab, both drawn from data:
|
|
|
|
- The steps that differ by system, opening a terminal and pasting the line,
|
|
as an inline SVG: one card per step, a pictogram and a short caption,
|
|
drawn with sitediagrams' primitives so it takes the theme's colours in
|
|
light and dark. The text steps above it (platforms/_guides.yml,
|
|
terminals) stay the accessible source. The SVG says the same in its title
|
|
and desc. The pictograms are generic shapes (a menu, a search field, a
|
|
window, keys, a mouse, the power symbol), no vendor's interface or logo.
|
|
- The installer demo recorded for that place (scripts/make_demo.py): the
|
|
full-screen interface on a handheld under "On the device", the terminal
|
|
recording under the others but Windows. No Windows recording is
|
|
published (make_demo.py says why), and a curl line recorded in a Linux
|
|
terminal under the PowerShell command would contradict it.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from xml.sax.saxutils import escape
|
|
|
|
import sitemarkup as markup
|
|
from sitediagrams import (
|
|
FONT,
|
|
MONO_STYLE,
|
|
NARROW,
|
|
STROKE,
|
|
TEXT_SIZE,
|
|
WIDE,
|
|
_badge,
|
|
_badge_side,
|
|
_line,
|
|
_n,
|
|
_rect,
|
|
_text,
|
|
wrap,
|
|
)
|
|
|
|
KEY_SIZE = 11.5
|
|
ICON_H = 44.0
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Step:
|
|
"""One card: a pictogram (*icon* and its *word*, the text it shows:
|
|
keys joined by "+", the word typed in a search field) and a caption."""
|
|
|
|
icon: str
|
|
caption: str
|
|
word: str = ""
|
|
|
|
|
|
STEPS: dict[str, tuple[str, tuple[Step, ...]]] = {
|
|
"windows": ("Windows: open PowerShell and paste the line", (
|
|
Step("menu", "Open the Start menu"),
|
|
Step("search", "Type PowerShell and press Enter", "PowerShell"),
|
|
Step("copy", "Click Copy, the button by the line"),
|
|
Step("mouse", "Right-click in the window to paste"),
|
|
Step("keys", "Press Enter", "Enter"),
|
|
)),
|
|
"macos": ("macOS: open Terminal and paste the line", (
|
|
Step("keys", "Press Cmd+Space", "Cmd+Space"),
|
|
Step("search", "Type Terminal and press Return", "Terminal"),
|
|
Step("copy", "Click Copy, the button by the line"),
|
|
Step("keys", "Press Cmd+V to paste", "Cmd+V"),
|
|
Step("keys", "Press Return", "Return"),
|
|
)),
|
|
"linux": ("Linux: open a terminal and paste the line", (
|
|
Step("keys", "Open a terminal, often Ctrl+Alt+T", "Ctrl+Alt+T"),
|
|
Step("copy", "Click Copy, the button by the line"),
|
|
Step("keys", "Press Ctrl+Shift+V to paste", "Ctrl+Shift+V"),
|
|
Step("keys", "Press Enter", "Enter"),
|
|
)),
|
|
"steamdeck": ("Steam Deck: open Konsole and paste the line", (
|
|
Step("power", "Hold power, pick Switch to Desktop"),
|
|
Step("window", "Open Konsole"),
|
|
Step("copy", "Click Copy, the button by the line"),
|
|
Step("keys", "Press Ctrl+Shift+V to paste", "Ctrl+Shift+V"),
|
|
Step("keys", "Press Enter", "Enter"),
|
|
)),
|
|
}
|
|
|
|
# The recording each tab shows (docs_assets/demo/demo.json keys), "" for none.
|
|
TAB_DEMOS = {"device": "handheld", "windows": ""}
|
|
DEFAULT_DEMO = "default"
|
|
|
|
|
|
def demo_variant(tab: str) -> str:
|
|
return TAB_DEMOS.get(tab, DEFAULT_DEMO)
|
|
|
|
|
|
def description(tab: str) -> str:
|
|
"""The steps in words: the SVG's desc."""
|
|
return " ".join(f"{index}. {step.caption}." for index, step in
|
|
enumerate(STEPS[tab][1], start=1))
|
|
|
|
|
|
# Pictograms, drawn in a box *w* wide and ICON_H high from (x, y).
|
|
|
|
|
|
def _stroke(d: str, cls: str = "rb-d-line", width: float = 1.5) -> str:
|
|
return f'<path d="{d}" fill="none" stroke="{STROKE}" stroke-width="{_n(width)}" class="{cls}"/>'
|
|
|
|
|
|
def _key_width(label: str) -> float:
|
|
return max(20.0, len(label) * KEY_SIZE * 0.62 + 10)
|
|
|
|
|
|
def _keys(x: float, y: float, w: float, word: str) -> list[str]:
|
|
labels = word.split("+")
|
|
total = sum(_key_width(label) for label in labels) + 4 * (len(labels) - 1)
|
|
left = x + (w - total) / 2
|
|
out = []
|
|
top = y + ICON_H / 2 - 12
|
|
for label in labels:
|
|
width = _key_width(label)
|
|
out.append(_rect(left, top, width, 24, accent=True))
|
|
out.append(
|
|
f'<text x="{_n(left + width / 2)}" y="{_n(top + 16.5)}" font-size="{_n(KEY_SIZE)}" '
|
|
f'text-anchor="middle" fill="currentColor" style="{MONO_STYLE}">{escape(label)}</text>')
|
|
left += width + 4
|
|
return out
|
|
|
|
|
|
def _menu(x: float, y: float, w: float) -> list[str]:
|
|
cx, top = x + w / 2, y + 4
|
|
out = [_rect(cx - 22, top, 44, 36)]
|
|
out += [_stroke(f"M{_n(cx - 14)} {_n(top + 10 + 8 * i)} H{_n(cx + 14)}") for i in range(3)]
|
|
return out
|
|
|
|
|
|
def _search(x: float, y: float, w: float, word: str) -> list[str]:
|
|
width = min(w - 4, len(word) * KEY_SIZE * 0.62 + 34)
|
|
left, top = x + (w - width) / 2, y + ICON_H / 2 - 12
|
|
return [
|
|
_rect(left, top, width, 24, accent=True),
|
|
f'<circle cx="{_n(left + 11)}" cy="{_n(top + 11)}" r="5" fill="none" stroke="{STROKE}" '
|
|
'stroke-width="1.5" class="rb-d-line"/>',
|
|
_stroke(f"M{_n(left + 15)} {_n(top + 15)} L{_n(left + 19)} {_n(top + 19)}"),
|
|
f'<text x="{_n(left + 25)}" y="{_n(top + 16.5)}" font-size="{_n(KEY_SIZE)}" '
|
|
f'fill="currentColor" style="{MONO_STYLE}">{escape(word)}</text>',
|
|
]
|
|
|
|
|
|
def _window(x: float, y: float, w: float) -> list[str]:
|
|
cx, top = x + w / 2, y + 2
|
|
return [
|
|
_rect(cx - 32, top, 64, 40),
|
|
_stroke(f"M{_n(cx - 32)} {_n(top + 9)} H{_n(cx + 32)}"),
|
|
f'<text x="{_n(cx - 24)}" y="{_n(top + 28)}" font-size="{_n(KEY_SIZE)}" '
|
|
f'fill="currentColor" style="{MONO_STYLE}">>_</text>',
|
|
]
|
|
|
|
|
|
def _copy(x: float, y: float, w: float) -> list[str]:
|
|
cx, top = x + w / 2, y + 6
|
|
return [_rect(cx - 12, top, 18, 24), _rect(cx - 6, top + 7, 18, 24, accent=True)]
|
|
|
|
|
|
def _mouse(x: float, y: float, w: float) -> list[str]:
|
|
cx, top = x + w / 2, y + 2
|
|
return [
|
|
f'<rect x="{_n(cx - 13)}" y="{_n(top)}" width="26" height="40" rx="12" fill="none" '
|
|
f'stroke="{STROKE}" stroke-width="1.5" class="rb-d-line"/>',
|
|
# The right button, the one to press.
|
|
f'<path d="M{_n(cx + 1)} {_n(top + 1)} A12 12 0 0 1 {_n(cx + 12)} {_n(top + 13)} '
|
|
f'L{_n(cx + 1)} {_n(top + 13)} Z" fill="currentColor" class="rb-d-num"/>',
|
|
_stroke(f"M{_n(cx)} {_n(top)} V{_n(top + 14)} M{_n(cx - 13)} {_n(top + 14)} "
|
|
f"H{_n(cx + 13)}"),
|
|
]
|
|
|
|
|
|
def _power(x: float, y: float, w: float) -> list[str]:
|
|
cx, cy = x + w / 2, y + ICON_H / 2 + 2
|
|
return [
|
|
_stroke(f"M{_n(cx - 9)} {_n(cy - 11)} A14 14 0 1 0 {_n(cx + 9)} {_n(cy - 11)}", width=2.5),
|
|
_stroke(f"M{_n(cx)} {_n(cy - 18)} V{_n(cy - 2)}", width=2.5),
|
|
]
|
|
|
|
|
|
def _icon(step: Step, x: float, y: float, w: float) -> list[str]:
|
|
if step.icon == "keys":
|
|
return _keys(x, y, w, step.word)
|
|
if step.icon == "search":
|
|
return _search(x, y, w, step.word)
|
|
return {"menu": _menu, "window": _window, "copy": _copy, "mouse": _mouse,
|
|
"power": _power}[step.icon](x, y, w)
|
|
|
|
|
|
def _wide(steps: tuple[Step, ...]) -> tuple[float, float, list[str]]:
|
|
"""The cards in a row, joined by arrows."""
|
|
gap = 14.0
|
|
width = (WIDE - gap * (len(steps) - 1) - 2) / len(steps)
|
|
captions = [wrap(step.caption, width - 12, TEXT_SIZE) for step in steps]
|
|
height = 34 + ICON_H + 10 + max(len(lines) for lines in captions) * 18 + 8
|
|
out: list[str] = []
|
|
for index, (step, lines) in enumerate(zip(steps, captions)):
|
|
x = 1 + index * (width + gap)
|
|
out.append(_rect(x, 1, width, height))
|
|
out += _badge(x + 8, 8, str(index + 1))
|
|
out += _icon(step, x + 4, 30, width - 8)
|
|
y = 30 + ICON_H + 24
|
|
for line in lines:
|
|
out.append(_text(x + width / 2, y, line, anchor="middle"))
|
|
y += 18
|
|
if index < len(steps) - 1:
|
|
mid = 1 + height / 2
|
|
out += _line([(x + width + 1, mid), (x + width + gap - 1, mid)])
|
|
return WIDE, height + 2, out
|
|
|
|
|
|
def _narrow(steps: tuple[Step, ...]) -> tuple[float, float, list[str]]:
|
|
"""One card a row: number, pictogram, caption."""
|
|
icon_w, row_gap = 112.0, 8.0
|
|
text_x = 8 + _badge_side("9") + 6 + icon_w + 8
|
|
out: list[str] = []
|
|
y = 1.0
|
|
for index, step in enumerate(steps):
|
|
lines = wrap(step.caption, NARROW - text_x - 8, TEXT_SIZE)
|
|
height = max(ICON_H + 12, len(lines) * 18 + 16)
|
|
out.append(_rect(1, y, NARROW - 2, height))
|
|
out += _badge(8, y + height / 2 - 10, str(index + 1))
|
|
out += _icon(step, 8 + _badge_side("9") + 6, y + (height - ICON_H) / 2, icon_w)
|
|
ty = y + height / 2 - (len(lines) - 1) * 9 + 5
|
|
for line in lines:
|
|
out.append(_text(text_x, ty, line))
|
|
ty += 18
|
|
y += height + row_gap
|
|
return NARROW, y - row_gap + 1, out
|
|
|
|
|
|
def _svg(tab: str, layout: str) -> str:
|
|
title, steps = STEPS[tab]
|
|
width, height, body = (_wide if layout == "wide" else _narrow)(steps)
|
|
base = f"rb-steps-{tab}-{layout}"
|
|
return (
|
|
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {_n(width)} {_n(height)}" '
|
|
f'width="{_n(width)}" height="{_n(height)}" role="img" '
|
|
f'aria-labelledby="{base}-t {base}-d" font-family="{FONT}" '
|
|
f'class="rb-diagram rb-diagram-{layout}">'
|
|
f'<title id="{base}-t">{escape(title)}</title>'
|
|
f'<desc id="{base}-d">{escape(description(tab))}</desc>'
|
|
+ "".join(body) + "</svg>"
|
|
)
|
|
|
|
|
|
def steps_figure(tab: str) -> list[str]:
|
|
"""The figure of a tab's steps, both layouts, or nothing for a tab with
|
|
no drawing."""
|
|
if tab not in STEPS:
|
|
return []
|
|
return [f'<figure class="rb-figure rb-steps" {markup.search_exclude()}>',
|
|
_svg(tab, "wide") + _svg(tab, "narrow"), "</figure>", ""]
|
|
|
|
|
|
def tab_demo(tab: str) -> list[str]:
|
|
"""The recording a tab shows, as generate_site writes the component."""
|
|
from generate_site import install_demo # noqa: PLC0415
|
|
|
|
variant = demo_variant(tab)
|
|
return install_demo(variant, tab) if variant else []
|
|
|
|
|
|
def install_box(tabs: tuple[str, ...]) -> tuple[dict[str, list[str]], dict[str, list[str]]]:
|
|
"""What terminal_tabs puts under each tab's steps and under its command."""
|
|
return ({tab: steps_figure(tab) for tab in tabs},
|
|
{tab: tab_demo(tab) for tab in tabs})
|