- Namespace all plugin CSS classes with ms- prefix (20 BEM blocks)
- Update all 19 templates to match prefixed class names
- Replace visually-hidden with -!-visually-hidden (core convention)
- Remove inline onerror handlers from avatar/photo images
- Remove dead source-type SVG icons (Fediverse/Bluesky/Web)
Confab-Link: http://localhost:8080/sessions/bb4a6ec4-b711-48cd-b3d7-942ec2a9851d
Add ability to mark all items from a specific feed/source as read at once,
instead of clicking each item individually. The mark-read button becomes a
split button group with a caret that opens a popover offering "Mark [source]
as read". Items without a feedId (AP items) keep the simple button.
Confab-Link: http://localhost:8080/sessions/a477883d-4aef-4013-983c-ce3d3157cfba
- Store feedType (rss/atom/jsonfeed/hfeed) on feed documents during polling
- Display feed type badge in reader feeds view
- Detect duplicate feeds across all channels using URL normalization
(trailing slashes, http/https variants)
- Show clear error message when subscribing to a feed that already exists
- Handle duplicates in both reader UI and Microsub API (HTTP 409)
- Bump version to 1.0.44
Confab-Link: http://localhost:8080/sessions/f1d9ff88-e037-4d6e-b595-ed6d9e00898e
Stripped dedup skeletons and unread items older than 30 days are now
hard-deleted on startup. Previously, stripped skeletons accumulated
forever and unread items had no expiry, causing unbounded collection
growth.
Confab-Link: http://localhost:8080/sessions/4d40ef89-a713-48c1-b4ed-0ffafca25677
Items from channels with userId: null (created during earlier setup)
appeared in the unified timeline but had no _channelUid, causing the
mark-read JS handler to silently abort. Fall back to channelId (MongoDB
ObjectId) when channelUid is unavailable, and resolve it server-side
via getChannelById.
Confab-Link: http://localhost:8080/sessions/4d40ef89-a713-48c1-b4ed-0ffafca25677
Adds a save icon to the item-card action bar that POSTs to
/readlater/save when the readlater plugin is installed. Button
only renders if application.readlaterEndpoint is set. Includes
JS handlers in both channel and timeline views.
Items in the cross-channel timeline now carry per-item channel UID so
the mark-read API call can target the correct channel. Adds the same
dismiss animation used in the channel view.
- Replace confusing colored left border with readable channel badge pills
- Add breadcrumb navigation across all reader views
- Default to timeline view when clicking Reader in sidebar
- Remove redundant back-link from channel view (breadcrumbs handle it)
Nunjucks slice(0, 4) creates 0 chunks (not Array.slice behavior),
producing an empty array. Photos and categories were never rendered
in item-card and actor views. Also add image proxying to reader
controller matching the Microsub API.
The normalizer fix (1.0.35) only applies to newly ingested items.
Existing items in MongoDB lack photo arrays because dedup prevents
re-processing. Add the same extractImagesFromHtml() fallback in
transformToJf2() so images are extracted from content.html at read
time, making existing xkcd comics and photo posts display immediately.
Feeds like xkcd embed images as <img> tags in the RSS description
rather than using enclosures or media:content. Similarly, h-feed
photo posts may have images only in e-content HTML without explicit
u-photo properties.
Add extractImagesFromHtml() that pulls <img> src URLs from sanitized
HTML content as a fallback when no explicit photos exist. Applied to
all three normalizers (RSS/Atom, JSON Feed, h-feed).
This makes comics, photo posts, and other image-centric feeds display
their images in the reader timeline card's photo grid.
Feedparser passes through HTML entities (“, ’, etc.) as
literal strings in item titles. Nunjucks auto-escaping then double-encodes
them, causing entities to render literally in the reader UI.
Apply sanitizeHtml with no allowed tags to decode entities in title fields
across RSS/Atom, JSON Feed, and feed metadata normalizers.
- Add lib/feeds/capabilities.js: detect feed source capabilities
(webmention, micropub, platform type) on subscribe and first fetch
- Enrich timeline items with source_type from capabilities or URL inference
- Add protocol indicator icons (Bluesky/Mastodon/web) to item-card.njk
- Auto-select syndication target in compose based on interaction URL protocol
- Modified: follow.js, processor.js, reader.js, item-card.njk
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add actor profile page with outbox fetcher for viewing AP actor posts
- Add follow/unfollow buttons on actor profile (delegates to AP plugin)
- Add AP actor link on item cards for posts from ActivityPub sources
- Add ensureActivityPubChannel() for auto-creating Fediverse channel
- Add AP-aware item storage with dedup, attachments, and categories
- Add CSS styles for actor profile cards and AP-specific UI elements
- Bump version to 1.0.31
- Add SSRF blocklist to media proxy (block private/internal IPs)
- Escape regex in searchItems() to prevent ReDoS
- Sanitize webmention content.html before storage (XSS prevention)
- Return 404 instead of redirect on failed media proxy (open redirect fix)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add translations for de, es, es-419, fr, hi, id, it, nl, pl, pt, pt-BR, sr, sv, zh-Hans-CN
to match upstream Indiekit's supported locales. Also add missing localesDirectory getter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevents dateString.split crash when Nunjucks | date filter receives
Date objects from MongoDB. Audit timestamps (createdAt, updatedAt,
lastFetchedAt, etc.) now use .toISOString(). Query-used fields
(published, nextFetchAt) kept as Date objects for MongoDB compatibility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add date filter to channel unread counts (UNREAD_RETENTION_DAYS = 30)
- Update getUnreadCount to also filter by date
- Prevents inflated counts from old accumulated items
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace invalid icons (edit→updatePost, discover→syndicate, refresh→repost)
- Remove date("relative") filter that fails on Date objects
- Bump to 1.0.25
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Integrate updateFeedStatus into polling processor for health tracking
- Add feed management UI showing status (active/error), errors, actions
- Add edit feed URL feature to change non-RSS URLs to actual feeds
- Add rediscover feature to run feed discovery and update URL
- Add refresh button to force immediate poll
- Update UI to use Indiekit's badge/button classes (badge--green/red, button--warning)
- Add routes: /feeds/:feedId/edit, /feeds/:feedId/rediscover, /feeds/:feedId/refresh
Fixes broken feeds by allowing users to:
1. Edit URL directly to the RSS/Atom feed
2. Click "Rediscover" to auto-find the feed from a blog URL
3. View error details and consecutive error counts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Runs cleanupAllReadItems on server startup to clean up accumulated
read items from all channels, keeping only the 30 most recent per
channel per user.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add flex-wrap to .item-actions so all buttons remain visible on
narrow screens. Previously the mark-as-read button was cut off.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Prevents database accumulation by automatically deleting older read
items when marking items as read. Keeps the 30 most recent read items
per channel per user to allow revisiting recently read content.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add bookmark button to item-card.njk (timeline view)
- Add mark-read button to item.njk (full item view)
- Add JavaScript handler for mark-read on item page
- Pass channel info to item template for mark-read API call
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The JavaScript was POSTing to /microsub/reader but the Microsub API
is at /microsub. Fixed by computing parent path from baseUrl.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add countReadItems function to storage/items.js
- Update getTimelineItems to filter out read items by default
- Add showRead query param support to channel controller
- Update channel.njk with show/hide read toggle buttons
- Add "All caught up!" state when all items are read
- Add JavaScript handler for individual mark-read buttons
- Mark-read now hides the item with smooth animation
- Add locale strings: showRead, hideRead, allRead
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Restore the "Mark all as read" button functionality in channel view.
The button form posts to /api/mark-read but the route and controller
function were missing from the standalone repo.
- Add markAllRead function to reader controller
- Add /api/mark-read route to index.js
- Import markItemsRead from storage/items.js
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fetch syndication targets from Micropub config
- Display checkboxes for each target in compose form
- Include mp-syndicate-to in Micropub request
- Include optional content/comments for likes and reposts
Fork of @indiekit/endpoint-microsub with customizations.
Enables subscribing to feeds and reading content using the Microsub protocol.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>