Documentation
¶
Index ¶
- Constants
- Variables
- func Confirm(message string, defaultYes bool) (bool, error)
- func OpenInRunningTUI(instance string, request OpenRequest) error
- func RenderWordmark(colorEnabled bool) string
- func Run(rootSDK, sdk *hey.Client, selected string, watchers Watchers, options Options) error
- type Calendar
- type CalendarWatcher
- type CalendarYear
- type Contact
- type MailConnection
- type MailWatchEvent
- type MailWatcher
- type OngoingTrack
- type OpenRequest
- type Options
- type Recording
- type ScreenerWatcher
- type Theme
- type Watchers
- type YearDay
Constants ¶
const AnyBoxChanged int64 = 0
AnyBoxChanged stands for "something changed, we don't know what" — a watcher sends it after a reconnect, where the changes broadcast while it was away were missed.
const Wordmark = `` /* 874-byte string literal not displayed */
Wordmark is the HEY braille-art logo shown in root help and on the setup wizard's welcome screen.
Variables ¶
var ErrCanceled = errors.New("canceled")
ErrCanceled reports that the user dismissed a prompt (esc or ctrl+c).
var ErrNoRunningTUI = errors.New("no running HEY TUI")
ErrNoRunningTUI means no active HEY TUI accepted an open request.
var ErrNotInteractive = errors.New("interactive prompt requires a terminal")
ErrNotInteractive reports that a prompt was requested without a terminal.
Functions ¶
func Confirm ¶ added in v0.2.0
Confirm shows a yes/no prompt on the terminal (rendered to stderr so stdout stays data) and reports the choice. Enter accepts the highlighted answer, y/n answer directly, arrows or h/l move, esc and ctrl+c cancel.
func OpenInRunningTUI ¶ added in v1.0.0
func OpenInRunningTUI(instance string, request OpenRequest) error
OpenInRunningTUI sends a destination to the active named TUI.
func RenderWordmark ¶ added in v0.2.0
RenderWordmark returns the wordmark, tinted when color is enabled.
Types ¶
type Calendar ¶ added in v0.2.0
type Calendar struct {
ID int64
Name string
// OwnerEmail is the account the calendar belongs to, which is what tells two calendars of
// the same name apart — a reader with a work account and a personal one has two "Maybe"s
// and no way to know which is which from the name. It is what the web app shows under a
// calendar in its own list.
OwnerEmail string
Color string
Personal bool
// External is a calendar HEY subscribes to rather than owns — haystack's `internal`
// scope is `where.missing(:subscription)`, and this is the other side of it.
External bool
}
Calendar is one of the reader's calendars, as the picker and the habit form need it. Personal is the one HEY files a habit or a todo on when no calendar is named, and it has no name of its own — HEY leaves the field empty and the web app labels the row from the identity instead.
type CalendarWatcher ¶ added in v1.0.0
CalendarWatcher opens the stream that says a calendar changed. It subscribes every calendar the account can see and folds them into one doorbell: which calendar rang does not matter, because the TUI re-reads whatever span is on screen either way. A watcher discovers calendars added or removed while it runs on its own and rings for those too. The stream closes when ctx is done, or when whatever is behind it has given up for good.
type CalendarYear ¶ added in v0.2.0
type CalendarYear struct {
// PaddingDays is how many cells sit before January 1st, so the grid lines up under
// the reader's first weekday.
PaddingDays int
Days []YearDay
SpannedEvents []Recording
}
CalendarYear is a year as HEY draws one: a grid of days, and the events that span more than one of them. It is deliberately not a year's worth of recordings — see renderYearView.
type Contact ¶ added in v0.2.0
Contact is someone in the reader's address book. An alias is a contact in its own right — another address the same person writes from — which is why Aliases nests.
type MailConnection ¶ added in v0.2.0
type MailConnection uint8
MailConnection is a transition reported by the connection behind a mail watch.
const ( MailConnectionUnchanged MailConnection = iota MailConnectionDisconnected MailConnectionReconnected )
type MailWatchEvent ¶ added in v0.2.0
type MailWatchEvent struct {
BoxID int64
Connection MailConnection
WillReconnect bool
}
MailWatchEvent reports either a changed box or a connection transition. A disconnected event says whether the connection is already retrying; a reconnect asks the TUI to catch up the box on screen because broadcasts sent during the gap were missed.
type MailWatcher ¶ added in v0.2.0
type MailWatcher func(ctx context.Context) (<-chan MailWatchEvent, error)
MailWatcher opens the stream of mail and connection events that keeps the TUI live. Box events ask it to re-read changed mail, while connection events let it show when live updates are reconnecting. The stream closes when ctx is done, or when whatever is behind it has given up for good.
type OngoingTrack ¶ added in v0.2.0
OngoingTrack is the time track HEY has running, for everything that says so: the time tracking menu, and the day's now row.
Category is there because HEY serves it, and it is empty for every track that is still going. That is HEY's shape rather than a gap here — the only write that files a track under a category is the same one that ends it — so the field fills in the day a running track can carry one.
type OpenRequest ¶ added in v1.0.0
type OpenRequest struct {
TopicID int64 `json:"topic_id,omitempty"`
AccountID int64 `json:"account_id,omitempty"`
Title string `json:"title,omitempty"`
Screener bool `json:"screener,omitempty"`
}
OpenRequest identifies a destination to open in the TUI. AccountID selects a linked account when the request comes from another process.
type Options ¶ added in v0.2.1
type Options struct {
Open OpenRequest
Instance string
}
Options configures the TUI's initial destination.
type Recording ¶ added in v0.2.0
type Recording struct {
ID int64
ParentID int64
Title string
AllDay bool
StartsAt time.Time
EndsAt time.Time
Type string
// StartsAtZone and EndsAtZone are the IANA names of the zones the event was set in, and
// they are empty for most events — HEY serves them only for one saved with a zone of its
// own. They do not move the event: it is at one instant whatever zone it was set in, and
// the calendar draws it where the reader's clock puts it. What they are for is the form,
// which shows a zoned event on the clock it was written on.
StartsAtZone string
EndsAtZone string
CompletedAt time.Time
Label string
Icon string
// Color is a habit's own color. An event has none — what it wears is its
// calendar's, which is CalendarColor, and the two are different fields in HEY too.
Color string
// Notes, Location, Link, Attendees and AttachedEntryID are what an event carries besides
// when it is. They are read as well as written because HEY's update clears the lot of them
// on any write that leaves them out — so an edit form that did not know them would wipe an
// event's notes and location every time somebody changed its name.
//
// Notes arrive as plain text however they were written: HEY serves the description through
// to_plain_text, so formatting cannot survive a round trip through any client.
Notes string
Location string
Link string
Attendees []string
AttachedEntryID int64
// Highlighted is what HEY calls the web app's "Circle event".
Highlighted bool
// Recurring and RepeatKind are how an event repeats. The kind is served but neither the
// date it runs until nor the number of times, which is why an edit form can keep a
// schedule or replace it but cannot show what it is bounded by.
Recurring bool
RepeatKind string
// ParentTitle is the title of the recording this one hangs off, which is the only place
// some recordings have one: a countdown carries a label — "10 days before" — and no title
// of its own, because what it is counting down to is the event above it.
ParentTitle string
// OccurrenceID names one instance of a repeating event — "153688907_2026-08-21", the
// series and the day — and is what HEY serves instead of an id for an occurrence it has
// not written down yet. Such a recording arrives with ID 0, which is why the arrows hold
// on to key() rather than to the id: selecting by the id alone meant a repeating event
// could never be picked out at all.
OccurrenceID string
// CalendarID is which calendar this is filed on, and CalendarColor how a reader tells
// whose event they are looking at. HEY leaves the color empty for the personal calendar
// and two calendars can wear the same one, so the id is what the edit form matches on and
// the color only what it falls back to.
CalendarID int64
CalendarColor string
Days []int32
}
Recording is anything HEY keeps on a calendar — an event, a todo, a habit, a time track — told apart by Type.
Its times are time.Time and stay that way. They used to be strings rendered in UTC and parsed back, which is how the whole calendar came to be drawn in UTC: an event at 14:00Z sat on the 14:00 column wherever the reader was. Read Starts and Ends rather than these fields — those answer in the zone a reader thinks in.
func (Recording) Starts ¶ added in v0.2.0
Starts and Ends are when a recording begins and ends, in the zone the reader is in.
An all-day event is the exception, and it is not one HEY leaves to guesswork: its timestamp is a calendar date, which haystack serves as UTC midnight on purpose — `_recording.jbuilder` wraps it in `Time.use_zone("UTC")` so no offset creeps in. Convert that and a birthday moves to the day before for every reader west of UTC.
type ScreenerWatcher ¶ added in v0.2.0
type ScreenerWatcher func(ctx, connectionCtx context.Context, signedStreamName string) (<-chan struct{}, error)
ScreenerWatcher opens the stream that says The Screener changed. ctx owns this signed subscription; connectionCtx owns the shared TUI connection that remains live when a signed stream is replaced. HEY serves the signed name alongside the pending count, so a watcher opens after that name has been read.
type Theme ¶ added in v0.2.0
type Theme struct {
Accent color.Color
Selection color.Color // nil when the theme gives no selection background
Muted color.Color
Bright color.Color
Error color.Color
// Background is the theme's own paper, and Hues are the colors it renders the ANSI
// slots as — both nil when no theme file said. They matter for anything drawn *on*
// a hue: an ANSI slot's nominal value says nothing about what a reader sees, since
// a theme retints the running terminal over OSC 4. ANSI blue is #000080 nominally
// and a light periwinkle in a dark Omarchy theme, so ink picked against the nominal
// value comes out backwards. Keyed by the color names HEY uses.
Background color.Color
Hues map[string]color.Color
// Dark reports whether the theme is for a dark background. HasMode is true when
// a theme file said so; otherwise Dark is a guess the terminal can correct.
Dark bool
HasMode bool
// Trusted marks a theme the user pointed at explicitly (HEY_THEME). Trusted
// values skip the accent and selection readability gates: the gates exist for
// machine-derived palettes, not for a file chosen by hand.
Trusted bool
// Source names the file the overlay came from, "" for the ANSI defaults and
// "NO_COLOR" when color is disabled.
Source string
}
Theme is the accent overlay the TUI lays over the terminal's own ANSI palette.
hey-cli deliberately styles with ANSI-16 colors so a terminal retint restyles the running TUI for free. A Theme only replaces the handful of semantic colors where a desktop theme has a better answer than "bright blue": the accent, the selection background, the muted tone, the emphasized foreground and the error color. Anything a theme file leaves out keeps its ANSI default.
func ResolveTheme ¶ added in v0.2.0
func ResolveTheme() Theme
ResolveTheme picks the active theme from the environment, in order:
- NO_COLOR set → no color at all
- HEY_THEME → a theme file at that path
- Omarchy's rendered hey.toml in the current theme, when a theme ships or templates one
- Omarchy's colors.toml in the current theme
- the ANSI defaults
Only the first file that exists is read; within it, only the keys it provides override the defaults.
type Watchers ¶ added in v0.2.0
type Watchers struct {
Mail MailWatcher
Screener ScreenerWatcher
Calendar CalendarWatcher
}
Watchers are the streams the TUI follows to stay live. All are optional: without them a list is the snapshot it was read as.
Source Files
¶
- accounts.go
- attachments.go
- brand.go
- bulk_reply.go
- calendar.go
- calendar_settings.go
- calendar_views.go
- calendars.go
- collections.go
- compose.go
- contact_form.go
- contacts.go
- contacts_list.go
- content.go
- cover_picker.go
- covers.go
- datetime.go
- errors.go
- event_form.go
- event_repeat.go
- folders.go
- habit_form.go
- habits.go
- help.go
- html.go
- image_budget.go
- image_fetcher.go
- image_renderer.go
- inline_markdown.go
- journal.go
- journal_form.go
- journal_list.go
- kitty.go
- labels.go
- live.go
- loading.go
- mail.go
- modal.go
- move.go
- nav.go
- open_file.go
- open_remote_unix.go
- prompt.go
- request_lane.go
- screener.go
- search.go
- section_view.go
- snippets.go
- styles.go
- theme.go
- theme_watch.go
- time_track.go
- time_track_form.go
- toast.go
- todos.go
- tui.go
- width.go
- width_probe.go
- width_probe_unix.go