{% extends "base.html" %} {% block title %}Share ยท {{ app_name }}{% endblock %} {% block bodyattrs %} class="share"{% endblock %} {# ๐Ÿ”ด THE PREVIEW, THE DOWNLOAD AND THE POST TEXT DESCRIBE ONE CARD. Every URL here comes from routes.share_qs over ONE normalised parameter set, and the page's clock is pinned into the preview and the download (`at=`), so the picture on screen, the file handed to the share sheet and the caption on the clipboard are the same instant. With JavaScript the page edits in place through /share/text.json and writes the server's canonical URL back with history.replaceState โ€” the address bar is still the whole truth. ๐Ÿ”ด EVERY CONTROL IS A LINK FIRST. Without JS each one is an that changes the query string and lands back on ITS OWN PANEL (#stats, #post, #look, #photo), with scroll-margin clearing the sticky head. With JS the same element carries `data-set` (or `data-stat` / `data-op`), share.js applies it in place, and nothing reloads โ€” so a run of stat toggles on a phone no longer throws the screen back to the top each time. ๐Ÿ”ด NOTHING ABOVE THE VIEWPORT CHANGES HEIGHT WHILE YOU EDIT. Twelve fixed slots, a fixed toolbar, catalogue rows of fixed height whose order badge toggles `visibility` (never display), and a panel container with overflow-anchor:none. A toggle repaints text in place; it never moves an element from one list to another. ๐Ÿ”ด The download is a plain : a fetch()+Blob download misbehaves on iOS Safari. Share uses a picture prefetched before the tap (share.js), never a fetch inside it. โš ๏ธ A control is only OFFERED when it would change the picture โ€” the location toggle needs a photo that has one, the badge toggle needs badges, Check-in needs a photo, the event stamp needs an event. A switch that silently does nothing reads as a broken feature. #} {% block body %} {%- set P = picker -%} {%- set W = plan.geom.W -%}{%- set H = plan.geom.H -%}
{{ post.alt }}
๐Ÿ“ค Share
โฌ‡
{{ first_line }} {% if nudge and nudge.id != q.photo %}๐Ÿ“ธ Today's check-in โ†’{% endif %}

On the card, in order fits {{ plan.capacity }} ๐Ÿ“–

{%- for s in P.slots %} {%- if s.empty %} {{ s.n }}โ†” {%- else %} {{ s.n }}{{ s.label }}{{ s.value if s.value else 'โ€”' }}{{ s.why }}โ†” {%- endif %} {%- endfor %}

+{{ P.more }} more in the link โ€” no card draws more than twelve.

โ†บ default list clear {{ 'Move or remove it with the buttons above.' if q.sel else 'Tap a slot to move or remove it.' }}

A stat with nothing to say at this moment is left out rather than printed as a zero, and a long value takes two cells instead of being cut short. Dashed slots won't fit on this card; dim ones repeat what the card already prints.

{%- for g in P.cat_groups %}
{{ g.title }} {{ g.entries|length }}
{%- endfor %}

Twelve is the most any card draws โ€” remove one to add another.

What a card starts with lives in Settings โ†’ Stamps & recap; a preset keeps a whole look.

Post text ๐Ÿ“–

{%- for s in styles %}{% set c = ctl({'style': s, 'tpl': ''}, 'post') %} {{ style_labels[s] }} {%- endfor %} Custom โœŽ

Hashtags {{ post.tags|length }} on ยท Instagram takes at most 5

{%- for t in post.tags %}{% set rest = post.tags|reject('equalto', t)|list %}{% set c = ctl({'tags': rest}, 'post') %} #{{ t }} โœ• {%- endfor %} {%- for t in body.suggested %}{% set c = ctl({'tags': post.tags + [t]}, 'post') %} +#{{ t }} {%- endfor %}

Suggestions are never on until you tap them. {day}, {days} and {year} in a hashtag fill in from the event.

    {% for w in post.warnings %}
  • {{ w }}
  • {% endfor %}
Alt text

{{ post.alt }}

Content warning

{{ post.cw }}

{%- for k, v in hidden_pairs(('cw',)) %}{% endfor %}
    {% for h in platform_hints %}
  • {{ h }}
  • {% endfor %}
Edit the template
{%- for k, v in hidden_pairs(('tpl',)) %}{% endfor %}
{% for f in fields %}{% endfor %}

Fields in braces fill in from the card; any stat id from the catalogue works too. A field with nothing to say drops its own ยท segment, and a line that empties is dropped.

{% set c = ctl({'tpl': ''}, 'post') %}back to the style

Layout ๐Ÿ“–

{%- for k, label in layouts.items() %} {%- if k != 'checkin' or can_checkin %} {%- set over = {'layout': k, 'photo': checkin_pick} if (k == 'checkin' and not q.photo and checkin_pick) else {'layout': k} %} {%- set c = ctl(over, 'look') %} {{ label }}{{ layout_hints[k] }} {%- endif %} {%- endfor %}

Drawn as {{ layouts[plan.layout] }}: Check-in needs a right now card with a photo, in story or square.

Kind

{%- for k, label in kinds.items() %} {%- if k != 'milestone' or held %} {%- set c = ctl({'kind': k, 'stats': none, 'sel': '', 'photo': 0 if k == 'milestone' else q.photo}, 'look') %} {{ label }} {%- endif %} {%- endfor %}

Right now is the live state. The last stretch counts a span and prints its dates. One badge is a record of when something was earned: every number on it is as it stood then. {%- if not held %} It appears here once you have earned a badge.{% endif %}

{% if plan.kind == 'week' %}

How long

{%- for n in [7, 14, 30, 90] %}{% set c = ctl({'days': n}, 'look') %} {{ n }} days {%- endfor %}
{% endif %} {% if plan.kind == 'milestone' %}

Which badge

{%- for b in held %}{% set c = ctl({'badge': b.id}, 'look') %} โ—† {{ b.title }} {{ b.awarded_at|local('%Y-%m-%d') }} {%- endfor %}

The card is drawn in the badge's own metal โ€” โ—† common, โ—† uncommon, โ—† rare, โ—† epic, โ—† legendary.

{% endif %}

Theme

{%- for t in themes %}{% set c = ctl({'theme': t.name}, 'look') %} {{ t.label }} {%- endfor %}

{% for t in themes if t.name == q.theme %}{{ t.blurb }}{% endfor %}

Shape

{%- for s in shapes %}{% set c = ctl({'shape': s}, 'look') %} {{ s }} {%- endfor %}

story 9:16 ยท square 1:1 ยท wide 1.91:1 for a link preview, stats only.

Event stamp ๐Ÿ“–

{%- set c = ctl({'seal': 1}, 'look') %}on {%- set c = ctl({'seal': 0}, 'look') %}off

A drawn stamp naming the event and the day โ€” on a photo, the day the shutter fired.

Badges

{% if plan.kind in ('milestone', 'week') %}

This card already shows badges โ€” the ones {{ 'earned in the window' if plan.kind == 'week' else 'you hold besides the one on the card' }}.

{% elif can_badges %}
{%- set c = ctl({'badges': 1}, 'look') %}๐Ÿ… show badges {%- set c = ctl({'badges': 0}, 'look') %}show recent events

Without a photo, the lower half carries your badges or the events that moved the clock most.

{% else %}

No badges yet. Until then the lower half carries the events that moved the clock most.

{% endif %}

Photo

{% if plan.kind == 'milestone' %}

A milestone card takes no photograph, on purpose. Its numbers are already as-of the day the badge was earned, and a picture from a third date is one moment more than anybody can keep straight.

{% elif q.shape == 'wide' %}

The wide shape is too short to carry a picture without cropping it to a sliver, so it carries stats only. Switch to story or square for a photo.

{% else %}
{% set c = ctl({'photo': 0, 'place': 0}, 'photo') %}no photo
{% if photos %}
{%- for m in photos %} {%- set has_place = m.place_src %}{% set c = ctl({'photo': m.id, 'place': q.place if has_place else 0}, 'photo') %} {%- if m.assignment_id %}training{% endif %} {%- if has_place %}โ—‰{% endif %} {%- if m.ev %}{{ m.ev.sign }} {{ m.ev.day }}{% endif %} {%- endfor %}

Newest first. The card burns the photo's own date and time into it โ€” on a Check-in card that moment is the whole card's; otherwise the stats around it are from now, and the card says which is which.

{% else %}

No photos yet. Verify a task with a picture, or use ๐Ÿ“ธ on the dashboard.

{% endif %} {% if q.photo %}

Location

{% if chosen_photo and chosen_photo.place_src %}
{%- set c = ctl({'place': 1}, 'photo') %}โ—‰ include place {%- set c = ctl({'place': 0}, 'photo') %}no place

Already rounded when the photo was saved โ€” it cannot be sharpened back. Change how coarse in Settings. Never in the post text.

{% else %}

This photo has no location โ€” nothing to include.

{% endif %} {% endif %} {% endif %}

Nothing is uploaded anywhere โ€” the card is drawn on this box and handed to you.

{#- What share.js repaints from โ€” the same body /share/text.json answers. `tojson` escapes < > & ' so the block cannot close itself; it is data, never executed. -#}
{% endblock %} {% block scripts %}{% endblock %}