data

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 11, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EndsWithQuestion added in v0.3.0

func EndsWithQuestion(text string) bool

EndsWithQuestion returns true if the text ends with a question mark.

func ExtractMentions added in v0.3.0

func ExtractMentions(html string) []string

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

func JaccardSimilarity(a, b []string) float64

JaccardSimilarity computes the Jaccard similarity coefficient between two token sets.

func RealmPool

func RealmPool[P Pooler](r *Realm, key string, create func() P) P

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

func RiverText(html string) string

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.

func StripTags added in v0.3.0

func StripTags(s string) string

StripTags removes HTML tags from a string, replacing them with spaces.

func Tokenize added in v0.3.0

func Tokenize(text string) []string

Tokenize splits text into lowercase word tokens, removing punctuation and stopwords.

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

type AccountInfo struct {
	ID   string
	Name string
}

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 FetchFunc

type FetchFunc[T any] func(ctx context.Context) (T, error)

FetchFunc retrieves data for a pool.

type ForwardInfo

type ForwardInfo struct {
	ID      int64
	Subject string
	From    string
}

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) Account

func (h *Hub) Account() *Realm

Account returns the active account realm, or nil.

func (*Hub) AccountContext

func (h *Hub) AccountContext() context.Context

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

func (h *Hub) ClearCardDueOn(ctx context.Context, accountID string, projectID, cardID int64) error

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

func (h *Hub) ClearTodoDueOn(ctx context.Context, accountID string, projectID, todoID int64) error

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

func (h *Hub) CompleteTodo(ctx context.Context, accountID string, projectID, todoID int64) error

CompleteTodo marks a todo as completed. Uses explicit accountID for cross-account mutations from aggregate views (Assignments, Hey).

func (*Hub) CompletedTodos

func (h *Hub) CompletedTodos(projectID, todolistID int64) *Pool[[]TodoInfo]

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

func (h *Hub) DeleteBoost(ctx context.Context, projectID, boostID int64) error

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

func (h *Hub) EnsureAccount(accountID string) *Realm

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

func (h *Hub) EnsureProject(projectID int64) *Realm

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) Global

func (h *Hub) Global() *Realm

Global returns the app-lifetime realm.

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) LeaveProject

func (h *Hub) LeaveProject()

LeaveProject tears down the project realm.

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

func (h *Hub) PinMessage(ctx context.Context, accountID string, projectID, messageID int64) error

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) Project

func (h *Hub) Project() *Realm

Project returns the active project realm, or nil.

func (*Hub) ProjectContext

func (h *Hub) ProjectContext() context.Context

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

func (h *Hub) SetRecentProjects(fn func(accountID string) []int64)

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

func (h *Hub) SetRoomStore(rs *RoomStore)

SetRoomStore configures the RoomStore used to filter BonfireRooms/BonfireDigest.

func (*Hub) SetTerminalFocused added in v0.3.0

func (h *Hub) SetTerminalFocused(focused bool)

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) Subscribe

func (h *Hub) Subscribe(ctx context.Context, accountID string, projectID, recordingID int64) error

Subscribe subscribes the current user to a recording.

func (*Hub) SwitchAccount

func (h *Hub) SwitchAccount(accountID string)

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

func (h *Hub) TrashComment(ctx context.Context, accountID string, projectID, commentID int64) error

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

func (h *Hub) UncompleteTodo(ctx context.Context, accountID string, projectID, todoID int64) error

UncompleteTodo reopens a completed todo.

func (*Hub) UnpinMessage

func (h *Hub) UnpinMessage(ctx context.Context, accountID string, projectID, messageID int64) error

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.

func (*Hub) UpdateTodolist

func (h *Hub) UpdateTodolist(ctx context.Context, accountID string, projectID, todolistID int64, name string) error

UpdateTodolist renames a todolist.

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

func (kp *KeyedPool[K, T]) Get(key K) *Pool[T]

Get returns the Pool for the given key, creating one if it doesn't exist.

func (*KeyedPool[K, T]) Has

func (kp *KeyedPool[K, T]) Has(key K) bool

Has returns true if a pool exists for the given key.

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

func (kp *KeyedPool[K, T]) SetTerminalFocused(focused bool)

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

type MutatingPool[T any] struct {
	*Pool[T]
	// contains filtered or unexported fields
}

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

func (mp *MutatingPool[T]) Apply(ctx context.Context, mutation Mutation[T]) tea.Cmd

Apply executes an optimistic mutation:

  1. Applies locally to the snapshot (immediate, synchronous)
  2. 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

type MutationErrorMsg struct {
	Key string
	Err error
}

MutationErrorMsg is sent when a mutation's remote apply fails.

type NavigationEvent struct {
	Timestamp time.Time
	ViewTitle string
	PoolKey   string
	Quality   float64 // 1.0=Fresh, 0.5=Stale, 0.0=Empty
}

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

type PollMsg struct {
	Tag string
	Gen uint64
}

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]) Clear

func (p *Pool[T]) Clear()

Clear resets the pool to its initial empty state.

