Commit Graph

126 Commits

Author SHA1 Message Date
Ricardo
7611dba40f feat: remove quick reply, streamline blog reply (v2.7.0)
Remove the quick-reply code path entirely — all replies now go through
Micropub as blog posts. Quick replies created orphan URLs that served
raw JSON-LD to browsers and caused unreadable links in conversations.

- Delete quick-reply controller (note-object.js) and route
- Remove ap_notes collection registration
- Simplify compose form: no mode toggle, no character counter
- Remove quick-reply CSS and locale strings

Confab-Link: http://localhost:8080/sessions/d116ad5b-ef8a-424e-9ebe-76c06bef1df6
2026-03-04 17:33:02 +01:00
Ricardo
ec41fec366 fix: dark mode color comfort — use adaptive tokens, add dark overrides
Replace undefined --color-accent/--color-neutral-lighter with Indiekit tokens.
Swap --color-primary to --color-primary-on-background for all text/links.
Swap --color-red45 to --color-error in danger contexts (auto dark adapt).
Add @media (prefers-color-scheme: dark) block: softer action button colors
(red80, green90), pastel post-type borders, toned-down notification glow,
softened post detail highlight, light-tinted card shadows.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 20:51:14 +01:00
Ricardo
9332421890 feat: visual polish, focus-point cropping, blurhash placeholders (Release 8)
Card styling: softer 8px radius, subtle box-shadow elevation, hover enhancement.
Action buttons: borderless with color-coded hover states via color-mix().
Typography: tighter line-height (4/3), larger avatars (44px), gallery images (220px).
Focus-point cropping: convert Mastodon focus.x/y to CSS object-position.
Blurhash placeholders: decode DC component to background-color on images.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 19:26:38 +01:00
Ricardo
b9fc98f40c feat: content enhancements — URL shortening, hashtag collapse, bot badge, edit indicator (Release 7)
Shorten long URLs in post content (30 char display limit with tooltip).
Collapse hashtag-heavy paragraphs into expandable <details> toggle.
Show BOT badge for Service/Application actors. Show pencil icon for
edited posts with hover tooltip showing edit timestamp.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 16:40:01 +01:00
Ricardo
fca1738bd3 feat: skeleton loaders replace loading text (Release 6)
Animated card-shaped placeholders with shimmer effect shown during
content loading instead of plain "Loading..." text. Applied to reader,
tag timeline, and explore tabs (both first-load and load-more states).

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 15:48:59 +01:00
Ricardo
2d2dcaec7d feat: interaction counts on timeline cards (Release 5)
Extract reply/boost/like counts from AP Collections (getReplies,
getLikes, getShares) and Mastodon API (replies_count, reblogs_count,
favourites_count). Display counts next to interaction buttons with
optimistic updates on like/boost actions.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 14:30:40 +01:00
Ricardo
c243b70629 feat: enriched media model with ALT badges (Release 3+4)
Change photo storage from bare URL strings to objects with url, alt,
width, height (AP) plus blurhash and focus (Mastodon API). Templates
handle both old string and new object format for backward compat.

Add ALT text badges on gallery images — click to expand the full
alt text in an overlay. Renders in both reader and explore views.

Also pass alt text through to lightbox and quote embed photos.

Bump version to 2.5.3.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 13:46:58 +01:00
Ricardo
e34d9c124d feat: relative timestamps in reader (Release 2)
Add Alpine.js directive x-relative-time that converts absolute dates
to human-friendly relative strings: just now, 5m, 3h, 2d, Mar 3.
Updates every 60s for posts less than 24h old. Server-rendered absolute
time stays as no-JS fallback and hover tooltip.

Applied to item cards, quote embeds, and notification cards.

Bump version to 2.5.2.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 13:34:01 +01:00
Ricardo
02d449d03c feat: render custom emoji in reader (Release 1)
Extract custom emoji from ActivityPub objects (Fedify Emoji tags) and
Mastodon API (status.emojis, account.emojis). Replace :shortcode:
patterns with <img> tags in the unified processing pipeline.

Emoji rendering applies to post content, author display names, boost
attribution, and quote embed authors. Uses the shared postProcessItems()
pipeline so both reader and explore views get emoji automatically.

Bump version to 2.5.1.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 13:13:28 +01:00
Ricardo
ab2363d123 docs: update CLAUDE.md and README.md for v2.5.0
Document unified item processing pipeline (gotcha #23), parameterized
infinite scroll component (gotcha #24), quote embeds (gotcha #25).
Update architecture tree with new modules and controllers. Expand
route table and admin UI pages with explore, tag timeline, post detail,
and API endpoints. Add reader features (explore, hashtags, quotes,
link previews, read tracking, infinite scroll) to README.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 13:03:48 +01:00
Ricardo
af2f899073 refactor: unify reader and explore processing pipeline (Release 0)
Extract shared item-processing.js module with postProcessItems(),
applyModerationFilters(), buildInteractionMap(), applyTabFilter(),
renderItemCards(), and loadModerationData(). All controllers (reader,
api-timeline, explore, hashtag-explore, tag-timeline) now flow through
the same pipeline.

Unify Alpine.js infinite scroll into single parameterized
apInfiniteScroll component configured via data attributes, replacing
the separate apExploreScroll component.

Also adds fetchAndStoreQuote() for quote enrichment and on-demand
quote fetching in post-detail controller.

Bump version to 2.5.0.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 12:48:40 +01:00
Ricardo
9fa3412875 fix: chain dropIndex before createIndex on ap_muted to prevent race condition
The non-async init() fired dropIndex and createIndex concurrently,
causing MongoDB to abort the index build (IndexBuildAborted error 276).
Chain createIndex via .then() so it runs after the drop completes.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-02 11:40:43 +01:00
Ricardo
76571b00ac chore: bump version to 2.4.0
Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-02 10:54:26 +01:00
Ricardo
508ac75363 feat: new posts banner, mark-as-read on scroll, unread filter
- Poll every 30s for new items, show sticky "N new posts — Load" banner
- IntersectionObserver marks cards as read at 50% visibility, batches to
  server every 5s
- Read cards fade to 70% opacity, full opacity on hover
- "Unread" toggle in tab bar filters to unread-only items
- New API: GET /api/timeline/count-new, POST /api/timeline/mark-read

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-02 10:54:11 +01:00
Ricardo
68aadb6ff2 chore: bump version to 2.3.0
Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-02 10:33:20 +01:00
Ricardo
120f2ee00e feat: render quoted posts as embedded cards in reader
Extract quoteUrl from Fedify Note objects (supports Mastodon, Misskey,
Fedibird quote formats). Fetch quoted post data asynchronously on inbox
receive and on-demand in post detail view. Render as rich embed card
with author avatar, handle, content, and timestamp.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-02 10:33:11 +01:00
Ricardo
abf1b94bd6 feat: migrate Fedify KV store and plugin cache from MongoDB to Redis
Replace unbounded ap_kv MongoDB collection (169K docs, 49MB) with Redis:
- Fedify KV store uses @fedify/redis RedisKvStore (native TTL support)
- Plugin cache (fedidb, batch-refollow state, migration flags) uses new
  redis-cache.js utility with indiekit: key prefix
- All controllers updated to remove kvCollection parameter passing
- Addresses OOM kills caused by ap_kv growing ~14K entries/day
2026-03-01 16:26:17 +01:00
Ricardo
5c2fd09f8f fix: normalize category to array in my-profile controller
When a post has a single category, Indiekit stores it as a string
(e.g. "Fraude") rather than an array. Nunjucks iterates strings
character by character, producing hashtag pills like #F #r #a #u #d #e.
2026-02-28 18:21:32 +01:00
Ricardo
ecba2b5748 fix: pass CSRF token to API-rendered item cards
The explore and hashtag API controllers rendered ap-item-card.njk with
csrfToken: "" causing Like/Boost/Save buttons in tab panels to fail
with 403 Invalid CSRF token. Now generates a proper token from the
session via getToken().
2026-02-28 17:31:54 +01:00
Ricardo
35f1f13096 chore: bump version to 2.1.0 2026-02-28 16:51:06 +01:00
Ricardo
55baa7cef5 feat: replace explore deck layout with full-width tabbed design
Replace the cramped deck/column layout on the explore page with a
tabbed interface. Three tab types: Search (always first), Instance
(pinned with local/federated badge), and Hashtag (aggregated across
all pinned instances).

- New ap_explore_tabs collection replaces ap_decks (clean start)
- Tab CRUD API: add, remove, reorder with CSRF/SSRF validation
- Per-tab infinite scroll with IntersectionObserver + AbortController
- Hashtag tabs query up to 10 instances in parallel, merge by date,
  deduplicate by URL
- WAI-ARIA tabs pattern with arrow key navigation
- LRU cache (5 tabs) for tab content
- Extract shared explore-utils.js (validators + status mapping)
- Remove all old deck code (JS, CSS, controllers, locale strings)
2026-02-28 16:30:48 +01:00
Ricardo
f97e9a82f4 chore: remove dev plans from published repo
Plans moved to central /home/rick/code/indiekit-dev/docs/plans/
2026-02-27 17:18:30 +01:00
Ricardo
611bd3661c feat: add save-for-later button to activitypub reader
Adds a save button to the AP item card action bar that POSTs to
/readlater/save when the readlater plugin is installed. Uses Alpine.js
for optimistic UI update. Button only renders if
application.readlaterEndpoint is set.
2026-02-27 16:02:48 +01:00
Ricardo
cfc5af8092 fix: rename tag template variable to hashtag to avoid Nunjucks macro collision
The base layout default.njk imports a `tag` component macro which shadows
the controller's `tag` variable in function/filter argument contexts.
Renaming to `hashtag` eliminates the collision entirely.
2026-02-27 14:05:44 +01:00
Ricardo
41bde656e4 chore: bump version to 2.0.34 2026-02-27 13:18:15 +01:00
Ricardo
39866a4d4b fix: tag timeline empty state rendering Nunjucks macro instead of hashtag name
The i18n __() call with `tag` as second argument collided with the `tag`
Nunjucks component macro imported by default.njk. Use | replace filter
instead of sprintf-style substitution to avoid the scoping issue.
2026-02-27 13:18:07 +01:00
Ricardo
10dfcf8d63 chore: bump version to 2.0.33 2026-02-27 12:10:41 +01:00
Ricardo
25513c7ea5 feat: add breadcrumb navigation across all ActivityPub UI pages
Document.njk pages (followers, following, activities, featured, tags,
profile, migrate) get parent breadcrumbs via the upstream heading
component. Reader pages (explore, notifications, compose, moderation,
tag timeline, post detail, remote profile, my profile) get a new
breadcrumb nav bar in ap-reader.njk layout.
2026-02-27 12:10:31 +01:00
Ricardo
4aff8895c0 chore: bump version to 2.0.32 2026-02-27 11:47:13 +01:00
Ricardo
0a0998cfcc fix: FediDB instance search now filters by query
The FediDB /v1/servers API ignores the q parameter and always returns
the same top-10 ranked list. Changed to fetch the full 40-server list
once (cached 24h) and filter server-side by domain/software match.
2026-02-27 11:45:19 +01:00
Ricardo
3bedcac4b7 chore: bump version to 2.0.31 2026-02-27 11:25:03 +01:00
Ricardo
145e329d2f feat: add TweetDeck-style deck layout for explore view
Users can favorite instances (with local or federated scope) as persistent
columns in a multi-column deck view. Each column streams its own public
timeline with independent infinite scroll. Includes two-tab explore UI
(Search + Decks), deck CRUD API with CSRF/SSRF protection, 8-deck limit,
responsive CSS Grid layout, and scope badges.
2026-02-27 11:24:53 +01:00
Ricardo
525abcbf84 feat: expose signed avatar resolver for cross-plugin use
Register resolveActorAvatar() on Indiekit.config.application during
init(). Uses Fedify's authenticated document loader to fetch actor
profiles from servers with Authorized Fetch enabled (e.g., hachyderm.io,
indieweb.social). Called by the conversations plugin's avatar backfill.
2026-02-27 11:01:56 +01:00
Ricardo
cd05cb68e6 chore: bump version to 2.0.29 2026-02-27 09:27:06 +01:00
Ricardo
cee0050be8 feat: add FediDB-powered autocomplete for explore and reader lookup
- Add FediDB API client (lib/fedidb.js) with MongoDB caching (24h TTL)
  for instance search, timeline support checks, and popular accounts
- Explore page: instance input now shows autocomplete suggestions from
  FediDB with software type, MAU count, and timeline support indicator
  (checkmark/cross) via background pre-check
- Reader page: @handle lookup input now shows popular fediverse accounts
  from FediDB with avatar, name, handle, and follower count
- Three new API endpoints: /api/instances, /api/instance-check,
  /api/popular-accounts
- Alpine.js components for both autocomplete UIs with keyboard navigation
2026-02-27 09:26:45 +01:00
Ricardo
27721e0969 chore: bump version to 2.0.28 2026-02-26 18:15:41 +01:00
Ricardo
a4f72a588d feat: enhance ActivityPub reader with mentions, hashtags, infinite scroll, explore, and tag following
- Fix mentions/hashtags bug: separate Fedify Mention and Hashtag types into
  distinct mentions[] and category[] arrays with proper @ and # rendering
- Add hashtag timeline filtering at /admin/reader/tag with regex-safe queries
- Replace prev/next pagination with AlpineJS infinite scroll (IntersectionObserver)
  with no-JS fallback pagination preserved
- Add public instance timeline explorer at /admin/reader/explore with SSRF
  prevention and XSS sanitization via Mastodon-compatible API
- Add hashtag following with ap_followed_tags collection, inbox listener
  integration for non-followed accounts, and followed tags sidebar display
- Include one-time migration script for legacy timeline data
2026-02-26 18:15:21 +01:00
Ricardo
2c4ffeaba0 fix: store actorAvatar in ap_activities for inbox interactions
Moved extractActorInfo() before logActivity() in Like, Announce, and
Reply handlers so the actor's avatar URL is persisted in ap_activities.
Previously only stored in ap_notifications, leaving conversation_items
without photos for non-followers.
2026-02-26 15:00:07 +01:00
Ricardo
ef0887e0dd docs: document Fedify workarounds and implementation notes in README
Add section covering all five workarounds carried against Fedify 2.0,
each with file location, upstream issue, and revisit conditions.
2026-02-25 09:56:32 +01:00
Ricardo
fceac1f344 feat: use authenticated document loader for all inbox handler fetches
Pass ctx.getDocumentLoader({ identifier: handle }) to every .getActor(),
.getObject(), and .getTarget() call in inbox handlers. This signs outbound
fetches with our actor's key, fixing silent failures against Authorized
Fetch (Secure Mode) servers like hachyderm.io.

The authenticated loader is also threaded through extractObjectData() and
extractActorInfo() in timeline-store.js so internal calls to
.getAttributedTo(), .getIcon(), .getTags(), and .getAttachments() also
use signed requests.

Also removes the endpoints.type workaround in federation-bridge.js since
Fedify 2.0 fixed issue #576 upstream. The attachment array workaround
for Mastodon compatibility remains.

Bumps version to 2.0.26.
2026-02-25 09:41:29 +01:00
Ricardo
17b0f582d1 feat: add remote follow / authorize interaction support
Add OStatus subscribe template to WebFinger responses so remote servers
(WordPress AP, Misskey, etc.) can discover and redirect users to complete
follow interactions. Unauthenticated users are sent to login first, then
redirected to the existing reader profile page with follow/unfollow UI.
2026-02-24 15:00:40 +01:00
Ricardo
a62a0d8aac fix: Fediverse field display text for Mastodon verification
Mastodon 4.5's extract_url_from_html requires element['href'] ==
element.text for a field to be verifiable. Using @handle@domain as
display text caused value_for_verification to return nil, making
the field permanently unverifiable.

Change to using the full actor URL as display text so the href and
text content match.
2026-02-24 14:05:03 +01:00
Ricardo
963dcc3014 feat: add Fediverse field to actor profile and fix Update compaction
Always include a "Fediverse" PropertyValue attachment in the actor
with the canonical @handle@domain address. This ensures 2+ attachments
when combined with user-defined fields, preventing Fedify's JSON-LD
compaction from collapsing single-element arrays to plain objects
(which Mastodon's update_account_fields silently rejects).

Also fixes the root cause of profile fields not appearing on Mastodon
for existing followers: Update(Person) activities were being sent with
compacted attachment objects that Mastodon ignored.
2026-02-24 12:07:50 +01:00
Ricardo
df5ccc397e fix: ensure attachment is always an array for Mastodon compatibility
Fedify's JSON-LD compaction collapses single-element arrays to plain
objects. Mastodon checks `attachment.is_a?(Array)` and silently skips
non-array values, causing profile links to never display.

Also adds profile links section to the my-profile admin page and
fixes rel=me on the public profile page for bidirectional verification.
2026-02-24 11:39:42 +01:00
Ricardo
4e159bfb9d fix: replace nonexistent Nunjucks min filter with inline conditional
The | min filter is Jinja2 syntax, not available in Nunjucks. This caused
"filter not found: min" crashes when posts had photos (never triggered
before the async iteration fix because photo arrays were always empty).
2026-02-24 11:14:55 +01:00
Ricardo
4052eebc9d fix: re-fetch media from Fedify when stored timeline item has none
Post-detail view now re-fetches from the origin server when the stored
timeline item has empty photo/video/audio arrays (from before the async
iteration fix). On successful extraction, updates MongoDB so future
views don't need to re-fetch.
2026-02-24 10:58:13 +01:00
Ricardo
046c776195 fix: guard index creation against undefined collections on startup
MongoDB collections may not be available yet when init() runs if the
database connection hasn't completed. Wrap all createIndex calls in
try-catch so the plugin doesn't crash on startup. Indexes already exist
from previous runs; this is non-fatal.
2026-02-24 10:52:11 +01:00
Ricardo
cd7d850b44 fix: use async iteration for Fedify 2.0 attachments/tags, add image lightbox
Fedify 2.0's getAttachments() and getTags() return async iterables, but the
code used synchronous for...of which silently yielded zero results. Changed
to for await...of so media URLs (photo/video/audio) and hashtags are now
properly extracted from incoming posts.

Also replaced the gallery's target=_blank links with an Alpine.js lightbox
modal for full-size image viewing with prev/next navigation and keyboard
support.
2026-02-24 10:25:15 +01:00
Ricardo
a95d68c98f fix: remove await in non-async init() causing SyntaxError on startup
dropIndex() was called with await inside the non-async init() method,
causing "Unexpected reserved word" and preventing Indiekit from starting.
Use promise .catch() instead since the result isn't needed.
2026-02-23 23:31:11 +01:00
Ricardo
18a17b054a chore: bump to 2.0.15
Fix unclosed moderation CW wrapper in ap-item-card.njk and replace
invalid Nunjucks .startsWith()/.replace() with simple field comparisons.
2026-02-23 23:17:28 +01:00