Documentation
¶
Index ¶
- func EndsWithQuestion(text string) bool
- func ExtractMentions(html string) []string
- func JaccardSimilarity(a, b []string) float64
- func RealmPool[P Pooler](r *Realm, key string, create func() P) P
- func RiverText(html string) string
- func StripTags(s string) string
- func Tokenize(text string) []string
- type AccountData
- type AccountInfo
- type AccountResult
- type ActivityEntryInfo
- type AssignmentInfo
- type BonfireDigestEntry
- type BonfireRoomConfig
- type BoostEmbed
- type BoostInfo
- type BoostPreview
- type BoostSummary
- type CampfireLineInfo
- type CampfireLinesResult
- type CardColumnInfo
- type CardCreateMutation
- type CardInfo
- type CardMoveMutation
- type CheckinAnswerInfo
- type CheckinQuestionInfo
- type DockToolInfo
- type DocsFilesItemInfo
- type FetchFunc
- type ForwardInfo
- type HeyEntryInfo
- type Hub
- func (h *Hub) Account() *Realm
- func (h *Hub) AccountContext() context.Context
- func (h *Hub) Assignments() *Pool[[]AssignmentInfo]
- func (h *Hub) BonfireDigest() *Pool[[]BonfireDigestEntry]
- func (h *Hub) BonfireLines(room RoomID) *Pool[CampfireLinesResult]
- func (h *Hub) BonfireRooms() *Pool[[]BonfireRoomConfig]
- func (h *Hub) Boosts(projectID, recordingID int64) *Pool[BoostSummary]
- func (h *Hub) CampfireLines(projectID, campfireID int64) *Pool[CampfireLinesResult]
- func (h *Hub) Cards(projectID, tableID int64) *MutatingPool[[]CardColumnInfo]
- func (h *Hub) CheckinAnswers(projectID, questionID int64) *Pool[[]CheckinAnswerInfo]
- func (h *Hub) Checkins(projectID, questionnaireID int64) *Pool[[]CheckinQuestionInfo]
- func (h *Hub) ClearCardAssignees(ctx context.Context, accountID string, projectID, cardID int64) error
- func (h *Hub) ClearCardDueOn(ctx context.Context, accountID string, projectID, cardID int64) error
- func (h *Hub) ClearTodoAssignees(ctx context.Context, accountID string, projectID, todoID int64) error
- func (h *Hub) ClearTodoDueOn(ctx context.Context, accountID string, projectID, todoID int64) error
- func (h *Hub) CompleteTodo(ctx context.Context, accountID string, projectID, todoID int64) error
- func (h *Hub) CompletedTodos(projectID, todolistID int64) *Pool[[]TodoInfo]
- func (h *Hub) CreateBoost(ctx context.Context, accountID string, projectID, recordingID int64, ...) (BoostInfo, error)
- func (h *Hub) CreateCheckinAnswer(ctx context.Context, accountID string, projectID, questionID int64, ...) error
- func (h *Hub) CreateDocument(ctx context.Context, accountID string, projectID, vaultID int64, title string) error
- func (h *Hub) CreateScheduleEntry(ctx context.Context, accountID string, projectID, scheduleID int64, ...) error
- func (h *Hub) CreateTodolist(ctx context.Context, accountID string, projectID, todosetID int64, name string) error
- func (h *Hub) CreateVault(ctx context.Context, accountID string, projectID, parentVaultID int64, ...) error
- func (h *Hub) DeleteBoost(ctx context.Context, projectID, boostID int64) error
- func (h *Hub) DocsFiles(projectID, vaultID int64) *Pool[[]DocsFilesItemInfo]
- func (h *Hub) EnsureAccount(accountID string) *Realm
- func (h *Hub) EnsureProject(projectID int64) *Realm
- func (h *Hub) Forwards(projectID, inboxID int64) *Pool[[]ForwardInfo]
- func (h *Hub) Global() *Realm
- func (h *Hub) HeyActivity() *Pool[[]ActivityEntryInfo]
- func (h *Hub) LeaveProject()
- func (h *Hub) Messages(projectID, boardID int64) *Pool[[]MessageInfo]
- func (h *Hub) Metrics() *PoolMetrics
- func (h *Hub) MultiStore() *MultiStore
- func (h *Hub) People() *Pool[[]PersonInfo]
- func (h *Hub) PinMessage(ctx context.Context, accountID string, projectID, messageID int64) error
- func (h *Hub) PingRooms() *Pool[[]PingRoomInfo]
- func (h *Hub) Project() *Realm
- func (h *Hub) ProjectContext() context.Context
- func (h *Hub) ProjectTimeline(projectID int64) *Pool[[]TimelineEventInfo]
- func (h *Hub) Projects() *Pool[[]ProjectInfo]
- func (h *Hub) Pulse() *Pool[[]ActivityEntryInfo]
- func (h *Hub) ScheduleEntries(projectID, scheduleID int64) *Pool[[]ScheduleEntryInfo]
- func (h *Hub) SetRecentProjects(fn func(accountID string) []int64)
- func (h *Hub) SetRoomStore(rs *RoomStore)
- func (h *Hub) SetTerminalFocused(focused bool)
- func (h *Hub) Shutdown()
- func (h *Hub) Subscribe(ctx context.Context, accountID string, projectID, recordingID int64) error
- func (h *Hub) SwitchAccount(accountID string)
- func (h *Hub) Timeline() *Pool[[]TimelineEventInfo]
- func (h *Hub) Todolists(projectID, todosetID int64) *Pool[[]TodolistInfo]
- func (h *Hub) Todos(projectID, todolistID int64) *MutatingPool[[]TodoInfo]
- func (h *Hub) TrashComment(ctx context.Context, accountID string, projectID, commentID int64) error
- func (h *Hub) TrashRecording(ctx context.Context, accountID string, projectID, recordingID int64) error
- func (h *Hub) TrashTodolist(ctx context.Context, accountID string, projectID, todolistID int64) error
- func (h *Hub) UncompleteTodo(ctx context.Context, accountID string, projectID, todoID int64) error
- func (h *Hub) UnpinMessage(ctx context.Context, accountID string, projectID, messageID int64) error
- func (h *Hub) Unsubscribe(ctx context.Context, accountID string, projectID, recordingID int64) error
- func (h *Hub) UpdateCard(ctx context.Context, accountID string, projectID, cardID int64, ...) error
- func (h *Hub) UpdateComment(ctx context.Context, accountID string, projectID, commentID int64, ...) error
- func (h *Hub) UpdateMessage(ctx context.Context, accountID string, projectID, messageID int64, ...) error
- func (h *Hub) UpdateTodo(ctx context.Context, accountID string, projectID, todoID int64, ...) error
- func (h *Hub) UpdateTodolist(ctx context.Context, accountID string, projectID, todolistID int64, ...) error
- type KeyedPool
- type MessageInfo
- type MetricsSummary
- type MixerStore
- type MixerVolumes
- type MultiStore
- func (ms *MultiStore) Accounts() []AccountInfo
- func (ms *MultiStore) ClientFor(accountID string) *basecamp.AccountClient
- func (ms *MultiStore) DiscoverAccounts(ctx context.Context) ([]AccountInfo, error)
- func (ms *MultiStore) FanOut(ctx context.Context, ...) []AccountResult
- func (ms *MultiStore) FanOutSingle(ctx context.Context, accountID string, ...) (any, error)
- func (ms *MultiStore) Identity() *basecamp.Identity
- func (ms *MultiStore) SetAccountsForTest(accounts []AccountInfo)
- type MutatingPool
- type Mutation
- type MutationErrorMsg
- type NavigationEvent
- type PersonInfo
- type PingRoomInfo
- type PollMsg
- type Pool
- func (p *Pool[T]) Clear()
- func (p *Pool[T]) Fetch(ctx context.Context) tea.Cmd
- func (p *Pool[T]) FetchIfStale(ctx context.Context) tea.Cmd
- func (p *Pool[T]) Get() Snapshot[T]
- func (p *Pool[T]) Invalidate()
- func (p *Pool[T]) Key() string
- func (p *Pool[T]) PollInterval() time.Duration
- func (p *Pool[T]) RecordHit()
- func (p *Pool[T]) RecordMiss()
- func (p *Pool[T]) Set(data T)
- func (p *Pool[T]) SetCache(c *PoolCache)
- func (p *Pool[T]) SetFocused(focused bool)
- func (p *Pool[T]) SetMetrics(m *PoolMetrics)
- func (p *Pool[T]) SetPollConfig(cfg PoolConfig)
- func (p *Pool[T]) SetPushMode(enabled bool)
- func (p *Pool[T]) SetTerminalFocused(focused bool)
- func (p *Pool[T]) Status() PoolStatus
- func (p *Pool[T]) Version() uint64
- type PoolCache
- type PoolConfig
- type PoolEvent
- type PoolEventType
- type PoolMetrics
- func (m *PoolMetrics) Apdex() float64
- func (m *PoolMetrics) PoolStatsList() []PoolStatus
- func (m *PoolMetrics) RecentEvents(n int) []PoolEvent
- func (m *PoolMetrics) Record(e PoolEvent)
- func (m *PoolMetrics) RecordNavigation(e NavigationEvent)
- func (m *PoolMetrics) RegisterPool(key string, reporter func() PoolStatus)
- func (m *PoolMetrics) Summary() MetricsSummary
- func (m *PoolMetrics) UnregisterPool(key string)
- type PoolStats
- type PoolStatus
- type PoolUpdatedMsg
- type Pooler
- type ProjectInfo
- type ReadTracker
- type Realm
- type RecordingWithBoosts
- type RiverLine
- type RoomID
- type RoomOverride
- type RoomOverrideEntry
- type RoomStore
- type ScheduleEntryInfo
- type SearchResultInfo
- type Segment
- type Segmenter
- type SegmenterConfig
- type Snapshot
- type SnapshotState
- type TimelineEventInfo
- type TodoCompleteMutation
- type TodoCreateMutation
- type TodoInfo
- type TodolistInfo
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EndsWithQuestion ¶ added in v0.3.0
EndsWithQuestion returns true if the text ends with a question mark.
func ExtractMentions ¶ added in v0.3.0
ExtractMentions returns the names mentioned in HTML content. Names are returned without a leading '@' so they can be compared directly to Creator fields (which are plain names like "Bob").
func JaccardSimilarity ¶ added in v0.3.0
JaccardSimilarity computes the Jaccard similarity coefficient between two token sets.
func RealmPool ¶
RealmPool retrieves or creates a typed pool within a realm. The type parameter P must implement Pooler (satisfied by *Pool[T], *MutatingPool[T], and *KeyedPool[K, T]).
Each key maps to exactly one concrete type — callers must be consistent. The Hub's typed accessors enforce this.
func RiverText ¶ added in v0.3.0
RiverText converts campfire line HTML to compact plain text for the river view. Replaces attachments with a paperclip indicator, strips remaining tags, and collapses whitespace to a single line.
Types ¶
type AccountData ¶
type AccountData[T any] struct { Account AccountInfo Data T Err error }
AccountData holds the typed outcome of a fan-out query against one account. Replaces the type-erased AccountResult for new pool-based code.
func FanOut ¶
func FanOut[T any](ctx context.Context, ms *MultiStore, fn func(acct AccountInfo, client *basecamp.AccountClient) (T, error), ) []AccountData[T]
FanOut runs fn against all discovered accounts concurrently, limiting parallelism to maxConcurrent. Results are returned in account order. Unlike MultiStore.FanOut, the result is fully typed — no type assertions.
type AccountInfo ¶
AccountInfo represents a discovered Basecamp account.
type AccountResult ¶
type AccountResult struct {
Account AccountInfo
Data any
Err error
}
AccountResult holds the outcome of a single fan-out query against one account.
type ActivityEntryInfo ¶
type ActivityEntryInfo struct {
ID int64
Title string
Type string // "Todo", "Message", "Document", etc.
Creator string
Account string
AccountID string
Project string
ProjectID int64
UpdatedAt string // formatted time
UpdatedAtTS int64 // unix timestamp for sorting
}
ActivityEntryInfo represents a recording from any account for the activity feed.
type AssignmentInfo ¶
type AssignmentInfo struct {
ID int64
Content string
DueOn string
Completed bool
Account string
AccountID string
Project string
ProjectID int64
Todolist string
Overdue bool
}
AssignmentInfo represents a todo assigned to the current user.
type BonfireDigestEntry ¶ added in v0.3.0
type BonfireDigestEntry struct {
RoomID
RoomName string
LastAuthor string
LastMessage string
LastAt string
LastAtTS int64
NewCount int // messages since last read
}
BonfireDigestEntry is the last message from a single campfire room. Used by the Ticker for ambient display.
type BonfireRoomConfig ¶ added in v0.3.0
type BonfireRoomConfig struct {
RoomID
RoomName string // campfire title (from the API's Title field)
ProjectName string
}
BonfireRoomConfig describes a discovered campfire room for bonfire.
func CapRoomsRoundRobin ¶ added in v0.3.0
func CapRoomsRoundRobin(rooms []BonfireRoomConfig, maxRooms int) []BonfireRoomConfig
CapRoomsRoundRobin selects up to maxRooms from rooms, distributing evenly across accounts. Ensures every account gets at least one room before any account gets a second.
type BoostEmbed ¶
type BoostEmbed struct {
BoostsSummary BoostSummary
}
BoostEmbed can be embedded in recording info types to add boost support.
func (BoostEmbed) GetBoosts ¶
func (be BoostEmbed) GetBoosts() BoostSummary
GetBoosts returns the embedded boost summary.
func (*BoostEmbed) SetBoosts ¶
func (be *BoostEmbed) SetBoosts(summary BoostSummary)
SetBoosts sets the boost summary on the embedded field.
type BoostInfo ¶
type BoostInfo struct {
ID int64
Content string // emoji or short text
Booster string // name of person who boosted
BoosterID int64
CreatedAt string // formatted time
}
BoostInfo is a lightweight representation of a boost (emoji reaction).
type BoostPreview ¶
type BoostPreview struct {
Content string // the boost content (emoji or text)
BoosterID int64 // for avatar-based display
}
BoostPreview is a single boost preview for list display. Shows either emoji content or avatar indicator depending on context.
type BoostSummary ¶
type BoostSummary struct {
Count int // total boost count
Preview []BoostPreview // compact preview for list display
}
BoostSummary holds boost metadata for display on list items. Includes both a count and a preview of who's boosting.
func (BoostSummary) GetBoosts ¶
func (b BoostSummary) GetBoosts() BoostSummary
GetBoosts returns an empty boost summary - recording types should embed BoostEmbed and override this if they have boosts.
type CampfireLineInfo ¶
type CampfireLineInfo struct {
ID int64
Body string // HTML content
Creator string
CreatedAt string // formatted time
CreatedAtTS time.Time // raw timestamp for grouping
BoostEmbed
}
CampfireLineInfo is a lightweight representation of a campfire line.
type CampfireLinesResult ¶
type CampfireLinesResult struct {
Lines []CampfireLineInfo
TotalCount int
}
CampfireLinesResult holds the lines plus pagination metadata from a campfire fetch. This compound type is the Pool's data value so that views can access TotalCount for pagination without a side-channel.
type CardColumnInfo ¶
type CardColumnInfo struct {
ID int64
Title string
Color string
Type string // "Kanban::Triage", "Kanban::Column", "Kanban::DoneColumn", "Kanban::NotNowColumn"
CardsCount int // from column metadata (available without fetching cards)
Deferred bool // true when cards were not fetched (Done/NotNow columns)
Cards []CardInfo
}
CardColumnInfo represents a kanban column with its cards.
type CardCreateMutation ¶
type CardCreateMutation struct {
Title string
ColumnID int64 // identity-based column target (not index)
ProjectID int64
Client *basecamp.AccountClient
// contains filtered or unexported fields
}
CardCreateMutation optimistically prepends a new card to a kanban column. Implements Mutation[[]CardColumnInfo] for use with MutatingPool.
The createdID field uses atomic.Int64 because ApplyRemotely runs in the mutation's tea.Cmd goroutine while a concurrent background fetch can trigger reconcile → IsReflectedIn under the pool lock in a different goroutine. Pointer receiver required so the atomic field is shared.
func (*CardCreateMutation) ApplyLocally ¶
func (m *CardCreateMutation) ApplyLocally(columns []CardColumnInfo) []CardColumnInfo
ApplyLocally prepends a placeholder card to the target column (by ID). If the column is not found (e.g. reorder during flight), no-op.
func (*CardCreateMutation) ApplyRemotely ¶
func (m *CardCreateMutation) ApplyRemotely(ctx context.Context) error
ApplyRemotely calls the SDK to create the card.
func (*CardCreateMutation) IsReflectedIn ¶
func (m *CardCreateMutation) IsReflectedIn(columns []CardColumnInfo) bool
IsReflectedIn returns true when the created card appears in the target column. Returns false if ApplyRemotely hasn't completed yet (createdID == 0).
type CardInfo ¶
type CardInfo struct {
ID int64
Title string
Assignees []string
DueOn string
Position int
Completed bool
StepsTotal int
StepsDone int
CommentsCount int
BoostEmbed // embedded boost support
}
CardInfo represents a single card.
type CardMoveMutation ¶
type CardMoveMutation struct {
CardID int64
SourceColIdx int // index of the source column
TargetColIdx int // index of the target column
TargetColumnID int64 // API ID of target column
Client *basecamp.AccountClient
ProjectID int64
}
CardMoveMutation moves a card from one column to another. Implements Mutation[[]CardColumnInfo] for use with MutatingPool.
func (CardMoveMutation) ApplyLocally ¶
func (m CardMoveMutation) ApplyLocally(columns []CardColumnInfo) []CardColumnInfo
ApplyLocally moves the card between columns in the local data.
func (CardMoveMutation) ApplyRemotely ¶
func (m CardMoveMutation) ApplyRemotely(ctx context.Context) error
ApplyRemotely calls the SDK to move the card.
func (CardMoveMutation) IsReflectedIn ¶
func (m CardMoveMutation) IsReflectedIn(columns []CardColumnInfo) bool
IsReflectedIn returns true when the card appears in the target column in the remote data. For deferred target columns (Done/NotNow) whose Cards slice is always empty, we check that the card is absent from the source column instead.
type CheckinAnswerInfo ¶
type CheckinAnswerInfo struct {
ID int64
Creator string
CreatedAt time.Time
Content string // HTML
GroupOn string // YYYY-MM-DD or empty
CommentsCount int
}
CheckinAnswerInfo is a lightweight representation of a check-in answer.
type CheckinQuestionInfo ¶
type CheckinQuestionInfo struct {
ID int64
Title string
Paused bool
AnswersCount int
Frequency string
}
CheckinQuestionInfo is a lightweight representation of a check-in question.
type DockToolInfo ¶
type DockToolInfo struct {
ID int64
Name string // "todoset", "chat", "message_board", etc.
Title string
Enabled bool
}
DockToolInfo represents an enabled tool on a project's dock.
type DocsFilesItemInfo ¶
type DocsFilesItemInfo struct {
ID int64
Title string
Type string // "Folder", "Document", "Upload"
CreatedAt string
Creator string
VaultsCount int // sub-folders (Type=="Folder" only)
DocsCount int // documents (Type=="Folder" only)
UploadsCount int // uploads (Type=="Folder" only)
}
DocsFilesItemInfo is a lightweight representation of a vault item.
type ForwardInfo ¶
ForwardInfo is a lightweight representation of an email forward.
type HeyEntryInfo ¶
type HeyEntryInfo struct {
ID int64
Title string
Excerpt string
Creator string
Project string
CreatedAt string
IsRead bool
}
HeyEntryInfo is a lightweight representation of an inbox entry.
type Hub ¶
type Hub struct {
// contains filtered or unexported fields
}
Hub is the central data coordinator providing typed, realm-scoped pool access.
Hub manages three realm tiers:
- Global: app lifetime (identity, account list)
- Account: active account session (projects, people)
- Project: active project context (schedule, campfire, messages, etc.)
Typed pool accessors return realm-scoped pools whose lifecycle is automatic: project pools are torn down on LeaveProject/EnsureProject(different), account pools on SwitchAccount, global pools on Shutdown.
func NewHub ¶
func NewHub(multi *MultiStore, cacheDir string) *Hub
NewHub creates a Hub with a global realm and the given dependencies. cacheDir may be empty to disable persistent caching.
func (*Hub) AccountContext ¶
AccountContext returns the account realm's context, or the global context as fallback. Views should pass this to pool Fetch calls for account-scoped data so that SwitchAccount cancels in-flight fetches.
func (*Hub) Assignments ¶
func (h *Hub) Assignments() *Pool[[]AssignmentInfo]
Assignments returns a global-scope pool of cross-account todo assignments.
func (*Hub) BonfireDigest ¶ added in v0.3.0
func (h *Hub) BonfireDigest() *Pool[[]BonfireDigestEntry]
BonfireDigest returns a global-scope pool of last-message-per-room summaries. Self-sufficient: if BonfireRooms hasn't been populated yet, triggers a room fetch inline before reading. This ensures the ticker works on fresh sessions without depending on another view having fetched rooms first.
func (*Hub) BonfireLines ¶ added in v0.3.0
func (h *Hub) BonfireLines(room RoomID) *Pool[CampfireLinesResult]
BonfireLines returns a global-scope pool of campfire lines for a specific room. Uses h.multi.ClientFor(room.AccountID) — no EnsureProject needed. Keyed as "bonfire-lines:{accountID}:{projectID}:{campfireID}".
func (*Hub) BonfireRooms ¶ added in v0.3.0
func (h *Hub) BonfireRooms() *Pool[[]BonfireRoomConfig]
BonfireRooms returns a global-scope pool of campfire rooms from bookmarked and recently visited projects. Uses FanOut like PingRooms but filters for project campfires (not 1:1 pings). For accounts with no bookmarks, falls back to recently visited projects (from recents store). The RoomStore (if configured via SetRoomStore) can further narrow or widen via explicit includes/excludes.
func (*Hub) Boosts ¶
func (h *Hub) Boosts(projectID, recordingID int64) *Pool[BoostSummary]
Boosts returns a project-scoped pool of boosts for a recording. The pool stores BoostSummary (count + preview) for list item display.
func (*Hub) CampfireLines ¶
func (h *Hub) CampfireLines(projectID, campfireID int64) *Pool[CampfireLinesResult]
CampfireLines returns a project-scoped pool of campfire lines with polling config. The pool stores CampfireLinesResult (lines + TotalCount) for pagination support. Pagination (fetchOlderLines) and writes (sendLine) remain view-owned.
func (*Hub) Cards ¶
func (h *Hub) Cards(projectID, tableID int64) *MutatingPool[[]CardColumnInfo]
Cards returns a project-scoped MutatingPool of card columns with their cards. The MutatingPool supports optimistic card moves via CardMoveMutation. Done and Not Now columns are deferred: metadata only, no card fetching.
func (*Hub) CheckinAnswers ¶
func (h *Hub) CheckinAnswers(projectID, questionID int64) *Pool[[]CheckinAnswerInfo]
CheckinAnswers returns a project-scoped pool of answers for a specific check-in question.
func (*Hub) Checkins ¶
func (h *Hub) Checkins(projectID, questionnaireID int64) *Pool[[]CheckinQuestionInfo]
Checkins returns a project-scoped pool of check-in questions.
func (*Hub) ClearCardAssignees ¶
func (h *Hub) ClearCardAssignees(ctx context.Context, accountID string, projectID, cardID int64) error
ClearCardAssignees clears all assignees on a card. Uses a raw Put to bypass the SDK's omitempty on AssigneeIDs which prevents sending empty slices.
func (*Hub) ClearCardDueOn ¶
ClearCardDueOn clears the due date on a card. Uses a raw Put to bypass the SDK's omitempty on DueOn which prevents sending empty strings.
func (*Hub) ClearTodoAssignees ¶
func (h *Hub) ClearTodoAssignees(ctx context.Context, accountID string, projectID, todoID int64) error
ClearTodoAssignees clears all assignees on a todo. Uses a raw Put to bypass the SDK's omitempty on AssigneeIDs which prevents sending empty slices.
func (*Hub) ClearTodoDueOn ¶
ClearTodoDueOn clears the due date on a todo. Uses a raw Put to bypass the SDK's omitempty on DueOn which prevents sending empty strings.
func (*Hub) CompleteTodo ¶
CompleteTodo marks a todo as completed. Uses explicit accountID for cross-account mutations from aggregate views (Assignments, Hey).
func (*Hub) CompletedTodos ¶
CompletedTodos returns a project-scoped Pool of completed todos for a specific todolist. Unlike Todos(), this is a plain Pool (not MutatingPool) since un-completing uses invalidate+refetch rather than optimistic mutation.
func (*Hub) CreateBoost ¶
func (h *Hub) CreateBoost(ctx context.Context, accountID string, projectID, recordingID int64, content string) (BoostInfo, error)
CreateBoost creates a new boost on a recording. accountID selects which account's client to use; empty means the Hub's current account. Returns the created BoostInfo or an error.
func (*Hub) CreateCheckinAnswer ¶
func (h *Hub) CreateCheckinAnswer(ctx context.Context, accountID string, projectID, questionID int64, content string) error
CreateCheckinAnswer posts a new answer to a check-in question.
func (*Hub) CreateDocument ¶
func (h *Hub) CreateDocument(ctx context.Context, accountID string, projectID, vaultID int64, title string) error
CreateDocument creates a new document in a vault.
func (*Hub) CreateScheduleEntry ¶
func (h *Hub) CreateScheduleEntry(ctx context.Context, accountID string, projectID, scheduleID int64, req *basecamp.CreateScheduleEntryRequest) error
CreateScheduleEntry creates a new schedule entry.
func (*Hub) CreateTodolist ¶
func (h *Hub) CreateTodolist(ctx context.Context, accountID string, projectID, todosetID int64, name string) error
CreateTodolist creates a new todolist in a todoset.
func (*Hub) CreateVault ¶
func (h *Hub) CreateVault(ctx context.Context, accountID string, projectID, parentVaultID int64, title string) error
CreateVault creates a new sub-folder in a vault.
func (*Hub) DeleteBoost ¶
DeleteBoost deletes a boost by ID.
func (*Hub) DocsFiles ¶
func (h *Hub) DocsFiles(projectID, vaultID int64) *Pool[[]DocsFilesItemInfo]
DocsFiles returns a project-scoped pool of vault items (folders, documents, uploads).
func (*Hub) EnsureAccount ¶
EnsureAccount returns the account realm, creating one if needed. If called with a different accountID than the current realm, the old realm is torn down (along with any project realm) and a fresh one created.
func (*Hub) EnsureProject ¶
EnsureProject returns the project realm, creating one if needed. If called with a different projectID than the current realm, the old realm is torn down and a fresh one created.
func (*Hub) Forwards ¶
func (h *Hub) Forwards(projectID, inboxID int64) *Pool[[]ForwardInfo]
Forwards returns a project-scoped pool of email forwards.
func (*Hub) HeyActivity ¶
func (h *Hub) HeyActivity() *Pool[[]ActivityEntryInfo]
HeyActivity returns a global-scope pool of cross-account activity entries. The pool fans out Recordings.List across all accounts, caches for 30s (fresh) / 5m (stale), and polls at 30s/2m intervals.
func (*Hub) Messages ¶
func (h *Hub) Messages(projectID, boardID int64) *Pool[[]MessageInfo]
Messages returns a project-scoped pool of message board posts.
func (*Hub) Metrics ¶
func (h *Hub) Metrics() *PoolMetrics
Metrics returns the pool metrics collector.
func (*Hub) MultiStore ¶
func (h *Hub) MultiStore() *MultiStore
MultiStore returns the cross-account SDK access layer.
func (*Hub) People ¶
func (h *Hub) People() *Pool[[]PersonInfo]
People returns an account-scoped pool of people in the current account. Panics if no account realm is active — callers must EnsureAccount first.
func (*Hub) PinMessage ¶
PinMessage pins a message to the top of its board.
func (*Hub) PingRooms ¶
func (h *Hub) PingRooms() *Pool[[]PingRoomInfo]
PingRooms returns a global-scope pool of 1:1 campfire threads.
func (*Hub) ProjectContext ¶
ProjectContext returns the project realm's context, or the account/global context as fallback. Views should pass this to pool Fetch calls for project-scoped data so that LeaveProject cancels in-flight fetches.
func (*Hub) ProjectTimeline ¶
func (h *Hub) ProjectTimeline(projectID int64) *Pool[[]TimelineEventInfo]
ProjectTimeline returns a project-scoped pool of timeline events.
func (*Hub) Projects ¶
func (h *Hub) Projects() *Pool[[]ProjectInfo]
Projects returns a global-scope pool of all projects across accounts. Each project carries account attribution for cross-account navigation. Used by Home (bookmarks), Projects view, and Dock.
func (*Hub) Pulse ¶
func (h *Hub) Pulse() *Pool[[]ActivityEntryInfo]
Pulse returns a global-scope pool of cross-account recent activity. Like HeyActivity but includes more recording types and groups by account.
func (*Hub) ScheduleEntries ¶
func (h *Hub) ScheduleEntries(projectID, scheduleID int64) *Pool[[]ScheduleEntryInfo]
ScheduleEntries returns a project-scoped pool of schedule entries.
func (*Hub) SetRecentProjects ¶ added in v0.3.0
SetRecentProjects configures a function that returns recently visited project IDs scoped to a single account. Used by BonfireRooms as a fallback when an account has no bookmarked projects.
func (*Hub) SetRoomStore ¶ added in v0.3.0
SetRoomStore configures the RoomStore used to filter BonfireRooms/BonfireDigest.
func (*Hub) SetTerminalFocused ¶ added in v0.3.0
SetTerminalFocused propagates terminal focus state to all active realms. When the terminal window loses OS focus, poll intervals are extended to reduce unnecessary background network activity.
func (*Hub) Shutdown ¶
func (h *Hub) Shutdown()
Shutdown tears down all realms. Call on program exit.
func (*Hub) SwitchAccount ¶
SwitchAccount tears down the project and account realms, then creates a fresh account realm. Replaces the store.Clear() + router.Reset() sledgehammer with targeted realm teardown.
func (*Hub) Timeline ¶
func (h *Hub) Timeline() *Pool[[]TimelineEventInfo]
Timeline returns a global-scope pool of cross-account timeline events. Uses the richer Timeline.Progress() API instead of Recordings.List.
func (*Hub) Todolists ¶
func (h *Hub) Todolists(projectID, todosetID int64) *Pool[[]TodolistInfo]
Todolists returns a project-scoped pool of todolists.
func (*Hub) Todos ¶
func (h *Hub) Todos(projectID, todolistID int64) *MutatingPool[[]TodoInfo]
Todos returns a project-scoped MutatingPool of todos for a specific todolist. The MutatingPool supports optimistic todo completion via TodoCompleteMutation.
func (*Hub) TrashComment ¶
TrashComment moves a comment to the trash.
func (*Hub) TrashRecording ¶
func (h *Hub) TrashRecording(ctx context.Context, accountID string, projectID, recordingID int64) error
TrashRecording moves a recording to the trash.
func (*Hub) TrashTodolist ¶
func (h *Hub) TrashTodolist(ctx context.Context, accountID string, projectID, todolistID int64) error
TrashTodolist moves a todolist to the trash.
func (*Hub) UncompleteTodo ¶
UncompleteTodo reopens a completed todo.
func (*Hub) UnpinMessage ¶
UnpinMessage unpins a message from the board.
func (*Hub) Unsubscribe ¶
func (h *Hub) Unsubscribe(ctx context.Context, accountID string, projectID, recordingID int64) error
Unsubscribe unsubscribes the current user from a recording.
func (*Hub) UpdateCard ¶
func (h *Hub) UpdateCard(ctx context.Context, accountID string, projectID, cardID int64, req *basecamp.UpdateCardRequest) error
UpdateCard updates a card's fields.
func (*Hub) UpdateComment ¶
func (h *Hub) UpdateComment(ctx context.Context, accountID string, projectID, commentID int64, content string) error
UpdateComment updates a comment's content.
func (*Hub) UpdateMessage ¶
func (h *Hub) UpdateMessage(ctx context.Context, accountID string, projectID, messageID int64, req *basecamp.UpdateMessageRequest) error
UpdateMessage updates a message's fields.
func (*Hub) UpdateTodo ¶
func (h *Hub) UpdateTodo(ctx context.Context, accountID string, projectID, todoID int64, req *basecamp.UpdateTodoRequest) error
UpdateTodo updates a todo's fields.
type KeyedPool ¶
type KeyedPool[K comparable, T any] struct { // contains filtered or unexported fields }
KeyedPool manages sub-collection pools keyed by a parent ID. For data keyed by a parent: todos by todolist, campfire lines by campfire, comments by recording.
func NewKeyedPool ¶
func NewKeyedPool[K comparable, T any](factory func(key K) *Pool[T]) *KeyedPool[K, T]
NewKeyedPool creates a KeyedPool with the given factory for creating new pools on demand. All pools share the factory's config but have independent fetch state.
func (*KeyedPool[K, T]) Clear ¶
func (kp *KeyedPool[K, T]) Clear()
Clear removes all sub-pools and their data.
func (*KeyedPool[K, T]) Get ¶
Get returns the Pool for the given key, creating one if it doesn't exist.
func (*KeyedPool[K, T]) Invalidate ¶
func (kp *KeyedPool[K, T]) Invalidate()
Invalidate marks all sub-pools as stale.
func (*KeyedPool[K, T]) SetTerminalFocused ¶ added in v0.3.0
SetTerminalFocused fans out terminal focus state to all sub-pools.
type MessageInfo ¶
type MessageInfo struct {
ID int64
Subject string
Creator string
CreatedAt string
Category string
Pinned bool
BoostEmbed // embedded boost support
}
MessageInfo represents a message board post.
type MetricsSummary ¶
type MetricsSummary struct {
ActivePools int
P50Latency time.Duration
ErrorRate float64
Apdex float64
}
MetricsSummary provides a point-in-time snapshot of pool health.
type MixerStore ¶ added in v0.3.0
type MixerStore struct {
// contains filtered or unexported fields
}
MixerStore persists mixer volume settings to disk.
func NewMixerStore ¶ added in v0.3.0
func NewMixerStore(cacheDir string) *MixerStore
NewMixerStore creates a MixerStore backed by the given cache directory.
func (*MixerStore) Load ¶ added in v0.3.0
func (ms *MixerStore) Load() (MixerVolumes, error)
Load reads mixer volumes from disk.
func (*MixerStore) Save ¶ added in v0.3.0
func (ms *MixerStore) Save(v MixerVolumes) error
Save writes mixer volumes to disk atomically with read-merge-write.
type MixerVolumes ¶ added in v0.3.0
type MixerVolumes struct {
Volumes map[string]int `json:"volumes"` // room key -> volume level (0-4)
}
MixerVolumes stores per-room volume levels.
type MultiStore ¶
type MultiStore struct {
// contains filtered or unexported fields
}
MultiStore manages cross-account data access. It discovers all accessible accounts, caches per-account SDK clients, and provides FanOut for running the same query against all accounts concurrently with semaphore limiting.
func NewMultiStore ¶
func NewMultiStore(sdk *basecamp.Client) *MultiStore
NewMultiStore creates a MultiStore backed by the given SDK client.
func (*MultiStore) Accounts ¶
func (ms *MultiStore) Accounts() []AccountInfo
Accounts returns the discovered account list. Returns nil before DiscoverAccounts.
func (*MultiStore) ClientFor ¶
func (ms *MultiStore) ClientFor(accountID string) *basecamp.AccountClient
ClientFor returns an AccountClient for the given account ID. Clients are cached and reused across calls. Returns nil if the SDK is not initialized.
func (*MultiStore) DiscoverAccounts ¶
func (ms *MultiStore) DiscoverAccounts(ctx context.Context) ([]AccountInfo, error)
DiscoverAccounts fetches all accessible accounts and initializes the store. Safe to call multiple times; subsequent calls refresh the account list.
func (*MultiStore) FanOut ¶
func (ms *MultiStore) FanOut(ctx context.Context, fn func(acct AccountInfo, client *basecamp.AccountClient) (any, error)) []AccountResult
FanOut runs fn against all discovered accounts concurrently, limiting parallelism to maxConcurrent. Results are returned in account order. Individual account failures are captured in AccountResult.Err — the overall operation only fails if no accounts are discovered yet.
func (*MultiStore) FanOutSingle ¶
func (ms *MultiStore) FanOutSingle(ctx context.Context, accountID string, fn func(ctx context.Context, client *basecamp.AccountClient) (any, error)) (any, error)
FanOutSingle runs fn against a single account by ID. Returns ctx.Err() immediately if the context is already canceled.
func (*MultiStore) Identity ¶
func (ms *MultiStore) Identity() *basecamp.Identity
Identity returns the logged-in user's identity, or nil before discovery.
func (*MultiStore) SetAccountsForTest ¶
func (ms *MultiStore) SetAccountsForTest(accounts []AccountInfo)
SetAccountsForTest sets the account list directly, bypassing DiscoverAccounts. Only for use in tests.
type MutatingPool ¶
MutatingPool extends Pool with optimistic mutation support. Go equivalent of iOS BaseConcurrentDataStore / Android BaseConcurrentRepository.
func NewMutatingPool ¶
func NewMutatingPool[T any](key string, config PoolConfig, fetchFn FetchFunc[T]) *MutatingPool[T]
NewMutatingPool creates a MutatingPool with the given key, config, and fetch function.
func (*MutatingPool[T]) Apply ¶
Apply executes an optimistic mutation:
- Applies locally to the snapshot (immediate, synchronous)
- Returns a Cmd that applies remotely, then re-fetches and reconciles
The caller should read pool.Get() after calling Apply to get the optimistic data for immediate rendering.
func (*MutatingPool[T]) Clear ¶
func (mp *MutatingPool[T]) Clear()
Clear overrides Pool.Clear to also reset mutation state.
func (*MutatingPool[T]) Fetch ¶
func (mp *MutatingPool[T]) Fetch(ctx context.Context) tea.Cmd
Fetch overrides Pool.Fetch to reconcile pending mutations after a successful fetch rather than overwriting them.
func (*MutatingPool[T]) FetchIfStale ¶
func (mp *MutatingPool[T]) FetchIfStale(ctx context.Context) tea.Cmd
FetchIfStale overrides Pool.FetchIfStale to route through MutatingPool.Fetch.
type Mutation ¶
type Mutation[T any] interface { // ApplyLocally modifies the current state optimistically. ApplyLocally(current T) T // ApplyRemotely performs the server-side operation. ApplyRemotely(ctx context.Context) error // IsReflectedIn returns true when the remote data already contains // this mutation's effect (for pending-mutation pruning on re-fetch). IsReflectedIn(remote T) bool }
Mutation describes an optimistic mutation lifecycle. Go equivalent of iOS's Mutation protocol / Android's Mutation<T, A>.
type MutationErrorMsg ¶
MutationErrorMsg is sent when a mutation's remote apply fails.
type NavigationEvent ¶
type NavigationEvent struct {
}
NavigationEvent records a view navigation with data quality.
type PersonInfo ¶
type PersonInfo struct {
ID int64
Name string
Email string
Title string
Admin bool
Owner bool
Client bool
PersonType string // "User", "Client", etc.
Company string
}
PersonInfo is a lightweight representation of a person for the view.
type PingRoomInfo ¶
type PingRoomInfo struct {
CampfireID int64
ProjectID int64
PersonName string
Account string
AccountID string
LastMessage string
LastAt string
LastAtTS int64 // unix timestamp for sorting
}
PingRoomInfo represents a 1:1 campfire thread.
type PollMsg ¶
PollMsg is sent when a poll interval fires. Tag identifies which poller triggered. Gen is a generation counter used by views to discard ticks from superseded poll chains (e.g. after a terminal-focus reschedule).
type Pool ¶
type Pool[T any] struct { // contains filtered or unexported fields }
Pool is a typed, self-refreshing data source. Go equivalent of iOS RemoteReadService / Android BaseApiRepository. One Pool per logical data set (projects, hey-activity, campfire-lines).
The Pool does not subscribe or push — it's a typed cache with fetch capabilities. TEA's polling mechanism (PollMsg -> view calls FetchIfStale) drives the refresh cycle.
func NewPool ¶
func NewPool[T any](key string, config PoolConfig, fetchFn FetchFunc[T]) *Pool[T]
NewPool creates a Pool with the given key, config, and fetch function.
func (*Pool[T]) Fetch ¶
Fetch returns a Cmd that fetches fresh data and emits PoolUpdatedMsg. Concurrent fetches are deduped — returns nil if a fetch is in progress.
func (*Pool[T]) FetchIfStale ¶
FetchIfStale returns a Fetch Cmd if data is stale or empty, nil if fresh.
func (*Pool[T]) Get ¶
Get returns the current snapshot. Never blocks. Recalculates state based on TTL — a snapshot stored as Fresh may be returned as Stale if FreshTTL has elapsed, or expired (HasData=false) if StaleTTL has also elapsed.
func (*Pool[T]) Invalidate ¶
func (p *Pool[T]) Invalidate()
Invalidate marks current data as stale. Next FetchIfStale will re-fetch.
func (*Pool[T]) PollInterval ¶
PollInterval returns the current recommended polling interval, accounting for focus state, push mode, and miss backoff.
func (*Pool[T]) RecordHit ¶
func (p *Pool[T]) RecordHit()
RecordHit resets the miss counter (new data arrived).
func (*Pool[T]) RecordMiss ¶
func (p *Pool[T]) RecordMiss()
RecordMiss increments the miss counter for adaptive backoff.
func (*Pool[T]) Set ¶
func (p *Pool[T]) Set(data T)
Set writes data directly into the pool (for prefetch / dual-write patterns).
func (*Pool[T]) SetCache ¶ added in v0.3.0
SetCache sets the disk cache for this pool. If cached data exists, seeds the snapshot as Stale for SWR boot. FetchedAt is set to now (not the original fetch time) so the data falls within the stale window and isn't immediately expired by Get().
func (*Pool[T]) SetFocused ¶
SetFocused marks whether the view consuming this pool has focus.
func (*Pool[T]) SetMetrics ¶
func (p *Pool[T]) SetMetrics(m *PoolMetrics)
SetMetrics sets the metrics collector for this pool.
func (*Pool[T]) SetPollConfig ¶ added in v0.3.0
func (p *Pool[T]) SetPollConfig(cfg PoolConfig)
SetPollConfig replaces the pool's timing configuration. Does NOT bump generation or return a Cmd — timer invalidation is the caller's responsibility (bump view-side pollGen, re-arm schedulePoll).
func (*Pool[T]) SetPushMode ¶
SetPushMode enables/disables push mode (SSE connected). In push mode, poll intervals are extended significantly.
func (*Pool[T]) SetTerminalFocused ¶ added in v0.3.0
SetTerminalFocused marks whether the terminal window has OS focus. When false, poll intervals are extended 4× to reduce background load.
func (*Pool[T]) Status ¶
func (p *Pool[T]) Status() PoolStatus
Status returns a live status snapshot for the metrics panel.
type PoolCache ¶ added in v0.3.0
type PoolCache struct {
// contains filtered or unexported fields
}
PoolCache provides disk-backed persistence for pool snapshots. On successful fetch, data is written to disk. On pool creation, cached data seeds the snapshot as Stale so the TUI boots into real screens while background refresh runs.
func NewPoolCache ¶ added in v0.3.0
NewPoolCache creates a cache backed by the given directory. Returns nil if dir is empty.
type PoolConfig ¶
type PoolConfig struct {
FreshTTL time.Duration // how long data is "fresh" (0 = no expiry)
StaleTTL time.Duration // how long stale data is served during revalidation
PollBase time.Duration // base polling interval when focused (0 = no auto-poll)
PollBg time.Duration // background polling interval when blurred
PollMax time.Duration // max interval after consecutive misses
}
PoolConfig configures a Pool's timing behavior.
type PoolEvent ¶
type PoolEvent struct {
Timestamp time.Time
PoolKey string
EventType PoolEventType
Duration time.Duration
DataSize int
Detail string // optional context (error message, etc.)
}
PoolEvent records a single pool lifecycle event.
type PoolEventType ¶
type PoolEventType int
PoolEventType classifies pool lifecycle events.
const ( FetchStart PoolEventType = iota FetchComplete FetchError CacheHit CacheMiss CacheSeeded PoolInvalidated )
type PoolMetrics ¶
type PoolMetrics struct {
// contains filtered or unexported fields
}
PoolMetrics collects pool fetch telemetry for status bar display.
func NewPoolMetrics ¶
func NewPoolMetrics() *PoolMetrics
NewPoolMetrics creates an empty metrics collector.
func (*PoolMetrics) Apdex ¶
func (m *PoolMetrics) Apdex() float64
Apdex returns the navigation quality score (0.0-1.0). Fresh = satisfied (1.0), Stale = tolerating (0.5), Empty = frustrated (0.0).
func (*PoolMetrics) PoolStatsList ¶
func (m *PoolMetrics) PoolStatsList() []PoolStatus
PoolStatsList returns live status from all registered pools. Copies the reporter slice under lock, then invokes reporters without holding the metrics lock to avoid lock-order inversion with pool mutexes.
func (*PoolMetrics) RecentEvents ¶ added in v0.3.0
func (m *PoolMetrics) RecentEvents(n int) []PoolEvent
RecentEvents returns a copy of the last n events from the ring buffer.
func (*PoolMetrics) Record ¶
func (m *PoolMetrics) Record(e PoolEvent)
Record adds a pool event to the ring buffer and updates stats.
func (*PoolMetrics) RecordNavigation ¶
func (m *PoolMetrics) RecordNavigation(e NavigationEvent)
RecordNavigation logs a view navigation with data quality.
func (*PoolMetrics) RegisterPool ¶
func (m *PoolMetrics) RegisterPool(key string, reporter func() PoolStatus)
RegisterPool adds a live status reporter for a pool.
func (*PoolMetrics) Summary ¶
func (m *PoolMetrics) Summary() MetricsSummary
Summary returns aggregate metrics for status bar display.
func (*PoolMetrics) UnregisterPool ¶
func (m *PoolMetrics) UnregisterPool(key string)
UnregisterPool removes a pool's status reporter.
type PoolStatus ¶
type PoolStatus struct {
Key string
State SnapshotState
Fetching bool
FetchedAt time.Time
CachedFetchedAt time.Time // real FetchedAt from disk cache (zero after first live fetch)
PollInterval time.Duration
HitCount int
MissCount int
FetchCount int
ErrorCount int
AvgLatency time.Duration
}
PoolStatus is a live status snapshot from a registered pool.
type PoolUpdatedMsg ¶
type PoolUpdatedMsg struct {
Key string
}
PoolUpdatedMsg is sent when a pool's snapshot changes. Views match on Key to identify which pool updated, then read typed data via the pool's Get() method.
type Pooler ¶
type Pooler interface {
Invalidate()
Clear()
SetTerminalFocused(focused bool)
}
Pooler is the non-generic interface for pool lifecycle management. Realm uses this to manage pools of different types uniformly.
type ProjectInfo ¶
type ProjectInfo struct {
ID int64
Name string
Description string
Purpose string
Bookmarked bool
AccountID string
AccountName string
Dock []DockToolInfo
}
ProjectInfo wraps a project with account attribution for multi-account pools. basecamp.Project doesn't carry which account it belongs to, so the Hub's Projects() FetchFunc annotates each project during fan-out.
type ReadTracker ¶ added in v0.3.0
type ReadTracker struct {
// contains filtered or unexported fields
}
ReadTracker tracks the last-read line ID per campfire room.
func NewReadTracker ¶ added in v0.3.0
func NewReadTracker(cacheDir string) *ReadTracker
NewReadTracker creates a ReadTracker backed by the given cache directory.
func (*ReadTracker) Flush ¶ added in v0.3.0
func (rt *ReadTracker) Flush() error
Flush merges local state with disk (max per room) and writes atomically.
func (*ReadTracker) LastRead ¶ added in v0.3.0
func (rt *ReadTracker) LastRead(room RoomID) int64
LastRead returns the last-read line ID for a room.
func (*ReadTracker) LoadFromDisk ¶ added in v0.3.0
func (rt *ReadTracker) LoadFromDisk()
LoadFromDisk reads persisted read positions into memory.
func (*ReadTracker) MarkRead ¶ added in v0.3.0
func (rt *ReadTracker) MarkRead(room RoomID, lineID int64)
MarkRead records a line as read. Keeps the max of local and lineID.
func (*ReadTracker) UnreadCount ¶ added in v0.3.0
func (rt *ReadTracker) UnreadCount(room RoomID, lines []CampfireLineInfo) int
UnreadCount returns the number of lines after the last-read position.
type Realm ¶
type Realm struct {
// contains filtered or unexported fields
}
Realm manages a group of pools with a shared lifecycle. Pools belong to realms. Realm teardown cancels all owned pools' in-flight fetches and clears their data.
Three realms mirror the mobile architecture:
- Global: app lifetime (identity, account list)
- Account: active account session (projects, hey, assignments)
- Project: active project context (todos, campfire, messages)
func (*Realm) Context ¶
Context returns the realm's context. Canceled on teardown. Pass this to pool fetch functions so they abort when the realm dies.
func (*Realm) Invalidate ¶
func (r *Realm) Invalidate()
Invalidate marks all pools in this realm as stale.
func (*Realm) Register ¶
Register adds a pool to this realm for lifecycle management. If the terminal is currently blurred, the pool inherits that state.
func (*Realm) SetTerminalFocused ¶ added in v0.3.0
SetTerminalFocused persists the state and fans out to all pools in this realm. Newly registered pools will also inherit this state.
type RecordingWithBoosts ¶
type RecordingWithBoosts interface {
GetBoosts() BoostSummary
}
RecordingWithBoosts extends recording info types to include boost metadata.
type RiverLine ¶ added in v0.3.0
type RiverLine struct {
CampfireLineInfo
Room RoomID
RoomName string
}
RiverLine is a campfire line annotated with room context for the River view.
type RoomID ¶ added in v0.3.0
RoomID uniquely identifies a campfire room across accounts and projects.
type RoomOverride ¶ added in v0.3.0
type RoomOverride struct {
Includes map[string]RoomOverrideEntry `json:"includes,omitempty"`
Excludes map[string]RoomOverrideEntry `json:"excludes,omitempty"`
}
RoomOverride stores user room selection preferences.
type RoomOverrideEntry ¶ added in v0.3.0
type RoomOverrideEntry struct {
Name string `json:"name,omitempty"`
UpdatedAt string `json:"updated_at"`
}
RoomOverrideEntry records when a room was added/excluded.
type RoomStore ¶ added in v0.3.0
type RoomStore struct {
// contains filtered or unexported fields
}
RoomStore persists room selection overrides to disk.
func NewRoomStore ¶ added in v0.3.0
NewRoomStore creates a RoomStore backed by the given cache directory.
func (*RoomStore) EffectiveRooms ¶ added in v0.3.0
func (rs *RoomStore) EffectiveRooms(discovered []BonfireRoomConfig, o RoomOverride) []BonfireRoomConfig
EffectiveRooms filters discovered rooms through overrides. Includes override: only those rooms. Excludes override: remove those rooms. If no overrides, returns all discovered rooms.
type ScheduleEntryInfo ¶
type ScheduleEntryInfo struct {
ID int64
Summary string
StartsAt string
EndsAt string
AllDay bool
Participants []string
}
ScheduleEntryInfo is a lightweight representation of a schedule entry.
type SearchResultInfo ¶
type SearchResultInfo struct {
ID int64
Title string
Excerpt string
Type string // "todo", "message", "document", etc.
Project string
ProjectID int64
Account string // account name (populated in multi-account mode)
AccountID string // account ID for navigation
CreatedAt string
CreatedAtTS int64 // unix timestamp for sorting
}
SearchResultInfo represents a single search result.
type Segment ¶ added in v0.3.0
type Segment struct {
RoomID RoomID
RoomName string
Lines []RiverLine
StartTime time.Time
EndTime time.Time
Sealed bool // true when segment is complete (gap or room switch)
}
Segment is a group of related campfire lines from one room.
type Segmenter ¶ added in v0.3.0
type Segmenter struct {
// contains filtered or unexported fields
}
Segmenter groups incoming campfire lines into conversation segments.
func NewSegmenter ¶ added in v0.3.0
func NewSegmenter(config SegmenterConfig) *Segmenter
NewSegmenter creates a Segmenter with the given config.
func (*Segmenter) IngestSnapshot ¶ added in v0.3.0
func (s *Segmenter) IngestSnapshot(room RoomID, roomName string, lines []CampfireLineInfo)
IngestSnapshot processes a full snapshot of lines from one room. Only lines with IDs newer than lastSeenID are processed (dedup).
func (*Segmenter) PruneRoom ¶ added in v0.3.0
PruneRoom removes all segments and tracking state for a room. Called when a room falls out of the active Bonfire set.
func (*Segmenter) SealStale ¶ added in v0.3.0
SealStale seals any open segments where the last message is older than maxAge.
func (*Segmenter) SegmentCount ¶ added in v0.3.0
SegmentCount returns the total number of segments.
type SegmenterConfig ¶ added in v0.3.0
type SegmenterConfig struct {
// SealAfter is the max silence before a segment is sealed.
// 0 means use adaptive gap threshold.
SealAfter time.Duration
// MinGap is the minimum adaptive gap threshold.
MinGap time.Duration // default 1 min
// MaxGap is the maximum adaptive gap threshold.
MaxGap time.Duration // default 10 min
// AnnouncementMinChars for standalone announcement detection.
AnnouncementMinChars int // default 200
// ContinuityThreshold is the score below which a new segment starts.
ContinuityThreshold float64 // default 0.35
}
SegmenterConfig tunes the conversation detection heuristics.
func DefaultSegmenterConfig ¶ added in v0.3.0
func DefaultSegmenterConfig() SegmenterConfig
DefaultSegmenterConfig returns sensible defaults.
type Snapshot ¶
type Snapshot[T any] struct { Data T State SnapshotState Err error FetchedAt time.Time HasData bool // distinguishes zero-value T from "never fetched" }
Snapshot holds typed data along with its fetch state. Every pool exposes Snapshot[T] — consumers never see any.
type SnapshotState ¶
type SnapshotState int
SnapshotState represents the freshness state of a data snapshot. Go equivalent of iOS DataUpdate<T> / Android Data<T>.
const ( StateEmpty SnapshotState = iota // no data yet StateFresh // data within TTL StateStale // data past TTL, usable for SWR StateLoading // fetch in progress (may have stale data) StateError // fetch failed (may have stale data) )
func (SnapshotState) String ¶
func (s SnapshotState) String() string
type TimelineEventInfo ¶
type TimelineEventInfo struct {
ID int64
RecordingID int64 // parent recording ID for navigation/detail
CreatedAt string // formatted time
CreatedAtTS int64 // unix timestamp for sorting
Kind string // "todo_completed", "message_created", etc.
Action string // "completed", "created", "commented on"
Target string // "Todo", "Message", "Document"
Title string
SummaryExcerpt string // first ~100 chars of body
Creator string
Project string
ProjectID int64
Account string
AccountID string
}
TimelineEventInfo is a lightweight representation of a timeline event.
type TodoCompleteMutation ¶
type TodoCompleteMutation struct {
TodoID int64
Completed bool // target state (true = complete, false = uncomplete)
Client *basecamp.AccountClient
ProjectID int64
}
TodoCompleteMutation toggles a todo's completion state. Implements Mutation[[]TodoInfo] for use with MutatingPool.
func (TodoCompleteMutation) ApplyLocally ¶
func (m TodoCompleteMutation) ApplyLocally(todos []TodoInfo) []TodoInfo
ApplyLocally toggles the todo's Completed field in the local data.
func (TodoCompleteMutation) ApplyRemotely ¶
func (m TodoCompleteMutation) ApplyRemotely(ctx context.Context) error
ApplyRemotely calls the SDK to complete or uncomplete the todo.
func (TodoCompleteMutation) IsReflectedIn ¶
func (m TodoCompleteMutation) IsReflectedIn(todos []TodoInfo) bool
IsReflectedIn returns true when the remote data shows the todo in the target completion state.
type TodoCreateMutation ¶
type TodoCreateMutation struct {
Content string
TodolistID int64
ProjectID int64
Client *basecamp.AccountClient
// contains filtered or unexported fields
}
TodoCreateMutation optimistically prepends a new todo to a todolist. Implements Mutation[[]TodoInfo] for use with MutatingPool.
The createdID field uses atomic.Int64 because ApplyRemotely runs in the mutation's tea.Cmd goroutine while a concurrent background fetch can trigger reconcile → IsReflectedIn under the pool lock in a different goroutine. Pointer receiver required so the atomic field is shared.
func (*TodoCreateMutation) ApplyLocally ¶
func (m *TodoCreateMutation) ApplyLocally(todos []TodoInfo) []TodoInfo
ApplyLocally prepends a placeholder todo with a temporary negative ID.
func (*TodoCreateMutation) ApplyRemotely ¶
func (m *TodoCreateMutation) ApplyRemotely(ctx context.Context) error
ApplyRemotely calls the SDK to create the todo.
func (*TodoCreateMutation) IsReflectedIn ¶
func (m *TodoCreateMutation) IsReflectedIn(todos []TodoInfo) bool
IsReflectedIn returns true when the created todo appears in the remote data. Returns false if ApplyRemotely hasn't completed yet (createdID == 0).