diff --git a/assets/reader.css b/assets/reader.css index b52ce3e..6ceb5e8 100644 --- a/assets/reader.css +++ b/assets/reader.css @@ -4,6 +4,37 @@ * Uses Indiekit CSS custom properties for automatic dark mode support */ +/* ========================================================================== + Breadcrumb Navigation + ========================================================================== */ + +.ap-breadcrumb { + display: flex; + align-items: center; + gap: var(--space-xs); + margin-bottom: var(--space-m); + font-size: var(--font-size-s); + color: var(--color-on-offset); +} + +.ap-breadcrumb a { + color: var(--color-accent); + text-decoration: none; +} + +.ap-breadcrumb a:hover { + text-decoration: underline; +} + +.ap-breadcrumb__separator { + color: var(--color-on-offset); +} + +.ap-breadcrumb__current { + color: var(--color-on-background); + font-weight: var(--font-weight-bold); +} + /* ========================================================================== Fediverse Lookup ========================================================================== */ diff --git a/lib/controllers/activities.js b/lib/controllers/activities.js index 7423b36..fcfe2d4 100644 --- a/lib/controllers/activities.js +++ b/lib/controllers/activities.js @@ -12,6 +12,7 @@ export function activitiesController(mountPath) { if (!collection) { return response.render("activitypub-activities", { title: response.locals.__("activitypub.activities"), + parent: { href: mountPath, text: response.locals.__("activitypub.title") }, activities: [], mountPath, }); @@ -32,6 +33,7 @@ export function activitiesController(mountPath) { response.render("activitypub-activities", { title: response.locals.__("activitypub.activities"), + parent: { href: mountPath, text: response.locals.__("activitypub.title") }, activities, mountPath, cursor, diff --git a/lib/controllers/compose.js b/lib/controllers/compose.js index 25a77fc..5f6d353 100644 --- a/lib/controllers/compose.js +++ b/lib/controllers/compose.js @@ -141,6 +141,7 @@ export function composeController(mountPath, plugin) { response.render("activitypub-compose", { title: response.locals.__("activitypub.compose.title"), + readerParent: { href: `${mountPath}/admin/reader`, text: response.locals.__("activitypub.reader.title") }, replyTo, replyContext, syndicationTargets, diff --git a/lib/controllers/explore.js b/lib/controllers/explore.js index f64ac15..6b424f7 100644 --- a/lib/controllers/explore.js +++ b/lib/controllers/explore.js @@ -141,10 +141,13 @@ export function exploreController(mountPath) { const csrfToken = getToken(request.session); const deckCount = decks.length; + const readerParent = { href: `${mountPath}/admin/reader`, text: response.locals.__("activitypub.reader.title") }; + // No instance specified — render clean initial page (no error) if (!rawInstance.trim()) { return response.render("activitypub-explore", { title: response.locals.__("activitypub.reader.explore.title"), + readerParent, instance: "", scope, items: [], @@ -163,6 +166,7 @@ export function exploreController(mountPath) { if (!instance) { return response.render("activitypub-explore", { title: response.locals.__("activitypub.reader.explore.title"), + readerParent, instance: rawInstance, scope, items: [], @@ -228,6 +232,7 @@ export function exploreController(mountPath) { response.render("activitypub-explore", { title: response.locals.__("activitypub.reader.explore.title"), + readerParent, instance, scope, items, diff --git a/lib/controllers/featured-tags.js b/lib/controllers/featured-tags.js index 1bb796d..db37e22 100644 --- a/lib/controllers/featured-tags.js +++ b/lib/controllers/featured-tags.js @@ -15,6 +15,7 @@ export function featuredTagsGetController(mountPath) { response.render("activitypub-featured-tags", { title: response.locals.__("activitypub.featuredTags") || "Featured Tags", + parent: { href: mountPath, text: response.locals.__("activitypub.title") }, tags, mountPath, }); diff --git a/lib/controllers/featured.js b/lib/controllers/featured.js index 45098be..3de5b33 100644 --- a/lib/controllers/featured.js +++ b/lib/controllers/featured.js @@ -57,6 +57,7 @@ export function featuredGetController(mountPath) { response.render("activitypub-featured", { title: response.locals.__("activitypub.featured") || "Pinned Posts", + parent: { href: mountPath, text: response.locals.__("activitypub.title") }, pinned, availablePosts, maxPins: MAX_PINS, diff --git a/lib/controllers/followers.js b/lib/controllers/followers.js index 98310dd..9060bc0 100644 --- a/lib/controllers/followers.js +++ b/lib/controllers/followers.js @@ -12,6 +12,7 @@ export function followersController(mountPath) { if (!collection) { return response.render("activitypub-followers", { title: response.locals.__("activitypub.followers"), + parent: { href: mountPath, text: response.locals.__("activitypub.title") }, followers: [], followerCount: 0, mountPath, @@ -33,6 +34,7 @@ export function followersController(mountPath) { response.render("activitypub-followers", { title: `${totalCount} ${response.locals.__("activitypub.followers")}`, + parent: { href: mountPath, text: response.locals.__("activitypub.title") }, followers, followerCount: totalCount, mountPath, diff --git a/lib/controllers/following.js b/lib/controllers/following.js index 3ee3155..ac02b06 100644 --- a/lib/controllers/following.js +++ b/lib/controllers/following.js @@ -12,6 +12,7 @@ export function followingController(mountPath) { if (!collection) { return response.render("activitypub-following", { title: response.locals.__("activitypub.following"), + parent: { href: mountPath, text: response.locals.__("activitypub.title") }, following: [], followingCount: 0, mountPath, @@ -33,6 +34,7 @@ export function followingController(mountPath) { response.render("activitypub-following", { title: `${totalCount} ${response.locals.__("activitypub.following")}`, + parent: { href: mountPath, text: response.locals.__("activitypub.title") }, following, followingCount: totalCount, mountPath, diff --git a/lib/controllers/migrate.js b/lib/controllers/migrate.js index b2cfa5f..91c38c0 100644 --- a/lib/controllers/migrate.js +++ b/lib/controllers/migrate.js @@ -24,6 +24,7 @@ export function migrateGetController(mountPath, pluginOptions) { response.render("activitypub-migrate", { title: response.locals.__("activitypub.migrate.title"), + parent: { href: mountPath, text: response.locals.__("activitypub.title") }, mountPath, currentAlias, result: null, @@ -61,6 +62,7 @@ export function migratePostController(mountPath, pluginOptions) { response.render("activitypub-migrate", { title: response.locals.__("activitypub.migrate.title"), + parent: { href: mountPath, text: response.locals.__("activitypub.title") }, mountPath, currentAlias, result, diff --git a/lib/controllers/moderation.js b/lib/controllers/moderation.js index a56c004..83304ae 100644 --- a/lib/controllers/moderation.js +++ b/lib/controllers/moderation.js @@ -301,6 +301,7 @@ export function moderationController(mountPath) { response.render("activitypub-moderation", { title: response.locals.__("activitypub.moderation.title"), + readerParent: { href: `${mountPath}/admin/reader`, text: response.locals.__("activitypub.reader.title") }, muted, blocked, mutedActors, diff --git a/lib/controllers/my-profile.js b/lib/controllers/my-profile.js index 7ac9ec5..d4bb79c 100644 --- a/lib/controllers/my-profile.js +++ b/lib/controllers/my-profile.js @@ -219,6 +219,7 @@ export function myProfileController(plugin) { response.render("activitypub-my-profile", { title: response.locals.__("activitypub.myProfile.title"), + readerParent: { href: mountPath, text: response.locals.__("activitypub.title") }, profile: profile || {}, handle, domain, diff --git a/lib/controllers/post-detail.js b/lib/controllers/post-detail.js index 68b7481..0bc3b67 100644 --- a/lib/controllers/post-detail.js +++ b/lib/controllers/post-detail.js @@ -197,6 +197,7 @@ export function postDetailController(mountPath, plugin) { // Truly not found (no local item either) return response.status(404).render("activitypub-post-detail", { title: response.locals.__("activitypub.reader.post.title"), + readerParent: { href: `${mountPath}/admin/reader`, text: response.locals.__("activitypub.reader.title") }, notFound: true, objectUrl, mountPath, item: null, interactionMap: {}, csrfToken: null, parentPosts: [], replyPosts: [], @@ -318,6 +319,7 @@ export function postDetailController(mountPath, plugin) { response.render("activitypub-post-detail", { title: response.locals.__("activitypub.reader.post.title"), + readerParent: { href: `${mountPath}/admin/reader`, text: response.locals.__("activitypub.reader.title") }, item: timelineItem, interactionMap, csrfToken, diff --git a/lib/controllers/profile.js b/lib/controllers/profile.js index 6db58ba..079688f 100644 --- a/lib/controllers/profile.js +++ b/lib/controllers/profile.js @@ -18,6 +18,7 @@ export function profileGetController(mountPath) { response.render("activitypub-profile", { title: response.locals.__("activitypub.profile.title"), + parent: { href: mountPath, text: response.locals.__("activitypub.title") }, mountPath, profile, actorTypes: ACTOR_TYPES, @@ -96,6 +97,7 @@ export function profilePostController(mountPath, plugin) { response.render("activitypub-profile", { title: response.locals.__("activitypub.profile.title"), + parent: { href: mountPath, text: response.locals.__("activitypub.title") }, mountPath, profile, actorTypes: ACTOR_TYPES, diff --git a/lib/controllers/profile.remote.js b/lib/controllers/profile.remote.js index 6a05ff1..2e0c629 100644 --- a/lib/controllers/profile.remote.js +++ b/lib/controllers/profile.remote.js @@ -126,6 +126,7 @@ export function remoteProfileController(mountPath, plugin) { response.render("activitypub-remote-profile", { title: name, + readerParent: { href: `${mountPath}/admin/reader`, text: response.locals.__("activitypub.reader.title") }, actorUrl, name, actorHandle, diff --git a/lib/controllers/reader.js b/lib/controllers/reader.js index b05c721..6fd15de 100644 --- a/lib/controllers/reader.js +++ b/lib/controllers/reader.js @@ -203,6 +203,7 @@ export function readerController(mountPath) { response.render("activitypub-reader", { title: response.locals.__("activitypub.reader.title"), + readerParent: { href: mountPath, text: response.locals.__("activitypub.title") }, items, tab, before: result.before, @@ -253,6 +254,7 @@ export function notificationsController(mountPath) { response.render("activitypub-notifications", { title: response.locals.__("activitypub.notifications.title"), + readerParent: { href: `${mountPath}/admin/reader`, text: response.locals.__("activitypub.reader.title") }, items: result.items, before: result.before, tab, diff --git a/lib/controllers/tag-timeline.js b/lib/controllers/tag-timeline.js index 9f10e0f..7e5aeeb 100644 --- a/lib/controllers/tag-timeline.js +++ b/lib/controllers/tag-timeline.js @@ -131,6 +131,7 @@ export function tagTimelineController(mountPath) { response.render("activitypub-tag-timeline", { title: `#${tag}`, + readerParent: { href: `${mountPath}/admin/reader`, text: response.locals.__("activitypub.reader.title") }, tag, items, before: result.before, diff --git a/views/layouts/ap-reader.njk b/views/layouts/ap-reader.njk index 12bcaa0..6698b54 100644 --- a/views/layouts/ap-reader.njk +++ b/views/layouts/ap-reader.njk @@ -18,6 +18,14 @@ {# AP link interception for internal navigation #} + {% if readerParent %} + + {% endif %} + {% block readercontent %} {% endblock %} {% endblock %}