func (*Pool[T]) Fetch

func (p *Pool[T]) Fetch(ctx context.Context) tea.Cmd

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

func (p *Pool[T]) FetchIfStale(ctx context.Context) tea.Cmd

FetchIfStale returns a Fetch Cmd if data is stale or empty, nil if fresh.

func (*Pool[T]) Get

func (p *Pool[T]) Get() Snapshot[T]

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]) Key

func (p *Pool[T]) Key() string

Key returns the pool's identifier.

func (*Pool[T]) PollInterval

func (p *Pool[T]) PollInterval() time.Duration

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

func (p *Pool[T]) SetCache(c *PoolCache)

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

func (p *Pool[T]) SetFocused(focused bool)

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

func (p *Pool[T]) SetPushMode(enabled bool)

SetPushMode enables/disables push mode (SSE connected). In push mode, poll intervals are extended significantly.

func (*Pool[T]) SetTerminalFocused added in v0.3.0

func (p *Pool[T]) SetTerminalFocused(focused bool)

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.

func (*Pool[T]) Version

func (p *Pool[T]) Version() uint64

Version returns the current data version.

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

func NewPoolCache(dir string) *PoolCache

NewPoolCache creates a cache backed by the given directory. Returns nil if dir is empty.

func (*PoolCache) Load added in v0.3.0

func (c *PoolCache) Load(key string, dst any) (time.Time, bool)

Load reads cached data into dst. Returns fetchedAt and whether cache exists.

func (*PoolCache) Save added in v0.3.0

func (c *PoolCache) Save(key string, data any, fetchedAt time.Time) error

Save writes pool data to disk as JSON.

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 PoolStats

type PoolStats struct {
	FetchCount  int
	ErrorCount  int
	TotalTimeMs int64
	LastFetch   time.Time
}

PoolStats holds aggregate statistics for a single pool.

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 NewRealm

func NewRealm(name string, parent context.Context) *Realm

NewRealm creates a realm with a cancellable context derived from parent.

func (*Realm) Context

func (r *Realm) Context() context.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) Name

func (r *Realm) Name() string

Name returns the realm's identifier.

func (*Realm) Pool

func (r *Realm) Pool(key string) Pooler

Pool returns a registered pool by key, or nil if not found.

func (*Realm) Register

func (r *Realm) Register(key string, p Pooler)

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

func (r *Realm) SetTerminalFocused(focused bool)

SetTerminalFocused persists the state and fans out to all pools in this realm. Newly registered pools will also inherit this state.

func (*Realm) Teardown

func (r *Realm) Teardown()

Teardown cancels the realm's context and clears all pools. After teardown, the realm should not be reused.

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

type RoomID struct {
	AccountID  string
	ProjectID  int64
	CampfireID int64
}

RoomID uniquely identifies a campfire room across accounts and projects.

func (RoomID) Color added in v0.3.0

func (r RoomID) Color(paletteSize int) int

Color returns a deterministic color index from the room key. Uses FNV-1a so the same room always gets the same color.

func (RoomID) Key added in v0.3.0

func (r RoomID) Key() string

Key returns a stable string key for maps and cache filenames.

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

func NewRoomStore(cacheDir string) *RoomStore

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.

func (*RoomStore) Load added in v0.3.0

func (rs *RoomStore) Load(ctx context.Context) (RoomOverride, error)

Load reads the room override file with file locking.

func (*RoomStore) Save added in v0.3.0

func (rs *RoomStore) Save(ctx context.Context, o RoomOverride) error

Save writes room overrides with read-merge-write and atomic rename.

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

func (s *Segmenter) PruneRoom(roomKey string)

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

func (s *Segmenter) SealStale(now time.Time, maxAge time.Duration)

SealStale seals any open segments where the last message is older than maxAge.

func (*Segmenter) SegmentCount added in v0.3.0

func (s *Segmenter) SegmentCount() int

SegmentCount returns the total number of segments.

func (*Segmenter) Segments added in v0.3.0

func (s *Segmenter) Segments() []*Segment

Segments returns all segments, ordered for River display: sealed segments sorted by EndTime, then open segments with most recently active last.

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.

func (Snapshot[T]) Fresh

func (s Snapshot[T]) Fresh() bool

Fresh returns true if the snapshot has data in the Fresh state.

func (Snapshot[T]) Loading

func (s Snapshot[T]) Loading() bool

Loading returns true if a fetch is in progress.

func (Snapshot[T]) Usable

func (s Snapshot[T]) Usable() bool

Usable returns true if the snapshot has data, regardless of freshness.

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).

type TodoInfo

type TodoInfo struct {
	ID          int64
	Content     string
	Description string
	Completed   bool
	DueOn       string
	Assignees   []string // names
	Position    int
	BoostEmbed  // embedded boost support
}

TodoInfo is a lightweight representation of a todo for the view.

type TodolistInfo

type TodolistInfo struct {
	ID             int64
	Title          string
	CompletedRatio string
	TodosURL       string
}

TodolistInfo is a lightweight representation of a todolist for the view.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL