Backport s3 style

main
Peder Bergebakken Sundt 2022-10-14 00:50:52 +02:00
parent c31b17bb8e
commit 9d15daf227
13 changed files with 763 additions and 1 deletions

1
.envrc
View File

@ -1,3 +1,4 @@
#!/usr/bin/env bash
# this file is to be loaded with 'direnv'
use nix
export CARDS_ACCESS_TOKEN="hunter2"

32
Makefile Normal file
View File

@ -0,0 +1,32 @@
STYLES := $(patsubst templates/%/render.sh,%,$(wildcard templates/*/render.sh))
help:
@printf "make %s\n" \
render-styles \
$(patsubst %,render-style-%-stdout,$(STYLES)) \
$(patsubst %,render-example-card-%-stdout,$(STYLES)) \
$(patsubst %,render-style-%-live,$(STYLES)) \
$(patsubst %,render-example-card-%-live,$(STYLES)) \
$(patsubst %,render-style-%,$(STYLES)) \
$(patsubst %,render-example-card-%,$(STYLES))
render-styles: $(patsubst %,render-style-%,$(STYLES))
render-style-%-stdout: templates/%/render.sh
./"$<" | bat --language html --plain --paging never
render-example-card-%-stdout: templates/%/example.sh
./"$<" | bat --language html --plain --paging never
render-style-%-live:
fd --type f . templates/$* | entr make render-style-$*-stdout
render-example-card-%-live:
fd --type f . templates/$* | entr make render-example-card-$*-stdout
render-style-%:
make --quiet render-style-$*-stdout > pvv/styles/$*.html
render-example-card-%:
./"$<"

View File

@ -10,7 +10,7 @@ mkdir -p pvv/{cards,styles}
./api.py get-styles | hjson -c | jq '.[].id' --raw-output |
while read style_id; do
(set -x
./api.py get-style $style_id > pvv/styles/$style_id.xml
./api.py get-style $style_id > pvv/styles/$style_id.html
) &
done

View File

@ -5,6 +5,7 @@ pkgs.mkShell {
hjson # to deal with broken json
j2cli
sass
libxslt
python3Packages.rich
python3Packages.httpx
python3Packages.typer

7
templates/6/example.sh Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" &> /dev/null && pwd)"
xsltproc \
<(./render.sh) \
test_card.xml

9
templates/6/render.sh Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" &> /dev/null && pwd)"
css_data="$(sass --style expanded style.scss)" \
jinja_data="$(cat style.html.j2)" \
js_data="$(cat style.js)" \
card_header="$(j2 templates/card_header.html.j2)" \
j2 templates/style.xsl.j2

203
templates/6/style.html.j2 Normal file
View File

@ -0,0 +1,203 @@
{% set xml_image_not_done = [1] %}{# why must nunjucks suck so much? #}
{% macro figure_layer(figure) %}
{% set figure_style %}{% filter cull_whitespace %}
{% if figure.opacity!=1 %}
opacity: {{ figure.opacity }};
{% endif %}
{% if figure.color!=1 %}
color: {{ figure.color }};
{% endif %}
transform:
{% if figure.offset and figure.offset|any %}
translate({{ figure.offset[0]*100 }}%, {{ figure.offset[1]*100 }}%)
{% endif %}
{% if figure.rotation %}
rotate({{ figure.rotation }}deg)
{% endif %}
{% if figure.flip_x %}
scaleX(-1)
{% endif %}
{% if figure.flip_y %}
scaleY(-1)
{% endif %}
{% if figure.scale and figure.scale!=1 %}
scale({{figure.scale | string | replace("[", "") | replace("]", "") }})
{% endif %}
;
{% endfilter %}{% endset %}
{% if figure.source == "xml" %}
{% set figure_name = xml.image if xml.image | startswith("http") else xml.image | split(":") | tail | join(":") %}
{% set figure_source = "url" if xml.image | startswith("http") else xml.image | split(":") | first %}
{% if xml_image_not_done.pop() %}{% endif %}
{% else %}
{% set figure_name = figure.name %}
{% set figure_source = figure.source %}
{% endif %}
<div class="layer" style="{{ figure_style }}">
{% if figure_source == "material-icons" %}
{# https://material.io/icons/ #}
<i class="material-icons figure">{{ figure_name }}</i>
{% elif figure_source == "mdi" %}
{# https://materialdesignicons.com/ #}
<i class="mdi mdi-{{ figure_name }}"></i>
{% elif figure_source == "fa" %}
{# http://fontawesome.io/icons/ #}
<i class="fa fa-{{ figure_name }}"></i>
{% elif figure_source == "lnr" %}
{# https://linearicons.com/free #}
<span class="lnr lnr-{{ figure_name }}"></span>
{% elif figure_source == "oi" %}
{# https://useiconic.com/open #}
<span class="oi" data-glyph="{{ figure_name }}"></span>
{% elif figure_source == "svg" %}
{# pvv.ntnu.no/~pederbs/cards/svg #}
{% if figure.color == true %}
<img src="https://pvv.ntnu.no/~pederbs/cards/svg/data/{{ figure_name }}">
{% elif figure.color %}
<img src="https://pvv.ntnu.no/~pederbs/cards/svg/data/{{ figure_name }}" class="colored" style="--figure-color: {{ figure.color }}">
{% else %}
<img src="https://pvv.ntnu.no/~pederbs/cards/svg/data/{{ figure_name }}" class="colored">
{% endif %}
{% elif figure_source in ["img", "emoji"] %}
{# pvv.ntnu.no/~pederbs/cards/img #}
{# /cards/emoji #}
{% if figure.color == true %}
<img src="https://pvv.ntnu.no/~pederbs/cards/{{ figure_source }}/data/{{ figure_name }}" class="colored">
{% elif figure.color %}
<img src="https://pvv.ntnu.no/~pederbs/cards/{{ figure_source }}/data/{{ figure_name }}" class="colored" style="--figure-color: {{ figure.color }}">
{% else %}
<img src="https://pvv.ntnu.no/~pederbs/cards/{{ figure_source }}/data/{{ figure_name }}">
{% endif %}
{% elif figure_source == "url" %}
{% if figure.color == true %}
<img src="{{ figure.name }}" class="colored">
{% elif figure.color %}
<img src="{{ figure.name }}" class="colored" style="--figure-color: {{ figure.color }}">
{% else %}
<img src="{{ figure.name }}">
{% endif %}
{% endif %}
</div>
{% endmacro %}
<div class="fjomp_card{{" " + yaml.style if yaml.style}}">
<header>
{{ yaml.name or xml.name }}
</header>
{% if xml.symbol %}
<div class="symbol">
{{ xml.symbol }}
</div>
{% endif %}
<figure>
{% for figure in yaml.figures or [] %}
{{ figure_layer(figure) }}
{% endfor %}
{% if xml.image and xml_image_not_done | length %}
{{ figure_layer({
'name': xml.image if xml.image | startswith("http") else xml.image | split(":") | tail | join(":"),
'source': "url" if xml.image | startswith("http") else xml.image | split(":") | first
}) }}
{% endif %}
</figure>
<div class="costbar">
{% for playcosts in [xml.playcost, yaml.playcost] %}
{% if playcosts %}
{% for playcost in (playcosts.split(",") if playcosts | is_string else playcosts ) %}
{% if playcost | trim | split(" ") | first | is_number %}
<section>
<big>{{ playcost | trim | split(" ") | first }}</big><br>
<small>{{ playcost | trim | split(" ") | tail | join(" ") }}</small>
</section>
{% elif playcost | trim | split(" ") | length == 1 %}
<section>
<big>{{ 1 }}</big><br>
<small>{{ playcost }}</small>
</section>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% for label, data in [
["Artistic Value", xml.artistic_value],
["Difficulty", xml.difficulty or yaml.difficulty],
["Power", xml.power or yaml.power],
["Range", xml.range or yaml.range],
["Duration", xml.duration or yaml.duration],
["CP", xml.cp or yaml.cp]
] %}
{% if data %}
<section>
<big>
{% for part in data | split("+") %}
{% if "death" not in part %}
{{"+" if not loop.first }}
{{ part }}
{% endif %}
{% endfor %}
</big><br>
{# <big>{{ data }}</big><br> #}
<small>{{ label }}</small>
</section>
{% endif %}
{% endfor %}
</div>
<div class="description">
<ul>
{% for playcosts in [xml.playcost, yaml.playcost] %}
{% if playcosts %}
{% for playcost in (playcosts.split(",") if playcosts | is_string else playcosts ) %}
{% if playcost | trim | split(" ") | first | is_number %}
{% elif playcost | trim | split(" ") | length == 1 %}
{% else %}
<li>{{ playcost }}</li>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
</ul>
{% if yaml.flavor %}
<center><i>"{{ yaml.flavor }}"</i></center>
{% endif %}
{% if yaml.description %}
<div class="markdown">{{ yaml.description | markdown | safe }}</div>
{% endif %}
{% if xml.description %}
<div class="markdown">{{ xml.description | markdown | safe }}</div>
{% endif %}
{% if yaml.steps %}
<ul>
{% for step in yaml.steps %}
<li>{{ step }}</li>
{% endfor %}
</ul>
{% endif %}
<div class="bottom" style="text-align: center; font-size:1.6em;">
{% for component in xml.components %}
{% if component["@db_entry"] | endswith("Mastery") %}
&#x274F; &#x274F; &#x274F; &#x274F; &#x274F;
&#x274F; &#x274F; &#x274F; &#x274F; &#x274F; <br>
{% endif %}
{% endfor %}
</div>
</div>
</div>
{#
{% for item in xml.components %}
{{item["@x"]}},
{{item["@y"]}},
{{item["@db_entry"]}}<br>
{% endfor %}
#}

74
templates/6/style.js Normal file
View File

@ -0,0 +1,74 @@
var md = window.markdownit();
var context = {
'xml' : {},
'yaml' : jsyaml.load(document.getElementById("yaml_data").innerHTML),
'alert' : alert,
};
// read xml data
var xml_data_items = document.getElementsByClassName("xml_data")
for (var i=0; i < xml_data_items.length; i++) {
var key = xml_data_items[i].id.substr(9);
var val = xml_data_items[i].innerHTML;
context.xml[key] = val;
}
var xml_component_items = document.getElementsByClassName("xml_component")
context.xml["components"] = [];
for (var i=0; i < xml_component_items.length/3; i++) {
context.xml["components"].push({
'@x' : document.getElementById("xml_component_" + (i+1) + "_x").innerHTML,
'@y' : document.getElementById("xml_component_" + (i+1) + "_y").innerHTML,
'@db_entry': document.getElementById("xml_component_" + (i+1) + "_db_entry").innerHTML,
});
}
var env = new nunjucks.Environment([], {
autoescape: true,
trimBlocks: true,
lstripBlocks: true,
});
env.addFilter('markdown', function(str) {
return md.render(str);
});
env.addFilter('cull_whitespace', function(str) {
return str.split(/\s+/).join(' ').trim();
});
env.addFilter('any', function(iterable) {
for (var index = 0; index < iterable.length; index++) {
if (iterable[index]) return true;
}
return false;
});
env.addFilter('all', function(iterable) {
for (var index = 0; index < iterable.length; index++) {
if (!iterable[index]) return false;
}
return true;
});
env.addFilter('startswith', function(string, match) {
return string.slice(0, match.length) === match;
});
env.addFilter('endswith', function(string, match) {
return string.endsWith(match);
});
env.addFilter('tail', function(sequence) {
return sequence.slice(1);
});
env.addFilter('split', function(string, delimiter) {
return string.split(delimiter);
});
env.addFilter('is_number', function(string) {
return !isNaN(string);
});
env.addFilter('is_string', function(string) {
return String(string) === string;
});
try {
rendered = env.renderString(jinja_template, context);
} catch(err) {
rendered = "<pre style=\"font-size:0.8em; width:100%; border-radius:5px; padding:2mm; box-sizing: border-box; border: 2px solid black; white-space: normal; background-color:#ff8888;\">" + err + "</pre>";
}
//console.log(rendered);
document.write(rendered);

197
templates/6/style.scss Normal file
View File

@ -0,0 +1,197 @@
.fjomp_card {
--figure-size: 0.9in;
--figure-size: 1.35in;
--figure-size: 1.5in;
--color-border: MidnightBlue;
--color-bg-figure: white;
--color-bg: PaleTurquoise; // PaleTurquoise;
--color-bg-body: LightCyan; // PaleTurquoise;
--color-bg-frame: PaleTurquoise; // LightSkyBlue;
--color-bg-costbar: SandyBrown; // DarkOrange;
--color-text-title: MidnightBlue;
--color-text-body: black;
--color-text-costbar: white;
--color-shadow-costbar: 0.08em 0 0.05em Maroon,
-0.08em 0 0.05em Maroon,
0 0.08em 0.05em Maroon,
0 -0.08em 0.05em Maroon;
--figure-color: #555;
width: 2.5in;
height: 3.5in;
box-sizing: border-box;
border-radius: 0mm;
border-width: 0.3mm;
border-color: var(--color-border);
border-style: solid;
background-color: var(--color-bg);
overflow: hidden;
font-size: 2.5mm;
font-family: sans-serif;
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: 1.9em var(--figure-size) 2.3em auto;
grid-template-areas:
"symbol title"
"figure figure"
"costbar costbar"
"description description";
header {
font-family: montserrat, sans-serif;
font-style: normal;
font-weight: 200;
grid-area: title;
font-size: 1.8em;
line-height: 1em;
margin-left: -1em;
border-radius: 1mm 1mm 0 0;
text-align: center;
background-color: var(--color-bg-frame);
border-radius: 0;
color: var(--color-text-title);
}
.symbol {
grid-area: symbol;
text-align: center;
background-color: var(--color-bg-frame);
color: var(--color-text-title);
padding-top: 0.6mm;
padding-left: 0.8mm;
white-space: nowrap;
}
figure {
grid-area: figure;
background-color: var(--color-bg-figure);
overflow: hidden;
position: relative;
border-radius: 0;
margin: 0;
.layer {
color: var(--figure-color);
position: absolute;
top:0;right:0;left:0;bottom:0;
height: var(--figure-size);
width: var(--figure-size);
margin: auto;
text-align: center;
>* {
width: 100%;
height: 100%;
}
img {
width: calc(var(--figure-size));
height: calc(var(--figure-size));
object-fit: contain;
}
img.colored {
transform: translateY(-10000px);
filter: drop-shadow(0px 10000px var(--figure-color));
}
.mdi,
.fa,
.oi,
.material-icons.figure {
font-size: calc(var(--figure-size) * 0.9);
line-height: var(--figure-size);
}
.lnr {
font-size: calc(var(--figure-size) * 0.8);
line-height: calc(var(--figure-size) * 0.95);
}
}
}
.playcosts {
grid-area: playcosts;
border-left: solid 0.3mm #ccc;
background-color: var(--color-bg-frame);
color: #444 ;
padding: 0.5mm;
ul {
margin:0;
list-style-type: none;
padding: 0;
li {
}
}
}
.costbar {
grid-area: costbar;
border-radius: 0;
background-color: var(--color-bg-costbar);
color: var(--color-text-costbar);
text-shadow: var(--color-shadow-costbar);
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
justify-content: center;
text-align: center;
font-size: 1.1em;
section{
font-size: 0.8em;
margin-left: 0.8mm;
margin-right: 0.8mm;
line-height: 0.9em;
}
}
.description {
grid-area: description;
margin: 1mm;
border-radius: 1mm;
background-color: var(--color-bg-body);
color: var(--color-text-body);
font-size: 0.9em;
position: relative;
.markdown {
text-align: center;
table {
margin-top:-0.3em;
margin-left:auto;
margin-right:auto;
th, td {
font-size: 2.5mm; /* user agent style override */
}
}
p + table {
margin-top:-1em;
}
li {
text-align: left;
}
}
ul, ol {
padding-left: 1.5em;
li {
}
}
.bottom {
position: absolute;
bottom:0;
left:0;
right:0;
}
}
}
.fjomp_card.item,
.fjomp_card.figure,
.fjomp_card.repertoire {
--figure-size: 2.4in;
}

