Documentation
¶
Index ¶
- func FormatClock(ts int32) string
- func FormatDayLabel(ts int32) string
- func FormatLastSeen(ts int32) string
- func FormatRelativeShort(ts int32) string
- func FormatRelativeTime(ts int32) string
- func FormatTimestamp(ts int32) string
- func FormatTimestampSmart(ts int32) string
- func Now() time.Time
- func PinClock(t time.Time) (restore func())
- func PinClockFunc(c clock) (restore func())
- func RenderInline(ft *telegram.FormattedText, roles theme.Roles, o textOpts) string
- func RenderText(ft *telegram.FormattedText, roles theme.Roles, width int) []string
- func SameDay(a, b int32) bool
- func SenderName(msg *telegram.Message, s *store.Store) string
- type BodyOptions
- type MessageRenderer
- func (r *MessageRenderer) RenderBody(msg *telegram.Message, s *store.Store, opts BodyOptions) []string
- func (r *MessageRenderer) SetHyperlinks(on bool)
- func (r *MessageRenderer) SetImageProtocol(protocol media.Protocol, maxCols, maxRows int)
- func (r *MessageRenderer) SetInlineImages(policy string)
- func (r *MessageRenderer) SetRoles(roles theme.Roles)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func FormatClock ¶
FormatClock is the thread grid's time column: bare wall-clock, always five cells, no date part. The grid puts the date in a day divider instead, so repeating it on every row would only cost body width.
func FormatDayLabel ¶
FormatDayLabel is the label of a thread grid day divider. It is upper case because a divider is a rule with a word set into it, not a sentence.
The year is dropped within the current year: the divider's job is to say which day the messages under it belong to, and "12 AUG" answers that for anything recent while "12 AUG 2024" is what you need once it does not.
func FormatLastSeen ¶
FormatLastSeen formats a user's last seen timestamp.
func FormatRelativeShort ¶
FormatRelativeShort is a relative time in the smallest space that still says something: "now", "4m", "4h", "yd", "2d", or a date once weeks have passed.
The rail's right-hand field is four cells and the chat list's is five. "4h ago" does not fit beside a name, and the "ago" is the half that carries no information — a relative time is already relative.
Hours give way to days at the DAY boundary, not at twenty-four hours. "23h" for something sent at nine last night is arithmetic; what the reader wants to know is whether it happened today, and a message from yesterday evening reading "14h" while one from this morning reads "6h" puts them on the same scale when the useful distinction is the night between them.
func FormatRelativeTime ¶
FormatRelativeTime returns a relative time string (e.g., "2m ago").
func FormatTimestamp ¶
FormatTimestamp formats a Unix timestamp for display.
func FormatTimestampSmart ¶
FormatTimestampSmart formats a Unix timestamp using a compact, date-aware form suitable for lists (e.g. the chat list) where a full timestamp on every row would be redundant: messages from today show only the time, messages from the last 6 calendar days show a short weekday plus time, and anything older falls back to an ISO date.
func Now ¶
Now is the instant the renderer measures against: the real clock, unless a test has pinned one.
func PinClock ¶
PinClock fixes the renderer's clock at t and returns the function that restores the previous one. Callers must restore it — a leaked pin makes every later test in the process render at somebody else's instant.
func PinClockFunc ¶
func PinClockFunc(c clock) (restore func())
PinClockFunc is PinClock with a moving clock, for the tests that need one instant per call rather than one for the whole test.
func RenderInline ¶
func RenderInline(ft *telegram.FormattedText, roles theme.Roles, o textOpts) string
RenderInline styles a whole formatted text with the semantic palette, and with OSC 8 hyperlinks when o.links says the terminal has them.
The hyperlinks were absent through phases 4 to 8 for a reason that turned out to be smaller than it looked: ansi.Wrap breaks a line between a link's opening and closing sequences and repairs neither, so the rest of that row — its padding and the panel rule beside it — becomes part of the link. The design record concluded that fixing it meant owning a grapheme-aware wrapper, because reopening a link means knowing which runes belong to it after wrapping. It does not: the URI is carried in the opening sequence, so cell.OpenLink can recover it from the wrapped line the same way OpenStyle recovers an SGR run. See docs/tui-2.0.md, divergence 14.
func RenderText ¶
RenderText lays formatted text out as body lines, exactly as a message body is drawn: blocks split out, entities styled, everything wrapped to width with each line closing its own styles.
It is exported for the composer's preview, which has to be drawn by the same code that draws received messages. A preview rendered by a second implementation is a preview that can disagree with what arrives, and being trusted is the entire purpose of it.
No hyperlinks and no revealed spoilers: this is what the text WILL be, and a clickable link in a draft is an invitation to click a thing that has not been sent yet.
Types ¶
type BodyOptions ¶
type BodyOptions struct {
// Width is the body column's width in display cells.
Width int
// RevealSpoilers un-hides spoiler spans. True only for the message
// under the cursor, and only after x.
RevealSpoilers bool
}
BodyOptions is how a message is to be laid out for the thread grid.
type MessageRenderer ¶
type MessageRenderer struct {
// contains filtered or unexported fields
}
MessageRenderer turns message content into terminal lines. It holds the image renderer and its cache; everything else it does is stateless.
func NewMessageRenderer ¶
func NewMessageRenderer() *MessageRenderer
func (*MessageRenderer) RenderBody ¶
func (r *MessageRenderer) RenderBody(msg *telegram.Message, s *store.Store, opts BodyOptions) []string
RenderBody lays a message's content out for the TUI 2.0 thread grid's body column: one string per terminal line, with no frame of its own. The grid owns everything to the left of the body column, so this returns content and nothing else.
The result is never empty — a message with nothing renderable still occupies one line, because a zero-line message would make the scroll index disagree with what is on screen.
func (*MessageRenderer) SetHyperlinks ¶
func (r *MessageRenderer) SetHyperlinks(on bool)
SetHyperlinks turns OSC 8 hyperlinks on the message body on or off. The caller resolves the policy and the terminal's capability together, once, so the renderer never has to.
func (*MessageRenderer) SetImageProtocol ¶
func (r *MessageRenderer) SetImageProtocol(protocol media.Protocol, maxCols, maxRows int)
SetImageProtocol replaces the image renderer used for photo bubbles. Zero cols/rows fall back to the historical 50x25 bubble size.
func (*MessageRenderer) SetInlineImages ¶
func (r *MessageRenderer) SetInlineImages(policy string)
SetInlineImages sets the resolved ui.inline_images policy: whether a photo is drawn as art or as a metadata card.
"never" is the setting that matters. A terminal whose image support was guessed wrong turns every photo into a screenful of garbage, and the user needs a way to say so that does not also turn off downloading.
func (*MessageRenderer) SetRoles ¶
func (r *MessageRenderer) SetRoles(roles theme.Roles)
SetRoles supplies the TUI 2.0 semantic palette used for entity styling, code frames, quotes and media cards.