Skip to content

ActivityPub Message Viewer

The built-in activity_pub_msg_viewer module provides a read-only, scrollable viewer for individual ActivityPub Notes from the Fediverse. It uses a two-art layout — a body art containing the message text and header labels, and a footer art containing a horizontal action menu — mirroring the FSE view-mode design.

Two separate art files are used:

Art keyForm IDContents
art.body1Message body (%MT1) and header label views (%TL10+).
art.footer4Single-row action menu (%HM1).

The body occupies the top portion of the screen; the footer is drawn immediately below it.

The footer (%HM1) is always focused — this matches FSE view mode. The body form (form 1) exists only to hold %MT1 and the %TL10+ label views; it is never directly focused and accepts no input.

All user interaction — including scrolling the message body — is handled by action keys and the HM1 menu in form 4.

KeyRequiredDescription
art.bodyYesArt spec for the body screen (contains %MT1 and %TL10+).
art.footerYesArt spec for the footer screen (contains %HM1).
dateTimeFormatNomoment.js format string for the message date. am/pm are collapsed to a/p. Defaults to MM/DD hh:mma.

Example:

activityPubMsgViewer: {
desc: ActivityPub Message Viewer
module: ./activitypub/activity_pub_msg_viewer
config: {
art: {
body: activitypub_msg_viewer
footer: activitypub_msg_viewer_footer
}
}
// ...
}

A multi-line text view displaying the decoded Note content. Should be configured in preview mode with acceptsFocus: false and acceptsInput: false. width and height must be set explicitly in the theme to match the art layout — the view system does not derive dimensions from art for MT type views.

Example in theme.hjson:

activityPubMsgViewer: {
1: {
mci: {
MT1: {
mode: preview
width: 78
height: 17
}
}
}
}

%TL10, %TL11, etc. may be placed anywhere in the body art and configured via bodyInfoFormat## keys in the menu’s config block:

PropertyDescription
fromSender handle in @user@host form.
subjectMessage subject/summary. Prefixed with [CW] for content-warned posts, re: for replies.
dateFormatted timestamp string (see dateTimeFormat).
likesLike count as a string; empty when zero.
boostsBoost count as a string; empty when zero.
attAttachment indicator character, or a space when no attachment.
hasAtt'1' if the Note has attachments, otherwise ''.
threadPosPosition of this Note within its thread (e.g. '3'), or '' if unknown.
threadTotalTotal number of Notes in the thread, or '' if unknown.
threadInfoFormatted thread position string (e.g. '3 of 7'), or '' if unknown.
hasPrev'1' if the Note is a reply (has a parent), otherwise ''.
hasNext'1' if a next Note exists in the thread, otherwise ''.
modeLabelSource mode label passed from the browser: Federated, Local, Timeline, Mentions, or Thread.
likeIndicatorLike indicator character (CP437 ♥ by default; configurable via likeIndicator in config).
boostIndicatorBoost indicator character (CP437 ▲ by default; configurable via boostIndicator in config).

Example config:

activityPubMsgViewer: {
config: {
bodyInfoFormat10: "{modeLabel}"
bodyInfoFormat11: "{from:<40.40}"
bodyInfoFormat12: "{subject:<40.40}"
bodyInfoFormat13: "{date:<14.14}"
bodyInfoFormat14: "{threadInfo}"
}
}
Section titled “MCI 1 — %HM1 Footer Action Menu (Form 4)”

A horizontal menu with the following items (by index):

IndexDefault labelAction
0prevNavigate to the previous message in the browser list.
1nextNavigate to the next message in the browser list.
2next thdNavigate to the next Note in the thread context (]).
3prev thdNavigate to the parent Note via inReplyTo ([).
4boostBoost (announce) the current Note.
5likeLike the current Note.
6replyReply to the current Note.
7quitExit the viewer and return to the browser.

Items are plain strings; focus appearance is controlled by the art file or focusTextStyle in the HM1 MCI config. Place %HM1^[[...m%HM1 (two codes with a focus SGR between them) in the art to give the focused item a distinct style.

These may be changed in your board’s menu.hjson. All keys are handled by the footer form (form 4) — the footer is always focused.

Key(s)Action
up arrowScroll message body up one line.
down arrowScroll message body down one line.
page upScroll message body up one page.
page downScroll message body down one page.
[Navigate to the parent Note via inReplyTo (thread nav).
]Navigate to the next Note in the thread context.
bBoost the current Note.
lLike the current Note.
rReply to the current Note.
q, Q, escapeQuit and return to the browser.

Menu config example:

activityPubMsgViewer: {
form: {
1: {
mci: {
MT1: {
mode: preview
acceptsFocus: false
acceptsInput: false
}
}
}
4: {
mci: {
HM1: {
items: ["prev", "next", "next thd", "prev thd", "boost", "like", "reply", "quit"]
focusItemIndex: 1
submit: true
}
}
submit: {
*: [
{ value: { 1: 0 } action: @method:prevNote }
{ value: { 1: 1 } action: @method:nextNote }
{ value: { 1: 2 } action: @method:threadNext }
{ value: { 1: 3 } action: @method:threadPrev }
{ value: { 1: 4 } action: @method:boostNote }
{ value: { 1: 5 } action: @method:likeNote }
{ value: { 1: 6 } action: @method:replyNote }
{ value: { 1: 7 } action: @method:quitViewer }
]
}
actionKeys: [
{
keys: ["up arrow", "down arrow", "page up", "page down"]
action: @method:movementKeyPressed
}
{
keys: ["["]
action: @method:threadPrev
}
{
keys: ["]"]
action: @method:threadNext
}
{
keys: ["b"]
action: @method:boostNote
}
{
keys: ["l"]
action: @method:likeNote
}
{
keys: ["r"]
action: @method:replyNote
}
{
keys: ["escape", "q", "shift + q"]
action: @method:quitViewer
}
]
}
}
}

List navigation (prev/next menu items, Enter): walks backwards and forwards through the message list that was open in the browser when the viewer was launched. This is the primary navigation mechanism.

Thread navigation ([ / ]): [ follows the inReplyTo link to the parent Note; ] loads the thread context and advances to the next Note within it. These are available regardless of how the viewer was opened, but only work when the Note has inReplyTo or context/conversation fields set.