View File

@ -0,0 +1,49 @@
<style>
@page
{
size: auto;
margin: 0.5cm;
}
body {
background-color: white;
margin: 0;
padding: 0;
-webkit-print-color-adjust: exact;
}
.card_wrapper {
width: 2.5in;
height: 3.5in;
float: left;
border: 1px solid transparent;
page-break-inside: avoid;
position: relative;
}
.card_cover,
iframe {
position: absolute;
top:0;
left:0;
width:2.5in;
height:3.5in;
margin: none;
padding: none;
border: none;
}
.card_cover {
cursor: pointer;
}
</style>
<div>
{% for cardname in cards %}
<div class="card_wrapper">
<iframe src="{{ cardname }}" scrolling=no></iframe>
<div class="card_cover" onclick='window.location = {{cardname|tojson}}'></div>
</div>
{% endfor %}
</div>

View File

@ -0,0 +1,15 @@
{% set async = "media=\"none\" onload=\"if(media!='all')media='all'\"" -%}
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/3.13.1/js-yaml.min.js"></script>
<script type="text/javascript" src="https://mozilla.github.io/nunjucks/files/nunjucks.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/9.1.0/markdown-it.min.js"></script>
<!--fonts, loaded asyncronously-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/open-iconic/1.1.1/font/css/open-iconic.min.css" {{async}}/>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" {{async}}/>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" {{async}}/>
<link rel="stylesheet" href="https://cdn.materialdesignicons.com/2.0.46/css/materialdesignicons.min.css" {{async}}/>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" {{async}}/>
<link rel="stylesheet" href="https://cdn.linearicons.com/free/1.0.0/icon-font.min.css" {{async}}/>
<link rel="stylesheet" href='https://fonts.googleapis.com/css?family=Montserrat'>

