From 377027e79ae4383908b670ebeee0ca3db60b905a Mon Sep 17 00:00:00 2001 From: Phil Skentelbery Date: Wed, 10 Dec 2025 11:35:11 -0700 Subject: [PATCH] feat(templates): Add microformats2 h-feed and p-category markup for tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement Phase 2 of v1.3.0 per microformats-tags-design.md Template Updates: - templates/index.html: Add h-feed properties (u-url, enhanced p-author with u-photo/p-note, feed-level u-photo) - templates/index.html: Add p-category markup with rel="tag" to note previews - templates/note.html: Add p-category markup with rel="tag" for tags - templates/note.html: Enhance author h-card with u-photo and p-note (hidden for parsers) - templates/note.html: Document u-photo placement outside e-content per draft spec - templates/tag.html: Create new tag archive template with h-feed structure Key Decisions Applied: - Tags ordered alphabetically by display_name (ready for backend) - rel="tag" on all p-category links per microformats2 spec - Author bio (p-note) hidden with display: none for semantic parsing - Dual u-photo elements intentional for parser compatibility - Graceful fallback when author photo/bio not available Templates are backward compatible and ready for backend integration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../2025-12-10-phase2-implementation.md | 138 ++++++++++++++++++ templates/index.html | 31 +++- templates/note.html | 22 ++- templates/tag.html | 65 +++++++++ 4 files changed, 251 insertions(+), 5 deletions(-) create mode 100644 docs/design/v1.3.0/2025-12-10-phase2-implementation.md create mode 100644 templates/tag.html diff --git a/docs/design/v1.3.0/2025-12-10-phase2-implementation.md b/docs/design/v1.3.0/2025-12-10-phase2-implementation.md new file mode 100644 index 0000000..2d3f132 --- /dev/null +++ b/docs/design/v1.3.0/2025-12-10-phase2-implementation.md @@ -0,0 +1,138 @@ +# v1.3.0 Phase 2: Templates Implementation Report + +**Date**: 2025-12-10 +**Developer**: StarPunk Developer Agent +**Phase**: Phase 2 - Templates +**Status**: Complete + +## Summary + +Successfully implemented Phase 2 of the v1.3.0 Microformats2 Compliance and Tags feature. All template updates have been completed according to the design specification in `microformats-tags-design.md`. + +## Changes Implemented + +### 1. Updated `templates/index.html` + +Added h-feed required properties per microformats2 specification: + +- **u-url**: Added self-referencing feed URL (hidden with `display: none`) +- **Enhanced p-author h-card**: + - Added `u-photo` when author.photo is available + - Added `p-note` (bio) when author.note is available + - Maintained hidden state for semantic parsing +- **Feed-level u-photo**: Added duplicate u-photo for broad parser compatibility (intentional per architect decision) +- **p-category markup**: Added tag links with `rel="tag"` attribute on note previews + - Uses `tag.name` for URL (normalized) + - Displays `tag.display_name` (preserves case) + +### 2. Updated `templates/note.html` + +Enhanced individual note pages with: + +- **u-photo placement documentation**: Added comment explaining that u-photo must be direct child of h-entry (not inside e-content) per draft spec +- **p-category markup**: Added tags section in footer with: + - `p-category` class on each tag link + - `rel="tag"` attribute per microformats2 specification + - Links to `/tag/` route +- **Enhanced author h-card**: + - Reordered: photo first, then name/url + - Added `p-note` (bio) with `display: none` for semantic-only parsing + - Graceful fallback when photo or bio not available + +### 3. Created `templates/tag.html` + +New template for tag archive pages: + +- Extends `base.html` and imports `display_media` macro +- Uses h-feed structure with p-name showing tag display name +- Shows all notes with the tag (no pagination for v1.3.0) +- Reuses same note preview structure as index.html for consistency: + - Conditional p-name for explicit titles + - Media previews + - Truncated e-content (300 chars) + - Full note metadata including tags, timestamp, author +- Empty state message when no notes found +- Back navigation link to homepage + +## Key Design Decisions Applied + +All architect Q&A decisions were correctly implemented: + +1. **Tag ordering**: Alphabetically by display_name (case-insensitive) - ready for backend +2. **rel="tag" attribute**: Added to all p-category links per specification +3. **Author bio visibility**: Hidden with `display: none` - semantic only for parsers +4. **Dual u-photo elements**: Maintained intentionally for parser compatibility +5. **u-photo placement**: Verified and documented as correct (outside e-content) + +## Files Changed + +### Modified Files +- `/home/phil/Projects/starpunk/templates/index.html` +- `/home/phil/Projects/starpunk/templates/note.html` + +### New Files +- `/home/phil/Projects/starpunk/templates/tag.html` + +## Verification + +### Template Structure Checks + +All templates follow the architect's microformats2 specifications: + +- h-feed on index and tag pages with required properties (p-name, u-url, p-author, u-photo) +- h-entry on note previews and individual pages with required properties (e-content, dt-published, u-url) +- p-category markup with rel="tag" on all tag links +- Enhanced h-card with u-photo and p-note where available +- Graceful fallback for missing author properties + +### Code Quality + +- Clean, readable Jinja2 syntax +- Consistent with existing template patterns +- Comprehensive comments explaining microformats decisions +- No hardcoded values - all URLs use `url_for()` +- Proper conditional rendering for optional fields + +## Dependencies for Next Phase + +Templates are ready and will work once backend implementation is complete: + +- **Phase 1 backend**: Must implement `tags` module and update `notes.py` to populate `note.tags` +- **Phase 3 routes**: Tag links will 404 until `/tag/` route is implemented +- **Context processor**: Author data already injected via existing context processor + +## Testing Notes + +Templates can be manually inspected but full testing requires: + +1. Backend implementation to populate `note.tags` property +2. Tag route implementation for tag archive pages +3. Author discovery system (already exists) providing photo and bio data + +## Standards Compliance + +All changes follow: + +- Microformats2 h-feed specification +- Microformats2 h-entry specification +- Microformats2 h-card specification +- Microformats2 p-category specification with rel="tag" +- Project coding standards for templates +- Existing template patterns and structure + +## Issues Encountered + +None. All template updates completed successfully according to specification. + +## Next Steps + +Ready for Phase 3 implementation: +1. Add tag archive route to `starpunk/routes/public.py` +2. Update admin forms for tag editing +3. Load tags in public routes (index, note) + +## Notes + +- The test failure in `test_migration_race_condition.py` is a pre-existing flaky test unrelated to template changes +- Templates are backward compatible - work fine when `note.tags` is empty or None +- No CSS changes required per architect decision (out of scope) diff --git a/templates/index.html b/templates/index.html index d0cb7ab..9a4288e 100644 --- a/templates/index.html +++ b/templates/index.html @@ -5,15 +5,33 @@ {% block content %}
+ {# h-feed required properties per microformats.org/wiki/h-feed #}

{{ config.SITE_NAME or 'Recent Notes' }}

- {# Feed-level author h-card (per Q24) #} + {# u-url for feed (self-reference) #} + + + {# Feed-level author h-card with all properties #} + {# Hidden because it's semantic-only markup for parsers, not visual content #} + {# The visible author display is on individual note pages #} {% if author %} {% endif %} + {# u-photo at feed level (duplicate of author photo for broad parser compatibility) #} + {# Some parsers expect feed-level u-photo, others look inside author h-card #} + {% if author and author.photo %} + + {% endif %} + {% if notes %} {% for note in notes %}
@@ -41,6 +59,15 @@ + {# Tags in preview #} + {% if note.tags %} + + {% for tag in note.tags %} + + {% endfor %} + + {% endif %} + {# Author h-card nested in each h-entry (per Q20) #} {% if author %}
diff --git a/templates/note.html b/templates/note.html index 460d88b..d09b2e9 100644 --- a/templates/note.html +++ b/templates/note.html @@ -13,7 +13,8 @@

{{ note.title }}

{% endif %} - {# Media display at TOP (v1.2.0 Phase 3, per ADR-057) #} + {# u-photo placement: Per draft spec, u-photo must be direct child of h-entry, #} + {# NOT inside e-content. Media is rendered ABOVE e-content to meet this requirement. #} {{ display_media(note.media) }} {# e-content: note content BELOW media (per ADR-057) #} @@ -36,14 +37,29 @@ {% endif %} + {# Tags / Categories #} + {# rel="tag" per microformats2 p-category specification #} + {% if note.tags %} +
+ {% for tag in note.tags %} + + {% endfor %} +
+ {% endif %} + {# Author h-card (nested within h-entry per Q20) #} {% if author %}
+ {% if author.photo %} + {{ author.name or 'Author' }} + {% endif %} {{ author.name or author.url or author.me }} - {% if author.photo %} - {{ author.name or 'Author' }} + {% if author.note %} + {% endif %}
{% endif %} diff --git a/templates/tag.html b/templates/tag.html new file mode 100644 index 0000000..58780c1 --- /dev/null +++ b/templates/tag.html @@ -0,0 +1,65 @@ +{% extends "base.html" %} +{% from "partials/media.html" import display_media %} + +{% block title %}{{ tag.display_name }} - StarPunk{% endblock %} + +{% block content %} +
+

Notes tagged "{{ tag.display_name }}"

+ + {% if notes %} + {% for note in notes %} +
+ {# Detect if note has explicit title (starts with # heading) - per Q22 #} + {% set has_explicit_title = note.content.strip().startswith('#') %} + + {# p-name only if note has explicit title (per Q22) #} + {% if has_explicit_title %} +

{{ note.title }}

+ {% endif %} + + {# Media preview (if available) #} + {{ display_media(note.media) }} + + {# e-content: note content (preview) #} +
+ {{ note.html[:300]|safe }}{% if note.html|length > 300 %}...{% endif %} +
+ + +
+ {% endfor %} + {% else %} +

No notes with this tag.

+ {% endif %} + + +
+{% endblock %}