feat: autolink URLs in list items, link picker, and inline edit

- Add autolink Jinja2 filter to detect URLs and make them clickable
- Add link picker dropdown to insert existing link URLs into list item content
- Add inline edit with link picker on each list item row
- Apply autolink filter on list detail and focus available list items

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 17:58:21 +00:00
parent 7a2c6d3f2a
commit 97027f2de4
6 changed files with 199 additions and 8 deletions

49
core/template_filters.py Normal file
View File

@@ -0,0 +1,49 @@
"""Jinja2 custom template filters."""
import re
from markupsafe import Markup, escape
# Match http(s):// URLs and bare www. URLs
_URL_RE = re.compile(
r'(https?://[^\s<>\"\'\]]+|www\.[^\s<>\"\'\]]+)',
re.IGNORECASE,
)
# Trailing punctuation that usually isn't part of the URL
_TRAILING_PUNCT = re.compile(r'[.,;:!?\)]+$')
def autolink(text):
"""Detect URLs in plain text and wrap them in clickable <a> tags.
Escapes all content first (XSS-safe), then replaces URL patterns.
Returns Markup so Jinja2 won't double-escape.
"""
if not text:
return text
escaped = str(escape(text))
def _replace(match):
raw = match.group(0)
# Strip trailing punctuation that got captured
trail = ''
m = _TRAILING_PUNCT.search(raw)
if m:
trail = m.group(0)
raw = raw[:m.start()]
href = raw
if raw.lower().startswith('www.'):
href = 'https://' + raw
# Truncate display text for readability
display = raw if len(raw) <= 60 else raw[:57] + '...'
return (
f'<a href="{href}" target="_blank" rel="noopener noreferrer" '
f'class="autolink">{display}</a>{trail}'
)
result = _URL_RE.sub(_replace, escaped)
return Markup(result)