View File

@ -0,0 +1,111 @@
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="/*">
{% set newline = "<xsl:text disable-output-escaping='yes'>\n</xsl:text>" %}
{% set gt = "<xsl:text disable-output-escaping='yes'>&gt;</xsl:text>" %}
{% set lt = "<xsl:text disable-output-escaping='yes'>&lt;</xsl:text>" %}
{% set amp = "<xsl:text disable-output-escaping='yes'>&amp;</xsl:text>" %}
<html>
{% filter replace(">", ">foobarhuehuehue")
| replace("<", lt)
| replace(">foobarhuehuehue", gt)
| replace("\n", newline)
| replace("</xsl:text><xsl:text disable-output-escaping='yes'>", "") %}
<head>
{{ card_header }}
<title>Status Card</title>
</head>
{% endfilter %}
<body content_type="html5" style="margin:0px;">
<style type="text/css">
{{ css_data.strip() }}
</style>
{{ newline*2 }}
<script type="text/html" id="yaml_data">
<xsl:value-of select="yaml_data"/>
</script>
{{ newline*2 }}
{# just name all the xml fields of interest here #}
{% for value in [
"name",
"description",
"image",
"cp",
"range",
"power",
"symbol",
"difficulty",
"duration",
"artistic_value",
"playcost"] %}
{{lt}}script type="text/html" class="xml_data" id="xml_data_{{ value }}"{{gt}}<xsl:value-of select="{{ value }}"/>{{lt}}/script{{gt}}
{{ newline }}
{% endfor %}
{{ newline }}
<xsl:for-each select="component">
{{lt}}script type="text/html" class="xml_component" id="xml_component{#
#}_<xsl:value-of select="position()"/>_{#
#}x"{{gt}}
<xsl:value-of select="@x"/>
{{lt}}/script{{gt}}
{{ newline }}
{{lt}}script type="text/html" class="xml_component" id="xml_component{#
#}_<xsl:value-of select="position()"/>_{#
#}y"{{gt}}
<xsl:value-of select="@y"/>
{{lt}}/script{{gt}}
{{ newline }}
{{lt}}script type="text/html" class="xml_component" id="xml_component{#
#}_<xsl:value-of select="position()"/>_{#
#}db_entry"{{gt}}
<xsl:value-of select="@db_entry"/>
{{lt}}/script{{gt}}
{{ newline }}
</xsl:for-each>
{{ newline*2 }}
<script type="text/javascript">
var jinja_template =
{{ jinja_data.strip() | tojson | replace("\\n", "\\n\" +\n\"") }};
</script>
{{ newline*2 }}
<script type="text/javascript">
{{ js_data.strip()
| replace(">", ">foobarhuehuehue")
| replace("&", "&spismegdinnisse")
| replace("<", lt)
| replace(">foobarhuehuehue", gt)
| replace("&spismegdinnisse", amp) }}
</script>
{{ newline*2 }}
</body>
{{ newline }}
</html>
</xsl:template>
</xsl:stylesheet>

63
templates/6/test_card.xml Normal file
View File

@ -0,0 +1,63 @@
<ability_card><yaml_data>
name: Hit enemy
#style: item
tags:
- combat
playcost:
- 'Element: fire'
- 'BODY+5 POWER'
#flavor: Enemies sure are squishy
#description: Perform an attack
#steps:
#- Do A
#- Do A
#- Do A
#- Then B
figures:
- name: piuy/chicken
source: img
scale: 2
notes: ''
</yaml_data>
<name>Mega Fireball</name>
<description>
| | |
| ----:|----|
| asd | asd |
| asd | asd |
# asd
Attack an enemy in range 5.
Roll MAG hit dice. Deal that amount of FIRE damage to the target.</description>
<image>https://freepngimg.com/download/fireball/27487-2-fireball-clipart.png</image>
<cp>18</cp>
<range>4</range>
<power>MAG + (0 to 2)</power>
<symbol>🔮🔥⚔️</symbol>
<difficulty>1</difficulty>
<duration></duration>
<playcost>2 ACT,
Not Silenced,
3 MP,
Equipment has Catalyst property</playcost>
<artistic_value>-1</artistic_value>
<component x="11" y="3" db_entry="🧩 Mastery"/>
<component x="7" y="1" db_entry="🧩 Set Ability Name"/>
<component x="9" y="1" db_entry="🧩 Set Image"/>
<component x="11" y="1" db_entry="🧩 Play Cost"/>
<component x="11" y="5" db_entry="🧩 CP Cost"/>
<component x="12" y="4" db_entry="🧩 Duration"/>
<component x="11" y="3" db_entry="🧩 Power"/>
<component x="11" y="4" db_entry="🧩 Difficulty"/>
<component x="12" y="3" db_entry="🧩 Symbols"/>
<component x="12" y="5" db_entry="🧩 Range"/>
<component x="8" y="2" db_entry="🧩 Set Description"/>
<component x="3" y="3" db_entry="🎺 Repertoire Card: Filler"/>
</ability_card>