put images in their own lane

This commit is contained in:
2025-08-16 15:50:44 +03:00
parent 4a8f87d64a
commit cf89070639
5 changed files with 99 additions and 31 deletions

View File

@ -2,7 +2,7 @@ from .babycode_parser import Parser
from markupsafe import escape
import re
BABYCODE_VERSION = 1
BABYCODE_VERSION = 2
NAMED_COLORS = [
'black', 'silver', 'gray', 'white', 'maroon', 'red',
@ -35,7 +35,19 @@ NAMED_COLORS = [
'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen',
]
def tag_code(children, attr):
def is_tag(e, tag):
if e is None:
return False
if isinstance(e, str):
return False
if e['type'] != 'bbcode':
return False
return e['name'] == tag
def is_text(e):
return isinstance(e, str)
def tag_code(children, attr, surrounding):
is_inline = children.find('\n') == -1
if is_inline:
return f"<code class=\"inline-code\">{children}</code>"
@ -49,7 +61,7 @@ def tag_list(children):
list_body = re.sub(r"\n\n+", "\1", list_body)
return " ".join([f"<li>{x}</li>" for x in list_body.split("\1") if x])
def tag_color(children, attr):
def tag_color(children, attr, surrounding):
hex_re = r"^#?([0-9a-f]{6}|[0-9a-f]{3})$"
potential_color = attr.lower().strip()
@ -63,31 +75,39 @@ def tag_color(children, attr):
# return just the way it was if we can't parse it
return f"[color={attr}]{children}[/color]"
def tag_spoiler(children, attr):
def tag_spoiler(children, attr, surrounding):
spoiler_name = attr if attr else "Spoiler"
content = f"<div class='accordion-content post-accordion-content hidden'>{children}</div>"
container = f"""<div class='accordion hidden'><div class='accordion-header'><button type='button' class='accordion-toggle'>+</button><span>{spoiler_name}</span></div>{content}</div>"""
return container
def tag_image(children, attr, surrounding):
img = f"<img class=\"post-image\" src=\"{attr}\" alt=\"{children}\">"
if not is_tag(surrounding[0], 'img'):
img = f"<div class=post-img-container>{img}"
if not is_tag(surrounding[1], 'img'):
img = f"{img}</div>"
return img
TAGS = {
"b": lambda children, attr: f"<strong>{children}</strong>",
"i": lambda children, attr: f"<em>{children}</em>",
"s": lambda children, attr: f"<del>{children}</del>",
"u": lambda children, attr: f"<u>{children}</u>",
"b": lambda children, attr, _: f"<strong>{children}</strong>",
"i": lambda children, attr, _: f"<em>{children}</em>",
"s": lambda children, attr, _: f"<del>{children}</del>",
"u": lambda children, attr, _: f"<u>{children}</u>",
"img": lambda children, attr: f"<div class=\"post-img-container\"><img class=\"block-img\" src=\"{attr}\" alt=\"{children}\"></div>",
"url": lambda children, attr: f"<a href={attr}>{children}</a>",
"quote": lambda children, attr: f"<blockquote>{children}</blockquote>",
"img": tag_image,
"url": lambda children, attr, _: f"<a href={attr}>{children}</a>",
"quote": lambda children, attr, _: f"<blockquote>{children}</blockquote>",
"code": tag_code,
"ul": lambda children, attr: f"<ul>{tag_list(children)}</ul>",
"ol": lambda children, attr: f"<ol>{tag_list(children)}</ol>",
"ul": lambda children, attr, _: f"<ul>{tag_list(children)}</ul>",
"ol": lambda children, attr, _: f"<ol>{tag_list(children)}</ol>",
"big": lambda children, attr: f"<span style='font-size: 2rem;'>{children}</span>",
"small": lambda children, attr: f"<span style='font-size: 0.75rem;'>{children}</span>",
"big": lambda children, attr, _: f"<span style='font-size: 2rem;'>{children}</span>",
"small": lambda children, attr, _: f"<span style='font-size: 0.75rem;'>{children}</span>",
"color": tag_color,
"center": lambda children, attr: f"<div style='text-align: center;'>{children}</div>",
"right": lambda children, attr: f"<div style='text-align: right;'>{children}</div>",
"center": lambda children, attr, _: f"<div style='text-align: center;'>{children}</div>",
"right": lambda children, attr, _: f"<div style='text-align: right;'>{children}</div>",
"spoiler": tag_spoiler,
}
@ -156,9 +176,8 @@ def babycode_to_html(s):
parser.valid_emotes = EMOJI.keys()
elements = parser.parse()
# print(elements)
out = ""
def fold(element, nobr):
def fold(element, nobr, surrounding):
if isinstance(element, str):
if nobr:
return element
@ -167,10 +186,16 @@ def babycode_to_html(s):
match element['type']:
case "bbcode":
c = ""
for child in element['children']:
for i in range(len(element['children'])):
child = element['children'][i]
_surrounding = (
element['children'][i - 1] if i-1 >= 0 else None,
element['children'][i + 1] if i+1 < len(element['children']) else None
)
_nobr = element['name'] == "code" or element['name'] == "ul" or element['name'] == "ol"
c = c + fold(child, _nobr)
res = TAGS[element['name']](c, element['attr'])
c = c + fold(child, _nobr, _surrounding)
res = TAGS[element['name']](c, element['attr'], surrounding)
return res
case "link":
return f"<a href=\"{element['url']}\">{element['url']}</a>"
@ -178,6 +203,13 @@ def babycode_to_html(s):
return EMOJI[element['name']]
case "rule":
return "<hr>"
for e in elements:
out = out + fold(e, False)
# for e in elements:
# out = out + fold(e, False)
for i in range(len(elements)):
e = elements[i]
surrounding = (
elements[i - 1] if i-1 >= 0 else None,
elements[i + 1] if i+1 < len(elements) else None
)
out = out + fold(e, False, surrounding)
return out

View File

@ -126,8 +126,8 @@
<code class="inline-code">[img=https://forum.poto.cafe/avatars/default.webp]the Python logo with a cowboy hat[/img]</code>
{{ '[img=/static/avatars/default.webp]the Python logo with a cowboy hat[/img]' | babycode | safe }}
</p>
<p>Text inside the tag becomes the alt text. The attribute is the image URL.</p>
<p>Images will always break up a paragraph and will get scaled down to a maximum of 400px. The text inside the tag will become the image's alt text.</p>
<p>Text inside the tag becomes the alt text. The attribute is the image URL. The text inside the tag will become the image's alt text.</p>
<p>Images will always break up a paragraph and will get scaled down to a maximum of 400px. However, if there is no space between consecutive <code class="inline-code">[img]</code> tags, </p>
<p>Multiple images attached to a post can be clicked to open a dialog to view them.</p>
</section>
<section class="babycode-guide-section">

View File

@ -142,8 +142,22 @@ document.addEventListener("DOMContentLoaded", () => {
//lightboxes
lightboxObj = constructLightbox();
document.body.appendChild(lightboxObj.dialog);
const postImages = document.querySelectorAll(".post-inner img.block-img");
function setImageMaxSize(img) {
const { maxWidth: origMaxWidth } = getComputedStyle(img);
if (img.naturalWidth >= parseInt(origMaxWidth)) {
return;
}
img.style.maxWidth = img.naturalWidth + "px";
}
const postImages = document.querySelectorAll(".post-inner img.post-image");
postImages.forEach(postImage => {
if (postImage.complete) {
setImageMaxSize(postImage);
} else {
postImage.addEventListener("load", () => setImageMaxSize(postImage));
}
const belongingTo = postImage.closest(".post-inner");
const images = lightboxImages.get(belongingTo) ?? [];
images.push({

View File

@ -511,10 +511,21 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus, select:focus
width: 100%;
}
.block-img {
.post-img-container {
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.post-image {
object-fit: contain;
max-width: 400px;
max-height: 400px;
max-width: 300px;
max-height: 300px;
min-width: 200px;
min-height: 200px;
flex: 1 1 0%;
width: auto;
height: auto;
}
.thread-info-container {

View File

@ -512,10 +512,21 @@ input[type="text"], input[type="password"], textarea, select {
}
}
.block-img {
.post-img-container {
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.post-image {
object-fit: contain;
max-width: 400px;
max-height: 400px;
min-width: 200px;
min-height: 200px;
flex: 1 1 0%;
width: auto;
height: auto;
}
.thread-info-container {