diff --git a/examples/boilerplate.lisp b/examples/boilerplate.lisp index 66b4553..6c17a22 100644 --- a/examples/boilerplate.lisp +++ b/examples/boilerplate.lisp @@ -1,1310 +1,15 @@ -(text "{% macro avatar(username, size=\"24px\", selector_type=\"username\") -%}") -(img - ("title" "{{ username }}'s avatar") - ("src" "/api/v1/auth/user/{{ username }}/avatar?selector_type={{ selector_type }}") - ("alt" "@{{ username }}") - ("class" "avatar shadow") - ("loading" "lazy") - ("style" "--size: {{ size }}")) +(text "") ; using a raw string for the doctype is fine +(html + (head + ; everything that belongs in the head element + (title (text "Document")) -(text "{%- endmacro %} {% macro community_avatar(id, community=false, size=\"24px\") -%} {% if community -%}") -(img - ("src" "/api/v1/communities/{{ id }}/avatar") - ("alt" "{{ community.title }}'s avatar") - ("class" "avatar shadow") - ("loading" "lazy") - ("style" "--size: {{ size }}")) + (meta ("charset" "UTF-8")) + (meta ("name" "viewport") ("content" "width=device-width, initial-scale=1.0")) + (meta ("http-equiv" "X-UA-Compatible") ("content" "ie=edge")) -(text "{% else %}") -(img - ("src" "/api/v1/communities/{{ id }}/avatar") - ("alt" "{{ id }}'s avatar") - ("class" "avatar shadow") - ("loading" "lazy") - ("style" "--size: {{ size }}")) + (link ("rel" "stylesheet") ("href" "#"))) -(text "{%- endif %} {%- endmacro %} {% macro banner(username, border_radius=\"var(--radius)\") -%}") -(img - ("title" "{{ username }}'s banner") - ("src" "/api/v1/auth/user/{{ username }}/banner") - ("alt" "@{{ username }}'s banner") - ("class" "banner shadow w-full") - ("loading" "lazy") - ("style" "border-radius: {{ border_radius }};")) - -(text "{%- endmacro %} {% macro community_banner(id, community=false) -%} {% if community %}") -(img - ("src" "/api/v1/communities/{{ id }}/banner") - ("alt" "{{ community.title }}'s banner") - ("class" "banner shadow") - ("loading" "lazy")) - -(text "{% else %}") -(img - ("src" "/api/v1/communities/{{ id }}/banner") - ("alt" "{{ id }}'s banner") - ("class" "banner shadow") - ("loading" "lazy")) - -(text "{%- endif %} {%- endmacro %} {% macro community_listing_card(community) -%}") -(a - ("class" "card secondary w-full flex items-center gap-4") - ("href" "/community/{{ community.title }}") - (text "{{ self::community_avatar(id=community.id, community=community, size=\"48px\") }}") - (div - ("class" "flex flex-col") - (h3 - ("class" "name lg:long") - (text "{{ community.context.display_name }}")) - (span - ("class" "fade") - (b - (text "{{ community.member_count }}")) - (text "members")))) - -(text "{%- endmacro %} {% macro username(user) -%}") -(div - ("style" "display: contents") - (text "{% if user.settings.display_name -%} {{ user.settings.display_name }} {% else %} {{ user.username }} {%- endif %}")) - -(text "{%- endmacro %} {% macro likes(id, asset_type, likes=0, dislikes=0, secondary=false) -%}") -(button - ("title" "Like") - ("class" "{% if secondary -%}quaternary{% else %}camo{%- endif %} small") - ("hook_element" "reaction.like") - ("onclick" "trigger('me::react', [event.target, '{{ id }}', '{{ asset_type }}', true])") - (text "{{ icon \"heart\" }} {% if likes > 0 -%}") - (span - (text "{{ likes }}")) - (text "{%- endif %}")) - -(text "{% if not user or not user.settings.hide_dislikes -%}") -(button - ("title" "Dislike") - ("class" "{% if secondary -%}quaternary{% else %}camo{%- endif %} small") - ("hook_element" "reaction.dislike") - ("onclick" "trigger('me::react', [event.target, '{{ id }}', '{{ asset_type }}', false])") - (text "{{ icon \"heart-crack\" }} {% if dislikes > 0 -%}") - (span - (text "{{ dislikes }}")) - (text "{%- endif %}")) - -(text "{%- endif %} {%- endmacro %} {% macro full_username(user) -%}") -(div - ("class" "flex items-center") - (a - ("href" "/@{{ user.username }}") - ("class" "flush") - ("style" "font-weight: 600") - ("target" "_top") - (text "{{ self::username(user=user) }}")) - (text "{{ self::online_indicator(user=user) }} {% if user.is_verified -%}") - (span - ("title" "Verified") - ("style" "color: var(--color-primary)") - ("class" "flex items-center") - (text "{{ icon \"badge-check\" }}")) - (text "{%- endif %}")) - -(text "{%- endmacro %} {% macro repost(repost, post, owner, secondary=false, community=false, show_community=true, can_manage_post=false) -%}") -(div - ("style" "display: contents") - (text "{{ self::post(post=post, owner=owner, secondary=secondary, community=community, show_community=show_community, can_manage_post=can_manage_post, repost=repost, expect_repost=true) }}")) - -(text "{%- endmacro %} {% macro post(post, owner, question=false, secondary=false, community=false, show_community=true, can_manage_post=false, repost=false, expect_repost=false) -%} {% if community and show_community and community.id != config.town_square or question %}") -(div - ("class" "card-nest") - (text "{% if question -%} {{ self::question(question=question[0], owner=question[1], profile=owner) }} {% else %}") - (div - ("class" "card small") - (a - ("href" "/api/v1/communities/find/{{ post.community }}") - ("class" "flush flex gap-1 items-center") - (text "{{ self::community_avatar(id=post.community, community=community) }}") - (b - (text "{% if community.context.display_name -%} {{ community.context.display_name }} {% else %} {{ community.title }} {%- endif %}")) - (text "{% if post.context.is_pinned or post.context.is_profile_pinned -%} {{ icon \"pin\" }} {%- endif %}"))) - (text "{%- endif %} {%- endif %}") - (div - ("class" "card flex flex-col gap-2 {% if secondary -%}secondary{%- endif %}") - ("id" "post:{{ post.id }}") - ("data-community" "{{ post.community }}") - ("data-ownsup" "{{ owner.permissions|has_supporter }}") - ("hook" "verify_emojis") - (div - ("class" "w-full flex gap-2") - (text "{% if not expect_repost -%}") - (a - ("href" "/@{{ owner.username }}") - (text "{{ self::avatar(username=owner.username, size=\"52px\", selector_type=\"username\") }}")) - (text "{%- endif %}") - (div - ("class" "flex flex-col w-full gap-1 post_right {% if expect_repost -%}repost{%- endif %}") - (div - ("class" "flex flex-wrap gap-2 items-center") - (text "{% if expect_repost -%}") - (a - ("href" "/@{{ owner.username }}") - (text "{{ self::avatar(username=owner.username, size=\"24px\", selector_type=\"username\") }}")) - (text "{%- endif %}") - (span - ("class" "name") - (text "{{ self::full_username(user=owner) }}")) - (text "{% if post.context.edited != 0 -%}") - (div - ("class" "flex") - (span - ("class" "fade date") - (text "{{ post.context.edited }}")) - (sup - ("title" "Edited") - (text "*"))) - (text "{% else %}") - (span - ("class" "fade date") - (text "{{ post.created }}")) - (text "{%- endif %} {% if post.context.is_nsfw -%}") - (span - ("title" "NSFW post") - ("class" "flex items-center") - ("style" "color: var(--color-primary)") - (text "{{ icon \"square-asterisk\" }}")) - (text "{%- endif %} {% if post.context.repost and post.context.repost.reposting %}") - (span - ("title" "Repost") - ("class" "flex items-center") - ("style" "color: var(--color-primary)") - (text "{{ icon \"repeat-2\" }}")) - (text "{%- endif %} {% if post.community == config.town_square -%}") - (span - ("title" "Posted to profile") - ("class" "flex items-center") - ("style" "color: var(--color-primary)") - (text "{{ icon \"user-round\" }}")) - (text "{%- endif %} {% if post.is_deleted -%}") - (span - ("title" "Deleted") - ("class" "flex items-center") - ("style" "color: var(--color-primary)") - (text "{{ icon \"trash-2\" }}")) - (text "{%- endif %}")) - (text "{% if not post.context.content_warning -%}") - (span - ("id" "post-content:{{ post.id }}") - ("class" "no_p_margin") - ("hook" "long") - (text "{{ post.content|markdown|safe }} {% if expect_repost -%} {% if repost -%} {{ self::post(post=repost[1], owner=repost[0], secondary=not secondary, community=false, show_community=false, can_manage_post=false) }} {% else %}") - (div - ("class" "card tertiary red flex items-center gap-2") - (text "{{ icon \"frown\" }}") - (span - (text "Could not find original post..."))) - (text "{%- endif %} {%- endif %}")) - (text "{{ self::post_media(upload_ids=post.uploads) }} {% else %}") - (details - (summary - ("class" "card flex gap-2 flex-wrap items-center tertiary red w-full") - (text "{{ icon \"triangle-alert\" }}") - (b - (text "{{ post.context.content_warning }}"))) - (div - ("class" "flex flex-col gap-2") - (span - ("id" "post-content:{{ post.id }}") - ("class" "no_p_margin") - ("hook" "long") - (text "{{ post.content|markdown|safe }} {% if expect_repost -%} {% if repost -%} {{ self::post(post=repost[1], owner=repost[0], secondary=not secondary, community=false, show_community=false, can_manage_post=false) }} {% else %}") - (div - ("class" "card tertiary red flex items-center gap-2") - (text "{{ icon \"frown\" }}") - (span - (text "Could not find original post..."))) - (text "{%- endif %} {%- endif %}")) - (text "{{ self::post_media(upload_ids=post.uploads) }}"))) - (text "{%- endif %}") - (div - ("class" "flex flex-wrap gap-2 fade") - (text "{% for tag in post.context.tags %}") - (a - ("href" "/@{{ owner.username }}?tag={{ tag }}") - ("class" "flush fade") - (text "#{{ tag }}")) - (text "{% endfor %}")))) - (div - ("class" "flex justify-between items-center gap-2 w-full") - (text "{% if user -%}") - (div - ("class" "flex gap-1 reactions_box") - ("hook" "check_reactions") - ("hook-arg:id" "{{ post.id }}") - (text "{% if post.context.reactions_enabled -%} {% if post.content|length > 0 -%} {{ self::likes(id=post.id, asset_type=\"Post\", likes=post.likes, dislikes=post.dislikes) }} {%- endif %} {%- endif %} {% if post.context.repost and post.context.repost.reposting -%}") - (a - ("href" "/post/{{ post.context.repost.reposting }}") - ("class" "button small camo") - (text "{{ icon \"expand\" }}")) - (text "{%- endif %}")) - (text "{% else %}") - (div) - (text "{%- endif %}") - (div - ("class" "flex gap-1 buttons_box") - (a - ("href" "/post/{{ post.id }}") - ("class" "button camo small") - (text "{{ icon \"message-circle\" }}") - (span - (text "{{ post.comment_count }}"))) - (a - ("href" "/post/{{ post.id }}") - ("class" "button camo small") - ("target" "_blank") - (text "{{ icon \"external-link\" }}")) - (text "{% if user -%}") - (div - ("class" "dropdown") - (button - ("class" "camo small") - ("onclick" "trigger('atto::hooks::dropdown', [event])") - ("exclude" "dropdown") - (text "{{ icon \"ellipsis\" }}")) - (div - ("class" "inner") - (text "{% if config.town_square and post.context.reposts_enabled %}") - (b - ("class" "title") - (text "{{ text \"general:label.share\" }}")) - (button - ("onclick" "trigger('me::repost', ['{{ post.id }}', '', '{{ config.town_square }}'])") - (text "{{ icon \"repeat-2\" }}") - (span - (text "{{ text \"communities:label.repost\" }}"))) - (a - ("class" "button") - ("href" "/communities/intents/post?quote={{ post.id }}") - (text "{{ icon \"quote\" }}") - (span - (text "{{ text \"communities:label.quote_post\" }}"))) - (text "{%- endif %} {% if user.id != post.owner -%}") - (b - ("class" "title") - (text "{{ text \"general:label.safety\" }}")) - (button - ("class" "red") - ("onclick" "trigger('me::report', ['{{ post.id }}', 'post'])") - (text "{{ icon \"flag\" }}") - (span - (text "{{ text \"general:action.report\" }}"))) - (text "{%- endif %} {% if (user.id == post.owner) or is_helper or can_manage_post %}") - (b - ("class" "title") - (text "{{ text \"general:action.manage\" }}")) - (text "{% if user.id == post.owner -%}") - (a - ("href" "/post/{{ post.id }}#/edit") - (text "{{ icon \"pen\" }}") - (span - (text "{{ text \"communities:label.edit_content\" }}"))) - (text "{%- endif %}") - (a - ("href" "/post/{{ post.id }}#/configure") - (text "{{ icon \"settings\" }}") - (span - (text "{{ text \"communities:action.configure\" }}"))) - (text "{% if not post.is_deleted -%}") - (button - ("class" "red") - ("onclick" "trigger('me::remove_post', ['{{ post.id }}'])") - (text "{{ icon \"trash\" }}") - (span - (text "{{ text \"general:action.delete\" }}"))) - (text "{%- endif %} {% if is_helper and post.is_deleted -%}") - (button - ("class" "red") - ("onclick" "trigger('me::purge_post', ['{{ post.id }}'])") - (text "{{ icon \"trash-2\" }}") - (span - (text "{{ text \"general:action.purge\" }}"))) - (button - ("class" "green") - ("onclick" "trigger('me::restore_post', ['{{ post.id }}'])") - (text "{{ icon \"undo\" }}") - (span - (text "{{ text \"general:action.restore\" }}"))) - (text "{%- endif %} {%- endif %}"))) - (text "{%- endif %}")))) - (text "{% if community and show_community and community.id != config.town_square or question %}")) - -(text "{%- endif %} {%- endmacro %} {% macro post_media(upload_ids) -%} {% if upload_ids|length > 0%}") -(div - ("class" "media_gallery gap-2") - (text "{% for upload in upload_ids %}") - (img - ("src" "/api/v1/uploads/{{ upload }}") - ("alt" "Image upload") - ("onclick" "trigger('ui::lightbox_open', ['/api/v1/uploads/{{ upload }}'])")) - (text "{% endfor %}")) - -(text "{%- endif %} {%- endmacro %} {% macro notification(notification) -%}") -(div - ("class" "w-full card-nest") - (div - ("class" "card small notif_title flex items-center") - (text "{% if not notification.read -%}") - (svg - ("width" "24") - ("height" "24") - ("viewBox" "0 0 24 24") - ("style" "fill: var(--color-link)") - (circle - ("cx" "12") - ("cy" "12") - ("r" "6"))) - (text "{%- endif %}") - (b - ("class" "no_p_margin") - (text "{{ notification.title|markdown|safe }}"))) - (div - ("class" "card notif_content flex flex-col gap-2") - (span - ("class" "no_p_margin") - (text "{{ notification.content|markdown|safe }}")) - (div - ("class" "card secondary w-full flex flex-wrap gap-2") - (text "{% if notification.read -%}") - (button - ("class" "tertiary") - ("onclick" "trigger('me::update_notification_read_status', ['{{ notification.id }}', false])") - (text "{{ icon \"undo\" }}") - (span - (text "{{ text \"notifs:action.mark_as_unread\" }}"))) - (text "{% else %}") - (button - ("class" "green tertiary") - ("onclick" "trigger('me::update_notification_read_status', ['{{ notification.id }}', true])") - (text "{{ icon \"check\" }}") - (span - (text "{{ text \"notifs:action.mark_as_read\" }}"))) - (text "{%- endif %}") - (button - ("class" "red tertiary") - ("onclick" "trigger('me::remove_notification', ['{{ notification.id }}'])") - (text "{{ icon \"trash\" }}") - (span - (text "{{ text \"general:action.delete\" }}")))))) - -(text "{%- endmacro %} {% macro user_card(user) -%}") -(a - ("class" "card-nest w-full") - ("href" "/@{{ user.username }}") - (div - ("class" "card small") - ("style" "padding: 0") - (text "{{ self::banner(username=user.username, border_radius=\"0px\") }}")) - (div - ("class" "card secondary flex items-center gap-4") - (text "{{ self::avatar(username=user.username, size=\"48px\") }}") - (div - ("class" "flex items-center") - (b - (text "{{ self::username(user=user) }}")) - (text "{{ self::online_indicator(user=user) }}")))) - -(text "{%- endmacro %} {% macro pagination(page=0, items=0, key=\"\", value=\"\") -%}") -(div - ("class" "flex justify-between gap-2 w-full") - (text "{% if page > 0 -%}") - (a - ("class" "button quaternary") - ("href" "?page={{ page - 1 }}{{ key }}{{ value }}") - (text "{{ icon \"arrow-left\" }}") - (span - (text "{{ text \"general:link.previous\" }}"))) - (text "{% else %}") - (div) - (text "{%- endif %} {% if items != 0 -%}") - (a - ("class" "button quaternary") - ("href" "?page={{ page + 1 }}{{ key }}{{ value }}") - (span - (text "{{ text \"general:link.next\" }}")) - (text "{{ icon \"arrow-right\"}}")) - (text "{%- endif %}")) - -(text "{%- endmacro %} {% macro online_indicator(user) -%} {% if not user.settings.private_last_seen or is_helper %}") -(div - ("class" "online_indicator") - ("style" "display: contents") - ("hook" "online_indicator") - ("hook-arg:last_seen" "{{ user.last_seen }}") - (div - ("style" "display: none") - ("hook_ui_ident" "online") - ("title" "Online") - (svg - ("width" "24") - ("height" "24") - ("viewBox" "0 0 24 24") - ("style" "fill: var(--color-green)") - (circle - ("cx" "12") - ("cy" "12") - ("r" "6")))) - (div - ("style" "display: none") - ("hook_ui_ident" "idle") - ("title" "Idle") - (svg - ("width" "24") - ("height" "24") - ("viewBox" "0 0 24 24") - ("style" "fill: var(--color-yellow)") - (circle - ("cx" "12") - ("cy" "12") - ("r" "6")))) - (div - ("style" "display: none") - ("hook_ui_ident" "offline") - ("title" "Offline") - (svg - ("width" "24") - ("height" "24") - ("viewBox" "0 0 24 24") - ("style" "fill: hsl(0, 0%, 50%)") - (circle - ("cx" "12") - ("cy" "12") - ("r" "6"))))) - -(text "{% else %}") -(div - ("title" "Offline") - ("style" "display: contents") - (svg - ("width" "24") - ("height" "24") - ("viewBox" "0 0 24 24") - ("style" "fill: hsl(0, 0%, 50%)") - (circle - ("cx" "12") - ("cy" "12") - ("r" "6")))) - -(text "{%- endif %} {%- endmacro %} {% macro theme(user, theme_preference) -%} {% if user %} {% if user.settings.theme_hue -%}") -(style - (text ":root, * { - --hue: {{ user.settings.theme_hue }} !important; - }")) - -(text "{%- endif %} {% if user.settings.theme_sat -%}") -(style - (text ":root, * { - --sat: {{ user.settings.theme_sat }} !important; - }")) - -(text "{%- endif %} {% if user.settings.theme_lit -%}") -(style - (text ":root, * { - --lit: {{ user.settings.theme_lit }} !important; - }")) - -(text "{%- endif %} {% if theme_preference -%}") -(script - (text "function match_user_theme() { - const pref = \"{{ theme_preference }}\".toLowerCase(); - - if (pref === \"auto\") { - return; - } - - document.documentElement.className = pref; - } - - setTimeout(() => { - match_user_theme(); - }, 150);")) - -(text "{%- endif %}") -(div - ("style" "display: none;") - (text "{{ self::theme_color(color=user.settings.theme_color_surface, css=\"color-surface\") }} {{ self::theme_color(color=user.settings.theme_color_text, css=\"color-text\") }} {{ self::theme_color(color=user.settings.theme_color_text_link, css=\"color-link\") }} {{ self::theme_color(color=user.settings.theme_color_lowered, css=\"color-lowered\") }} {{ self::theme_color(color=user.settings.theme_color_text_lowered, css=\"color-text-lowered\") }} {{ self::theme_color(color=user.settings.theme_color_super_lowered, css=\"color-super-lowered\") }} {{ self::theme_color(color=user.settings.theme_color_raised, css=\"color-raised\") }} {{ self::theme_color(color=user.settings.theme_color_text_raised, css=\"color-text-raised\") }} {{ self::theme_color(color=user.settings.theme_color_super_raised, css=\"color-super-raised\") }} {{ self::theme_color(color=user.settings.theme_color_primary, css=\"color-primary\") }} {{ self::theme_color(color=user.settings.theme_color_text_primary, css=\"color-text-primary\") }} {{ self::theme_color(color=user.settings.theme_color_primary_lowered, css=\"color-primary-lowered\") }} {{ self::theme_color(color=user.settings.theme_color_secondary, css=\"color-secondary\") }} {{ self::theme_color(color=user.settings.theme_color_text_secondary, css=\"color-text-secondary\") }} {{ self::theme_color(color=user.settings.theme_color_secondary_lowered, css=\"color-secondary-lowered\") }} {% if user.permissions|has_supporter -%}") - (style - (text "{{ user.settings.theme_custom_css }}")) - (text "{%- endif %}")) - -(text "{%- endif %} {%- endmacro %} {% macro theme_color(color, css) -%} {% if color -%}") -(style - (text ":root, - * { - --{{ css }}: {{ color|color }} !important; - }")) - -(text "{%- endif %} {%- endmacro %} {% macro question(question, owner, show_community=true, secondary=false, profile=false) -%}") -(div - ("class" "card{% if secondary -%} secondary{%- endif %} flex gap-2") - (text "{% if owner.id == 0 -%}") - (span - (text "{% if profile and profile.settings.anonymous_avatar_url -%}") - (img - ("src" "/api/v1/util/proxy?url={{ profile.settings.anonymous_avatar_url }}") - ("alt" "anonymous' avatar") - ("class" "avatar shadow") - ("loading" "lazy") - ("style" "--size: 52px")) - (text "{% else %} {{ self::avatar(username=owner.username, selector_type=\"username\", size=\"52px\") }} {%- endif %}")) - (text "{% else %}") - (a - ("href" "/@{{ owner.username }}") - (text "{{ self::avatar(username=owner.username, selector_type=\"username\", size=\"52px\") }}")) - (text "{%- endif %}") - (div - ("class" "flex flex-col gap-1") - (div - ("class" "flex items-center gap-2 flex-wrap") - (span - ("class" "name") - (text "{% if owner.id == 0 -%} {% if profile and profile.settings.anonymous_username -%}") - (span - ("class" "flex items-center gap-2") - (b - (text "{{ profile.settings.anonymous_username }}")) - (span - ("title" "Anonymous user") - ("class" "flex items-center") - ("style" "color: var(--color-primary)") - (text "{{ icon \"drama\" }}"))) - (text "{% else %}") - (b - (text "anonymous")) - (text "{%- endif %} {% else %} {{ self::full_username(user=owner) }} {%- endif %}")) - (span - ("class" "date") - (text "{{ question.created }}")) - (span - ("title" "Question") - ("class" "flex items-center") - ("style" "color: var(--color-primary)") - (text "{{ icon \"message-circle-heart\" }}")) - (text "{% if question.context.is_nsfw -%}") - (span - ("title" "NSFW community") - ("class" "flex items-center") - ("style" "color: var(--color-primary)") - (text "{{ icon \"square-asterisk\" }}")) - (text "{%- endif %} {% if question.community > 0 and show_community -%}") - (a - ("href" "/api/v1/communities/find/{{ question.community }}") - ("class" "flex items-center") - (text "{{ self::community_avatar(id=question.community, size=\"24px\") }}")) - (text "{%- endif %} {% if question.is_global -%}") - (a - ("class" "notification chip") - ("href" "/question/{{ question.id }}") - (text "{{ question.answer_count }} answers")) - (text "{%- endif %}")) - (span - ("class" "no_p_margin") - ("style" "font-weight: 500") - (text "{{ question.content|markdown|safe }}")) - (div - ("class" "flex gap-2 items-center justify-between")))) - -(text "{%- endmacro %} {% macro create_question_form(receiver=\"0\", community=\"\", header=\"\", is_global=false) -%}") -(div - ("class" "card-nest") - (div - ("class" "card small flex items-center gap-2") - (text "{{ icon \"message-circle-heart\" }}") - (span - ("class" "no_p_margin") - (text "{% if header -%} {{ header|markdown|safe }} {% else %} {{ text \"requests:label.ask_question\" }} {%- endif %}"))) - (form - ("class" "card flex flex-col gap-2") - ("onsubmit" "create_question_from_form(event)") - (div - ("class" "flex flex-col gap-1") - (label - ("for" "content") - (text "{{ text \"communities:label.content\" }}")) - (textarea - ("type" "text") - ("name" "content") - ("id" "content") - ("placeholder" "content") - ("required" "") - ("minlength" "2") - ("maxlength" "4096"))) - (button - ("class" "primary") - (text "{{ text \"communities:action.create\" }}")))) - -(script - (text "async function create_question_from_form(e) { - e.preventDefault(); - await trigger(\"atto::debounce\", [\"questions::create\"]); - fetch(\"/api/v1/questions\", { - method: \"POST\", - headers: { - \"Content-Type\": \"application/json\", - }, - body: JSON.stringify({ - content: e.target.content.value, - receiver: \"{{ receiver }}\", - community: \"{{ community }}\", - is_global: \"{{ is_global }}\" == \"true\", - }), - }) - .then((res) => res.json()) - .then((res) => { - trigger(\"atto::toast\", [ - res.ok ? \"success\" : \"error\", - res.message, - ]); - - if (res.ok) { - e.target.reset(); - } - }); - }")) - -(text "{%- endmacro %} {% macro global_question(question, can_manage_questions=false, secondary=false, show_community=true) -%}") -(div - ("class" "card-nest") - (text "{{ self::question(question=question[0], owner=question[1], show_community=show_community) }}") - (div - ("class" "small card flex justify-between flex-wrap gap-2{% if secondary -%} secondary{%- endif %}") - (div - ("class" "flex gap-1 reactions_box") - ("hook" "check_reactions") - ("hook-arg:id" "{{ question[0].id }}") - (text "{{ self::likes(id=question[0].id, asset_type=\"Question\", likes=question[0].likes, dislikes=question[0].dislikes, secondary=false) }}")) - (div - ("class" "flex gap-1 buttons_box") - (a - ("href" "/question/{{ question[0].id }}") - ("class" "button small") - (text "{{ icon \"external-link\" }} {% if user -%}") - (span - (text "{{ text \"requests:label.answer\" }}")) - (text "{% else %}") - (span - (text "{{ text \"general:action.open\" }}")) - (text "{%- endif %}")) - (text "{% if user -%} {% if can_manage_questions or is_helper or question[1].id == user.id %}") - (div - ("class" "dropdown") - (button - ("class" "camo small") - ("onclick" "trigger('atto::hooks::dropdown', [event])") - ("exclude" "dropdown") - (text "{{ icon \"ellipsis\" }}")) - (div - ("class" "inner") - (button - ("class" "camo small red") - ("onclick" "trigger('me::remove_question', ['{{ question[0].id }}'])") - (text "{{ icon \"trash\" }}") - (span - (text "{{ text \"general:action.delete\" }}"))))) - (text "{%- endif %} {%- endif %}")))) - -(text "{%- endmacro %} {% macro spotify_playing(state, size=\"60px\") -%} {% if state and state.data %}") -(div - ("class" "card-nest") - (div - ("class" "card flex items-center justify-between gap-2 small") - (div - ("class" "flex items-center gap-2") - (b - (text "Listening on")) - (text "{{ icon \"spotify\" }}")) - (span - ("class" "fade date short") - (text "{{ state.data.timestamp }}"))) - (div - ("class" "card secondary flex gap-2") - (a - ("href" "{{ state.external_urls.album }}") - (img - ("src" "{{ state.external_urls.album_img }}") - ("alt" "Album cover") - ("loading" "lazy") - ("class" "avatar") - ("style" "--size: {{ size }}"))) - (div - ("class" "flex flex-col") - (h5 - ("class" "w-full") - (a - ("href" "{{ state.external_urls.track }}") - ("class" "flush") - (text "{{ state.data.track }}"))) - (span - ("class" "fade") - (a - ("href" "{{ state.external_urls.artist }}") - ("class" "flush") - (text "{{ state.data.artist }}"))) - (span - ("hook" "spotify_time_text") - ("hook-arg:updated" "{{ state.data.timestamp }}") - ("hook-arg:progress" "{{ state.data.progress_ms }}") - ("hook-arg:duration" "{{ state.data.duration_ms }}") - ("hook-arg:display" "full"))))) - -(text "{%- endif %} {%- endmacro %} {% macro last_fm_playing(state, size=\"60px\") -%} {% if state and state.data %}") -(div - ("class" "card-nest") - (div - ("class" "card flex items-center justify-between gap-2 small") - (div - ("class" "flex items-center gap-2") - (b - (text "Listening on")) - (text "{{ icon \"last_fm\" }}")) - (span - ("class" "fade date short") - (text "{{ state.data.timestamp }}"))) - (div - ("class" "card secondary flex gap-2") - (a - ("href" "{{ state.external_urls.track }}") - (img - ("src" "{{ state.external_urls.track_img }}") - ("alt" "Track cover") - ("loading" "lazy") - ("class" "avatar") - ("style" "--size: {{ size }}"))) - (div - ("class" "flex flex-col") - (h5 - ("class" "w-full") - (a - ("href" "{{ state.external_urls.track }}") - ("class" "flush") - (text "{{ state.data.track }}"))) - (span - ("class" "fade") - (a - ("href" "{{ state.external_urls.artist }}") - ("class" "flush") - (text "{{ state.data.artist }}"))) - (text "{% if state.data.duration_ms and state.data.duration_ms != \"0\" -%}") - (span - ("hook" "spotify_time_text") - ("hook-arg:updated" "{{ state.data.timestamp }}") - ("hook-arg:progress" "25000") - ("hook-arg:duration" "{{ state.data.duration_ms }}") - ("hook-arg:display" "full")) - (text "{%- endif %}")))) - -(text "{%- endif %} {%- endmacro %} {% macro connection_icon(key) -%}") -(div - ("style" "display: contents;") - (text "{% if key == \"Spotify\" -%} {{ icon \"spotify\" }} {% elif key == \"LastFm\" %} {{ icon \"last_fm\" }} {%- endif %}")) - -(text "{%- endmacro %} {% macro connection_url(key, value) -%} {% if value[0].data.url %} {{ value[0].data.url }} {% elif key == \"LastFm\" %} https://last.fm/user/{{ value[0].data.name }} {%- endif %} {%- endmacro %} {% macro message_actions(can_manage_message, owner, message, owner) -%}") -(div - ("class" "dropdown") - (button - ("class" "camo small") - ("onclick" "trigger('atto::hooks::dropdown', [event])") - ("exclude" "dropdown") - (text "{{ icon \"ellipsis\" }}")) - (div - ("class" "inner") - (text "{% if can_manage_message or (user and user.id == message.owner) -%}") - (button - ("class" "red") - ("onclick" "delete_message('{{ message.id }}')") - (text "{{ icon \"trash\" }}") - (span - (text "{{ text \"general:action.delete\" }}"))) - (text "{%- endif %}") - (button - ("onclick" "window.location.href = `${window.location.origin}/chats/{{ community }}/{{ channel }}?message={{ message.id }}`") - (text "{{ icon \"external-link\" }}") - (span - (text "{{ text \"general:action.open\" }}"))) - (button - ("onclick" "trigger('atto::copy_text', [`${window.location.origin}/chats/{{ community }}/{{ channel }}?message={{ message.id }}`])") - (text "{{ icon \"copy\" }}") - (span - (text "{{ text \"general:action.copy_link\" }}"))) - (button - ("onclick" "mention_user('{{ owner.username }}')") - (text "{{ icon \"at-sign\" }}") - (span - (text "{{ text \"chats:action.mention_user\" }}"))))) - -(text "{%- endmacro %} {% macro message(user, message, can_manage_message=false, grouped=false) -%}") -(div - ("class" "card secondary message flex gap-2 {% if grouped -%}grouped{%- endif %}") - ("id" "message-{{ message.id }}") - (text "{% if not grouped -%}") - (a - ("href" "/@{{ user.username }}") - ("target" "_top") - (text "{{ self::avatar(username=user.username, size=\"42px\") }}")) - (text "{%- endif %}") - (div - ("class" "flex flex-col gap-1 w-full") - (text "{% if not grouped -%}") - (div - ("class" "flex gap-2 w-full justify-between flex-wrap") - (div - ("class" "flex gap-2") - (text "{{ self::full_username(user=user) }} {% if message.edited != message.created %}") - (span - ("class" "date") - (text "{{ message.edited }}") - (sup - ("title" "Edited") - (text "*"))) - (text "{% else %}") - (span - ("class" "date") - (text "{{ message.created }}")) - (text "{%- endif %}")) - (div - ("class" "flex gap-2 hidden") - (text "{{ self::message_actions(owner=user, message=message, can_manage_message=can_manage_message) }}"))) - (text "{%- endif %}") - (div - ("class" "flex w-full gap-2 justify-between") - (span - ("class" "no_p_margin") - (text "{{ message.content|markdown|safe }}")) - (text "{% if grouped -%}") - (div - ("class" "hidden") - (text "{{ self::message_actions(owner=user, message=message, can_manage_message=can_manage_message) }}")) - (text "{%- endif %}")))) - -(text "{%- endmacro %} {% macro user_menu() -%}") -(div - ("class" "inner") - (b - ("class" "title") - (text "{{ user.username }}")) - (a - ("href" "/@{{ user.username }}") - (text "{{ icon \"circle-user-round\" }}") - (span - (text "{{ text \"auth:link.my_profile\" }}"))) - (a - ("href" "/settings") - (text "{{ icon \"settings\" }}") - (span - (text "{{ text \"auth:link.settings\" }}"))) - (text "{% if is_helper -%}") - (b - ("class" "title") - (text "{{ text \"general:label.mod\" }}")) - (a - ("href" "/mod_panel/audit_log") - (text "{{ icon \"scroll-text\" }}") - (span - (text "{{ text \"general:link.audit_log\" }}"))) - (a - ("href" "/mod_panel/reports") - (text "{{ icon \"flag\" }}") - (span - (text "{{ text \"general:link.reports\" }}"))) - (a - ("href" "/mod_panel/ip_bans") - (text "{{ icon \"ban\" }}") - (span - (text "{{ text \"general:link.ip_bans\" }}"))) - (a - ("href" "/mod_panel/stats") - (text "{{ icon \"chart-line\" }}") - (span - (text "{{ text \"general:link.stats\" }}"))) - (text "{%- endif %}") - (b - ("class" "title") - (text "{{ config.name }}")) - (a - ("href" "https://trisua.com/t/tetratto") - (text "{{ icon \"code\" }}") - (span - (text "{{ text \"general:link.source_code\" }}"))) - ; - ; {{ icon "book" }} - ; {{ text "general:link.reference" }} - ; - (div - ("class" "title")) - (button - ("onclick" "trigger('me::switch_account')") - (text "{{ icon \"ellipsis\" }}") - (span - (text "{{ text \"general:action.switch_account\" }}"))) - (button - ("class" "red") - ("onclick" "trigger('me::logout')") - (text "{{ icon \"log-out\" }}") - (span - (text "{{ text \"auth:action.logout\" }}")))) - -(text "{%- endmacro %} {% macro user_status(other_user) -%} {% if other_user.settings.status %}") -(div - ("class" "flex items-center gap-2") - (span - (text "{{ other_user.settings.status }}")) - ; connection icon - (text "{% if (other_user.connections.LastFm[1].data and other_user.connections.LastFm[1].data.track) or (other_user.connections.Spotify[1].data and other_user.connections.Spotify[1].data.track) %} {{ icon \"music\" }} {% endif %}")) - -(text "{% elif other_user.connections.LastFm[0].data.name and other_user.connections.LastFm[1].data and other_user.connections.LastFm[1].data.track %}") -(div - ("class" "flex items-center gap-2") - (text "{{ icon \"music\" }}") - (span - (b - (text "Listening to")) - (text "{{ other_user.connections.LastFm[1].data.artist }}"))) - -(text "{% elif other_user.connections.Spotify[0].data.name and other_user.connections.Spotify[1].data and other_user.connections.Spotify[1].data.track %}") -(div - ("class" "flex items-center gap-2") - (text "{{ icon \"music\" }}") - (span - (b - (text "Listening to")) - (text "{{ other_user.connections.Spotify[1].data.artist }}"))) - -(text "{%- endif %} {%- endmacro %} {% macro user_plate(user, show_menu=false, show_kick=false, secondary=false) -%}") -(div - ("class" "flex gap-2 items-center card tiny user_plate {% if secondary -%}secondary{%- endif %}") - (a - ("href" "/@{{ user.username }}") - (text "{{ self::avatar(username=user.username, size=\"42px\", selector_type=\"username\") }}")) - (div - ("class" "flex justify-center flex-col") - ("style" "{% if show_menu or show_kick -%}width: 60%{% else %}max-width: 150px{%- endif %}") - (text "{{ self::full_username(user=user) }}") - (div - ("class" "user_status") - (text "{{ self::user_status(other_user=user) }}"))) - (text "{% if show_menu -%}") - (div - ("class" "dropdown") - (button - ("class" "camo small square") - ("onclick" "trigger('atto::hooks::dropdown', [event])") - ("exclude" "dropdown") - (text "{{ icon \"settings\" c(dropdown-arrow) }}")) - (text "{{ self::user_menu() }}")) - (text "{% elif show_kick %}") - (div - ("class" "dropdown") - ("style" "margin-left: auto") - (button - ("class" "camo small square") - ("onclick" "trigger('atto::hooks::dropdown', [event])") - ("exclude" "dropdown") - (text "{{ icon \"ellipsis\" }}")) - (div - ("class" "inner") - (button - ("class" "red") - ("onclick" "kick_member('{{ channel.id }}', '{{ user.id }}')") - (text "{{ icon \"x\" }}") - (span - (text "{{ text \"chats:action.kick_member\" }}"))))) - (text "{%- endif %}")) - -(text "{%- endmacro %} {% macro emoji_picker(element_id, render_dialog=false) -%}") -(button - ("class" "button small square quaternary") - ("onclick" "window.EMOJI_PICKER_TEXT_ID = '{{ element_id }}'; document.getElementById('emoji_dialog').showModal()") - ("title" "Emojis") - ("type" "button") - (text "{{ icon \"smile-plus\" }}")) - -(text "{% if render_dialog -%}") -(dialog - ("id" "emoji_dialog") - (div - ("class" "inner flex flex-col gap-2") - (script - ("type" "module") - ("src" "https://unpkg.com/emoji-picker-element@1.22.8/index.js")) - (emoji-picker - ("style" " - --border-radius: var(--radius); - --background: var(--color-super-raised); - --input-border-radiFus: var(--radius); - --input-border-color: var(--color-primary); - --indicator-color: var(--color-primary); - --emoji-padding: 0.25rem; - box-shadow: 0 0 4px var(--color-shadow); - ") - ("class" "w-full")) - (script - (text "setTimeout(async () => { - document.querySelector(\"emoji-picker\").customEmoji = - await trigger(\"me::emojis\"); - - const style = document.createElement(\"style\"); - style.textContent = `.custom-emoji { border-radius: 4px !important; } .category { font-weight: 600; }`; - document - .querySelector(\"emoji-picker\") - .shadowRoot.appendChild(style); - }, 150); - - document - .querySelector(\"emoji-picker\") - .addEventListener(\"emoji-click\", async (event) => { - if (event.detail.skinTone > 0) { - document.getElementById( - window.EMOJI_PICKER_TEXT_ID, - ).value += event.detail.unicode; - - document.getElementById(\"emoji_dialog\").close(); - return; - } - - if (event.detail.unicode) { - document.getElementById( - window.EMOJI_PICKER_TEXT_ID, - ).value += ` :${await ( - await fetch(\"/api/v1/lookup_emoji\", { - method: \"POST\", - body: event.detail.unicode, - }) - ).text()}:`; - } else { - document.getElementById( - window.EMOJI_PICKER_TEXT_ID, - ).value += ` :${event.detail.emoji.shortcodes[0]}:`; - } - - document.getElementById(\"emoji_dialog\").close(); - });")) - (div - ("class" "flex justify-between") - (div) - (div - ("class" "flex gap-2") - (button - ("class" "bold red quaternary") - ("onclick" "document.getElementById('emoji_dialog').close()") - ("type" "button") - (text "{{ icon \"x\" }} {{ text \"dialog:action.close\" }}")))))) - -(text "{%- endif %} {%- endmacro %} {% macro file_picker(files_list_id) -%}") -(button - ("class" "button small square quaternary") - ("onclick" "pick_file()") - ("title" "Images") - ("type" "button") - (text "{{ icon \"image-up\" }}")) - -(input - ("type" "file") - ("multiple" "") - ("accept" "image/png,image/jpeg,image/avif,image/webp") - ("style" "display: none") - ("name" "file_picker")) - -(div - ("style" "display: none") - ("id" "file_template") - (text "{{ icon \"image\" }}") - (b - ("class" "name shorter") - ("style" "overflow-wrap: normal") - (text ".file_name"))) - -(script - (text "(() => { - const input = document.querySelector(\"input[name=file_picker]\"); - const element = document.getElementById(\"{{ files_list_id }}\"); - const template = document.getElementById(\"file_template\"); - - globalThis.pick_file = () => { - input.click(); - }; - - globalThis.render_file_picker_files = () => { - element.innerHTML = \"\"; - - let idx = 0; - for (const file of input.files) { - element.innerHTML += `
${template.innerHTML.replace( - \".file_name\", - file.name, - )}
`; - - idx += 1; - } - }; - - globalThis.remove_file = (idx) => { - const files = Array.from(input.files); - files.splice(idx - 1, 1); - - // update files - const list = new DataTransfer(); - - for (item of files) { - list.items.add(item); - } - - input.files = list.files; - - // render - render_file_picker_files(); - }; - - input.addEventListener(\"change\", () => { - render_file_picker_files(); - }); - })();")) - -(text "{%- endmacro %} {% macro supporter_ad(body=\"\") -%} {% if config.stripe and not is_supporter %}") -(div - ("class" "card w-full supporter_ad") - ("ui_ident" "supporter_ad") - ("onclick" "window.location.href = '/settings#/account/billing'") - (div - ("class" "card w-full flex flex-wrap items-center gap-2 justify-between") - (text "{% if body -%}") - (b - (text "{{ body }}")) - (text "{% else %}") - (b - (text "{{ text \"general:label.supporter_motivation\" }}")) - (text "{%- endif %}") - (a - ("href" "/settings#/account/billing") - ("class" "button small") - (text "{{ icon \"heart\" }}") - (span - (text "{{ text \"general:action.become_supporter\" }}"))))) - -(text "{%- endif %} {%- endmacro %} {% macro create_post_options() -%}") -(div - ("class" "flex gap-2") - (text "{{ components::emoji_picker(element_id=\"content\", render_dialog=true) }} {% if not quoting -%} {% if is_supporter -%} {{ components::file_picker(files_list_id=\"files_list\") }} {%- endif %} {%- endif %}") - (button - ("class" "small square quaternary") - ("title" "More options") - ("onclick" "document.getElementById('post_options_dialog').showModal()") - ("type" "button") - (text "{{ icon \"ellipsis\" }}"))) - -(dialog - ("id" "post_options_dialog") - (div - ("class" "inner flex flex-col gap-2") - (div - ("id" "post_options") - ("class" "flex flex-col gap-2")) - (hr) - (div - ("class" "flex justify-between") - (div) - (div - ("class" "flex gap-2") - (button - ("class" "bold red quaternary") - ("onclick" "document.getElementById('post_options_dialog').close()") - ("type" "button") - (text "{{ icon \"x\" }} {{ text \"dialog:action.close\" }}")))) - (script - (text "setTimeout(() => { - window.POST_INITIAL_SETTINGS = { - comments_enabled: true, - reposts_enabled: true, - reactions_enabled: true, - is_nsfw: false, - content_warning: \"\", - tags: [], - }; - - window.BLANK_INITIAL_SETTINGS = JSON.stringify( - window.POST_INITIAL_SETTINGS, - ); - - const settings_fields = [ - [ - [ - \"comments_enabled\", - \"Allow people to comment on your post\", - ], - window.POST_INITIAL_SETTINGS.comments_enabled.toString(), - \"checkbox\", - ], - [ - [ - \"reposts_enabled\", - \"Allow people to repost/quote your post\", - ], - window.POST_INITIAL_SETTINGS.reposts_enabled.toString(), - \"checkbox\", - ], - [ - [ - \"reactions_enabled\", - \"Allow people to like/dislike your post\", - ], - window.POST_INITIAL_SETTINGS.reactions_enabled.toString(), - \"checkbox\", - ], - [ - [\"is_nsfw\", \"Hide from public timelines\"], - window.POST_INITIAL_SETTINGS.is_nsfw.toString(), - \"checkbox\", - ], - [ - [\"content_warning\", \"Content warning\"], - window.POST_INITIAL_SETTINGS.content_warning, - \"textarea\", - ], - [ - [\"tags\", \"Tags\"], - window.POST_INITIAL_SETTINGS.tags, - \"input\", - { - embed_html: - 'Tags should be separated by a comma.', - }, - ], - ]; - - document.getElementById(\"post_options\").innerHTML = \"\"; - trigger(\"ui::generate_settings_ui\", [ - document.getElementById(\"post_options\"), - settings_fields, - window.POST_INITIAL_SETTINGS, - { - tags: (new_tags) => { - window.POST_INITIAL_SETTINGS.tags = new_tags - .split(\",\") - .map((t) => t.trim()); - }, - }, - ]); - }, 250); - - globalThis.update_settings_maybe = async (id) => { - if ( - JSON.stringify(window.POST_INITIAL_SETTINGS) !== - window.BLANK_INITIAL_SETTINGS - ) { - await fetch(`/api/v1/posts/${id}/context`, { - method: \"POST\", - headers: { - \"Content-Type\": \"application/json\", - }, - body: JSON.stringify({ - context: window.POST_INITIAL_SETTINGS, - }), - }); - } - };")))) - -(text "{%- endmacro %}") + (body + ; the actual body only starts here + (span ("style" "color: red") (text "Hello, world!")))) diff --git a/src/lib.rs b/src/lib.rs index a857fb5..d1b74ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,7 +92,7 @@ mod test { .unwrap(); let duration = start.elapsed().unwrap(); - println!("took: {}μs", duration.as_millis()); - assert!(duration < Duration::from_millis(100)) + println!("took: {}μs", duration.as_micros()); + assert!(duration < Duration::from_micros(500)) } }